mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
commit
341a043c24
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
{},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, ®->hostid);
|
||||
nvmet_pr_resv_released(pr, &ctrl->hostid);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user