Skip to content

Commit 9bd82b1

Browse files
baegjaeKeith Busch
authored andcommitted
nvme-multipath: fix sysfs dangerously created links
If multipathing is enabled, each NVMe subsystem creates a head namespace (e.g., nvme0n1) and multiple private namespaces (e.g., nvme0c0n1 and nvme0c1n1) in sysfs. When creating links for private namespaces, links of head namespace are used, so the namespace creation order must be followed (e.g., nvme0n1 -> nvme0c1n1). If the order is not followed, links of sysfs will be incomplete or kernel panic will occur. The kernel panic was: kernel BUG at fs/sysfs/symlink.c:27! Call Trace: nvme_mpath_add_disk_links+0x5d/0x80 [nvme_core] nvme_validate_ns+0x5c2/0x850 [nvme_core] nvme_scan_work+0x1af/0x2d0 [nvme_core] Correct order Context A Context B nvme0n1 nvme0c0n1 nvme0c1n1 Incorrect order Context A Context B nvme0c1n1 nvme0n1 nvme0c0n1 The nvme_mpath_add_disk (for creating head namespace) is called just before the nvme_mpath_add_disk_links (for creating private namespaces). In nvme_mpath_add_disk, the first context acquires the lock of subsystem and creates a head namespace, and other contexts do nothing by checking GENHD_FL_UP of a head namespace after waiting to acquire the lock. We verified the code with or without multipathing using three vendors of dual-port NVMe SSDs. Signed-off-by: Baegjae Sung <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Signed-off-by: Keith Busch <[email protected]>
1 parent f25a2df commit 9bd82b1

File tree

2 files changed

+13
-14
lines changed

2 files changed

+13
-14
lines changed

drivers/nvme/host/core.c

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2835,7 +2835,7 @@ static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl,
28352835
}
28362836

28372837
static int nvme_init_ns_head(struct nvme_ns *ns, unsigned nsid,
2838-
struct nvme_id_ns *id, bool *new)
2838+
struct nvme_id_ns *id)
28392839
{
28402840
struct nvme_ctrl *ctrl = ns->ctrl;
28412841
bool is_shared = id->nmic & (1 << 0);
@@ -2851,8 +2851,6 @@ static int nvme_init_ns_head(struct nvme_ns *ns, unsigned nsid,
28512851
ret = PTR_ERR(head);
28522852
goto out_unlock;
28532853
}
2854-
2855-
*new = true;
28562854
} else {
28572855
struct nvme_ns_ids ids;
28582856

@@ -2864,8 +2862,6 @@ static int nvme_init_ns_head(struct nvme_ns *ns, unsigned nsid,
28642862
ret = -EINVAL;
28652863
goto out_unlock;
28662864
}
2867-
2868-
*new = false;
28692865
}
28702866

28712867
list_add_tail(&ns->siblings, &head->list);
@@ -2936,7 +2932,6 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
29362932
struct nvme_id_ns *id;
29372933
char disk_name[DISK_NAME_LEN];
29382934
int node = dev_to_node(ctrl->dev), flags = GENHD_FL_EXT_DEVT;
2939-
bool new = true;
29402935

29412936
ns = kzalloc_node(sizeof(*ns), GFP_KERNEL, node);
29422937
if (!ns)
@@ -2962,7 +2957,7 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
29622957
if (id->ncap == 0)
29632958
goto out_free_id;
29642959

2965-
if (nvme_init_ns_head(ns, nsid, id, &new))
2960+
if (nvme_init_ns_head(ns, nsid, id))
29662961
goto out_free_id;
29672962
nvme_setup_streams_ns(ctrl, ns);
29682963

@@ -3028,8 +3023,7 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
30283023
pr_warn("%s: failed to register lightnvm sysfs group for identification\n",
30293024
ns->disk->disk_name);
30303025

3031-
if (new)
3032-
nvme_mpath_add_disk(ns->head);
3026+
nvme_mpath_add_disk(ns->head);
30333027
nvme_mpath_add_disk_links(ns);
30343028
return;
30353029
out_unlink_ns:

drivers/nvme/host/multipath.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,16 @@ void nvme_mpath_add_disk(struct nvme_ns_head *head)
198198
{
199199
if (!head->disk)
200200
return;
201-
device_add_disk(&head->subsys->dev, head->disk);
202-
if (sysfs_create_group(&disk_to_dev(head->disk)->kobj,
203-
&nvme_ns_id_attr_group))
204-
pr_warn("%s: failed to create sysfs group for identification\n",
205-
head->disk->disk_name);
201+
202+
mutex_lock(&head->subsys->lock);
203+
if (!(head->disk->flags & GENHD_FL_UP)) {
204+
device_add_disk(&head->subsys->dev, head->disk);
205+
if (sysfs_create_group(&disk_to_dev(head->disk)->kobj,
206+
&nvme_ns_id_attr_group))
207+
pr_warn("%s: failed to create sysfs group for identification\n",
208+
head->disk->disk_name);
209+
}
210+
mutex_unlock(&head->subsys->lock);
206211
}
207212

208213
void nvme_mpath_add_disk_links(struct nvme_ns *ns)

0 commit comments

Comments
 (0)