nvme_init_subnqn
/* * 函数名称: nvme_init_subnqn * 说明: 初始化NVMe子系统的NQN(NVMe 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->quirks&NVME_QUIRK_IGNORE_DEV_SUBNQN)){nqnlen=strnlen(id->subnqn,NVMF_NQN_SIZE);if(nqnlen>0&&nqnlen<NVMF_NQN_SIZE){// 如果存在有效的设备子系统NQN,则使用它strscpy(subsys->subnqn,id->subnqn,NVMF_NQN_SIZE);return;}// 如果版本号支持且存在问题,则输出警告信息if(ctrl->vs>=NVME_VS(1,2,1))dev_warn(ctrl->device,"missing or invalid SUBNQN field.\n");}/* * 生成一个类似于NVMe Base Specification 2.0第4.5节中的NQN,但由于历史原因, * 格式略有不同,并且无法更改。 */off=snprintf(subsys->subnqn,NVMF_NQN_SIZE,"nqn.2014.08.org.nvmexpress:%04x%04x",le16_to_cpu(id->vid),le16_to_cpu(id->ssvid));memcpy(subsys->subnqn+off,id->sn,sizeof(id->sn));off+=sizeof(id->sn);memcpy(subsys->subnqn+off,id->mn,sizeof(id->mn));off+=sizeof(id->mn);memset(subsys->subnqn+off,0,sizeof(subsys->subnqn)-off);}/* * 总结: 这个函数用于初始化NVMe子系统的NQN(NVMe 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*subsys=container_of(dev,structnvme_subsystem,dev);// 如果子系统的实例标识为非负值,释放实例标识if(subsys->instance>=0)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*subsys=container_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->opts&&ctrl->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->cntlid==ctrl->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->cmic&NVME_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;// 分配并初始化子系统结构subsys=kzalloc(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_id=le16_to_cpu(id->vid);subsys->cmic=id->cmic;/* 版本在1.4之前的控制器可能不一定报告有效的类型 */if(id->cntrltype==NVME_CTRL_DISC||!strcmp(subsys->subnqn,NVME_DISC_SUBSYS_NAME))subsys->subtype=NVME_NQN_DISC;elsesubsys->subtype=NVME_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->awupf=le16_to_cpu(id->awupf);nvme_mpath_default_iopolicy(subsys);// 初始化子系统设备subsys->dev.class=nvme_subsys_class;subsys->dev.release=nvme_release_subsystem;subsys->dev.groups=nvme_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);subsys=found;// 验证控制器的控制器标识if(!nvme_validate_cntlid(subsys,ctrl,id)){ret=-EINVAL;gotoout_put_subsystem;}}else{// 添加新的子系统设备ret=device_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链接ret=sysfs_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->instance=ctrl->instance;ctrl->subsys=subsys;// 将控制器添加到子系统的控制器列表中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 dwlen=nvme_bytes_to_numd(size);c.get_log_page.opcode=nvme_admin_get_log_page;c.get_log_page.nsid=cpu_to_le32(nsid);c.get_log_page.lid=log_page;c.get_log_page.lsp=lsp;c.get_log_page.numdl=cpu_to_le16(dwlen&((1<<16)-1));c.get_log_page.numdu=cpu_to_le16(dwlen>>16);c.get_log_page.lpol=cpu_to_le32(lower_32_bits(offset));c.get_log_page.lpou=cpu_to_le32(upper_32_bits(offset));c.get_log_page.csi=csi;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*cel=xa_load(&ctrl->cels,csi);intret;if(cel)gotoout;cel=kzalloc(sizeof(*cel),GFP_KERNEL);if(!cel)return-ENOMEM;ret=nvme_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:*log=cel;return0;}/* * 总结: 这个函数用于从NVMe控制器获取指定命令集标识的效果日志数据。函数首先检查 * 控制器的效果日志缓存中是否已经存在对应的效果日志数据,如果存在,则直接返回。 * 如果不存在,函数将分配内存用于存放效果日志数据,然后调用nvme_get_log函数 * 获取日志数据。如果获取成功,函数将效果日志数据存储到控制器的效果日志缓存中, * 并更新传入的指针以指向获取到的日志数据。最后,函数返回0表示成功获取效果日志数据, * 否则返回负数错误码。 */