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的大小排序。
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的大小排序。