mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-24 00:37:13 +07:00
ecca390e80
A deadlock happens in the following scenario with multipath:
1) scan_work(nvme0) detects a new nsid while nvme0
is an optimized path to it, path nvme1 happens to be
inaccessible.
2) Before scan_work is complete nvme0 disconnect is initiated
nvme_delete_ctrl_sync() sets nvme0 state to NVME_CTRL_DELETING
3) scan_work(1) attempts to submit IO,
but nvme_path_is_optimized() observes nvme0 is not LIVE.
Since nvme1 is a possible path IO is requeued and scan_work hangs.
--
Workqueue: nvme-wq nvme_scan_work [nvme_core]
kernel: Call Trace:
kernel: __schedule+0x2b9/0x6c0
kernel: schedule+0x42/0xb0
kernel: io_schedule+0x16/0x40
kernel: do_read_cache_page+0x438/0x830
kernel: read_cache_page+0x12/0x20
kernel: read_dev_sector+0x27/0xc0
kernel: read_lba+0xc1/0x220
kernel: efi_partition+0x1e6/0x708
kernel: check_partition+0x154/0x244
kernel: rescan_partitions+0xae/0x280
kernel: __blkdev_get+0x40f/0x560
kernel: blkdev_get+0x3d/0x140
kernel: __device_add_disk+0x388/0x480
kernel: device_add_disk+0x13/0x20
kernel: nvme_mpath_set_live+0x119/0x140 [nvme_core]
kernel: nvme_update_ns_ana_state+0x5c/0x60 [nvme_core]
kernel: nvme_set_ns_ana_state+0x1e/0x30 [nvme_core]
kernel: nvme_parse_ana_log+0xa1/0x180 [nvme_core]
kernel: nvme_mpath_add_disk+0x47/0x90 [nvme_core]
kernel: nvme_validate_ns+0x396/0x940 [nvme_core]
kernel: nvme_scan_work+0x24f/0x380 [nvme_core]
kernel: process_one_work+0x1db/0x380
kernel: worker_thread+0x249/0x400
kernel: kthread+0x104/0x140
--
4) Delete also hangs in flush_work(ctrl->scan_work)
from nvme_remove_namespaces().
Similiarly a deadlock with ana_work may happen: if ana_work has started
and calls nvme_mpath_set_live and device_add_disk, it will
trigger I/O. When we trigger disconnect I/O will block because
our accessible (optimized) path is disconnecting, but the alternate
path is inaccessible, so I/O blocks. Then disconnect tries to flush
the ana_work and hangs.
[ 605.550896] Workqueue: nvme-wq nvme_ana_work [nvme_core]
[ 605.552087] Call Trace:
[ 605.552683] __schedule+0x2b9/0x6c0
[ 605.553507] schedule+0x42/0xb0
[ 605.554201] io_schedule+0x16/0x40
[ 605.555012] do_read_cache_page+0x438/0x830
[ 605.556925] read_cache_page+0x12/0x20
[ 605.557757] read_dev_sector+0x27/0xc0
[ 605.558587] amiga_partition+0x4d/0x4c5
[ 605.561278] check_partition+0x154/0x244
[ 605.562138] rescan_partitions+0xae/0x280
[ 605.563076] __blkdev_get+0x40f/0x560
[ 605.563830] blkdev_get+0x3d/0x140
[ 605.564500] __device_add_disk+0x388/0x480
[ 605.565316] device_add_disk+0x13/0x20
[ 605.566070] nvme_mpath_set_live+0x5e/0x130 [nvme_core]
[ 605.567114] nvme_update_ns_ana_state+0x2c/0x30 [nvme_core]
[ 605.568197] nvme_update_ana_state+0xca/0xe0 [nvme_core]
[ 605.569360] nvme_parse_ana_log+0xa1/0x180 [nvme_core]
[ 605.571385] nvme_read_ana_log+0x76/0x100 [nvme_core]
[ 605.572376] nvme_ana_work+0x15/0x20 [nvme_core]
[ 605.573330] process_one_work+0x1db/0x380
[ 605.574144] worker_thread+0x4d/0x400
[ 605.574896] kthread+0x104/0x140
[ 605.577205] ret_from_fork+0x35/0x40
[ 605.577955] INFO: task nvme:14044 blocked for more than 120 seconds.
[ 605.579239] Tainted: G OE 5.3.5-050305-generic #201910071830
[ 605.580712] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[ 605.582320] nvme D 0 14044 14043 0x00000000
[ 605.583424] Call Trace:
[ 605.583935] __schedule+0x2b9/0x6c0
[ 605.584625] schedule+0x42/0xb0
[ 605.585290] schedule_timeout+0x203/0x2f0
[ 605.588493] wait_for_completion+0xb1/0x120
[ 605.590066] __flush_work+0x123/0x1d0
[ 605.591758] __cancel_work_timer+0x10e/0x190
[ 605.593542] cancel_work_sync+0x10/0x20
[ 605.594347] nvme_mpath_stop+0x2f/0x40 [nvme_core]
[ 605.595328] nvme_stop_ctrl+0x12/0x50 [nvme_core]
[ 605.596262] nvme_do_delete_ctrl+0x3f/0x90 [nvme_core]
[ 605.597333] nvme_sysfs_delete+0x5c/0x70 [nvme_core]
[ 605.598320] dev_attr_store+0x17/0x30
Fix this by introducing a new state: NVME_CTRL_DELETE_NOIO, which will
indicate the phase of controller deletion where I/O cannot be allowed
to access the namespace. NVME_CTRL_DELETING still allows mpath I/O to
be issued to the bottom device, and only after we flush the ana_work
and scan_work (after nvme_stop_ctrl and nvme_prep_remove_namespaces)
we change the state to NVME_CTRL_DELETING_NOIO. Also we prevent ana_work
from re-firing by aborting early if we are not LIVE, so we should be safe
here.
In addition, change the transport drivers to follow the updated state
machine.
Fixes: 0d0b660f21
("nvme: add ANA support")
Reported-by: Anton Eidelman <anton@lightbitslabs.com>
Signed-off-by: Sagi Grimberg <sagi@grimberg.me>
Signed-off-by: Christoph Hellwig <hch@lst.de>
192 lines
6.8 KiB
C
192 lines
6.8 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* NVMe over Fabrics common host code.
|
|
* Copyright (c) 2015-2016 HGST, a Western Digital Company.
|
|
*/
|
|
#ifndef _NVME_FABRICS_H
|
|
#define _NVME_FABRICS_H 1
|
|
|
|
#include <linux/in.h>
|
|
#include <linux/inet.h>
|
|
|
|
#define NVMF_MIN_QUEUE_SIZE 16
|
|
#define NVMF_MAX_QUEUE_SIZE 1024
|
|
#define NVMF_DEF_QUEUE_SIZE 128
|
|
#define NVMF_DEF_RECONNECT_DELAY 10
|
|
/* default to 600 seconds of reconnect attempts before giving up */
|
|
#define NVMF_DEF_CTRL_LOSS_TMO 600
|
|
|
|
/*
|
|
* Define a host as seen by the target. We allocate one at boot, but also
|
|
* allow the override it when creating controllers. This is both to provide
|
|
* persistence of the Host NQN over multiple boots, and to allow using
|
|
* multiple ones, for example in a container scenario. Because we must not
|
|
* use different Host NQNs with the same Host ID we generate a Host ID and
|
|
* use this structure to keep track of the relation between the two.
|
|
*/
|
|
struct nvmf_host {
|
|
struct kref ref;
|
|
struct list_head list;
|
|
char nqn[NVMF_NQN_SIZE];
|
|
uuid_t id;
|
|
};
|
|
|
|
/**
|
|
* enum nvmf_parsing_opts - used to define the sysfs parsing options used.
|
|
*/
|
|
enum {
|
|
NVMF_OPT_ERR = 0,
|
|
NVMF_OPT_TRANSPORT = 1 << 0,
|
|
NVMF_OPT_NQN = 1 << 1,
|
|
NVMF_OPT_TRADDR = 1 << 2,
|
|
NVMF_OPT_TRSVCID = 1 << 3,
|
|
NVMF_OPT_QUEUE_SIZE = 1 << 4,
|
|
NVMF_OPT_NR_IO_QUEUES = 1 << 5,
|
|
NVMF_OPT_TL_RETRY_COUNT = 1 << 6,
|
|
NVMF_OPT_KATO = 1 << 7,
|
|
NVMF_OPT_HOSTNQN = 1 << 8,
|
|
NVMF_OPT_RECONNECT_DELAY = 1 << 9,
|
|
NVMF_OPT_HOST_TRADDR = 1 << 10,
|
|
NVMF_OPT_CTRL_LOSS_TMO = 1 << 11,
|
|
NVMF_OPT_HOST_ID = 1 << 12,
|
|
NVMF_OPT_DUP_CONNECT = 1 << 13,
|
|
NVMF_OPT_DISABLE_SQFLOW = 1 << 14,
|
|
NVMF_OPT_HDR_DIGEST = 1 << 15,
|
|
NVMF_OPT_DATA_DIGEST = 1 << 16,
|
|
NVMF_OPT_NR_WRITE_QUEUES = 1 << 17,
|
|
NVMF_OPT_NR_POLL_QUEUES = 1 << 18,
|
|
NVMF_OPT_TOS = 1 << 19,
|
|
};
|
|
|
|
/**
|
|
* struct nvmf_ctrl_options - Used to hold the options specified
|
|
* with the parsing opts enum.
|
|
* @mask: Used by the fabrics library to parse through sysfs options
|
|
* on adding a NVMe controller.
|
|
* @transport: Holds the fabric transport "technology name" (for a lack of
|
|
* better description) that will be used by an NVMe controller
|
|
* being added.
|
|
* @subsysnqn: Hold the fully qualified NQN subystem name (format defined
|
|
* in the NVMe specification, "NVMe Qualified Names").
|
|
* @traddr: The transport-specific TRADDR field for a port on the
|
|
* subsystem which is adding a controller.
|
|
* @trsvcid: The transport-specific TRSVCID field for a port on the
|
|
* subsystem which is adding a controller.
|
|
* @host_traddr: A transport-specific field identifying the NVME host port
|
|
* to use for the connection to the controller.
|
|
* @queue_size: Number of IO queue elements.
|
|
* @nr_io_queues: Number of controller IO queues that will be established.
|
|
* @reconnect_delay: Time between two consecutive reconnect attempts.
|
|
* @discovery_nqn: indicates if the subsysnqn is the well-known discovery NQN.
|
|
* @kato: Keep-alive timeout.
|
|
* @host: Virtual NVMe host, contains the NQN and Host ID.
|
|
* @max_reconnects: maximum number of allowed reconnect attempts before removing
|
|
* the controller, (-1) means reconnect forever, zero means remove
|
|
* immediately;
|
|
* @disable_sqflow: disable controller sq flow control
|
|
* @hdr_digest: generate/verify header digest (TCP)
|
|
* @data_digest: generate/verify data digest (TCP)
|
|
* @nr_write_queues: number of queues for write I/O
|
|
* @nr_poll_queues: number of queues for polling I/O
|
|
* @tos: type of service
|
|
*/
|
|
struct nvmf_ctrl_options {
|
|
unsigned mask;
|
|
char *transport;
|
|
char *subsysnqn;
|
|
char *traddr;
|
|
char *trsvcid;
|
|
char *host_traddr;
|
|
size_t queue_size;
|
|
unsigned int nr_io_queues;
|
|
unsigned int reconnect_delay;
|
|
bool discovery_nqn;
|
|
bool duplicate_connect;
|
|
unsigned int kato;
|
|
struct nvmf_host *host;
|
|
int max_reconnects;
|
|
bool disable_sqflow;
|
|
bool hdr_digest;
|
|
bool data_digest;
|
|
unsigned int nr_write_queues;
|
|
unsigned int nr_poll_queues;
|
|
int tos;
|
|
};
|
|
|
|
/*
|
|
* struct nvmf_transport_ops - used to register a specific
|
|
* fabric implementation of NVMe fabrics.
|
|
* @entry: Used by the fabrics library to add the new
|
|
* registration entry to its linked-list internal tree.
|
|
* @module: Transport module reference
|
|
* @name: Name of the NVMe fabric driver implementation.
|
|
* @required_opts: sysfs command-line options that must be specified
|
|
* when adding a new NVMe controller.
|
|
* @allowed_opts: sysfs command-line options that can be specified
|
|
* when adding a new NVMe controller.
|
|
* @create_ctrl(): function pointer that points to a non-NVMe
|
|
* implementation-specific fabric technology
|
|
* that would go into starting up that fabric
|
|
* for the purpose of conneciton to an NVMe controller
|
|
* using that fabric technology.
|
|
*
|
|
* Notes:
|
|
* 1. At minimum, 'required_opts' and 'allowed_opts' should
|
|
* be set to the same enum parsing options defined earlier.
|
|
* 2. create_ctrl() must be defined (even if it does nothing)
|
|
* 3. struct nvmf_transport_ops must be statically allocated in the
|
|
* modules .bss section so that a pure module_get on @module
|
|
* prevents the memory from beeing freed.
|
|
*/
|
|
struct nvmf_transport_ops {
|
|
struct list_head entry;
|
|
struct module *module;
|
|
const char *name;
|
|
int required_opts;
|
|
int allowed_opts;
|
|
struct nvme_ctrl *(*create_ctrl)(struct device *dev,
|
|
struct nvmf_ctrl_options *opts);
|
|
};
|
|
|
|
static inline bool
|
|
nvmf_ctlr_matches_baseopts(struct nvme_ctrl *ctrl,
|
|
struct nvmf_ctrl_options *opts)
|
|
{
|
|
if (ctrl->state == NVME_CTRL_DELETING ||
|
|
ctrl->state == NVME_CTRL_DEAD ||
|
|
strcmp(opts->subsysnqn, ctrl->opts->subsysnqn) ||
|
|
strcmp(opts->host->nqn, ctrl->opts->host->nqn) ||
|
|
memcmp(&opts->host->id, &ctrl->opts->host->id, sizeof(uuid_t)))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
int nvmf_reg_read32(struct nvme_ctrl *ctrl, u32 off, u32 *val);
|
|
int nvmf_reg_read64(struct nvme_ctrl *ctrl, u32 off, u64 *val);
|
|
int nvmf_reg_write32(struct nvme_ctrl *ctrl, u32 off, u32 val);
|
|
int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl);
|
|
int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid, bool poll);
|
|
int nvmf_register_transport(struct nvmf_transport_ops *ops);
|
|
void nvmf_unregister_transport(struct nvmf_transport_ops *ops);
|
|
void nvmf_free_options(struct nvmf_ctrl_options *opts);
|
|
int nvmf_get_address(struct nvme_ctrl *ctrl, char *buf, int size);
|
|
bool nvmf_should_reconnect(struct nvme_ctrl *ctrl);
|
|
blk_status_t nvmf_fail_nonready_command(struct nvme_ctrl *ctrl,
|
|
struct request *rq);
|
|
bool __nvmf_check_ready(struct nvme_ctrl *ctrl, struct request *rq,
|
|
bool queue_live);
|
|
bool nvmf_ip_options_match(struct nvme_ctrl *ctrl,
|
|
struct nvmf_ctrl_options *opts);
|
|
|
|
static inline bool nvmf_check_ready(struct nvme_ctrl *ctrl, struct request *rq,
|
|
bool queue_live)
|
|
{
|
|
if (likely(ctrl->state == NVME_CTRL_LIVE ||
|
|
ctrl->state == NVME_CTRL_DELETING))
|
|
return true;
|
|
return __nvmf_check_ready(ctrl, rq, queue_live);
|
|
}
|
|
|
|
#endif /* _NVME_FABRICS_H */
|