提交 e90810b3 编写于 作者: S Sagi Grimberg 提交者: Yang Yingliang

nvme: fix controller removal race with scan work

mainline inclusion
from mainline-v5.3-rc5
commit 0157ec8d
category: bugfix
bugzilla: NA
CVE: NA
Link: https://gitee.com/openeuler/kernel/issues/I1WGZE

--------------------------------

With multipath enabled, nvme_scan_work() can read from the device
(through nvme_mpath_add_disk()) and hang [1]. However, with fabrics,
once ctrl->state is set to NVME_CTRL_DELETING, the reads will hang
(see nvmf_check_ready()) and the mpath stack device make_request
will block if head->list is not empty. However, when the head->list
consistst of only DELETING/DEAD controllers, we should actually not
block, but rather fail immediately.

In addition, before we go ahead and remove the namespaces, make sure
to clear the current path and kick the requeue list so that the
request will fast fail upon requeuing.

[1]:
Acked-by: NHanjun Guo <guohanjun@huawei.com>
Signed-off-by: NYang Yingliang <yangyingliang@huawei.com>
上级 dbf7a19d
...@@ -3505,6 +3505,13 @@ void nvme_remove_namespaces(struct nvme_ctrl *ctrl) ...@@ -3505,6 +3505,13 @@ void nvme_remove_namespaces(struct nvme_ctrl *ctrl)
struct nvme_ns *ns, *next; struct nvme_ns *ns, *next;
LIST_HEAD(ns_list); LIST_HEAD(ns_list);
/*
* make sure to requeue I/O to all namespaces as these
* might result from the scan itself and must complete
* for the scan_work to make progress
*/
nvme_mpath_clear_ctrl_paths(ctrl);
/* prevent racing with ns scanning */ /* prevent racing with ns scanning */
flush_work(&ctrl->scan_work); flush_work(&ctrl->scan_work);
......
...@@ -140,6 +140,17 @@ static const char *nvme_ana_state_names[] = { ...@@ -140,6 +140,17 @@ static const char *nvme_ana_state_names[] = {
[NVME_ANA_CHANGE] = "change", [NVME_ANA_CHANGE] = "change",
}; };
void nvme_mpath_clear_ctrl_paths(struct nvme_ctrl *ctrl)
{
struct nvme_ns *ns;
mutex_lock(&ctrl->scan_lock);
list_for_each_entry(ns, &ctrl->namespaces, list)
if (nvme_mpath_clear_current_path(ns))
kblockd_schedule_work(&ns->head->requeue_work);
mutex_unlock(&ctrl->scan_lock);
}
static struct nvme_ns *__nvme_find_path(struct nvme_ns_head *head) static struct nvme_ns *__nvme_find_path(struct nvme_ns_head *head)
{ {
struct nvme_ns *ns, *fallback = NULL; struct nvme_ns *ns, *fallback = NULL;
...@@ -180,6 +191,24 @@ inline struct nvme_ns *nvme_find_path(struct nvme_ns_head *head) ...@@ -180,6 +191,24 @@ inline struct nvme_ns *nvme_find_path(struct nvme_ns_head *head)
return ns; return ns;
} }
static bool nvme_available_path(struct nvme_ns_head *head)
{
struct nvme_ns *ns;
list_for_each_entry_rcu(ns, &head->list, siblings) {
switch (ns->ctrl->state) {
case NVME_CTRL_LIVE:
case NVME_CTRL_RESETTING:
case NVME_CTRL_CONNECTING:
/* fallthru */
return true;
default:
break;
}
}
return false;
}
static blk_qc_t nvme_ns_head_make_request(struct request_queue *q, static blk_qc_t nvme_ns_head_make_request(struct request_queue *q,
struct bio *bio) struct bio *bio)
{ {
...@@ -198,14 +227,14 @@ static blk_qc_t nvme_ns_head_make_request(struct request_queue *q, ...@@ -198,14 +227,14 @@ static blk_qc_t nvme_ns_head_make_request(struct request_queue *q,
disk_devt(ns->head->disk), disk_devt(ns->head->disk),
bio->bi_iter.bi_sector); bio->bi_iter.bi_sector);
ret = direct_make_request(bio); ret = direct_make_request(bio);
} else if (!list_empty_careful(&head->list)) { } else if (nvme_available_path(head)) {
dev_warn_ratelimited(dev, "no path available - requeuing I/O\n"); dev_warn_ratelimited(dev, "no usable path - requeuing I/O\n");
spin_lock_irq(&head->requeue_lock); spin_lock_irq(&head->requeue_lock);
bio_list_add(&head->requeue_list, bio); bio_list_add(&head->requeue_list, bio);
spin_unlock_irq(&head->requeue_lock); spin_unlock_irq(&head->requeue_lock);
} else { } else {
dev_warn_ratelimited(dev, "no path - failing I/O\n"); dev_warn_ratelimited(dev, "no available path - failing I/O\n");
bio->bi_status = BLK_STS_IOERR; bio->bi_status = BLK_STS_IOERR;
bio_endio(bio); bio_endio(bio);
......
...@@ -489,13 +489,17 @@ int nvme_mpath_init(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id); ...@@ -489,13 +489,17 @@ int nvme_mpath_init(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id);
void nvme_mpath_uninit(struct nvme_ctrl *ctrl); void nvme_mpath_uninit(struct nvme_ctrl *ctrl);
void nvme_mpath_stop(struct nvme_ctrl *ctrl); void nvme_mpath_stop(struct nvme_ctrl *ctrl);
static inline void nvme_mpath_clear_current_path(struct nvme_ns *ns) static inline bool nvme_mpath_clear_current_path(struct nvme_ns *ns)
{ {
struct nvme_ns_head *head = ns->head; struct nvme_ns_head *head = ns->head;
if (head && ns == rcu_access_pointer(head->current_path)) if (head && ns == rcu_access_pointer(head->current_path)) {
rcu_assign_pointer(head->current_path, NULL); rcu_assign_pointer(head->current_path, NULL);
return true;
}
return false;
} }
void nvme_mpath_clear_ctrl_paths(struct nvme_ctrl *ctrl);
struct nvme_ns *nvme_find_path(struct nvme_ns_head *head); struct nvme_ns *nvme_find_path(struct nvme_ns_head *head);
static inline void nvme_mpath_check_last_path(struct nvme_ns *ns) static inline void nvme_mpath_check_last_path(struct nvme_ns *ns)
...@@ -553,7 +557,11 @@ static inline void nvme_mpath_add_disk(struct nvme_ns *ns, ...@@ -553,7 +557,11 @@ static inline void nvme_mpath_add_disk(struct nvme_ns *ns,
static inline void nvme_mpath_remove_disk(struct nvme_ns_head *head) static inline void nvme_mpath_remove_disk(struct nvme_ns_head *head)
{ {
} }
static inline void nvme_mpath_clear_current_path(struct nvme_ns *ns) static inline bool nvme_mpath_clear_current_path(struct nvme_ns *ns)
{
return false;
}
static inline void nvme_mpath_clear_ctrl_paths(struct nvme_ctrl *ctrl)
{ {
} }
static inline void nvme_mpath_check_last_path(struct nvme_ns *ns) static inline void nvme_mpath_check_last_path(struct nvme_ns *ns)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册