nvme_queue_scan 分析

来源:互联网 发布:局域网建域名 编辑:程序博客网 时间:2024/06/06 05:54
nvme_reset_work 最后通过nvme_queue_scan(&dev->ctrl); 来scan controller
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);
}
可见执行的是ctrl->scan_work,而ctrl->scan_work的赋值是在nvme_init_ctrl中
    INIT_WORK(&ctrl->scan_work, nvme_scan_work);
继续看nvme_scan_work
static void nvme_scan_work(struct work_struct *work)
{
//通过container_of 通过work_struct得到nvme_ctrl
    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);
}
static int nvme_scan_ns_list(struct nvme_ctrl *ctrl, unsigned nn)
{
    struct nvme_ns *ns;
    __le32 *ns_list;
    unsigned i, j, nsid, prev = 0, num_lists = DIV_ROUND_UP(nn, 1024);
    int ret = 0;

    ns_list = kzalloc(0x1000, GFP_KERNEL);
    if (!ns_list)
        return -ENOMEM;
//便利0~num_lists 的nvme controller,通过nvme_identify_ns_list 来检查nvme controller,这里的num_lists 分别是0,1,等。而num_lists = DIV_ROUND_UP(nn, 1024);nn = le32_to_cpu(id->nn);也是村检测到的nvme controller 读到的。

    for (i = 0; i < num_lists; i++) {
//识别namespace 0的nvme controller,将结果存在ns_list,所以ns_list也是一个namespace
        ret = nvme_identify_ns_list(ctrl, prev, ns_list);
        if (ret)
            goto free;

        for (j = 0; j < min(nn, 1024U); j++) {
 //根据ns_list 中的数据不为0的话,就说明这个nsid有再使用,因此继续调用nvme_validate_ns 来申请gendisk,难道namespace 最多只能是两级?
            nsid = le32_to_cpu(ns_list[j]);
            if (!nsid)
                goto out;
//针对每一个nsid,调用nvme_validate_ns 来申请gendisk
            nvme_validate_ns(ctrl, nsid);

            while (++prev < nsid) {
            由于namespace是按照大小排序的,因此如果小于nsid找到nvme肯定是不正常的
                ns = nvme_find_get_ns(ctrl, prev);
                if (ns) {
                    nvme_ns_remove(ns);
                    nvme_put_ns(ns);
                }
            }
        }
        nn -= j;
    }
 out:
    nvme_remove_invalid_namespaces(ctrl, prev);
 free:
    kfree(ns_list);
    return ret;
}
这个函数nvme_scan_ns_list,如果执行成功的话,返回0,因此不再执行nvme_scan_work中的nvme_scan_ns_sequential。
在nvme_scan_work的最后,将所有找到且链接到ctrl->namespaces的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;
}
可见是通过ns_id的大小来排序的,因此nvme_scan_work 就是在nvme_reset_work 已经发现nvme controller的情况下,再次对这个nvme controller下面的进行扫描,因为namespace最多可以两级级联,每个nvme controller下的name space都是放在ctrl->namespaces 这个链表中,且是按照name space id的大小排序。

原创粉丝点击