nvme updates for Linux 7.4

- Parallelize nvme-tcp I/O queue setup and use the caller's network
    namespace
  - New multipath sysfs attributes for path state, available
    iopolicies, and fail on controller loss
  - Multipath fixes for failfast/ANA path evaluation and discovery
    subsystem iopolicy reporting
  - Apple M4 ANS2 NVMe support
  - Traffic based keep alive support for the target
  - DH-HMAC-CHAP hardening for host and target: enforce bidirectional
    auth, require auth before property set, fix state UAF, reject short
    payloads, restrict secret visibility
  - Target reservation preempt fixes
  - Target passthru I/O use-after-free fix
  - Fix out-of-bounds reads and memory leaks from device and host
    supplied data: FDP config log, ZNS zone reports, file namespace
    range errors
  - Enforce partition checks for unprivileged passthrough
  - nvme-rdma fix for bogus remote invalidation
  - nvmet-fc fix for recursive locking hang on port unlink
  - Revert DMA opt max_hw_sectors clamp due to performance regressions
  - Misc cleanups
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE3Fbyvv+648XNRdHTPe3zGtjzRgkFAmrROdIACgkQPe3zGtjz
 Rgkq4hAA6bZFleVy6i++KEHyCPG+ARrslrMs8P3i28ngMdEdv3Z5z6Yy8sm6cEyU
 Q/PUBbZgSy961lyP7PvPDRug20Cc2r7zQYT+TW2o7a90IY2eoUmS7uWDno2RLdGa
 YIGKqh0ZGWFauF9GB0OyDUPQ61BSzWD5IxUn9Ww9MXpGUJDSYxmi59gS6Ex4Pfqb
 89Ty+mbP6lIuC+rNrq1WWxhTjaKFzAHClrsePZogPUwZX7wNf4nKRxyhnnGU5Pad
 WS6vw/HNMt7nVahfuvOm4+l1g1+LYVLHhsLqMrZeCZiywH9K6obN6MW4CZiURbfv
 +SO3qXSM7YqIuTXMFiN11kUSQujeY/Uww00aHR1ieDf2v6uh91pS/r2mp34JWxtr
 j+IFBFBrCUN0KENvteoxeEi63ZUd7CEXHJXQF0ptbIg4JOiEFUTSszdFR3fkk/pw
 Ei7LSlQERBWwA+BGY6bKv/j47JCfpZTOyu5LJxLaY5CfkK2AscIofX8ztzjjKaAk
 OlyeRG9flsZt5jUiX+9s01nRRxEgyAUMbFtlWP4W+SqvYHJ2NXln0kXPGIHUOuJ2
 gw860Wg/ZpVgT4EZoEjHkZdbeQtjUMVlv0x+pfSA9DwmXM/XEnPwarFC0x7ZXZfk
 Ea6Ci7q9iHMiBNb+utDndhyTIrxeLccWU5me+wD5iPvYv5Pmq1g=
 =hX7L
 -----END PGP SIGNATURE-----

Merge tag 'nvme-7.4-2026-10-15' of git://git.infradead.org/nvme into for-7.4/block

Pull NVMe updates from Keith:

"- Parallelize nvme-tcp I/O queue setup and use the caller's network
   namespace
 - New multipath sysfs attributes for path state, available
   iopolicies, and fail on controller loss
 - Multipath fixes for failfast/ANA path evaluation and discovery
   subsystem iopolicy reporting
 - Apple M4 ANS2 NVMe support
 - Traffic based keep alive support for the target
 - DH-HMAC-CHAP hardening for host and target: enforce bidirectional
   auth, require auth before property set, fix state UAF, reject short
   payloads, restrict secret visibility
 - Target reservation preempt fixes
 - Target passthru I/O use-after-free fix
 - Fix out-of-bounds reads and memory leaks from device and host
   supplied data: FDP config log, ZNS zone reports, file namespace
   range errors
 - Enforce partition checks for unprivileged passthrough
 - nvme-rdma fix for bogus remote invalidation
 - nvmet-fc fix for recursive locking hang on port unlink
 - Revert DMA opt max_hw_sectors clamp due to performance regressions
 - Misc cleanups"

* tag 'nvme-7.4-2026-10-15' of git://git.infradead.org/nvme: (28 commits)
  Revert "nvme-pci: clamp max_hw_sectors based on DMA optimized limitation"
  nvme: make the DH-HMAC-CHAP secret attributes readable by root only
  nvmet: zns: don't leak stale memory in zone management receive
  nvme: bound FDP config log reads to the received buffer
  nvme: pass partition flags to passthrough checks
  nvmet-auth: reject undersized AUTH_SEND payloads
  nvmet-fc: don't drop the last assoc reference in nvmet_fc_delete_ctrl()
  nvmet: require authentication before property set
  nvmet: free the bvec on file namespace range errors
  nvme-auth: fail when the controller does not authenticate itself
  nvmet-auth: serialize access to DH-HMAC-CHAP state to fix UAF
  nvmet: fix preempt of another registrant by the reservation holder
  nvmet: fix use-after-free of the old registrant in nvmet_pr_preempt()
  nvme-multipath: add fail_on_ctrl_loss sysfs attribute
  nvme-multipath: fix path state evaluation for failfast and ANA
  nvme-multipath: report "none" iopolicy for discovery subsystems
  nvme-rdma: don't complete a request after a bogus remote invalidation
  nvmet: fix use-after-free in passthru I/O hotpath
  nvmet: introduce struct nvmet_passthru
  nvmet: Add traffic based keep alive support to configfs
  ...
This commit is contained in:
Jens Axboe 2026-10-15 16:16:27 -06:00
commit 341a043c24
24 changed files with 697 additions and 142 deletions

View File

@ -326,6 +326,27 @@ Description:
currently selected path. Returns empty if iopolicy is not
"numa". Requires CONFIG_NVME_MULTIPATH.
What: /sys/block/nvmeXcYnZ/path_state
Date: September 2026
KernelVersion: 7.4
Contact: Guixin Liu <kanie@linux.alibaba.com>
Description:
Shows the current multipath path state for this namespace
path device. Possible values:
"enabled": the path is usable for I/O.
"ctrl-down": the path is disabled because the controller
is not live.
"ana-pending": the path is disabled because an ANA state
change is pending.
"ns-not-ready": the path is disabled because the namespace
is not ready.
Requires CONFIG_NVME_MULTIPATH.
What: /sys/block/nvmeXnY/delayed_removal_secs
Date: May 2025
KernelVersion: 6.16
@ -337,6 +358,22 @@ Description:
is deferred. Only visible on multipath head devices.
Requires CONFIG_NVME_MULTIPATH.
What: /sys/block/nvmeXnY/fail_on_ctrl_loss
Date: October 2026
KernelVersion: 7.4
Contact: Krishna Iyer <kiyer@crusoe.ai>
Description:
Shows or sets the fail-on-ctrl-loss policy of the multipath
head device ("on" or "off", default "off"). When on, I/O
queued or arriving while no usable path exists is failed
immediately instead of being queued. This is the opposite of
the queue-if-no-path behavior enabled by a nonzero
delayed_removal_secs: when both are set fail_on_ctrl_loss takes
precedence and the parked I/O is failed rather than held for
the delayed_removal_secs window. Reconnect attempts are not
affected. Only visible on multipath head devices.
Requires CONFIG_NVME_MULTIPATH.
What: /sys/block/nvmeXnY/csi
What: /sys/block/nvmeXnY/metadata_bytes
What: /sys/block/nvmeXnY/nuse
@ -444,6 +481,21 @@ Description:
selections. Only available when CONFIG_NVME_MULTIPATH is
enabled.
A discovery subsystem has no namespaces and hence no I/O
paths to select from: reading returns "none" and writing
fails with EOPNOTSUPP.
What: /sys/class/nvme-subsystem/nvme-subsysX/iopolicies
Date: September 2026
KernelVersion: 7.4
Contact: Laurence Oberman <loberman@redhat.com>
Description:
Shows the I/O path selection policies that may be written
to the iopolicy attribute of this subsystem, separated by
spaces: "numa round-robin queue-depth". A discovery
subsystem accepts no policy at all and reports "none".
Only available when CONFIG_NVME_MULTIPATH is enabled.
What: /sys/class/nvme-subsystem/nvme-subsysX/subsystype
Date: September 2021
KernelVersion: 5.16

View File

@ -16,6 +16,7 @@ properties:
- items:
- const: apple,t6020-nvme-ans2
- const: apple,t8103-nvme-ans2
- const: apple,t8132-nvme-ans2
- items:
- enum:
# Do not add additional SoC to this list.

View File

@ -42,7 +42,11 @@
#define APPLE_ANS_ACQ_DB 0x1004
#define APPLE_ANS_IOCQ_DB 0x100c
#define APPLE_ANS_MAX_PEND_CMDS_CTRL 0x1210
#define APPLE_ANS_T8132_IOQ_CMDS 0x1200
#define APPLE_ANS_T8132_IOQ_CQES 0x1208
#define APPLE_ANS_T8132_IOQ_SIZE 0x1210
#define APPLE_ANS_T8103_MAX_PEND_CMDS_CTRL 0x1210
#define APPLE_ANS_BOOT_STATUS 0x1300
#define APPLE_ANS_BOOT_STATUS_OK 0xde71ce55
@ -184,6 +188,7 @@ struct apple_nvme_iod {
struct apple_nvme_hw {
bool has_lsq_nvmmu;
bool needs_ioq_register;
u32 max_queue_depth;
};
@ -1133,9 +1138,16 @@ static void apple_nvme_reset_work(struct work_struct *work)
anv->mmio_nvme + APPLE_ANS_LINEAR_SQ_CTRL);
/* Allow as many pending command as possible for both queues */
writel(anv->hw->max_queue_depth
| (anv->hw->max_queue_depth << 16), anv->mmio_nvme
+ APPLE_ANS_MAX_PEND_CMDS_CTRL);
if (!anv->hw->needs_ioq_register) {
/*
* Since T8132, this is replaced by IOQ_SIZE which is
* located at the same offset and is handled with the
* IO queue setup further below.
*/
writel(anv->hw->max_queue_depth
| (anv->hw->max_queue_depth << 16), anv->mmio_nvme
+ APPLE_ANS_T8103_MAX_PEND_CMDS_CTRL);
}
/* Setup the NVMMU for the maximum admin and IO queue depth */
writel(anv->hw->max_queue_depth - 1,
@ -1190,6 +1202,16 @@ static void apple_nvme_reset_work(struct work_struct *work)
if (ret)
goto out_remove_cq;
if (anv->hw->needs_ioq_register) {
writeq(anv->ioq.cq_dma_addr,
anv->mmio_nvme + APPLE_ANS_T8132_IOQ_CQES);
writeq(anv->ioq.sq_dma_addr,
anv->mmio_nvme + APPLE_ANS_T8132_IOQ_CMDS);
writel((anv->hw->max_queue_depth - 1)
| ((anv->hw->max_queue_depth - 1) << 16), anv->mmio_nvme
+ APPLE_ANS_T8132_IOQ_SIZE);
}
apple_nvme_init_queue(&anv->ioq);
nr_io_queues = 1;
ret = nvme_set_queue_count(&anv->ctrl, &nr_io_queues);
@ -1699,17 +1721,26 @@ static DEFINE_SIMPLE_DEV_PM_OPS(apple_nvme_pm_ops, apple_nvme_suspend,
static const struct apple_nvme_hw apple_nvme_t8015_hw = {
.has_lsq_nvmmu = false,
.needs_ioq_register = false,
.max_queue_depth = 16,
};
static const struct apple_nvme_hw apple_nvme_t8103_hw = {
.has_lsq_nvmmu = true,
.needs_ioq_register = false,
.max_queue_depth = 64,
};
static const struct apple_nvme_hw apple_nvme_t8132_hw = {
.has_lsq_nvmmu = true,
.needs_ioq_register = true,
.max_queue_depth = 64,
};
static const struct of_device_id apple_nvme_of_match[] = {
{ .compatible = "apple,t8015-nvme-ans2", .data = &apple_nvme_t8015_hw },
{ .compatible = "apple,t8103-nvme-ans2", .data = &apple_nvme_t8103_hw },
{ .compatible = "apple,t8132-nvme-ans2", .data = &apple_nvme_t8132_hw },
{ .compatible = "apple,nvme-ans2", .data = &apple_nvme_t8103_hw },
{},
};

View File

@ -358,8 +358,21 @@ static int nvme_auth_process_dhchap_success1(struct nvme_ctrl *ctrl,
nvme_auth_hmac_name(chap->hash_id),
nvme_auth_dhgroup_name(chap->dhgroup_id));
if (!data->rvalid)
if (!data->rvalid) {
/*
* The controller did not return a response. If we asked it to
* authenticate itself the session must not be used, otherwise
* mutual authentication would silently degrade to one-way.
*/
if (chap->bi_directional) {
dev_warn(ctrl->device,
"qid %d: controller did not authenticate\n",
chap->qid);
chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED;
return -ECONNREFUSED;
}
return 0;
}
/* Validate controller response */
if (crypto_memneq(chap->response, data->rval, data->hl)) {

View File

@ -2273,13 +2273,18 @@ static int nvme_query_fdp_granularity(struct nvme_ctrl *ctrl,
}
size = le32_to_cpu(hdr.sze);
if (size < sizeof(*h) + sizeof(*desc)) {
dev_warn(ctrl->device, "FDP config size too small:%zu\n",
size);
return 0;
}
if (size > PAGE_SIZE * MAX_ORDER_NR_PAGES) {
dev_warn(ctrl->device, "FDP config size too large:%zu\n",
size);
return 0;
}
h = kvmalloc(size, GFP_KERNEL);
h = kvzalloc(size, GFP_KERNEL);
if (!h)
return -ENOMEM;
@ -2305,9 +2310,9 @@ static int nvme_query_fdp_granularity(struct nvme_ctrl *ctrl,
desc = log;
end = log + size - sizeof(*h);
for (i = 0; i < fdp_idx; i++) {
u16 dsze = le16_to_cpu(desc->dsze);
u16 dsze = get_unaligned_le16(&desc->dsze);
if (!dsze || log + dsze > end) {
if (dsze < sizeof(*desc) || log + dsze + sizeof(*desc) > end) {
dev_warn(ctrl->device,
"FDP invalid config descriptor at index %d\n", i);
ret = 0;
@ -2317,13 +2322,13 @@ static int nvme_query_fdp_granularity(struct nvme_ctrl *ctrl,
desc = log;
}
if (le32_to_cpu(desc->nrg) > 1) {
if (get_unaligned_le32(&desc->nrg) > 1) {
dev_warn(ctrl->device, "FDP NRG > 1 not supported\n");
ret = 0;
goto out;
}
info->runs = le64_to_cpu(desc->runs);
info->runs = get_unaligned_le64(&desc->runs);
out:
kvfree(h);
return ret;

View File

@ -343,7 +343,7 @@ static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
c.common.cdw14 = cpu_to_le32(cmd.cdw14);
c.common.cdw15 = cpu_to_le32(cmd.cdw15);
if (!nvme_cmd_allowed(ctrl, ns, &c, 0, open_for_write))
if (!nvme_cmd_allowed(ctrl, ns, &c, flags, open_for_write))
return -EACCES;
if (cmd.timeout_ms)

View File

@ -307,7 +307,14 @@ int nvme_mpath_revalidate_zones(struct nvme_ns_head *head)
}
#endif /* CONFIG_BLK_DEV_ZONED */
static bool nvme_path_is_disabled(struct nvme_ns *ns)
enum nvme_path_state {
NVME_PATH_ENABLED,
NVME_PATH_DISABLED_CTRL_DOWN,
NVME_PATH_DISABLED_ANA_PENDING,
NVME_PATH_DISABLED_NS_NOT_READY,
};
static enum nvme_path_state nvme_path_get_state(struct nvme_ns *ns)
{
enum nvme_ctrl_state state = nvme_ctrl_state(ns->ctrl);
@ -317,11 +324,17 @@ static bool nvme_path_is_disabled(struct nvme_ns *ns)
* Otherwise it will fail immediately and return to the requeue list.
*/
if (state != NVME_CTRL_LIVE && state != NVME_CTRL_DELETING)
return true;
if (test_bit(NVME_NS_ANA_PENDING, &ns->flags) ||
!test_bit(NVME_NS_READY, &ns->flags))
return true;
return false;
return NVME_PATH_DISABLED_CTRL_DOWN;
if (test_bit(NVME_NS_ANA_PENDING, &ns->flags))
return NVME_PATH_DISABLED_ANA_PENDING;
if (!test_bit(NVME_NS_READY, &ns->flags))
return NVME_PATH_DISABLED_NS_NOT_READY;
return NVME_PATH_ENABLED;
}
static bool nvme_path_is_disabled(struct nvme_ns *ns)
{
return nvme_path_get_state(ns) != NVME_PATH_ENABLED;
}
static struct nvme_ns *__nvme_find_path(struct nvme_ns_head *head, int node)
@ -496,9 +509,17 @@ inline struct nvme_ns *nvme_find_path(struct nvme_ns_head *head)
}
}
static inline bool nvme_state_is_live(enum nvme_ana_state state)
{
return state == NVME_ANA_OPTIMIZED || state == NVME_ANA_NONOPTIMIZED;
}
static bool nvme_available_path(struct nvme_ns_head *head)
__must_hold_shared(&head->srcu)
{
bool fail_on_ctrl_loss = test_bit(NVME_NSHEAD_FAIL_ON_CTRL_LOSS,
&head->flags);
bool failfast = false;
struct nvme_ns *ns;
if (!test_bit(NVME_NSHEAD_DISK_LIVE, &head->flags))
@ -506,18 +527,30 @@ static bool nvme_available_path(struct nvme_ns_head *head)
list_for_each_entry_srcu(ns, &head->list, siblings,
srcu_read_lock_held(&head->srcu)) {
if (test_bit(NVME_CTRL_FAILFAST_EXPIRED, &ns->ctrl->flags))
if (test_bit(NVME_CTRL_FAILFAST_EXPIRED, &ns->ctrl->flags)) {
failfast = true;
continue;
}
switch (nvme_ctrl_state(ns->ctrl)) {
case NVME_CTRL_LIVE:
case NVME_CTRL_RESETTING:
case NVME_CTRL_CONNECTING:
if (!nvme_state_is_live(ns->ana_state) &&
ns->ana_state != NVME_ANA_CHANGE)
continue;
return true;
case NVME_CTRL_RESETTING:
return true;
case NVME_CTRL_CONNECTING:
if (!fail_on_ctrl_loss)
return true;
continue;
default:
break;
}
}
if (failfast || fail_on_ctrl_loss)
return false;
/*
* If "head->delayed_removal_secs" is configured (i.e., non-zero), do
* not immediately fail I/O. Instead, requeue the I/O for the configured
@ -874,11 +907,6 @@ static int nvme_parse_ana_log(struct nvme_ctrl *ctrl, void *data,
return 0;
}
static inline bool nvme_state_is_live(enum nvme_ana_state state)
{
return state == NVME_ANA_OPTIMIZED || state == NVME_ANA_NONOPTIMIZED;
}
static void nvme_update_ns_ana_state(struct nvme_ana_group_desc *desc,
struct nvme_ns *ns)
{
@ -913,8 +941,10 @@ static void nvme_update_ns_ana_state(struct nvme_ana_group_desc *desc,
* is not live but still create the sysfs link to this path from
* head node if head node of the path has already come alive.
*/
if (test_bit(NVME_NSHEAD_DISK_LIVE, &ns->head->flags))
if (test_bit(NVME_NSHEAD_DISK_LIVE, &ns->head->flags)) {
nvme_mpath_add_sysfs_link(ns->head);
kblockd_schedule_work(&ns->head->requeue_work);
}
}
}
@ -1041,6 +1071,10 @@ static ssize_t nvme_subsys_iopolicy_show(struct device *dev,
struct nvme_subsystem *subsys =
container_of(dev, struct nvme_subsystem, dev);
/* A discovery subsystem has no namespaces and hence no I/O paths */
if (subsys->subtype == NVME_NQN_DISC)
return sysfs_emit(buf, "none\n");
return sysfs_emit(buf, "%s\n",
nvme_iopolicy_names[READ_ONCE(subsys->iopolicy)]);
}
@ -1075,6 +1109,9 @@ static ssize_t nvme_subsys_iopolicy_store(struct device *dev,
container_of(dev, struct nvme_subsystem, dev);
int policy;
if (subsys->subtype == NVME_NQN_DISC)
return -EOPNOTSUPP;
policy = nvme_iopolicy_parse(buf);
if (policy < 0)
return policy;
@ -1085,6 +1122,26 @@ static ssize_t nvme_subsys_iopolicy_store(struct device *dev,
SUBSYS_ATTR_RW(iopolicy, S_IRUGO | S_IWUSR,
nvme_subsys_iopolicy_show, nvme_subsys_iopolicy_store);
static ssize_t iopolicies_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct nvme_subsystem *subsys =
container_of(dev, struct nvme_subsystem, dev);
int i, len = 0;
if (subsys->subtype == NVME_NQN_DISC)
return sysfs_emit(buf, "none\n");
for (i = 0; i < ARRAY_SIZE(nvme_iopolicy_names); i++)
len += sysfs_emit_at(buf, len, "%s%s", i ? " " : "",
nvme_iopolicy_names[i]);
len += sysfs_emit_at(buf, len, "\n");
return len;
}
struct device_attribute subsys_attr_iopolicies = __ATTR_RO(iopolicies);
static ssize_t ana_grpid_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@ -1113,6 +1170,28 @@ static ssize_t queue_depth_show(struct device *dev,
}
DEVICE_ATTR_RO(queue_depth);
static const char * const nvme_path_state_names[] = {
[NVME_PATH_ENABLED] = "enabled",
[NVME_PATH_DISABLED_CTRL_DOWN] = "ctrl-down",
[NVME_PATH_DISABLED_ANA_PENDING] = "ana-pending",
[NVME_PATH_DISABLED_NS_NOT_READY] = "ns-not-ready",
};
static ssize_t path_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct nvme_ns *ns = nvme_get_ns_from_dev(dev);
enum nvme_path_state state = nvme_path_get_state(ns);
const char *name = "unknown";
if (state < ARRAY_SIZE(nvme_path_state_names) &&
nvme_path_state_names[state])
name = nvme_path_state_names[state];
return sysfs_emit(buf, "%s\n", name);
}
DEVICE_ATTR_RO(path_state);
static ssize_t numa_nodes_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@ -1167,10 +1246,7 @@ static ssize_t delayed_removal_secs_store(struct device *dev,
mutex_lock(&head->subsys->lock);
head->delayed_removal_secs = sec;
if (sec)
set_bit(NVME_NSHEAD_QUEUE_IF_NO_PATH, &head->flags);
else
clear_bit(NVME_NSHEAD_QUEUE_IF_NO_PATH, &head->flags);
assign_bit(NVME_NSHEAD_QUEUE_IF_NO_PATH, &head->flags, sec);
mutex_unlock(&head->subsys->lock);
/*
* Ensure that update to NVME_NSHEAD_QUEUE_IF_NO_PATH is seen
@ -1183,6 +1259,47 @@ static ssize_t delayed_removal_secs_store(struct device *dev,
DEVICE_ATTR_RW(delayed_removal_secs);
static ssize_t fail_on_ctrl_loss_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gendisk *disk = dev_to_disk(dev);
struct nvme_ns_head *head = disk->private_data;
return sysfs_emit(buf, test_bit(NVME_NSHEAD_FAIL_ON_CTRL_LOSS,
&head->flags) ? "on\n" : "off\n");
}
static ssize_t fail_on_ctrl_loss_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct gendisk *disk = dev_to_disk(dev);
struct nvme_ns_head *head = disk->private_data;
bool enable;
int ret;
ret = kstrtobool(buf, &enable);
if (ret < 0)
return ret;
if (enable) {
if (test_and_set_bit(NVME_NSHEAD_FAIL_ON_CTRL_LOSS, &head->flags))
return count;
} else {
if (!test_and_clear_bit(NVME_NSHEAD_FAIL_ON_CTRL_LOSS,
&head->flags))
return count;
}
synchronize_srcu(&head->srcu);
if (enable)
kblockd_schedule_work(&head->requeue_work);
return count;
}
DEVICE_ATTR_RW(fail_on_ctrl_loss);
static ssize_t multipath_failover_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{

View File

@ -589,6 +589,7 @@ struct nvme_ns_head {
#define NVME_NSHEAD_DISK_LIVE 0
#define NVME_NSHEAD_QUEUE_IF_NO_PATH 1
#define NVME_NSHEAD_CDEV_LIVE 2
#define NVME_NSHEAD_FAIL_ON_CTRL_LOSS 3
struct nvme_ns __rcu_guarded *current_path[];
#endif
};
@ -1095,11 +1096,14 @@ extern struct device_attribute dev_attr_ana_grpid;
extern struct device_attribute dev_attr_ana_state;
extern struct device_attribute dev_attr_queue_depth;
extern struct device_attribute dev_attr_numa_nodes;
extern struct device_attribute dev_attr_path_state;
extern struct device_attribute dev_attr_delayed_removal_secs;
extern struct device_attribute dev_attr_fail_on_ctrl_loss;
extern struct device_attribute dev_attr_multipath_failover_count;
extern struct device_attribute dev_attr_io_requeue_no_usable_path_count;
extern struct device_attribute dev_attr_io_fail_no_available_path_count;
extern struct device_attribute subsys_attr_iopolicy;
extern struct device_attribute subsys_attr_iopolicies;
static inline bool nvme_disk_is_ns_head(struct gendisk *disk)
{

View File

@ -3747,7 +3747,7 @@ static struct nvme_dev *nvme_pci_alloc_dev(struct pci_dev *pdev,
*/
dev->ctrl.max_hw_sectors = min_t(u32,
NVME_MAX_BYTES >> SECTOR_SHIFT,
dma_opt_mapping_size(&pdev->dev) >> 9);
dma_max_mapping_size(&pdev->dev) >> 9);
dev->ctrl.max_segments = NVME_MAX_SEGS;
dev->ctrl.max_integrity_segments = 1;
return dev;

View File

@ -1748,6 +1748,7 @@ static void nvme_rdma_process_nvme_rsp(struct nvme_rdma_queue *queue,
"Bogus remote invalidation for rkey %#x\n",
req->mr ? req->mr->rkey : 0);
nvme_rdma_error_recovery(queue->ctrl);
return;
}
} else if (req->mr) {
int ret;

View File

@ -263,7 +263,9 @@ static struct attribute *nvme_ns_attrs[] = {
&dev_attr_ana_state.attr,
&dev_attr_queue_depth.attr,
&dev_attr_numa_nodes.attr,
&dev_attr_path_state.attr,
&dev_attr_delayed_removal_secs.attr,
&dev_attr_fail_on_ctrl_loss.attr,
#endif
&dev_attr_io_passthru_err_log_enabled.attr,
NULL,
@ -296,11 +298,13 @@ static umode_t nvme_ns_attrs_are_visible(struct kobject *kobj,
if (!nvme_ctrl_use_ana(nvme_get_ns_from_dev(dev)->ctrl))
return 0;
}
if (a == &dev_attr_queue_depth.attr || a == &dev_attr_numa_nodes.attr) {
if (a == &dev_attr_queue_depth.attr || a == &dev_attr_numa_nodes.attr ||
a == &dev_attr_path_state.attr) {
if (nvme_disk_is_ns_head(dev_to_disk(dev)))
return 0;
}
if (a == &dev_attr_delayed_removal_secs.attr) {
if (a == &dev_attr_delayed_removal_secs.attr ||
a == &dev_attr_fail_on_ctrl_loss.attr) {
struct gendisk *disk = dev_to_disk(dev);
if (!nvme_disk_is_ns_head(disk))
@ -809,7 +813,7 @@ static DEVICE_ATTR(io_timeout, S_IRUGO | S_IWUSR,
nvme_io_timeout_show, nvme_io_timeout_store);
#ifdef CONFIG_NVME_HOST_AUTH
static ssize_t nvme_ctrl_dhchap_secret_show(struct device *dev,
static ssize_t dhchap_secret_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
@ -820,7 +824,7 @@ static ssize_t nvme_ctrl_dhchap_secret_show(struct device *dev,
return sysfs_emit(buf, "%s\n", opts->dhchap_secret);
}
static ssize_t nvme_ctrl_dhchap_secret_store(struct device *dev,
static ssize_t dhchap_secret_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
@ -864,10 +868,9 @@ static ssize_t nvme_ctrl_dhchap_secret_store(struct device *dev,
return count;
}
static DEVICE_ATTR(dhchap_secret, S_IRUGO | S_IWUSR,
nvme_ctrl_dhchap_secret_show, nvme_ctrl_dhchap_secret_store);
static DEVICE_ATTR_ADMIN_RW(dhchap_secret);
static ssize_t nvme_ctrl_dhchap_ctrl_secret_show(struct device *dev,
static ssize_t dhchap_ctrl_secret_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
@ -878,7 +881,7 @@ static ssize_t nvme_ctrl_dhchap_ctrl_secret_show(struct device *dev,
return sysfs_emit(buf, "%s\n", opts->dhchap_ctrl_secret);
}
static ssize_t nvme_ctrl_dhchap_ctrl_secret_store(struct device *dev,
static ssize_t dhchap_ctrl_secret_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
@ -922,8 +925,7 @@ static ssize_t nvme_ctrl_dhchap_ctrl_secret_store(struct device *dev,
return count;
}
static DEVICE_ATTR(dhchap_ctrl_secret, S_IRUGO | S_IWUSR,
nvme_ctrl_dhchap_ctrl_secret_show, nvme_ctrl_dhchap_ctrl_secret_store);
static DEVICE_ATTR_ADMIN_RW(dhchap_ctrl_secret);
#endif
static struct attribute *nvme_dev_attrs[] = {
@ -1298,6 +1300,7 @@ static struct attribute *nvme_subsys_attrs[] = {
&subsys_attr_subsystype.attr,
#ifdef CONFIG_NVME_MULTIPATH
&subsys_attr_iopolicy.attr,
&subsys_attr_iopolicies.attr,
#endif
NULL,
};

View File

@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/async.h>
#include <linux/err.h>
#include <linux/crc32.h>
#include <linux/nvme-tcp.h>
@ -54,7 +55,8 @@ MODULE_PARM_DESC(tls_handshake_timeout,
"nvme TLS handshake timeout in seconds (default 10)");
#endif
static atomic_t nvme_tcp_cpu_queues[NR_CPUS];
static int nvme_tcp_cpu_queues[NR_CPUS];
static DEFINE_SPINLOCK(nvme_tcp_cpu_queues_lock);
enum nvme_tcp_send_state {
NVME_TCP_SEND_CMD_PDU = 0,
@ -154,6 +156,12 @@ struct nvme_tcp_queue {
static DEFINE_MUTEX(nvme_tcp_ctrl_mutex);
static LIST_HEAD_GUARDED(nvme_tcp_ctrl_list, nvme_tcp_ctrl_mutex);
struct nvme_tcp_queue_setup_ctx {
struct nvme_ctrl *ctrl;
int qid;
int *err;
};
struct nvme_tcp_ctrl {
/* read only in the hot path */
struct nvme_tcp_queue *queues;
@ -171,6 +179,7 @@ struct nvme_tcp_ctrl {
struct delayed_work connect_work;
struct nvme_tcp_request async_req;
u32 io_queues[HCTX_MAX_TYPES];
struct net *net;
};
static struct workqueue_struct *nvme_tcp_wq;
@ -1730,9 +1739,10 @@ static void nvme_tcp_set_queue_io_cpu(struct nvme_tcp_queue *queue)
goto out;
/* Search for the least used cpu from the mq_map */
spin_lock(&nvme_tcp_cpu_queues_lock);
io_cpu = WORK_CPU_UNBOUND;
for_each_online_cpu(cpu) {
int num_queues = atomic_read(&nvme_tcp_cpu_queues[cpu]);
int num_queues = nvme_tcp_cpu_queues[cpu];
if (mq_map[cpu] != qid)
continue;
@ -1743,9 +1753,10 @@ static void nvme_tcp_set_queue_io_cpu(struct nvme_tcp_queue *queue)
}
if (io_cpu != WORK_CPU_UNBOUND) {
queue->io_cpu = io_cpu;
atomic_inc(&nvme_tcp_cpu_queues[io_cpu]);
nvme_tcp_cpu_queues[io_cpu]++;
set_bit(NVME_TCP_Q_IO_CPU_SET, &queue->flags);
}
spin_unlock(&nvme_tcp_cpu_queues_lock);
out:
dev_dbg(ctrl->ctrl.device, "queue %d: using cpu %d\n",
qid, queue->io_cpu);
@ -1858,7 +1869,7 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl, int qid,
queue->cmnd_capsule_len = sizeof(struct nvme_command) +
NVME_TCP_ADMIN_CCSZ;
ret = sock_create_kern(current->nsproxy->net_ns,
ret = sock_create_kern(ctrl->net,
ctrl->addr.ss_family, SOCK_STREAM,
IPPROTO_TCP, &queue->sock);
if (ret) {
@ -2022,8 +2033,11 @@ static void nvme_tcp_stop_queue_nowait(struct nvme_ctrl *nctrl, int qid)
if (!test_bit(NVME_TCP_Q_ALLOCATED, &queue->flags))
return;
if (test_and_clear_bit(NVME_TCP_Q_IO_CPU_SET, &queue->flags))
atomic_dec(&nvme_tcp_cpu_queues[queue->io_cpu]);
if (test_and_clear_bit(NVME_TCP_Q_IO_CPU_SET, &queue->flags)) {
spin_lock(&nvme_tcp_cpu_queues_lock);
nvme_tcp_cpu_queues[queue->io_cpu]--;
spin_unlock(&nvme_tcp_cpu_queues_lock);
}
mutex_lock(&queue->queue_lock);
if (test_and_clear_bit(NVME_TCP_Q_LIVE, &queue->flags))
@ -2130,25 +2144,6 @@ static void nvme_tcp_stop_io_queues(struct nvme_ctrl *ctrl)
nvme_tcp_wait_queue(ctrl, i);
}
static int nvme_tcp_start_io_queues(struct nvme_ctrl *ctrl,
int first, int last)
{
int i, ret;
for (i = first; i < last; i++) {
ret = nvme_tcp_start_queue(ctrl, i);
if (ret)
goto out_stop_queues;
}
return 0;
out_stop_queues:
for (i--; i >= first; i--)
nvme_tcp_stop_queue(ctrl, i);
return ret;
}
static int nvme_tcp_alloc_admin_queue(struct nvme_ctrl *ctrl)
{
int ret;
@ -2183,10 +2178,8 @@ static int nvme_tcp_alloc_admin_queue(struct nvme_ctrl *ctrl)
return ret;
}
static int __nvme_tcp_alloc_io_queues(struct nvme_ctrl *ctrl)
static int nvme_tcp_tls_check_psk(struct nvme_ctrl *ctrl)
{
int i, ret;
if (nvme_tcp_tls_configured(ctrl)) {
if (ctrl->opts->concat) {
/*
@ -2208,23 +2201,75 @@ static int __nvme_tcp_alloc_io_queues(struct nvme_ctrl *ctrl)
}
}
for (i = 1; i < ctrl->queue_count; i++) {
ret = nvme_tcp_alloc_queue(ctrl, i,
ctrl->tls_pskid);
if (ret)
goto out_free_queues;
return 0;
}
static void nvme_tcp_setup_queue_async(void *data, async_cookie_t cookie)
{
struct nvme_tcp_queue_setup_ctx *ctx = data;
struct nvme_ctrl *ctrl = ctx->ctrl;
int ret;
ret = nvme_tcp_alloc_queue(ctrl, ctx->qid, ctrl->tls_pskid);
if (ret)
goto out_err;
ret = nvme_tcp_start_queue(ctrl, ctx->qid);
if (ret)
goto out_err;
return;
out_err:
WRITE_ONCE(*ctx->err, ret);
}
static int nvme_tcp_setup_io_queues(struct nvme_ctrl *ctrl, unsigned int first,
unsigned int last)
{
ASYNC_DOMAIN_EXCLUSIVE(queue_domain);
struct nvme_tcp_queue_setup_ctx *ctxs;
int nr_queues = last - first;
int err = 0, i, ret;
if (first >= last)
return 0;
ctxs = kmalloc_objs(*ctxs, nr_queues);
if (!ctxs)
return -ENOMEM;
for (i = 0; i < nr_queues; i++) {
ctxs[i].ctrl = ctrl;
ctxs[i].qid = first + i;
ctxs[i].err = &err;
async_schedule_domain(nvme_tcp_setup_queue_async, &ctxs[i],
&queue_domain);
}
async_synchronize_full_domain(&queue_domain);
kfree(ctxs);
ret = READ_ONCE(err);
if (ret)
goto out_free_queues;
return 0;
out_free_queues:
for (i--; i >= 1; i--)
nvme_tcp_free_queue(ctrl, i);
for (i = first; i < last; i++) {
struct nvme_tcp_queue *queue = &to_tcp_ctrl(ctrl)->queues[i];
if (test_bit(NVME_TCP_Q_LIVE, &queue->flags))
nvme_tcp_stop_queue(ctrl, i);
if (test_bit(NVME_TCP_Q_ALLOCATED, &queue->flags))
nvme_tcp_free_queue(ctrl, i);
}
return ret;
}
static int nvme_tcp_alloc_io_queues(struct nvme_ctrl *ctrl)
static int nvme_tcp_set_io_queue_count(struct nvme_ctrl *ctrl)
{
unsigned int nr_io_queues;
int ret;
@ -2246,14 +2291,18 @@ static int nvme_tcp_alloc_io_queues(struct nvme_ctrl *ctrl)
nvmf_set_io_queues(ctrl->opts, nr_io_queues,
to_tcp_ctrl(ctrl)->io_queues);
return __nvme_tcp_alloc_io_queues(ctrl);
return 0;
}
static int nvme_tcp_configure_io_queues(struct nvme_ctrl *ctrl, bool new)
{
int ret, nr_queues;
ret = nvme_tcp_alloc_io_queues(ctrl);
ret = nvme_tcp_set_io_queue_count(ctrl);
if (ret)
return ret;
ret = nvme_tcp_tls_check_psk(ctrl);
if (ret)
return ret;
@ -2267,12 +2316,12 @@ static int nvme_tcp_configure_io_queues(struct nvme_ctrl *ctrl, bool new)
}
/*
* Only start IO queues for which we have allocated the tagset
* Only setup IO queues for which we have allocated the tagset
* and limited it to the available queues. On reconnects, the
* queue number might have changed.
*/
nr_queues = min(ctrl->tagset->nr_hw_queues + 1, ctrl->queue_count);
ret = nvme_tcp_start_io_queues(ctrl, 1, nr_queues);
ret = nvme_tcp_setup_io_queues(ctrl, 1, nr_queues);
if (ret)
goto out_cleanup_connect_q;
@ -2296,9 +2345,9 @@ static int nvme_tcp_configure_io_queues(struct nvme_ctrl *ctrl, bool new)
/*
* If the number of queues has increased (reconnect case)
* start all new queues now.
* setup all new queues now.
*/
ret = nvme_tcp_start_io_queues(ctrl, nr_queues,
ret = nvme_tcp_setup_io_queues(ctrl, nr_queues,
ctrl->tagset->nr_hw_queues + 1);
if (ret)
goto out_wait_freeze_timed_out;
@ -2650,6 +2699,7 @@ static void nvme_tcp_free_ctrl(struct nvme_ctrl *nctrl)
nvmf_free_options(nctrl->opts);
free_ctrl:
put_net(ctrl->net);
kfree(ctrl->queues);
kfree(ctrl);
}
@ -3048,12 +3098,15 @@ static struct nvme_tcp_ctrl *nvme_tcp_alloc_ctrl(struct device *dev,
goto out_free_ctrl;
}
ctrl->net = get_net(current->nsproxy->net_ns);
ret = nvme_init_ctrl(&ctrl->ctrl, dev, &nvme_tcp_ctrl_ops, 0);
if (ret)
goto out_kfree_queues;
return ctrl;
out_kfree_queues:
put_net(ctrl->net);
kfree(ctrl->queues);
out_free_ctrl:
kfree(ctrl);
@ -3139,7 +3192,7 @@ static int __init nvme_tcp_init_module(void)
return -ENOMEM;
for_each_possible_cpu(cpu)
atomic_set(&nvme_tcp_cpu_queues[cpu], 0);
nvme_tcp_cpu_queues[cpu] = 0;
nvmf_register_transport(&nvme_tcp_transport);
return 0;

View File

@ -696,7 +696,9 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req)
/* XXX: figure out what to do about RTD3R/RTD3 */
id->oaes = cpu_to_le32(NVMET_AEN_CFG_OPTIONAL);
ctratt = NVME_CTRL_ATTR_HID_128_BIT | NVME_CTRL_ATTR_TBKAS;
ctratt = NVME_CTRL_ATTR_HID_128_BIT;
if (ctrl->tbkas)
ctratt |= NVME_CTRL_ATTR_TBKAS;
if (nvmet_is_pci_ctrl(ctrl))
ctratt |= NVME_CTRL_ATTR_RHII;
id->ctratt = cpu_to_le32(ctratt);

View File

@ -229,6 +229,7 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq, bool reset)
void nvmet_auth_sq_free(struct nvmet_sq *sq)
{
sq->auth_expiry_active = false;
cancel_delayed_work(&sq->auth_expired_work);
kfree(sq->dhchap_c1);
sq->dhchap_c1 = NULL;

View File

@ -902,21 +902,34 @@ static ssize_t nvmet_passthru_device_path_show(struct config_item *item,
char *page)
{
struct nvmet_subsys *subsys = to_subsys(item->ci_parent);
ssize_t ret;
return snprintf(page, PAGE_SIZE, "%s\n", subsys->passthru_ctrl_path);
mutex_lock(&subsys->lock);
ret = snprintf(page, PAGE_SIZE, "%s\n",
subsys->passthru && subsys->passthru->ctrl_path ?
subsys->passthru->ctrl_path : "");
mutex_unlock(&subsys->lock);
return ret;
}
static ssize_t nvmet_passthru_device_path_store(struct config_item *item,
const char *page, size_t count)
{
struct nvmet_subsys *subsys = to_subsys(item->ci_parent);
struct nvmet_passthru *passthru;
size_t len;
int ret;
mutex_lock(&subsys->lock);
passthru = nvmet_subsys_passthru(subsys);
if (!passthru) {
ret = -ENOMEM;
goto out_unlock;
}
ret = -EBUSY;
if (subsys->passthru_ctrl)
if (passthru->ctrl)
goto out_unlock;
ret = -EINVAL;
@ -924,10 +937,10 @@ static ssize_t nvmet_passthru_device_path_store(struct config_item *item,
if (!len)
goto out_unlock;
kfree(subsys->passthru_ctrl_path);
kfree(passthru->ctrl_path);
ret = -ENOMEM;
subsys->passthru_ctrl_path = kstrndup(page, len, GFP_KERNEL);
if (!subsys->passthru_ctrl_path)
passthru->ctrl_path = kstrndup(page, len, GFP_KERNEL);
if (!passthru->ctrl_path)
goto out_unlock;
mutex_unlock(&subsys->lock);
@ -943,8 +956,13 @@ static ssize_t nvmet_passthru_enable_show(struct config_item *item,
char *page)
{
struct nvmet_subsys *subsys = to_subsys(item->ci_parent);
ssize_t ret;
return sprintf(page, "%d\n", subsys->passthru_ctrl ? 1 : 0);
mutex_lock(&subsys->lock);
ret = sprintf(page, "%d\n",
subsys->passthru && subsys->passthru->ctrl ? 1 : 0);
mutex_unlock(&subsys->lock);
return ret;
}
static ssize_t nvmet_passthru_enable_store(struct config_item *item,
@ -969,18 +987,34 @@ CONFIGFS_ATTR(nvmet_passthru_, enable);
static ssize_t nvmet_passthru_admin_timeout_show(struct config_item *item,
char *page)
{
return sprintf(page, "%u\n", to_subsys(item->ci_parent)->admin_timeout);
struct nvmet_subsys *subsys = to_subsys(item->ci_parent);
ssize_t ret;
mutex_lock(&subsys->lock);
ret = sprintf(page, "%u\n", subsys->passthru ?
subsys->passthru->admin_timeout : 0);
mutex_unlock(&subsys->lock);
return ret;
}
static ssize_t nvmet_passthru_admin_timeout_store(struct config_item *item,
const char *page, size_t count)
{
struct nvmet_subsys *subsys = to_subsys(item->ci_parent);
struct nvmet_passthru *passthru;
unsigned int timeout;
if (kstrtouint(page, 0, &timeout))
return -EINVAL;
subsys->admin_timeout = timeout;
mutex_lock(&subsys->lock);
passthru = nvmet_subsys_passthru(subsys);
if (!passthru) {
mutex_unlock(&subsys->lock);
return -ENOMEM;
}
passthru->admin_timeout = timeout;
mutex_unlock(&subsys->lock);
return count;
}
CONFIGFS_ATTR(nvmet_passthru_, admin_timeout);
@ -988,18 +1022,34 @@ CONFIGFS_ATTR(nvmet_passthru_, admin_timeout);
static ssize_t nvmet_passthru_io_timeout_show(struct config_item *item,
char *page)
{
return sprintf(page, "%u\n", to_subsys(item->ci_parent)->io_timeout);
struct nvmet_subsys *subsys = to_subsys(item->ci_parent);
ssize_t ret;
mutex_lock(&subsys->lock);
ret = sprintf(page, "%u\n",
subsys->passthru ? subsys->passthru->io_timeout : 0);
mutex_unlock(&subsys->lock);
return ret;
}
static ssize_t nvmet_passthru_io_timeout_store(struct config_item *item,
const char *page, size_t count)
{
struct nvmet_subsys *subsys = to_subsys(item->ci_parent);
struct nvmet_passthru *passthru;
unsigned int timeout;
if (kstrtouint(page, 0, &timeout))
return -EINVAL;
subsys->io_timeout = timeout;
mutex_lock(&subsys->lock);
passthru = nvmet_subsys_passthru(subsys);
if (!passthru) {
mutex_unlock(&subsys->lock);
return -ENOMEM;
}
passthru->io_timeout = timeout;
mutex_unlock(&subsys->lock);
return count;
}
CONFIGFS_ATTR(nvmet_passthru_, io_timeout);
@ -1007,18 +1057,34 @@ CONFIGFS_ATTR(nvmet_passthru_, io_timeout);
static ssize_t nvmet_passthru_clear_ids_show(struct config_item *item,
char *page)
{
return sprintf(page, "%u\n", to_subsys(item->ci_parent)->clear_ids);
struct nvmet_subsys *subsys = to_subsys(item->ci_parent);
ssize_t ret;
mutex_lock(&subsys->lock);
ret = sprintf(page, "%u\n",
subsys->passthru ? subsys->passthru->clear_ids : 0);
mutex_unlock(&subsys->lock);
return ret;
}
static ssize_t nvmet_passthru_clear_ids_store(struct config_item *item,
const char *page, size_t count)
{
struct nvmet_subsys *subsys = to_subsys(item->ci_parent);
struct nvmet_passthru *passthru;
unsigned int clear_ids;
if (kstrtouint(page, 0, &clear_ids))
return -EINVAL;
subsys->clear_ids = clear_ids;
mutex_lock(&subsys->lock);
passthru = nvmet_subsys_passthru(subsys);
if (!passthru) {
mutex_unlock(&subsys->lock);
return -ENOMEM;
}
passthru->clear_ids = clear_ids;
mutex_unlock(&subsys->lock);
return count;
}
CONFIGFS_ATTR(nvmet_passthru_, clear_ids);
@ -1369,6 +1435,34 @@ static ssize_t nvmet_subsys_attr_serial_store(struct config_item *item,
}
CONFIGFS_ATTR(nvmet_subsys_, attr_serial);
static ssize_t nvmet_subsys_attr_tbkas_show(struct config_item *item,
char *page)
{
return snprintf(page, PAGE_SIZE, "%d\n", to_subsys(item)->tbkas);
}
static ssize_t nvmet_subsys_attr_tbkas_store(struct config_item *item,
const char *page, size_t count)
{
struct nvmet_subsys *subsys = to_subsys(item);
struct nvmet_ctrl *ctrl;
bool tbkas;
if (kstrtobool(page, &tbkas))
return -EINVAL;
down_write(&nvmet_config_sem);
subsys->tbkas = tbkas;
/* Force reconnect */
list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry)
ctrl->ops->delete_ctrl(ctrl);
up_write(&nvmet_config_sem);
return count;
}
CONFIGFS_ATTR(nvmet_subsys_, attr_tbkas);
static ssize_t nvmet_subsys_attr_cntlid_min_show(struct config_item *item,
char *page)
{
@ -1706,6 +1800,7 @@ static struct configfs_attribute *nvmet_subsys_attrs[] = {
&nvmet_subsys_attr_attr_qid_max,
&nvmet_subsys_attr_attr_ieee_oui,
&nvmet_subsys_attr_attr_firmware,
&nvmet_subsys_attr_attr_tbkas,
#ifdef CONFIG_BLK_DEV_INTEGRITY
&nvmet_subsys_attr_attr_pi_enable,
#endif

View File

@ -811,6 +811,8 @@ static void __nvmet_req_complete(struct nvmet_req *req, u16 status)
nvmet_pr_put_ns_pc_ref(pc_ref);
if (ns)
nvmet_put_namespace(ns);
nvmet_put_passthru_ref(req);
}
void nvmet_req_complete(struct nvmet_req *req, u16 status)
@ -1187,6 +1189,7 @@ bool nvmet_req_init(struct nvmet_req *req, struct nvmet_sq *sq,
req->error_loc = NVMET_NO_ERROR_LOC;
req->error_slba = 0;
req->pc_ref = NULL;
req->p.ref_held = false;
/* no support for fused commands yet */
if (unlikely(flags & (NVME_CMD_FUSE_FIRST | NVME_CMD_FUSE_SECOND))) {
@ -1227,7 +1230,7 @@ bool nvmet_req_init(struct nvmet_req *req, struct nvmet_sq *sq,
goto fail;
}
if (sq->ctrl)
if (sq->ctrl && sq->ctrl->tbkas)
sq->ctrl->reset_tbkas = true;
return true;
@ -1637,8 +1640,16 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args)
#ifdef CONFIG_NVME_TARGET_PASSTHRU
/* By default, set loop targets to clear IDS by default */
if (ctrl->port->disc_addr.trtype == NVMF_TRTYPE_LOOP)
subsys->clear_ids = 1;
if (ctrl->port->disc_addr.trtype == NVMF_TRTYPE_LOOP) {
mutex_lock(&subsys->lock);
subsys->passthru = nvmet_subsys_passthru(subsys);
if (!subsys->passthru) {
mutex_unlock(&subsys->lock);
goto out_free_ctrl;
}
subsys->passthru->clear_ids = 1;
mutex_unlock(&subsys->lock);
}
#endif
INIT_WORK(&ctrl->async_event_work, nvmet_async_event_work);
@ -1677,6 +1688,7 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args)
mutex_lock(&subsys->lock);
ctrl->max_qid = subsys->max_qid;
ctrl->tbkas = subsys->tbkas;
ctrl->sqs = kzalloc_objs(struct nvmet_sq *, ctrl->max_qid + 1);
if (!ctrl->sqs)
@ -1865,6 +1877,7 @@ struct nvmet_subsys *nvmet_subsys_alloc(const char *subsysnqn,
}
subsys->ieee_oui = 0;
subsys->tbkas = true;
subsys->firmware_rev = kstrndup(UTS_RELEASE, NVMET_FR_MAX_SIZE, GFP_KERNEL);
if (!subsys->firmware_rev) {

View File

@ -6,6 +6,7 @@
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/blkdev.h>
#include <linux/jiffies.h>
#include <linux/random.h>
#include <linux/nvme-auth.h>
#include <crypto/kpp.h>
@ -17,16 +18,27 @@ static void nvmet_auth_expired_work(struct work_struct *work)
struct nvmet_sq *sq = container_of(to_delayed_work(work),
struct nvmet_sq, auth_expired_work);
mutex_lock(&sq->auth_lock);
/* A command may have cancelled or rearmed this timeout. */
if (!sq->auth_expiry_active ||
delayed_work_pending(&sq->auth_expired_work))
goto out_unlock;
sq->auth_expiry_active = false;
pr_debug("%s: ctrl %d qid %d transaction %u expired, resetting\n",
__func__, sq->ctrl->cntlid, sq->qid, sq->dhchap_tid);
sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
sq->dhchap_tid = -1;
out_unlock:
mutex_unlock(&sq->auth_lock);
}
void nvmet_auth_sq_init(struct nvmet_sq *sq)
{
/* Initialize in-band authentication */
mutex_init(&sq->auth_lock);
INIT_DELAYED_WORK(&sq->auth_expired_work, nvmet_auth_expired_work);
sq->auth_expiry_active = false;
sq->authenticated = false;
sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
}
@ -256,6 +268,8 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
u16 status = 0;
u8 dhchap_status;
mutex_lock(&req->sq->auth_lock);
if (req->cmd->auth_send.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) {
status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
req->error_loc =
@ -281,10 +295,18 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
offsetof(struct nvmf_auth_send_command, tl);
goto done;
}
if (!nvmet_check_transfer_len(req, tl)) {
if (unlikely(tl != req->transfer_len)) {
pr_debug("%s: transfer length mismatch (%u)\n", __func__, tl);
mutex_unlock(&req->sq->auth_lock);
nvmet_check_transfer_len(req, tl);
return;
}
if (tl < offsetof(struct nvmf_auth_dhchap_success2_data, rsvd1)) {
status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
req->error_loc =
offsetof(struct nvmf_auth_send_command, tl);
goto done;
}
d = kmalloc(tl, GFP_KERNEL);
if (!d) {
@ -406,10 +428,12 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
status, req->error_loc);
if (req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 &&
req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_FAILURE2) {
unsigned long auth_expire_secs = ctrl->kato ? ctrl->kato : 120;
unsigned long auth_expire = min_t(u64, MAX_JIFFY_OFFSET,
(u64)(ctrl->kato ? ctrl->kato : 120) * HZ);
req->sq->auth_expiry_active = true;
mod_delayed_work(system_percpu_wq, &req->sq->auth_expired_work,
auth_expire_secs * HZ);
auth_expire);
goto complete;
}
/* Final states, clear up variables */
@ -418,6 +442,7 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
nvmet_ctrl_fatal_error(ctrl);
complete:
mutex_unlock(&req->sq->auth_lock);
nvmet_req_complete(req, status);
}
@ -536,6 +561,8 @@ void nvmet_execute_auth_receive(struct nvmet_req *req)
u32 al;
u16 status = 0;
mutex_lock(&req->sq->auth_lock);
if (req->cmd->auth_receive.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) {
status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
req->error_loc =
@ -561,8 +588,10 @@ void nvmet_execute_auth_receive(struct nvmet_req *req)
offsetof(struct nvmf_auth_receive_command, al);
goto done;
}
if (!nvmet_check_transfer_len(req, al)) {
if (unlikely(al != req->transfer_len)) {
pr_debug("%s: transfer length mismatch (%u)\n", __func__, al);
mutex_unlock(&req->sq->auth_lock);
nvmet_check_transfer_len(req, al);
return;
}
@ -621,5 +650,6 @@ void nvmet_execute_auth_receive(struct nvmet_req *req)
nvmet_auth_sq_free(req->sq);
nvmet_ctrl_fatal_error(ctrl);
}
mutex_unlock(&req->sq->auth_lock);
nvmet_req_complete(req, status);
}

View File

@ -14,6 +14,10 @@ static void nvmet_execute_prop_set(struct nvmet_req *req)
if (!nvmet_check_transfer_len(req, 0))
return;
if (!nvmet_check_auth_status(req)) {
status = NVME_SC_AUTH_REQUIRED | NVME_STATUS_DNR;
goto out;
}
if (req->cmd->prop_set.attrib & 1) {
req->error_loc =

View File

@ -1567,8 +1567,14 @@ nvmet_fc_delete_ctrl(struct nvmet_ctrl *ctrl)
list_for_each_entry_rcu(assoc, &tgtport->assoc_list, a_list) {
queue = assoc->queues[0];
if (queue && queue->nvme_sq.ctrl == ctrl) {
if (nvmet_fc_tgt_a_get(assoc))
found_ctrl = true;
/*
* No assoc reference here: we're called with
* subsys->lock held, and if del_work beat us,
* our put would free the ctrl under that lock.
* RCU keeps the assoc alive until unlock.
*/
nvmet_fc_schedule_delete_assoc(assoc);
found_ctrl = true;
break;
}
}
@ -1576,11 +1582,8 @@ nvmet_fc_delete_ctrl(struct nvmet_ctrl *ctrl)
nvmet_fc_tgtport_put(tgtport);
if (found_ctrl) {
nvmet_fc_schedule_delete_assoc(assoc);
nvmet_fc_tgt_a_put(assoc);
if (found_ctrl)
return;
}
spin_lock_irqsave(&nvmet_fc_tgtlock, flags);
}

View File

@ -133,8 +133,8 @@ static bool nvmet_file_execute_io(struct nvmet_req *req, int ki_flags)
pos = le64_to_cpu(req->cmd->rw.slba) << req->ns->blksize_shift;
if (unlikely(pos + req->transfer_len > req->ns->size)) {
nvmet_req_complete(req, errno_to_nvme_status(req, -ENOSPC));
return true;
ret = -ENOSPC;
goto complete;
}
memset(&req->f.iocb, 0, sizeof(struct kiocb));

View File

@ -159,8 +159,10 @@ struct nvmet_sq {
u32 sqhd;
bool sqhd_disabled;
#ifdef CONFIG_NVME_TARGET_AUTH
struct mutex auth_lock;
bool authenticated;
struct delayed_work auth_expired_work;
bool auth_expiry_active;
u16 dhchap_tid;
u8 sc_c;
u8 dhchap_status;
@ -270,6 +272,7 @@ struct nvmet_ctrl {
u16 cntlid;
u16 max_qid;
u32 kato;
bool tbkas;
struct nvmet_port *port;
@ -317,6 +320,19 @@ struct nvmet_ctrl {
struct nvmet_pr_log_mgr pr_log_mgr;
};
struct nvmet_passthru {
struct percpu_ref ref;
struct completion disable_done;
#define NVMET_PASSTHRU_ENABLED 0
unsigned long flags;
struct nvme_ctrl *ctrl;
char *ctrl_path;
unsigned int admin_timeout;
unsigned int io_timeout;
unsigned int clear_ids;
};
struct nvmet_subsys {
enum nvme_subsys_type type;
@ -343,6 +359,7 @@ struct nvmet_subsys {
bool subsys_discovered;
char *subsysnqn;
bool pi_support;
bool tbkas;
struct config_group group;
@ -356,12 +373,8 @@ struct nvmet_subsys {
char *firmware_rev;
#ifdef CONFIG_NVME_TARGET_PASSTHRU
struct nvme_ctrl *passthru_ctrl;
char *passthru_ctrl_path;
struct nvmet_passthru *passthru;
struct config_group passthru_group;
unsigned int admin_timeout;
unsigned int io_timeout;
unsigned int clear_ids;
#endif /* CONFIG_NVME_TARGET_PASSTHRU */
#ifdef CONFIG_BLK_DEV_ZONED
@ -472,6 +485,7 @@ struct nvmet_req {
struct request *rq;
struct work_struct work;
bool use_workqueue;
bool ref_held;
} p;
#ifdef CONFIG_BLK_DEV_ZONED
struct {
@ -791,7 +805,39 @@ u16 nvmet_parse_passthru_admin_cmd(struct nvmet_req *req);
u16 nvmet_parse_passthru_io_cmd(struct nvmet_req *req);
static inline bool nvmet_is_passthru_subsys(struct nvmet_subsys *subsys)
{
return subsys->passthru_ctrl;
return subsys->passthru &&
test_bit(NVMET_PASSTHRU_ENABLED, &subsys->passthru->flags);
}
static inline struct nvmet_passthru *nvmet_subsys_passthru(
struct nvmet_subsys *subsys)
{
lockdep_assert_held(&subsys->lock);
if (!subsys->passthru) {
subsys->passthru = kzalloc_obj(*subsys->passthru);
if (!subsys->passthru)
return NULL;
}
return subsys->passthru;
}
static inline bool nvmet_get_passthru_ref(struct nvmet_req *req)
{
if (!percpu_ref_tryget_live(&nvmet_req_subsys(req)->passthru->ref))
return false;
req->p.ref_held = true;
return true;
}
static inline void nvmet_put_passthru_ref(struct nvmet_req *req)
{
if (!req->p.ref_held)
return;
req->p.ref_held = false;
percpu_ref_put(&nvmet_req_subsys(req)->passthru->ref);
}
#else /* CONFIG_NVME_TARGET_PASSTHRU */
static inline void nvmet_passthru_subsys_free(struct nvmet_subsys *subsys)
@ -812,6 +858,18 @@ static inline bool nvmet_is_passthru_subsys(struct nvmet_subsys *subsys)
{
return NULL;
}
static inline struct nvmet_passthru *nvmet_subsys_passthru(
struct nvmet_subsys *subsys)
{
return NULL;
}
static inline bool nvmet_get_passthru_ref(struct nvmet_req *req)
{
return false;
}
static inline void nvmet_put_passthru_ref(struct nvmet_req *req)
{
}
#endif /* CONFIG_NVME_TARGET_PASSTHRU */
static inline bool nvmet_is_passthru_req(struct nvmet_req *req)

View File

@ -26,7 +26,7 @@ void nvmet_passthrough_override_cap(struct nvmet_ctrl *ctrl)
* Multiple command set support can only be declared if the underlying
* controller actually supports it.
*/
if (!nvme_multi_css(ctrl->subsys->passthru_ctrl))
if (!nvme_multi_css(ctrl->subsys->passthru->ctrl))
ctrl->cap &= ~(1ULL << 43);
}
@ -39,7 +39,7 @@ static u16 nvmet_passthru_override_id_descs(struct nvmet_req *req)
void *data;
u8 csi;
if (!ctrl->subsys->clear_ids)
if (!ctrl->subsys->passthru->clear_ids)
return status;
data = kzalloc(NVME_IDENTIFY_DATA_SIZE, GFP_KERNEL);
@ -89,7 +89,7 @@ static u16 nvmet_passthru_override_id_descs(struct nvmet_req *req)
static u16 nvmet_passthru_override_id_ctrl(struct nvmet_req *req)
{
struct nvmet_ctrl *ctrl = req->sq->ctrl;
struct nvme_ctrl *pctrl = ctrl->subsys->passthru_ctrl;
struct nvme_ctrl *pctrl = ctrl->subsys->passthru->ctrl;
u16 status = NVME_SC_SUCCESS;
struct nvme_id_ctrl *id;
unsigned int max_hw_sectors;
@ -208,7 +208,7 @@ static u16 nvmet_passthru_override_id_ns(struct nvmet_req *req)
*/
id->mc = 0;
if (req->sq->ctrl->subsys->clear_ids) {
if (req->sq->ctrl->subsys->passthru->clear_ids) {
memset(id->nguid, 0, NVME_NIDT_NGUID_LEN);
memset(id->eui64, 0, NVME_NIDT_EUI64_LEN);
}
@ -305,8 +305,9 @@ static int nvmet_passthru_map_sg(struct nvmet_req *req, struct request *rq)
static void nvmet_passthru_execute_cmd(struct nvmet_req *req)
{
struct nvme_ctrl *ctrl = nvmet_req_subsys(req)->passthru_ctrl;
struct request_queue *q = ctrl->admin_q;
struct nvmet_passthru *passthru;
struct nvme_ctrl *ctrl;
struct request_queue *q;
struct nvme_ns *ns = NULL;
struct request *rq = NULL;
unsigned int timeout;
@ -314,6 +315,15 @@ static void nvmet_passthru_execute_cmd(struct nvmet_req *req)
u16 status;
int ret;
if (!nvmet_get_passthru_ref(req)) {
status = NVME_SC_INTERNAL | NVME_STATUS_DNR;
goto out;
}
passthru = nvmet_req_subsys(req)->passthru;
ctrl = passthru->ctrl;
q = ctrl->admin_q;
if (likely(req->sq->qid != 0)) {
u32 nsid = le32_to_cpu(req->cmd->common.nsid);
@ -325,9 +335,9 @@ static void nvmet_passthru_execute_cmd(struct nvmet_req *req)
}
q = ns->queue;
timeout = nvmet_req_subsys(req)->io_timeout;
timeout = passthru->io_timeout;
} else {
timeout = nvmet_req_subsys(req)->admin_timeout;
timeout = passthru->admin_timeout;
}
rq = blk_mq_alloc_request(q, nvme_req_op(req->cmd), 0);
@ -386,11 +396,17 @@ static void nvmet_passthru_execute_cmd(struct nvmet_req *req)
*/
static void nvmet_passthru_set_host_behaviour(struct nvmet_req *req)
{
struct nvme_ctrl *ctrl = nvmet_req_subsys(req)->passthru_ctrl;
struct nvme_ctrl *ctrl;
struct nvme_feat_host_behavior *host;
u16 status = NVME_SC_INTERNAL;
int ret;
if (!nvmet_get_passthru_ref(req)) {
status |= NVME_STATUS_DNR;
goto out_complete_req;
}
ctrl = nvmet_req_subsys(req)->passthru->ctrl;
host = kzalloc(sizeof(*host) * 2, GFP_KERNEL);
if (!host)
goto out_complete_req;
@ -584,17 +600,27 @@ u16 nvmet_parse_passthru_admin_cmd(struct nvmet_req *req)
}
}
static void nvmet_release_passthru_ctrl(struct percpu_ref *ref)
{
struct nvmet_passthru *passthru = container_of(ref,
struct nvmet_passthru, ref);
complete(&passthru->disable_done);
}
int nvmet_passthru_ctrl_enable(struct nvmet_subsys *subsys)
{
struct nvmet_passthru *passthru;
struct nvme_ctrl *ctrl;
struct file *file;
int ret = -EINVAL;
void *old;
mutex_lock(&subsys->lock);
if (!subsys->passthru_ctrl_path)
passthru = subsys->passthru;
if (!passthru || !passthru->ctrl_path)
goto out_unlock;
if (subsys->passthru_ctrl)
if (passthru->ctrl)
goto out_unlock;
if (subsys->nr_namespaces) {
@ -602,7 +628,7 @@ int nvmet_passthru_ctrl_enable(struct nvmet_subsys *subsys)
goto out_unlock;
}
file = filp_open(subsys->passthru_ctrl_path, O_RDWR, 0);
file = filp_open(passthru->ctrl_path, O_RDWR, 0);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
goto out_unlock;
@ -611,7 +637,7 @@ int nvmet_passthru_ctrl_enable(struct nvmet_subsys *subsys)
ctrl = nvme_ctrl_from_file(file);
if (!ctrl) {
pr_err("failed to open nvme controller %s\n",
subsys->passthru_ctrl_path);
passthru->ctrl_path);
goto out_put_file;
}
@ -626,7 +652,14 @@ int nvmet_passthru_ctrl_enable(struct nvmet_subsys *subsys)
if (old)
goto out_put_file;
subsys->passthru_ctrl = ctrl;
ret = percpu_ref_init(&passthru->ref, nvmet_release_passthru_ctrl,
0, GFP_KERNEL);
if (ret) {
xa_erase(&passthru_subsystems, ctrl->instance);
goto out_put_file;
}
init_completion(&passthru->disable_done);
passthru->ctrl = ctrl;
subsys->ver = ctrl->vs;
if (subsys->ver < NVME_VS(1, 2, 1)) {
@ -636,7 +669,8 @@ int nvmet_passthru_ctrl_enable(struct nvmet_subsys *subsys)
subsys->ver = NVME_VS(1, 2, 1);
}
nvme_get_ctrl(ctrl);
__module_get(subsys->passthru_ctrl->ops->module);
__module_get(passthru->ctrl->ops->module);
set_bit(NVMET_PASSTHRU_ENABLED, &passthru->flags);
ret = 0;
out_put_file:
@ -648,12 +682,33 @@ int nvmet_passthru_ctrl_enable(struct nvmet_subsys *subsys)
static void __nvmet_passthru_ctrl_disable(struct nvmet_subsys *subsys)
{
if (subsys->passthru_ctrl) {
xa_erase(&passthru_subsystems, subsys->passthru_ctrl->instance);
module_put(subsys->passthru_ctrl->ops->module);
nvme_put_ctrl(subsys->passthru_ctrl);
}
subsys->passthru_ctrl = NULL;
struct nvmet_passthru *passthru = subsys->passthru;
lockdep_assert_held(&subsys->lock);
if (!passthru)
return;
if (!test_and_clear_bit(NVMET_PASSTHRU_ENABLED, &passthru->flags))
return;
mutex_unlock(&subsys->lock);
/*
* Now new I/Os should not enter passthru hotpath as we cleared the
* enabled flag. Kill percpu reference and wait for in-flight I/Os
* to drain.
*/
percpu_ref_kill(&passthru->ref);
wait_for_completion(&passthru->disable_done);
percpu_ref_exit(&passthru->ref);
mutex_lock(&subsys->lock);
xa_erase(&passthru_subsystems, passthru->ctrl->instance);
module_put(passthru->ctrl->ops->module);
nvme_put_ctrl(passthru->ctrl);
passthru->ctrl = NULL;
subsys->ver = NVMET_DEFAULT_VS;
}
@ -668,6 +723,10 @@ void nvmet_passthru_subsys_free(struct nvmet_subsys *subsys)
{
mutex_lock(&subsys->lock);
__nvmet_passthru_ctrl_disable(subsys);
if (subsys->passthru) {
kfree(subsys->passthru->ctrl_path);
kfree(subsys->passthru);
subsys->passthru = NULL;
}
mutex_unlock(&subsys->lock);
kfree(subsys->passthru_ctrl_path);
}

View File

@ -585,11 +585,11 @@ static u16 nvmet_pr_preempt(struct nvmet_req *req,
&ctrl->hostid, abort);
}
if (holder == reg) {
if (holder == reg && prkey == holder->rkey) {
status = nvmet_pr_update_reg_attr(pr, holder,
nvmet_pr_update_holder_rtype, &rtype);
if (!status && original_rtype != rtype)
nvmet_pr_resv_released(pr, &reg->hostid);
nvmet_pr_resv_released(pr, &ctrl->hostid);
return status;
}

View File

@ -282,6 +282,11 @@ static void nvmet_bdev_zone_zmgmt_recv_work(struct work_struct *w)
if (status)
goto out;
/* Only the zone count and matching descriptors are written below. */
status = nvmet_zero_sgl(req, 0, out_bufsize);
if (status)
goto out;
if (!req_slba_nr_zones) {
status = NVME_SC_SUCCESS;
goto out;
@ -317,6 +322,11 @@ static void nvmet_bdev_zone_zmgmt_recv_work(struct work_struct *w)
void nvmet_bdev_execute_zone_mgmt_recv(struct nvmet_req *req)
{
u32 out_bufsize = (le32_to_cpu(req->cmd->zmr.numd) + 1) << 2;
if (!nvmet_check_transfer_len(req, out_bufsize))
return;
INIT_WORK(&req->z.zmgmt_work, nvmet_bdev_zone_zmgmt_recv_work);
queue_work(zbd_wq, &req->z.zmgmt_work);
}