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

NVMe Linux驱动系列一:host端<17>nvme alloc io tag set int nvme alloc io tag set struct nvme ctrl ctrl struct blk mq tag set set const struct blk mq ops ops unsigned int nr maps unsigned int

大家好,我是讯享网,很高兴认识大家。

nvme_alloc_io_tag_set

int nvme_alloc_io_tag_set(struct nvme_ctrl *ctrl, struct blk_mq_tag_set *set, const struct blk_mq_ops *ops, unsigned int nr_maps, unsigned int cmd_size) { 
    int ret; memset(set, 0, sizeof(*set)); set->ops = ops; set->queue_depth = min_t(unsigned, ctrl->sqsize, BLK_MQ_MAX_DEPTH - 1); /* * Some Apple controllers requires tags to be unique across admin and * the (only) I/O queue, so reserve the first 32 tags of the I/O queue. */ if (ctrl->quirks & NVME_QUIRK_SHARED_TAGS) set->reserved_tags = NVME_AQ_DEPTH; else if (ctrl->ops->flags & NVME_F_FABRICS) set->reserved_tags = NVMF_RESERVED_TAGS; set->numa_node = ctrl->numa_node; set->flags = BLK_MQ_F_SHOULD_MERGE; if (ctrl->ops->flags & NVME_F_BLOCKING) set->flags |= BLK_MQ_F_BLOCKING; set->cmd_size = cmd_size, set->driver_data = ctrl; set->nr_hw_queues = ctrl->queue_count - 1; set->timeout = NVME_IO_TIMEOUT; set->nr_maps = nr_maps; ret = blk_mq_alloc_tag_set(set); if (ret) return ret; if (ctrl->ops->flags & NVME_F_FABRICS) { 
    ctrl->connect_q = blk_mq_init_queue(set); if (IS_ERR(ctrl->connect_q)) { 
    ret = PTR_ERR(ctrl->connect_q); goto out_free_tag_set; } blk_queue_flag_set(QUEUE_FLAG_SKIP_TAGSET_QUIESCE, ctrl->connect_q); } ctrl->tagset = set; return 0; out_free_tag_set: blk_mq_free_tag_set(set); ctrl->connect_q = NULL; return ret; } EXPORT_SYMBOL_GPL(nvme_alloc_io_tag_set); 

讯享网

这段代码实现了一个名为nvme_alloc_io_tag_set的函数,用于为NVMe控制器分配I/O队列的标签集。以下是函数的主要步骤:

  1. 函数接受一个指向nvme_ctrl结构体的指针ctrl,一个指向blk_mq_tag_set结构体的指针set,一个指向blk_mq_ops结构体的指针ops,一个无符号整数nr_maps和一个无符号整数cmd_size作为参数。
  2. 使用memset函数将set结构体清零,然后根据提供的参数配置set结构体的各个字段:
    • ops字段:设置为传入的ops参数,表示块多队列操作。
    • queue_depth字段:根据控制器的sqsize(队列深度)和BLK_MQ_MAX_DEPTH - 1的较小值来设置,确保队列深度不超过最大深度。
    • 如果控制器的操作标志中包含NVME_F_FABRICS,则将reserved_tags字段设置为NVMF_RESERVED_TAGS
    • 如果控制器的操作标志中包含NVME_F_BLOCKING,则将flags字段设置为BLK_MQ_F_BLOCKING,表示支持阻塞操作。
    • numa_node字段:设置为控制器的NUMA节点。
    • cmd_size字段:设置为传入的cmd_size参数,表示命令大小。
    • driver_data字段:设置为指向控制器的指针。
    • nr_hw_queues字段:设置为控制器的队列数减1,表示硬件队列数。
    • timeout字段:设置为NVME_IO_TIMEOUT,表示I/O操作的超时时间。
    • nr_maps字段:设置为传入的nr_maps参数,表示映射数。
  3. 调用blk_mq_alloc_tag_set函数分配标签集,如果分配失败则返回错误代码。
  4. 如果控制器的操作标志中包含NVME_F_FABRICS,则使用相同的标签集初始化NVMe over Fabrics连接队列(connect_q)。如果初始化失败,则释放之前分配的标签集并返回错误代码。
  5. 将成功初始化的标签集保存在ctrl->tagset字段中,然后返回0表示成功。

总之,该函数用于为NVMe控制器分配I/O队列的标签集,同时根据控制器的操作标志设置是否支持NVMe over Fabrics。函数通过调用其他相关函数来完成标签集和队列的初始化,最终返回初始化结果。通过导出宏EXPORT_SYMBOL_GPL使其可在内核外部使用。

nvme_remove_io_tag_set

讯享网 void nvme_remove_io_tag_set(struct nvme_ctrl *ctrl) { 
    if (ctrl->ops->flags & NVME_F_FABRICS) { 
    blk_mq_destroy_queue(ctrl->connect_q); blk_put_queue(ctrl->connect_q); } blk_mq_free_tag_set(ctrl->tagset); } EXPORT_SYMBOL_GPL(nvme_remove_io_tag_set); 

这段代码实现了一个名为nvme_remove_io_tag_set的函数,用于移除之前为NVMe控制器分配的I/O队列的标签集。以下是函数的主要步骤:

  1. 函数接受一个指向nvme_ctrl结构体的指针ctrl作为参数。
  2. 如果控制器的操作标志中包含NVME_F_FABRICS,则调用blk_mq_destroy_queue函数销毁NVMe over Fabrics连接队列(connect_q),然后调用blk_put_queue函数释放连接队列。
  3. 调用blk_mq_free_tag_set函数释放之前为控制器分配的标签集。

总之,该函数用于移除之前为NVMe控制器分配的I/O队列的标签集,并释放相关资源。通过导出宏EXPORT_SYMBOL_GPL使其可在内核外部使用。


讯享网

nvme_stop_ctrl && nvme_start_ctrl

 void nvme_stop_ctrl(struct nvme_ctrl *ctrl) { 
    nvme_mpath_stop(ctrl); nvme_auth_stop(ctrl); nvme_stop_keep_alive(ctrl); nvme_stop_failfast_work(ctrl); flush_work(&ctrl->async_event_work); cancel_work_sync(&ctrl->fw_act_work); if (ctrl->ops->stop_ctrl) ctrl->ops->stop_ctrl(ctrl); } EXPORT_SYMBOL_GPL(nvme_stop_ctrl); void nvme_start_ctrl(struct nvme_ctrl *ctrl) { 
    nvme_start_keep_alive(ctrl); nvme_enable_aen(ctrl); /* * persistent discovery controllers need to send indication to userspace * to re-read the discovery log page to learn about possible changes * that were missed. We identify persistent discovery controllers by * checking that they started once before, hence are reconnecting back. */ if (test_bit(NVME_CTRL_STARTED_ONCE, &ctrl->flags) && nvme_discovery_ctrl(ctrl)) nvme_change_uevent(ctrl, "NVME_EVENT=rediscover"); if (ctrl->queue_count > 1) { 
    nvme_queue_scan(ctrl); nvme_unquiesce_io_queues(ctrl); nvme_mpath_update(ctrl); } nvme_change_uevent(ctrl, "NVME_EVENT=connected"); set_bit(NVME_CTRL_STARTED_ONCE, &ctrl->flags); } EXPORT_SYMBOL_GPL(nvme_start_ctrl); 

这两段代码实现了用于停止和启动NVMe控制器的函数,分别是nvme_stop_ctrlnvme_start_ctrl。这些函数用于管理控制器的停止和启动过程。以下是每个函数的主要步骤:

nvme_stop_ctrl

  1. 调用nvme_mpath_stop函数停止多路径支持。
  2. 调用nvme_auth_stop函数停止认证操作。
  3. 调用nvme_stop_keep_alive函数停止保持活跃的操作。
  4. 调用nvme_stop_failfast_work函数停止失败快速工作。
  5. 使用flush_work函数等待异步事件工作完成。
  6. 使用cancel_work_sync函数同步取消固件激活工作。
  7. 如果控制器的操作结构中存在stop_ctrl函数指针,调用ctrl->ops->stop_ctrl来执行额外的停止操作。

nvme_start_ctrl

  1. 调用nvme_start_keep_alive函数启动保持活跃的操作。
  2. 调用nvme_enable_aen函数启用异步事件通知。
  3. 如果控制器已经启动过一次(使用位掩码NVME_CTRL_STARTED_ONCE进行检查)并且是持久性发现控制器,通过发送事件通知让用户空间重新读取发现日志页,以了解可能的变化。
  4. 如果控制器有多个队列,调用nvme_queue_scan函数进行队列扫描。
  5. 调用nvme_unquiesce_io_queues函数解除I/O队列的静默状态。
  6. 调用nvme_mpath_update函数更新多路径信息。
  7. 发送事件通知表示控制器已连接。
  8. 使用位掩码NVME_CTRL_STARTED_ONCE标记控制器已经启动过一次。

这两个函数的作用是在控制器启动和停止时执行必要的操作,包括启用/停用功能、更新状态和发送事件通知等。通过导出宏EXPORT_SYMBOL_GPL使其可在内核外部使用。

nvme_uninit_ctrl

讯享网 void nvme_uninit_ctrl(struct nvme_ctrl *ctrl) { 
    nvme_hwmon_exit(ctrl); nvme_fault_inject_fini(&ctrl->fault_inject); dev_pm_qos_hide_latency_tolerance(ctrl->device); cdev_device_del(&ctrl->cdev, ctrl->device); nvme_put_ctrl(ctrl); } EXPORT_SYMBOL_GPL(nvme_uninit_ctrl); 

这段代码实现了一个函数nvme_uninit_ctrl,用于对NVMe控制器进行反初始化。以下是该函数的主要步骤:

  1. 调用nvme_hwmon_exit函数来退出硬件监控相关的操作。
  2. 调用nvme_fault_inject_fini函数来终止故障注入相关的操作。
  3. 使用dev_pm_qos_hide_latency_tolerance函数隐藏设备的电源管理参数。
  4. 调用cdev_device_del函数来从字符设备中删除控制器设备。
  5. 调用nvme_put_ctrl函数来减少对控制器的引用计数,释放控制器资源。

最终,通过导出宏EXPORT_SYMBOL_GPL使该函数可以在内核外部使用,用于对NVMe控制器进行反初始化操作。

nvme_free_cels

 static void nvme_free_cels(struct nvme_ctrl *ctrl) { 
    struct nvme_effects_log *cel; unsigned long i; xa_for_each(&ctrl->cels, i, cel) { 
    xa_erase(&ctrl->cels, i); kfree(cel); } xa_destroy(&ctrl->cels); } 

这段代码实现了一个函数nvme_free_cels,用于释放NVMe控制器中的效果日志(cel)资源。以下是该函数的主要步骤:

  1. 使用xa_for_each宏循环遍历控制器的效果日志数组(cels)。
  2. 在循环中,使用xa_erase函数从效果日志数组中移除当前位置的效果日志。
  3. 然后,使用kfree函数释放当前位置的效果日志所占用的内存。
  4. 循环结束后,使用xa_destroy函数销毁效果日志数组。

总之,该函数负责释放NVMe控制器中的效果日志资源,防止内存泄漏。

nvme_free_ctrl

讯享网 static void nvme_free_ctrl(struct device *dev) { 
    struct nvme_ctrl *ctrl = container_of(dev, struct nvme_ctrl, ctrl_device); struct nvme_subsystem *subsys = ctrl->subsys; if (!subsys || ctrl->instance != subsys->instance) ida_free(&nvme_instance_ida, ctrl->instance); nvme_free_cels(ctrl); nvme_mpath_uninit(ctrl); nvme_auth_stop(ctrl); nvme_auth_free(ctrl); __free_page(ctrl->discard_page); free_opal_dev(ctrl->opal_dev); if (subsys) { 
    mutex_lock(&nvme_subsystems_lock); list_del(&ctrl->subsys_entry); sysfs_remove_link(&subsys->dev.kobj, dev_name(ctrl->device)); mutex_unlock(&nvme_subsystems_lock); } ctrl->ops->free_ctrl(ctrl); if (subsys) nvme_put_subsystem(subsys); } 

这段代码实现了一个函数nvme_free_ctrl,用于释放NVMe控制器(ctrl)资源。以下是该函数的主要步骤:

  1. 通过container_of宏从设备对象(dev)中获取控制器结构体指针(ctrl)。
  2. 获取与控制器相关的子系统(subsys)指针。
  3. 如果子系统为空或控制器的实例号与子系统的实例号不匹配,则使用ida_free函数释放控制器实例号(在没有关联子系统的情况下)。
  4. 调用nvme_free_cels函数释放控制器的效果日志资源。
  5. 调用nvme_mpath_uninit函数释放控制器的多路径资源。
  6. 调用nvme_auth_stop函数停止控制器的认证。
  7. 调用nvme_auth_free函数释放控制器的认证相关资源。
  8. 使用__free_page函数释放控制器的丢弃页面(discard page)。
  9. 使用free_opal_dev函数释放控制器的OPAL设备(如果有的话)。
  10. 如果子系统存在,执行以下步骤:
    a. 在获取互斥锁的情况下,从子系统链表中移除控制器的条目。
    b. 使用sysfs_remove_link函数从子系统的设备对象的sysfs中移除与控制器设备相关的链接。
    c. 在释放互斥锁后,调用nvme_put_subsystem函数减少子系统的引用计数。
  11. 调用控制器特定的free_ctrl操作(函数指针),释放控制器相关的操作资源。
  12. 如果子系统存在,调用nvme_put_subsystem函数减少子系统的引用计数。

总之,该函数负责释放NVMe控制器及其相关资源,用于在不再需要控制器时进行资源清理。

nvme_init_ctrl

 /* * Initialize a NVMe controller structures. This needs to be called during * earliest initialization so that we have the initialized structured around * during probing. */ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev, const struct nvme_ctrl_ops *ops, unsigned long quirks) { 
    int ret; ctrl->state = NVME_CTRL_NEW; clear_bit(NVME_CTRL_FAILFAST_EXPIRED, &ctrl->flags); spin_lock_init(&ctrl->lock); mutex_init(&ctrl->scan_lock); INIT_LIST_HEAD(&ctrl->namespaces); xa_init(&ctrl->cels); init_rwsem(&ctrl->namespaces_rwsem); ctrl->dev = dev; ctrl->ops = ops; ctrl->quirks = quirks; ctrl->numa_node = NUMA_NO_NODE; INIT_WORK(&ctrl->scan_work, nvme_scan_work); INIT_WORK(&ctrl->async_event_work, nvme_async_event_work); INIT_WORK(&ctrl->fw_act_work, nvme_fw_act_work); INIT_WORK(&ctrl->delete_work, nvme_delete_ctrl_work); init_waitqueue_head(&ctrl->state_wq); INIT_DELAYED_WORK(&ctrl->ka_work, nvme_keep_alive_work); INIT_DELAYED_WORK(&ctrl->failfast_work, nvme_failfast_work); memset(&ctrl->ka_cmd, 0, sizeof(ctrl->ka_cmd)); ctrl->ka_cmd.common.opcode = nvme_admin_keep_alive; BUILD_BUG_ON(NVME_DSM_MAX_RANGES * sizeof(struct nvme_dsm_range) > PAGE_SIZE); ctrl->discard_page = alloc_page(GFP_KERNEL); if (!ctrl->discard_page) { 
    ret = -ENOMEM; goto out; } ret = ida_alloc(&nvme_instance_ida, GFP_KERNEL); if (ret < 0) goto out; ctrl->instance = ret; device_initialize(&ctrl->ctrl_device); ctrl->device = &ctrl->ctrl_device; ctrl->device->devt = MKDEV(MAJOR(nvme_ctrl_base_chr_devt), ctrl->instance); ctrl->device->class = nvme_class; ctrl->device->parent = ctrl->dev; if (ops->dev_attr_groups) ctrl->device->groups = ops->dev_attr_groups; else ctrl->device->groups = nvme_dev_attr_groups; ctrl->device->release = nvme_free_ctrl; dev_set_drvdata(ctrl->device, ctrl); ret = dev_set_name(ctrl->device, "nvme%d", ctrl->instance); if (ret) goto out_release_instance; nvme_get_ctrl(ctrl); cdev_init(&ctrl->cdev, &nvme_dev_fops); ctrl->cdev.owner = ops->module; ret = cdev_device_add(&ctrl->cdev, ctrl->device); if (ret) goto out_free_name; /* * Initialize latency tolerance controls. The sysfs files won't * be visible to userspace unless the device actually supports APST. */ ctrl->device->power.set_latency_tolerance = nvme_set_latency_tolerance; dev_pm_qos_update_user_latency_tolerance(ctrl->device, min(default_ps_max_latency_us, (unsigned long)S32_MAX)); nvme_fault_inject_init(&ctrl->fault_inject, dev_name(ctrl->device)); nvme_mpath_init_ctrl(ctrl); ret = nvme_auth_init_ctrl(ctrl); if (ret) goto out_free_cdev; return 0; out_free_cdev: nvme_fault_inject_fini(&ctrl->fault_inject); dev_pm_qos_hide_latency_tolerance(ctrl->device); cdev_device_del(&ctrl->cdev, ctrl->device); out_free_name: nvme_put_ctrl(ctrl); kfree_const(ctrl->device->kobj.name); out_release_instance: ida_free(&nvme_instance_ida, ctrl->instance); out: if (ctrl->discard_page) __free_page(ctrl->discard_page); return ret; } EXPORT_SYMBOL_GPL(nvme_init_ctrl); 

这段代码实现了一个函数nvme_init_ctrl,用于初始化NVMe控制器的数据结构。以下是该函数的主要步骤:

  1. 初始化控制器的状态、标志位、锁等成员。
  2. 初始化控制器的命名空间列表、效果日志列表等成员。
  3. 初始化控制器的互斥锁、读写信号量、工作队列等成员。
  4. 初始化控制器的Keep-Alive工作、异步事件工作、固件激活工作等成员。
  5. 初始化控制器的删除工作队列。
  6. 初始化控制器的等待队列头(用于等待控制器状态变化)。
  7. 初始化控制器的延迟工作队列(用于定时发送Keep-Alive命令)。
  8. 初始化控制器的故障注入模块。
  9. 使用alloc_page函数分配一个页面用于丢弃操作(discard operation)。
  10. 使用ida_alloc函数为控制器分配唯一实例号。
  11. 初始化控制器的设备对象(ctrl_device)并设置相关属性。
  12. 调用nvme_get_ctrl函数增加控制器的引用计数。
  13. 初始化字符设备结构(cdev)并添加到系统。
  14. 初始化控制器的电源管理相关属性。
  15. 初始化控制器的故障注入模块。
  16. 调用nvme_mpath_init_ctrl函数初始化控制器的多路径模块。
  17. 调用nvme_auth_init_ctrl函数初始化控制器的认证模块。
  18. 如果初始化成功,则返回0;如果发生错误,执行以下步骤:
    a. 释放故障注入模块的资源。
    b. 隐藏控制器的电源管理属性。
    c. 从字符设备中删除控制器的cdev,并删除设备对象的名称。
    d. 释放控制器的引用计数。
    e. 释放分配的实例号。
    f. 如果分配了丢弃页面,使用__free_page函数释放页面。

总之,该函数用于在早期初始化阶段初始化NVMe控制器的数据结构,并分配所需的资源。

小讯
上一篇 2025-01-16 17:59
下一篇 2025-02-24 11:31

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/117321.html