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队列的标签集。以下是函数的主要步骤:
- 函数接受一个指向
nvme_ctrl结构体的指针ctrl,一个指向blk_mq_tag_set结构体的指针set,一个指向blk_mq_ops结构体的指针ops,一个无符号整数nr_maps和一个无符号整数cmd_size作为参数。 - 使用
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参数,表示映射数。
- 调用
blk_mq_alloc_tag_set函数分配标签集,如果分配失败则返回错误代码。 - 如果控制器的操作标志中包含
NVME_F_FABRICS,则使用相同的标签集初始化NVMe over Fabrics连接队列(connect_q)。如果初始化失败,则释放之前分配的标签集并返回错误代码。 - 将成功初始化的标签集保存在
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队列的标签集。以下是函数的主要步骤:
- 函数接受一个指向
nvme_ctrl结构体的指针ctrl作为参数。 - 如果控制器的操作标志中包含
NVME_F_FABRICS,则调用blk_mq_destroy_queue函数销毁NVMe over Fabrics连接队列(connect_q),然后调用blk_put_queue函数释放连接队列。 - 调用
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_ctrl和nvme_start_ctrl。这些函数用于管理控制器的停止和启动过程。以下是每个函数的主要步骤:
nvme_stop_ctrl:
- 调用
nvme_mpath_stop函数停止多路径支持。 - 调用
nvme_auth_stop函数停止认证操作。 - 调用
nvme_stop_keep_alive函数停止保持活跃的操作。 - 调用
nvme_stop_failfast_work函数停止失败快速工作。 - 使用
flush_work函数等待异步事件工作完成。 - 使用
cancel_work_sync函数同步取消固件激活工作。 - 如果控制器的操作结构中存在
stop_ctrl函数指针,调用ctrl->ops->stop_ctrl来执行额外的停止操作。
nvme_start_ctrl:
- 调用
nvme_start_keep_alive函数启动保持活跃的操作。 - 调用
nvme_enable_aen函数启用异步事件通知。 - 如果控制器已经启动过一次(使用位掩码
NVME_CTRL_STARTED_ONCE进行检查)并且是持久性发现控制器,通过发送事件通知让用户空间重新读取发现日志页,以了解可能的变化。 - 如果控制器有多个队列,调用
nvme_queue_scan函数进行队列扫描。 - 调用
nvme_unquiesce_io_queues函数解除I/O队列的静默状态。 - 调用
nvme_mpath_update函数更新多路径信息。 - 发送事件通知表示控制器已连接。
- 使用位掩码
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控制器进行反初始化。以下是该函数的主要步骤:
- 调用
nvme_hwmon_exit函数来退出硬件监控相关的操作。 - 调用
nvme_fault_inject_fini函数来终止故障注入相关的操作。 - 使用
dev_pm_qos_hide_latency_tolerance函数隐藏设备的电源管理参数。 - 调用
cdev_device_del函数来从字符设备中删除控制器设备。 - 调用
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)资源。以下是该函数的主要步骤:
- 使用
xa_for_each宏循环遍历控制器的效果日志数组(cels)。 - 在循环中,使用
xa_erase函数从效果日志数组中移除当前位置的效果日志。 - 然后,使用
kfree函数释放当前位置的效果日志所占用的内存。 - 循环结束后,使用
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)资源。以下是该函数的主要步骤:
- 通过
container_of宏从设备对象(dev)中获取控制器结构体指针(ctrl)。 - 获取与控制器相关的子系统(
subsys)指针。 - 如果子系统为空或控制器的实例号与子系统的实例号不匹配,则使用
ida_free函数释放控制器实例号(在没有关联子系统的情况下)。 - 调用
nvme_free_cels函数释放控制器的效果日志资源。 - 调用
nvme_mpath_uninit函数释放控制器的多路径资源。 - 调用
nvme_auth_stop函数停止控制器的认证。 - 调用
nvme_auth_free函数释放控制器的认证相关资源。 - 使用
__free_page函数释放控制器的丢弃页面(discard page)。 - 使用
free_opal_dev函数释放控制器的OPAL设备(如果有的话)。 - 如果子系统存在,执行以下步骤:
a. 在获取互斥锁的情况下,从子系统链表中移除控制器的条目。
b. 使用sysfs_remove_link函数从子系统的设备对象的sysfs中移除与控制器设备相关的链接。
c. 在释放互斥锁后,调用nvme_put_subsystem函数减少子系统的引用计数。 - 调用控制器特定的
free_ctrl操作(函数指针),释放控制器相关的操作资源。 - 如果子系统存在,调用
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控制器的数据结构。以下是该函数的主要步骤:
- 初始化控制器的状态、标志位、锁等成员。
- 初始化控制器的命名空间列表、效果日志列表等成员。
- 初始化控制器的互斥锁、读写信号量、工作队列等成员。
- 初始化控制器的Keep-Alive工作、异步事件工作、固件激活工作等成员。
- 初始化控制器的删除工作队列。
- 初始化控制器的等待队列头(用于等待控制器状态变化)。
- 初始化控制器的延迟工作队列(用于定时发送Keep-Alive命令)。
- 初始化控制器的故障注入模块。
- 使用
alloc_page函数分配一个页面用于丢弃操作(discard operation)。 - 使用
ida_alloc函数为控制器分配唯一实例号。 - 初始化控制器的设备对象(
ctrl_device)并设置相关属性。 - 调用
nvme_get_ctrl函数增加控制器的引用计数。 - 初始化字符设备结构(
cdev)并添加到系统。 - 初始化控制器的电源管理相关属性。
- 初始化控制器的故障注入模块。
- 调用
nvme_mpath_init_ctrl函数初始化控制器的多路径模块。 - 调用
nvme_auth_init_ctrl函数初始化控制器的认证模块。 - 如果初始化成功,则返回0;如果发生错误,执行以下步骤:
a. 释放故障注入模块的资源。
b. 隐藏控制器的电源管理属性。
c. 从字符设备中删除控制器的cdev,并删除设备对象的名称。
d. 释放控制器的引用计数。
e. 释放分配的实例号。
f. 如果分配了丢弃页面,使用__free_page函数释放页面。
总之,该函数用于在早期初始化阶段初始化NVMe控制器的数据结构,并分配所需的资源。

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