这篇文章紧接上回分解,在nvme_probe函数的最后一步调用nvme_reset_work进行reset操作,nvme_reset_work的主要工作可以概括如下几个步骤:

  1. 进入nvme_reset_work函数后先检查NVME_CTRL_RESETTING标志,来确保nvme_reset_work不会被重复进入。

  2. 调用nvme_pci_enable

  3. 调用nvme_configure_admin_queue

  4. 调用nvme_init_queue

  5. 调用nvme_alloc_admin_tags

  6. 调用nvme_init_identify

  7. 调用nvme_setup_io_queues

  8. 调用nvme_start_queues/nvme_dev_add之后,接着调用nvme_queue_scan

上篇文章中,我们解析了nvme_setup_io_queues内容,本文我们接着介绍nvme_reset_work中最后的三个函数。

我们来看看nvme_start_queues的代码:

void nvme_start_queues(struct nvme_ctrl *ctrl)

{

struct nvme_ns *ns;

mutex_lock(&ctrl->namespaces_mutex);

list_for_each_entry(ns, &ctrl->namespaces, list) {

blk_mq_start_stopped_hw_queues(ns->queue, true);

blk_mq_kick_requeue_list(ns->queue);

}

mutex_unlock(&ctrl->namespaces_mutex);

}

这个过程比较简单,主要是调用了blk_mq_start_stopped_hw_queues和blk_mq_kick_requeue_list去启动mq-block层的hardware queues和request queues. 有关mq-block层的内容,我们在之前的文章中已阐述,请参考:

Linux NVMe Driver学习笔记之6:Admin Queue与Blk-mq初始化

接下来,我们看下比较关键的函数nvme_dev_add

static int nvme_dev_add(struct nvme_dev *dev)

{

if (!dev->ctrl.tagset) {

dev->tagset.ops = &nvme_mq_ops;

dev->tagset.nr_hw_queues = dev->online_queues - 1;

dev->tagset.timeout = NVME_IO_TIMEOUT;

dev->tagset.numa_node = dev_to_node(dev->dev);

dev->tagset.queue_depth =

min_t(int, dev->q_depth, BLK_MQ_MAX_DEPTH) - 1;

dev->tagset.cmd_size = nvme_cmd_size(dev);

dev->tagset.flags = BLK_MQ_F_SHOULD_MERGE;

dev->tagset.driver_data = dev;

if (blk_mq_alloc_tag_set(&dev->tagset))

return 0;

dev->ctrl.tagset = &dev->tagset;

} else {

blk_mq_update_nr_hw_queues(&dev->tagset, dev->online_queues - 1);

/* Free previously allocated queues that are no longer usable */

nvme_free_queues(dev, dev->online_queues);

}

return 0;

}

从上面的代码显示,nvme_dev_add对tagset结构体初始化,接着调用blk_mq_alloc_tag_set分配tag set并与request queue关联,这一点与admin_tagset类似,这里不再展开,请参考:

Linux NVMe Driver学习笔记之6:Admin Queue与Blk-mq初始化

我们再看一下最后一个函数nvme_queue_scan:

void nvme_queue_scan(struct nvme_ctrl *ctrl)

{

/*

 * Do not queue new scan work when a controller is reset during

 * removal.

 */

if (ctrl->state == NVME_CTRL_LIVE)

schedule_work(&ctrl->scan_work);

}

从上面代码可以发现,nvme_queue_scan最后调用的是ctrl->scan_work. 在之前nvme_init_ctrl函数(Linux NVMe Driver学习笔记之3:nvme_probe函数解析)中已经被赋值:

INIT_WORK(&ctrl->scan_work, nvme_scan_work);

那我们就看一下nvme_scan_work:

static void nvme_scan_work(struct work_struct *work)

{

struct nvme_ctrl *ctrl =

container_of(work, struct nvme_ctrl, scan_work);

struct nvme_id_ctrl *id;

unsigned nn;

if (ctrl->state != NVME_CTRL_LIVE)

return;

       // 再次识别nvme controller

if (nvme_identify_ctrl(ctrl, &id))

return;

nn = le32_to_cpu(id->nn);

       // 判断nvme version是否比1.1.0高,且没有设置NVME_QUIRK_IDENTIFY_CNS的话,就调用nvme_scan_ns_list

if (ctrl->vs >= NVME_VS(1, 1, 0) &&

    !(ctrl->quirks & NVME_QUIRK_IDENTIFY_CNS)) {

if (!nvme_scan_ns_list(ctrl, nn))

goto done;

}

       //检查namespace是否合法,去掉不合法的namespace

nvme_scan_ns_sequential(ctrl, nn);

 done:

mutex_lock(&ctrl->namespaces_mutex);

list_sort(NULL, &ctrl->namespaces, ns_cmp);

mutex_unlock(&ctrl->namespaces_mutex);

kfree(id);

}

上面代码中主要是调用了两个scan namespace的函数:nvme_scan_ns_listnvme_scan_ns_sequential。nvme_scan_ns_list,如果执行成功的话,返回0,因此不再执行nvme_scan_ns_sequential。nvme_scan_ns_sequential的作用就是检查namespace是否合法,去掉不合法的namespace。

在nvme_scan_work的最后,将所有找到的namespace通过list_sort(NULL, &ctrl->namespaces, ns_cmp)来排序.

static int ns_cmp(void *priv, struct list_head *a, struct list_head *b)
{
    struct nvme_ns *nsa = container_of(a, struct nvme_ns, list);
    struct nvme_ns *nsb = container_of(b, struct nvme_ns, list);

    return nsa->ns_id - nsb->ns_id;
}

nvme_scan_work 就是在nvme_reset_work已经发现nvme controller的情况下,再次对这个nvme controller下面的进行扫描,因为namespace最多可以两级级联,每个nvme controller下的name space都是放在ctrl->namespaces 这个链表中,且是按照name space id的大小排序。

GitHub 加速计划 / nv / nvm
78.06 K
7.82 K
下载
nvm-sh/nvm: 是一个 Node.js 版本管理器,用于在不同的 Node.js 版本之间进行切换。它可以帮助开发者轻松管理多个 Node.js 版本,方便进行开发和测试。特点包括轻量级、易于使用、支持跨平台等。
最近提交(Master分支:2 个月前 )
9c9ff4ba Moved issue template into ISSUE_TEMPLATE folder 9 天前
51ea809d - 9 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐