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

发布时间:2026/10/27 9:24:18
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表示成功获取效果日志数据 * 否则返回负数错误码。 */

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询