[PATCH v2 3/3] nvme/multipath: fix stale ana state for namespaces just added by scan work
Keith Busch
kbusch at kernel.org
Mon Sep 13 09:07:56 PDT 2021
On Mon, Sep 13, 2021 at 09:30:23AM -0600, Anton Eidelman wrote:
> Scan work initially adds new namespaces to ctrl->namespaces TAIL.
> They make the list unordered temporarily until nvme_scan_work()
> finally sorts the list.
>
> In case nvme_update_ana_state() runs while the list is unsorted,
> the recently added namespaces are missed and their ana state
> may remain not updated forever if timing between scan work and ana work
> is unfortunate, e.g.
> Initial state: namespaces = {2, 3}
> scan_work: adds nsid=1: namespaces = {2, 3, 1}
> scan_work: finds nsid=1 is still Inaccessible
> ana_work: log page has nsids = {1, 2, 3, 4}, all Optimized.
> ana_work: updates nsids {2, 3} but fails to find nsid=1 in namespaces.
> scan_work: adds nsid=4: namespaces = {2, 3, 1, 4}
> scan_work: finds nsid=4 is Optimized: sets it live.
> scan_work: completes an sorts namespaces = {1, 2, 3, 4}
> Result: nsid=1 will remain in Inaccessible state.
>
> Solution:
> In order to preserve the way ctrl->namespaces is updated and sorted,
> make nvme_update_ana_state() deal with the case where ctrl->namespaces
> is not fully sorted and has new namespaces appended with potentially
> lower nsids.
> nvme_update_ana_state() keeps track of the nsid seen in the list,
> detects the unsorted case (rare), and restarts scanning of desc->nsids.
>
> Signed-off-by: Anton Eidelman <anton at lightbitslabs.com>
I guess this works for the structures we have in place, but I think we
should replace the sorted list and with xarray. Then you can get
something simpler to follow like:
---
diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index 5d7bc58a27bd..2b4bd624bded 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -595,21 +595,15 @@ static int nvme_update_ana_state(struct nvme_ctrl *ctrl,
if (desc->state == NVME_ANA_CHANGE)
(*nr_change_groups)++;
- if (!nr_nsids)
- return 0;
-
- down_read(&ctrl->namespaces_rwsem);
- list_for_each_entry(ns, &ctrl->namespaces, list) {
- unsigned nsid = le32_to_cpu(desc->nsids[n]);
+ while (n < nr_nsids) {
+ unsigned nsid = le32_to_cpu(desc->nsids[n++]);
- if (ns->head->ns_id < nsid)
- continue;
- if (ns->head->ns_id == nsid)
+ ns = nvme_find_get_ns(ctrl, nsid);
+ if (ns) {
nvme_update_ns_ana_state(desc, ns);
- if (++n == nr_nsids)
- break;
+ nvme_put_ns(ns);
+ }
}
- up_read(&ctrl->namespaces_rwsem);
return 0;
}
--
More information about the Linux-nvme
mailing list