NVMe Linux驱动系列一:host端<11>

发布时间:2026/10/27 9:24:21

NVMe Linux驱动系列一:host端<11> nvme_init_subnqn/* * 函数名称: nvme_init_subnqn * 说明: 初始化NVMe子系统的NQNNVMe Qualified Name。如果存在有效的设备子系统NQN则使用之 * 否则生成一个伪造的NQN。NQN用于标识NVMe设备子系统并在NVMe命名空间中具有重要作用。 * 参数: * - subsys: 指向NVMe子系统结构的指针用于存储NQN信息。 * - ctrl: 指向NVMe控制器结构的指针包含控制器相关信息。 * - id: 指向NVMe设备ID结构的指针包含设备ID相关信息。 * 返回值: 无 */staticvoidnvme_init_subnqn(structnvme_subsystem*subsys,structnvme_ctrl*ctrl,structnvme_id_ctrl*id){size_tnqnlen;intoff;// 检查是否忽略设备子系统NQNif(!(ctrl-quirksNVME_QUIRK_IGNORE_DEV_SUBNQN)){nqnlenstrnlen(id-subnqn,NVMF_NQN_SIZE);if(nqnlen0nqnlenNVMF_NQN_SIZE){// 如果存在有效的设备子系统NQN则使用它strscpy(subsys-subnqn,id-subnqn,NVMF_NQN_SIZE);return;}// 如果版本号支持且存在问题则输出警告信息if(ctrl-vsNVME_VS(1,2,1))dev_warn(ctrl-device,missing or invalid SUBNQN field.\n);}/* * 生成一个类似于NVMe Base Specification 2.0第4.5节中的NQN但由于历史原因 * 格式略有不同并且无法更改。 */offsnprintf(subsys-subnqn,NVMF_NQN_SIZE,nqn.2014.08.org.nvmexpress:%04x%04x,le16_to_cpu(id-vid),le16_to_cpu(id-ssvid));memcpy(subsys-subnqnoff,id-sn,sizeof(id-sn));offsizeof(id-sn);memcpy(subsys-subnqnoff,id-mn,sizeof(id-mn));offsizeof(id-mn);memset(subsys-subnqnoff,0,sizeof(subsys-subnqn)-off);}/* * 总结: 这个函数用于初始化NVMe子系统的NQNNVMe Qualified Name。NQN是一个用于唯一标识 * NVMe设备子系统的字符串具有重要作用。函数首先检查是否应该忽略设备子系统NQN。如果不忽略 * 则尝试使用设备ID中的子系统NQN。如果不存在有效的子系统NQN且设备版本支持将输出警告信息。 * 然后函数根据指定的格式生成一个伪造的NQN并将其存储在NVMe子系统结构中。 */nvme_release_subsystem/* * 函数名称: nvme_release_subsystem * 说明: 释放NVMe子系统相关资源。该函数用于释放NVMe子系统结构及其关联的资源包括 * 实例标识和内存分配。 * 参数: * - dev: 指向设备结构的指针通常表示与NVMe子系统相关的设备。 * 返回值: 无 */staticvoidnvme_release_subsystem(structdevice*dev){// 将dev指针转换为nvme_subsystem结构指针structnvme_subsystem*subsyscontainer_of(dev,structnvme_subsystem,dev);// 如果子系统的实例标识为非负值释放实例标识if(subsys-instance0)ida_free(nvme_instance_ida,subsys-instance);// 释放子系统结构的内存kfree(subsys);}/* * 总结: 这个函数用于释放NVMe子系统相关的资源。在释放过程中函数首先根据设备指针获取 * 对应的nvme_subsystem结构指针。然后如果子系统的实例标识instance是非负值 * 则使用ida_free函数释放实例标识以便重新使用。最后函数释放nvme_subsystem结构的内存。 */nvme_destroy_subsystem/* * 函数名称: nvme_destroy_subsystem * 说明: 销毁NVMe子系统。该函数用于销毁NVMe子系统结构以及相关资源包括从子系统列表中删除、 * 销毁命名空间标识分配器、从系统中删除设备、释放设备引用。 * 参数: * - ref: 指向kref结构的指针通常表示与NVMe子系统关联的引用计数。 * 返回值: 无 */staticvoidnvme_destroy_subsystem(structkref*ref){// 将ref指针转换为nvme_subsystem结构指针structnvme_subsystem*subsyscontainer_of(ref,structnvme_subsystem,ref);// 锁定子系统列表以执行操作mutex_lock(nvme_subsystems_lock);// 从子系统列表中删除子系统条目list_del(subsys-entry);// 解锁子系统列表mutex_unlock(nvme_subsystems_lock);// 销毁命名空间标识分配器ida_destroy(subsys-ns_ida);// 从系统中删除设备device_del(subsys-dev);// 释放设备引用put_device(subsys-dev);}/* * 总结: 这个函数用于销毁NVMe子系统以及相关资源。在销毁过程中函数首先根据kref指针 * 获取对应的nvme_subsystem结构指针。然后函数在操作前锁定子系统列表执行以下操作 * 从子系统列表中删除当前子系统的条目解锁子系统列表。接着函数销毁子系统的命名空间 * 标识分配器、从系统中删除子系统设备并释放子系统设备引用。 */nvme_put_subsystem/* * 函数名称: nvme_put_subsystem * 说明: 减少NVMe子系统的引用计数并可能销毁子系统。该函数用于减少NVMe子系统的引用计数 * 当引用计数减少到零时可能会调用nvme_destroy_subsystem函数来销毁子系统及其资源。 * 参数: * - subsys: 指向NVMe子系统结构的指针表示要减少引用计数的子系统。 * 返回值: 无 */staticvoidnvme_put_subsystem(structnvme_subsystem*subsys){// 减少子系统的引用计数并可能触发销毁子系统kref_put(subsys-ref,nvme_destroy_subsystem);}/* * 总结: 这个函数用于减少NVMe子系统的引用计数并在引用计数减少到零时可能调用 * nvme_destroy_subsystem函数来销毁子系统及其资源。通过调用kref_put函数函数将 * 传入的子系统结构的引用计数减少并在引用计数为零时触发销毁子系统的操作。 */__nvme_find_get_subsystem/* * 函数名称: __nvme_find_get_subsystem * 说明: 查找并获取NVMe子系统。该函数用于根据给定的子系统NQN查找对应的NVMe子系统 * 并在找到时增加其引用计数以确保子系统在使用期间保持有效。 * 参数: * - subsysnqn: 指向要查找的子系统NQN的字符串。 * 返回值: * - 如果找到了匹配的NVMe子系统返回对应的子系统结构指针且其引用计数已增加。 * - 如果找不到匹配的NVMe子系统或者查找的是发现子系统返回NULL。 */staticstructnvme_subsystem*__nvme_find_get_subsystem(constchar*subsysnqn){structnvme_subsystem*subsys;lockdep_assert_held(nvme_subsystems_lock);/* * 对于发现子系统不执行匹配。这确保了每个发现控制器都与唯一的子系统绑定 * 避免了仅在存在单个唯一子系统时才为真的控制器值验证问题。可能存在多个 * 完全独立的实体提供发现控制器。 */if(!strcmp(subsysnqn,NVME_DISC_SUBSYS_NAME))returnNULL;// 遍历子系统列表查找匹配的子系统list_for_each_entry(subsys,nvme_subsystems,entry){if(strcmp(subsys-subnqn,subsysnqn))continue;if(!kref_get_unless_zero(subsys-ref))continue;returnsubsys;}returnNULL;}/* * 总结: 这个函数用于查找并获取NVMe子系统。函数根据给定的子系统NQN在子系统列表中 * 查找匹配的子系统并在找到时增加其引用计数以确保子系统在使用期间保持有效。 * 函数首先检查是否查找的是发现子系统如果是直接返回NULL。然后函数遍历子系统列表 * 检查每个子系统的子系统NQN是否与给定的子系统NQN匹配如果匹配增加子系统的引用计数 * 并返回对应的子系统结构指针。如果找不到匹配的子系统或者查找的是发现子系统返回NULL。 */nvme_discovery_ctrl/* * 函数名称: nvme_discovery_ctrl * 说明: 检查NVMe控制器是否是发现控制器。该函数用于检查给定的NVMe控制器是否是一个发现控制器 * 通过判断控制器的选项指针和选项中是否存在发现NQN来确定。 * 参数: * - ctrl: 指向NVMe控制器结构的指针表示要检查的控制器。 * 返回值: * - 如果给定的NVMe控制器是发现控制器且其选项中存在发现NQN返回true否则返回false。 */staticinlineboolnvme_discovery_ctrl(structnvme_ctrl*ctrl){returnctrl-optsctrl-opts-discovery_nqn;}/* * 总结: 这个函数用于检查给定的NVMe控制器是否是一个发现控制器。函数通过判断控制器的选项指针 * 是否存在以及选项中是否存在发现NQN来确定控制器是否是发现控制器。如果选项指针存在且选项中存在 * 发现NQN则函数返回true表示给定的NVMe控制器是发现控制器否则返回false。 */nvme_validate_cntlid/* * 函数名称: nvme_validate_cntlid * 说明: 验证NVMe控制器的控制器标识是否有效。该函数用于验证给定的NVMe控制器的控制器标识cntlid * 是否有效以及是否满足子系统的控制器约束。 * 参数: * - subsys: 指向NVMe子系统结构的指针表示与控制器相关的子系统。 * - ctrl: 指向NVMe控制器结构的指针表示要验证的控制器。 * - id: 指向NVMe设备ID结构的指针包含设备ID相关信息。 * 返回值: * - 如果控制器的控制器标识有效且满足子系统的控制器约束返回true。 * - 如果控制器的控制器标识重复返回false并输出错误信息。 * - 如果子系统不支持多个控制器返回false并输出错误信息。 */staticboolnvme_validate_cntlid(structnvme_subsystem*subsys,structnvme_ctrl*ctrl,structnvme_id_ctrl*id){structnvme_ctrl*tmp;lockdep_assert_held(nvme_subsystems_lock);// 遍历子系统中的控制器列表进行验证list_for_each_entry(tmp,subsys-ctrls,subsys_entry){// 跳过终端状态的控制器if(nvme_state_terminal(tmp))continue;// 检查控制器的控制器标识是否重复if(tmp-cntlidctrl-cntlid){dev_err(ctrl-device,Duplicate cntlid %u with %s, subsys %s, rejecting\n,ctrl-cntlid,dev_name(tmp-device),subsys-subnqn);returnfalse;}// 如果当前控制器支持多个控制器或者是发现控制器继续下一个控制器验证if((id-cmicNVME_CTRL_CMIC_MULTI_CTRL)||nvme_discovery_ctrl(ctrl))continue;// 如果子系统不支持多个控制器输出错误信息并返回falsedev_err(ctrl-device,Subsystem does not support multiple controllers\n);returnfalse;}// 控制器标识验证通过返回truereturntrue;}/* * 总结: 这个函数用于验证NVMe控制器的控制器标识是否有效并检查是否满足子系统的控制器约束。 * 函数首先遍历子系统中的控制器列表对每个控制器进行验证。如果控制器的控制器标识重复 * 则返回false并输出错误信息。如果当前控制器支持多个控制器或者是发现控制器继续下一个控制器验证。 * 如果子系统不支持多个控制器输出错误信息并返回false。如果所有验证都通过则返回true。 */nvme_init_subsystem/* * 函数名称: nvme_init_subsystem * 说明: 初始化NVMe子系统。该函数用于初始化NVMe子系统为NVMe控制器创建一个关联的子系统 * 并进行一系列初始化操作包括创建设备、添加到子系统列表、创建sysfs链接等。 * 参数: * - ctrl: 指向NVMe控制器结构的指针表示要初始化子系统的控制器。 * - id: 指向NVMe设备ID结构的指针包含设备ID相关信息。 * 返回值: * - 如果成功初始化子系统返回0。 * - 如果发生错误返回负数错误码。 */staticintnvme_init_subsystem(structnvme_ctrl*ctrl,structnvme_id_ctrl*id){structnvme_subsystem*subsys,*found;intret;// 分配并初始化子系统结构subsyskzalloc(sizeof(*subsys),GFP_KERNEL);if(!subsys)return-ENOMEM;subsys-instance-1;mutex_init(subsys-lock);kref_init(subsys-ref);INIT_LIST_HEAD(subsys-ctrls);INIT_LIST_HEAD(subsys-nsheads);// 初始化子系统的NQNnvme_init_subnqn(subsys,ctrl,id);memcpy(subsys-serial,id-sn,sizeof(subsys-serial));memcpy(subsys-model,id-mn,sizeof(subsys-model));subsys-vendor_idle16_to_cpu(id-vid);subsys-cmicid-cmic;/* 版本在1.4之前的控制器可能不一定报告有效的类型 */if(id-cntrltypeNVME_CTRL_DISC||!strcmp(subsys-subnqn,NVME_DISC_SUBSYS_NAME))subsys-subtypeNVME_NQN_DISC;elsesubsys-subtypeNVME_NQN_NVME;// 如果控制器是发现控制器且子系统类型不是发现类型则报错if(nvme_discovery_ctrl(ctrl)subsys-subtype!NVME_NQN_DISC){dev_err(ctrl-device,Subsystem %s is not a discovery controller,subsys-subnqn);kfree(subsys);return-EINVAL;}subsys-awupfle16_to_cpu(id-awupf);nvme_mpath_default_iopolicy(subsys);// 初始化子系统设备subsys-dev.classnvme_subsys_class;subsys-dev.releasenvme_release_subsystem;subsys-dev.groupsnvme_subsys_attrs_groups;dev_set_name(subsys-dev,nvme-subsys%d,ctrl-instance);device_initialize(subsys-dev);mutex_lock(nvme_subsystems_lock);// 查找已存在的子系统或者添加新子系统到列表found__nvme_find_get_subsystem(subsys-subnqn);if(found){put_device(subsys-dev);subsysfound;// 验证控制器的控制器标识if(!nvme_validate_cntlid(subsys,ctrl,id)){ret-EINVAL;gotoout_put_subsystem;}}else{// 添加新的子系统设备retdevice_add(subsys-dev);if(ret){dev_err(ctrl-device,failed to register subsystem device.\n);put_device(subsys-dev);gotoout_unlock;}// 初始化命名空间标识分配器ida_init(subsys-ns_ida);// 将子系统添加到子系统列表list_add_tail(subsys-entry,nvme_subsystems);}// 创建sysfs链接retsysfs_create_link(subsys-dev.kobj,ctrl-device-kobj,dev_name(ctrl-device));if(ret){dev_err(ctrl-device,failed to create sysfs link from subsystem.\n);gotoout_put_subsystem;}// 如果是新的子系统设置实例标识并将控制器与子系统关联if(!found)subsys-instancectrl-instance;ctrl-subsyssubsys;// 将控制器添加到子系统的控制器列表中list_add_tail(ctrl-subsys_entry,subsys-ctrls);mutex_unlock(nvme_subsystems_lock);return0;out_put_subsystem:// 出错时减少子系统的引用计数nvme_put_subsystem(subsys);out_unlock:mutex_unlock(nvme_subsystems_lock);returnret;}/* * 总结: 这个函数用于初始化NVMe子系统。函数为给定的NVMe控制器创建一个关联的子系统 * 并进行一系列初始化操作包括创建设备、添加到子系统列表、创建sysfs链接等。 * 函数首先分配并初始化子系统结构然后初始化子系统的各个字段。接着函数判断是否已存在 * 匹配的子系统如果已存在则验证控制器的控制器标识如果不满足要求返回错误。 * 如果不存在匹配的子系统函数将新的子系统设备添加到系统中并进行必要的初始化操作。 * 最后函数创建sysfs链接设置实例标识将控制器与子系统关联并将控制器添加到子系统的 * 控制器列表中。 */nvme_get_log/* * 函数名称: nvme_get_log * 说明: 获取NVMe日志。该函数用于从NVMe控制器获取指定日志页的日志数据。 * 参数: * - ctrl: 指向NVMe控制器结构的指针表示要获取日志的控制器。 * - nsid: 命名空间ID表示要获取日志的命名空间。 * - log_page: 日志页号表示要获取的日志页。 * - lsp: 日志页大小表示要获取的日志页的大小。 * - csi: 日志页的命令集标识表示要获取的日志页的命令集标识。 * - log: 指向存放日志数据的缓冲区的指针。 * - size: 缓冲区的大小表示要获取的日志数据的大小。 * - offset: 日志页的偏移量表示要获取的日志数据的偏移量。 * 返回值: * - 如果成功获取日志数据返回0。 * - 如果发生错误返回负数错误码。 */intnvme_get_log(structnvme_ctrl*ctrl,u32 nsid,u8 log_page,u8 lsp,u8 csi,void*log,size_tsize,u64 offset){structnvme_commandc{};u32 dwlennvme_bytes_to_numd(size);c.get_log_page.opcodenvme_admin_get_log_page;c.get_log_page.nsidcpu_to_le32(nsid);c.get_log_page.lidlog_page;c.get_log_page.lsplsp;c.get_log_page.numdlcpu_to_le16(dwlen((116)-1));c.get_log_page.numducpu_to_le16(dwlen16);c.get_log_page.lpolcpu_to_le32(lower_32_bits(offset));c.get_log_page.lpoucpu_to_le32(upper_32_bits(offset));c.get_log_page.csicsi;returnnvme_submit_sync_cmd(ctrl-admin_q,c,log,size);}/* * 总结: 这个函数用于从NVMe控制器获取指定日志页的日志数据。函数构造一个获取日志页的NVMe命令 * 包括指定的命名空间ID、日志页号、日志页大小、命令集标识、日志数据的缓冲区指针和大小、偏移量等。 * 然后函数通过调用nvme_submit_sync_cmd函数提交同步的NVMe命令以获取日志数据。 * 如果成功获取日志数据函数返回0否则返回负数错误码。 */nvme_get_effects_log/* * 函数名称: nvme_get_effects_log * 说明: 获取NVMe效果日志。该函数用于从NVMe控制器获取指定命令集标识的效果日志数据。 * 参数: * - ctrl: 指向NVMe控制器结构的指针表示要获取效果日志的控制器。 * - csi: 命令集标识表示要获取的效果日志的命令集标识。 * - log: 用于存放效果日志数据的指针的指针函数将更新该指针以指向获取到的日志数据。 * 返回值: * - 如果成功获取效果日志数据返回0。 * - 如果发生错误返回负数错误码。 */staticintnvme_get_effects_log(structnvme_ctrl*ctrl,u8 csi,structnvme_effects_log**log){structnvme_effects_log*celxa_load(ctrl-cels,csi);intret;if(cel)gotoout;celkzalloc(sizeof(*cel),GFP_KERNEL);if(!cel)return-ENOMEM;retnvme_get_log(ctrl,0x00,NVME_LOG_CMD_EFFECTS,0,csi,cel,sizeof(*cel),0);if(ret){kfree(cel);returnret;}xa_store(ctrl-cels,csi,cel,GFP_KERNEL);out:*logcel;return0;}/* * 总结: 这个函数用于从NVMe控制器获取指定命令集标识的效果日志数据。函数首先检查 * 控制器的效果日志缓存中是否已经存在对应的效果日志数据如果存在则直接返回。 * 如果不存在函数将分配内存用于存放效果日志数据然后调用nvme_get_log函数 * 获取日志数据。如果获取成功函数将效果日志数据存储到控制器的效果日志缓存中 * 并更新传入的指针以指向获取到的日志数据。最后函数返回0表示成功获取效果日志数据 * 否则返回负数错误码。 */
延伸阅读

更多相关文章

2026/10/27 9:24:21

NVDLA系列之C-model:cvif<99>

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/27 11:15:29

STTR算法解析:Transformer在立体匹配中的创新应用

1. STTR算法核心思想解析 STTR(Stereo Transformer)是2021年提出的基于Transformer架构的立体匹配算法,它彻底颠覆了传统基于卷积神经网络(CNN)的立体匹配方法。我在实际项目中使用STTR处理无人机航拍图像时发现,其跨视角特征匹配能力比传统方法提升了约…

2026/10/27 11:15:29

Flink实时计算实战:从核心概念、状态容错到线上调优

第一次接手实时数据链路的时候,我对着选型表发愁了好几天:消息队列有了,存储有了,中间那个"算"的部分到底用谁。微批方案试过,延迟卡在秒级上不去;早期那批纯流式引擎跑得确实快,但状…

2026/10/27 11:15:29

基于YOLOv5与CRNN的工业级二维码识别系统

1. 项目概述:当二维码遇上深度学习 去年帮学弟调试毕业设计时,我意外发现市面上90%的二维码检测方案仍在使用传统图像处理。这就像在智能手机时代还在用传呼机——明明基于深度学习的方案在复杂场景下识别率能提升40%以上。这个开源项目正是用YOLOv5CRNN…

2026/10/27 11:15:29

解决Maven前端构建中Node.js下载404问题

1. 问题现象与背景分析 最近在项目中使用Maven编译前端代码时,频繁遇到404报错问题。具体表现为执行 mvn clean install 命令时,控制台输出类似"Downloading Node.js distribution failed. Unexpected status 404 Not Found"的错误信息。这种…

2026/10/27 11:15:29

Windows窗口防截图技术解析与隐私保护实践

1. 项目概述:窗口防截图的隐私保护刚需 在数字化办公成为主流的今天,我们每天都会在电脑上处理各种敏感信息——从财务数据到私人聊天记录,从商业企划到个人证件扫描件。但你可能没意识到,当这些内容显示在屏幕上时,正…

2026/10/27 11:10:28

arp-scan 1.9 编译安装与IP冲突排查实践

简介:arp-scan 1.9 源码包面向 Linux 网络运维、安全测试及协议学习人员,用于在局域网中扫描 ARP 响应,定位 IP 地址冲突对应的 MAC 地址,也可辅助排查异常主机与设备接入情况。压缩包共 57 个文件、约 360KB,以 15 个…

2026/10/26 0:20:20

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/27 9:34:54

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/27 0:59:21

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/27 0:02:28

WorkBuddy_新建任务与工作模式19_“模式选错”的典型代价

“模式选错”的典型代价 摘要 在软件工程中,设计模式、架构模式、并发模式乃至团队协作模式的选择,往往在项目早期就决定了后续的开发效率和维护成本。本文通过多个真实场景,剖析“模式选错”带来的典型代价——包括返工成本、技术债务累积、…

2026/10/27 0:02:28

WorkBuddy_新建任务与工作模式20_新手推荐路径

新手推荐路径 摘要 在 AI 辅助开发工具快速普及的今天,新手面对最多的困惑往往不是"工具够不够强",而是"我该从哪一步开始用"。本文提出一条经过实践验证的三阶段学习路径:先从 Ask(问答式交互)起…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑