From 7ba150834b840f6f5cdd07ca69a4ccf39df59a66 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Fri, 7 Apr 2023 15:05:35 -0500 Subject: block: Rename BLK_STS_NEXUS to BLK_STS_RESV_CONFLICT BLK_STS_NEXUS is used for NVMe/SCSI reservation conflicts and DASD's locking feature which works similar to NVMe/SCSI reservations where a host can get a lock on a device and when the lock is taken it will get failures. This patch renames BLK_STS_NEXUS so it better reflects this type of use. Signed-off-by: Mike Christie Link: https://lore.kernel.org/r/20230407200551.12660-3-michael.christie@oracle.com Acked-by: Stefan Haberland Reviewed-by: Bart Van Assche Reviewed-by: Chaitanya Kulkarni Reviewed-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/nvme/host/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/nvme') diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index c2730b116dc6..535a26ceb205 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -279,7 +279,7 @@ static blk_status_t nvme_error_status(u16 status) case NVME_SC_INVALID_PI: return BLK_STS_PROTECTION; case NVME_SC_RESERVATION_CONFLICT: - return BLK_STS_NEXUS; + return BLK_STS_RESV_CONFLICT; case NVME_SC_HOST_PATH_ERROR: return BLK_STS_TRANSPORT; case NVME_SC_ZONE_TOO_MANY_ACTIVE: -- cgit v1.2.3-70-g09d2 From d45b446bd81822f5a0c3019f58e6c69f7ce71ce6 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Fri, 7 Apr 2023 15:05:41 -0500 Subject: nvme: Don't hardcode the data len for pr commands Reservation Report support needs to pass in a variable sized buffer, so this patch has the pr command helpers take a data length argument. Signed-off-by: Mike Christie Link: https://lore.kernel.org/r/20230407200551.12660-9-michael.christie@oracle.com Reviewed-by: Christoph Hellwig Reviewed-by: Keith Busch Reviewed-by: Chaitanya Kulkarni Signed-off-by: Martin K. Petersen --- drivers/nvme/host/core.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/nvme') diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 535a26ceb205..1c1c4cde5a74 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -2072,7 +2072,7 @@ static char nvme_pr_type(enum pr_type type) } static int nvme_send_ns_head_pr_command(struct block_device *bdev, - struct nvme_command *c, u8 data[16]) + struct nvme_command *c, u8 *data, unsigned int data_len) { struct nvme_ns_head *head = bdev->bd_disk->private_data; int srcu_idx = srcu_read_lock(&head->srcu); @@ -2081,17 +2081,17 @@ static int nvme_send_ns_head_pr_command(struct block_device *bdev, if (ns) { c->common.nsid = cpu_to_le32(ns->head->ns_id); - ret = nvme_submit_sync_cmd(ns->queue, c, data, 16); + ret = nvme_submit_sync_cmd(ns->queue, c, data, data_len); } srcu_read_unlock(&head->srcu, srcu_idx); return ret; } static int nvme_send_ns_pr_command(struct nvme_ns *ns, struct nvme_command *c, - u8 data[16]) + u8 *data, unsigned int data_len) { c->common.nsid = cpu_to_le32(ns->head->ns_id); - return nvme_submit_sync_cmd(ns->queue, c, data, 16); + return nvme_submit_sync_cmd(ns->queue, c, data, data_len); } static int nvme_sc_to_pr_err(int nvme_sc) @@ -2131,10 +2131,11 @@ static int nvme_pr_command(struct block_device *bdev, u32 cdw10, if (IS_ENABLED(CONFIG_NVME_MULTIPATH) && bdev->bd_disk->fops == &nvme_ns_head_ops) - ret = nvme_send_ns_head_pr_command(bdev, &c, data); + ret = nvme_send_ns_head_pr_command(bdev, &c, data, + sizeof(data)); else ret = nvme_send_ns_pr_command(bdev->bd_disk->private_data, &c, - data); + data, sizeof(data)); if (ret < 0) return ret; -- cgit v1.2.3-70-g09d2 From b668f2f5467c3316b67fa04975e2fccb0baec576 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Fri, 7 Apr 2023 15:05:42 -0500 Subject: nvme: Move pr code to it's own file This patch moves the pr code to it's own file because I'm going to be adding more functions and core.c is getting bigger. Signed-off-by: Mike Christie Link: https://lore.kernel.org/r/20230407200551.12660-10-michael.christie@oracle.com Reviewed-by: Chaitanya Kulkarni Reviewed-by: Keith Busch Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/nvme/host/Makefile | 2 +- drivers/nvme/host/core.c | 148 ------------------------------------------ drivers/nvme/host/nvme.h | 2 + drivers/nvme/host/pr.c | 158 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+), 149 deletions(-) create mode 100644 drivers/nvme/host/pr.c (limited to 'drivers/nvme') diff --git a/drivers/nvme/host/Makefile b/drivers/nvme/host/Makefile index e27202d22c7d..06c18a65da99 100644 --- a/drivers/nvme/host/Makefile +++ b/drivers/nvme/host/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_NVME_FC) += nvme-fc.o obj-$(CONFIG_NVME_TCP) += nvme-tcp.o obj-$(CONFIG_NVME_APPLE) += nvme-apple.o -nvme-core-y += core.o ioctl.o +nvme-core-y += core.o ioctl.o pr.o nvme-core-$(CONFIG_NVME_VERBOSE_ERRORS) += constants.o nvme-core-$(CONFIG_TRACING) += trace.o nvme-core-$(CONFIG_NVME_MULTIPATH) += multipath.o diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 1c1c4cde5a74..cb9339017b4c 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -2051,154 +2051,6 @@ static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info) } } -static char nvme_pr_type(enum pr_type type) -{ - switch (type) { - case PR_WRITE_EXCLUSIVE: - return 1; - case PR_EXCLUSIVE_ACCESS: - return 2; - case PR_WRITE_EXCLUSIVE_REG_ONLY: - return 3; - case PR_EXCLUSIVE_ACCESS_REG_ONLY: - return 4; - case PR_WRITE_EXCLUSIVE_ALL_REGS: - return 5; - case PR_EXCLUSIVE_ACCESS_ALL_REGS: - return 6; - default: - return 0; - } -} - -static int nvme_send_ns_head_pr_command(struct block_device *bdev, - struct nvme_command *c, u8 *data, unsigned int data_len) -{ - struct nvme_ns_head *head = bdev->bd_disk->private_data; - int srcu_idx = srcu_read_lock(&head->srcu); - struct nvme_ns *ns = nvme_find_path(head); - int ret = -EWOULDBLOCK; - - if (ns) { - c->common.nsid = cpu_to_le32(ns->head->ns_id); - ret = nvme_submit_sync_cmd(ns->queue, c, data, data_len); - } - srcu_read_unlock(&head->srcu, srcu_idx); - return ret; -} - -static int nvme_send_ns_pr_command(struct nvme_ns *ns, struct nvme_command *c, - u8 *data, unsigned int data_len) -{ - c->common.nsid = cpu_to_le32(ns->head->ns_id); - return nvme_submit_sync_cmd(ns->queue, c, data, data_len); -} - -static int nvme_sc_to_pr_err(int nvme_sc) -{ - if (nvme_is_path_error(nvme_sc)) - return PR_STS_PATH_FAILED; - - switch (nvme_sc) { - case NVME_SC_SUCCESS: - return PR_STS_SUCCESS; - case NVME_SC_RESERVATION_CONFLICT: - return PR_STS_RESERVATION_CONFLICT; - case NVME_SC_ONCS_NOT_SUPPORTED: - return -EOPNOTSUPP; - case NVME_SC_BAD_ATTRIBUTES: - case NVME_SC_INVALID_OPCODE: - case NVME_SC_INVALID_FIELD: - case NVME_SC_INVALID_NS: - return -EINVAL; - default: - return PR_STS_IOERR; - } -} - -static int nvme_pr_command(struct block_device *bdev, u32 cdw10, - u64 key, u64 sa_key, u8 op) -{ - struct nvme_command c = { }; - u8 data[16] = { 0, }; - int ret; - - put_unaligned_le64(key, &data[0]); - put_unaligned_le64(sa_key, &data[8]); - - c.common.opcode = op; - c.common.cdw10 = cpu_to_le32(cdw10); - - if (IS_ENABLED(CONFIG_NVME_MULTIPATH) && - bdev->bd_disk->fops == &nvme_ns_head_ops) - ret = nvme_send_ns_head_pr_command(bdev, &c, data, - sizeof(data)); - else - ret = nvme_send_ns_pr_command(bdev->bd_disk->private_data, &c, - data, sizeof(data)); - if (ret < 0) - return ret; - - return nvme_sc_to_pr_err(ret); -} - -static int nvme_pr_register(struct block_device *bdev, u64 old, - u64 new, unsigned flags) -{ - u32 cdw10; - - if (flags & ~PR_FL_IGNORE_KEY) - return -EOPNOTSUPP; - - cdw10 = old ? 2 : 0; - cdw10 |= (flags & PR_FL_IGNORE_KEY) ? 1 << 3 : 0; - cdw10 |= (1 << 30) | (1 << 31); /* PTPL=1 */ - return nvme_pr_command(bdev, cdw10, old, new, nvme_cmd_resv_register); -} - -static int nvme_pr_reserve(struct block_device *bdev, u64 key, - enum pr_type type, unsigned flags) -{ - u32 cdw10; - - if (flags & ~PR_FL_IGNORE_KEY) - return -EOPNOTSUPP; - - cdw10 = nvme_pr_type(type) << 8; - cdw10 |= ((flags & PR_FL_IGNORE_KEY) ? 1 << 3 : 0); - return nvme_pr_command(bdev, cdw10, key, 0, nvme_cmd_resv_acquire); -} - -static int nvme_pr_preempt(struct block_device *bdev, u64 old, u64 new, - enum pr_type type, bool abort) -{ - u32 cdw10 = nvme_pr_type(type) << 8 | (abort ? 2 : 1); - - return nvme_pr_command(bdev, cdw10, old, new, nvme_cmd_resv_acquire); -} - -static int nvme_pr_clear(struct block_device *bdev, u64 key) -{ - u32 cdw10 = 1 | (key ? 0 : 1 << 3); - - return nvme_pr_command(bdev, cdw10, key, 0, nvme_cmd_resv_release); -} - -static int nvme_pr_release(struct block_device *bdev, u64 key, enum pr_type type) -{ - u32 cdw10 = nvme_pr_type(type) << 8 | (key ? 0 : 1 << 3); - - return nvme_pr_command(bdev, cdw10, key, 0, nvme_cmd_resv_release); -} - -const struct pr_ops nvme_pr_ops = { - .pr_register = nvme_pr_register, - .pr_reserve = nvme_pr_reserve, - .pr_release = nvme_pr_release, - .pr_preempt = nvme_pr_preempt, - .pr_clear = nvme_pr_clear, -}; - #ifdef CONFIG_BLK_SED_OPAL static int nvme_sec_submit(void *data, u16 spsp, u8 secp, void *buffer, size_t len, bool send) diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index bf46f122e9e1..c0762346b441 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -19,6 +19,8 @@ #include +extern const struct pr_ops nvme_pr_ops; + extern unsigned int nvme_io_timeout; #define NVME_IO_TIMEOUT (nvme_io_timeout * HZ) diff --git a/drivers/nvme/host/pr.c b/drivers/nvme/host/pr.c new file mode 100644 index 000000000000..ca7a8d531a23 --- /dev/null +++ b/drivers/nvme/host/pr.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2015 Intel Corporation + * Keith Busch + */ +#include +#include +#include + +#include "nvme.h" + +static char nvme_pr_type(enum pr_type type) +{ + switch (type) { + case PR_WRITE_EXCLUSIVE: + return 1; + case PR_EXCLUSIVE_ACCESS: + return 2; + case PR_WRITE_EXCLUSIVE_REG_ONLY: + return 3; + case PR_EXCLUSIVE_ACCESS_REG_ONLY: + return 4; + case PR_WRITE_EXCLUSIVE_ALL_REGS: + return 5; + case PR_EXCLUSIVE_ACCESS_ALL_REGS: + return 6; + default: + return 0; + } +} + +static int nvme_send_ns_head_pr_command(struct block_device *bdev, + struct nvme_command *c, u8 *data, unsigned int data_len) +{ + struct nvme_ns_head *head = bdev->bd_disk->private_data; + int srcu_idx = srcu_read_lock(&head->srcu); + struct nvme_ns *ns = nvme_find_path(head); + int ret = -EWOULDBLOCK; + + if (ns) { + c->common.nsid = cpu_to_le32(ns->head->ns_id); + ret = nvme_submit_sync_cmd(ns->queue, c, data, data_len); + } + srcu_read_unlock(&head->srcu, srcu_idx); + return ret; +} + +static int nvme_send_ns_pr_command(struct nvme_ns *ns, struct nvme_command *c, + u8 *data, unsigned int data_len) +{ + c->common.nsid = cpu_to_le32(ns->head->ns_id); + return nvme_submit_sync_cmd(ns->queue, c, data, data_len); +} + +static int nvme_sc_to_pr_err(int nvme_sc) +{ + if (nvme_is_path_error(nvme_sc)) + return PR_STS_PATH_FAILED; + + switch (nvme_sc) { + case NVME_SC_SUCCESS: + return PR_STS_SUCCESS; + case NVME_SC_RESERVATION_CONFLICT: + return PR_STS_RESERVATION_CONFLICT; + case NVME_SC_ONCS_NOT_SUPPORTED: + return -EOPNOTSUPP; + case NVME_SC_BAD_ATTRIBUTES: + case NVME_SC_INVALID_OPCODE: + case NVME_SC_INVALID_FIELD: + case NVME_SC_INVALID_NS: + return -EINVAL; + default: + return PR_STS_IOERR; + } +} + +static int nvme_pr_command(struct block_device *bdev, u32 cdw10, + u64 key, u64 sa_key, u8 op) +{ + struct nvme_command c = { }; + u8 data[16] = { 0, }; + int ret; + + put_unaligned_le64(key, &data[0]); + put_unaligned_le64(sa_key, &data[8]); + + c.common.opcode = op; + c.common.cdw10 = cpu_to_le32(cdw10); + + if (IS_ENABLED(CONFIG_NVME_MULTIPATH) && + bdev->bd_disk->fops == &nvme_ns_head_ops) + ret = nvme_send_ns_head_pr_command(bdev, &c, data, + sizeof(data)); + else + ret = nvme_send_ns_pr_command(bdev->bd_disk->private_data, &c, + data, sizeof(data)); + if (ret < 0) + return ret; + + return nvme_sc_to_pr_err(ret); +} + +static int nvme_pr_register(struct block_device *bdev, u64 old, + u64 new, unsigned flags) +{ + u32 cdw10; + + if (flags & ~PR_FL_IGNORE_KEY) + return -EOPNOTSUPP; + + cdw10 = old ? 2 : 0; + cdw10 |= (flags & PR_FL_IGNORE_KEY) ? 1 << 3 : 0; + cdw10 |= (1 << 30) | (1 << 31); /* PTPL=1 */ + return nvme_pr_command(bdev, cdw10, old, new, nvme_cmd_resv_register); +} + +static int nvme_pr_reserve(struct block_device *bdev, u64 key, + enum pr_type type, unsigned flags) +{ + u32 cdw10; + + if (flags & ~PR_FL_IGNORE_KEY) + return -EOPNOTSUPP; + + cdw10 = nvme_pr_type(type) << 8; + cdw10 |= ((flags & PR_FL_IGNORE_KEY) ? 1 << 3 : 0); + return nvme_pr_command(bdev, cdw10, key, 0, nvme_cmd_resv_acquire); +} + +static int nvme_pr_preempt(struct block_device *bdev, u64 old, u64 new, + enum pr_type type, bool abort) +{ + u32 cdw10 = nvme_pr_type(type) << 8 | (abort ? 2 : 1); + + return nvme_pr_command(bdev, cdw10, old, new, nvme_cmd_resv_acquire); +} + +static int nvme_pr_clear(struct block_device *bdev, u64 key) +{ + u32 cdw10 = 1 | (key ? 0 : 1 << 3); + + return nvme_pr_command(bdev, cdw10, key, 0, nvme_cmd_resv_release); +} + +static int nvme_pr_release(struct block_device *bdev, u64 key, enum pr_type type) +{ + u32 cdw10 = nvme_pr_type(type) << 8 | (key ? 0 : 1 << 3); + + return nvme_pr_command(bdev, cdw10, key, 0, nvme_cmd_resv_release); +} + +const struct pr_ops nvme_pr_ops = { + .pr_register = nvme_pr_register, + .pr_reserve = nvme_pr_reserve, + .pr_release = nvme_pr_release, + .pr_preempt = nvme_pr_preempt, + .pr_clear = nvme_pr_clear, +}; -- cgit v1.2.3-70-g09d2 From f0614790b77300d69a1f37265f98b68c4835811b Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Fri, 7 Apr 2023 15:05:43 -0500 Subject: nvme: Add helper to send pr command Move the code that checks for multipath support and sends the pr command to a new helper so it can be used by the reservation report support added in the next patches. Signed-off-by: Mike Christie Link: https://lore.kernel.org/r/20230407200551.12660-11-michael.christie@oracle.com Reviewed-by: Christoph Hellwig Reviewed-by: Chaitanya Kulkarni Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/nvme/host/pr.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'drivers/nvme') diff --git a/drivers/nvme/host/pr.c b/drivers/nvme/host/pr.c index ca7a8d531a23..cd93d2e5b340 100644 --- a/drivers/nvme/host/pr.c +++ b/drivers/nvme/host/pr.c @@ -30,7 +30,7 @@ static char nvme_pr_type(enum pr_type type) } static int nvme_send_ns_head_pr_command(struct block_device *bdev, - struct nvme_command *c, u8 *data, unsigned int data_len) + struct nvme_command *c, void *data, unsigned int data_len) { struct nvme_ns_head *head = bdev->bd_disk->private_data; int srcu_idx = srcu_read_lock(&head->srcu); @@ -46,7 +46,7 @@ static int nvme_send_ns_head_pr_command(struct block_device *bdev, } static int nvme_send_ns_pr_command(struct nvme_ns *ns, struct nvme_command *c, - u8 *data, unsigned int data_len) + void *data, unsigned int data_len) { c->common.nsid = cpu_to_le32(ns->head->ns_id); return nvme_submit_sync_cmd(ns->queue, c, data, data_len); @@ -74,6 +74,17 @@ static int nvme_sc_to_pr_err(int nvme_sc) } } +static int nvme_send_pr_command(struct block_device *bdev, + struct nvme_command *c, void *data, unsigned int data_len) +{ + if (IS_ENABLED(CONFIG_NVME_MULTIPATH) && + bdev->bd_disk->fops == &nvme_ns_head_ops) + return nvme_send_ns_head_pr_command(bdev, c, data, data_len); + + return nvme_send_ns_pr_command(bdev->bd_disk->private_data, c, data, + data_len); +} + static int nvme_pr_command(struct block_device *bdev, u32 cdw10, u64 key, u64 sa_key, u8 op) { @@ -87,13 +98,7 @@ static int nvme_pr_command(struct block_device *bdev, u32 cdw10, c.common.opcode = op; c.common.cdw10 = cpu_to_le32(cdw10); - if (IS_ENABLED(CONFIG_NVME_MULTIPATH) && - bdev->bd_disk->fops == &nvme_ns_head_ops) - ret = nvme_send_ns_head_pr_command(bdev, &c, data, - sizeof(data)); - else - ret = nvme_send_ns_pr_command(bdev->bd_disk->private_data, &c, - data, sizeof(data)); + ret = nvme_send_pr_command(bdev, &c, data, sizeof(data)); if (ret < 0) return ret; -- cgit v1.2.3-70-g09d2 From 5fd96a4e15de8442915a912233d800c56f49001d Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Fri, 7 Apr 2023 15:05:44 -0500 Subject: nvme: Add pr_ops read_keys support This patch adds support for the pr_ops read_keys callout by calling the NVMe Reservation Report helper, then parsing that info to get the controller's registered keys. Because the callout is only used in the kernel where the callers, like LIO, do not know about controller/host IDs, the callout just returns the registered keys which is required by the SCSI PR in READ KEYS command. Signed-off-by: Mike Christie Link: https://lore.kernel.org/r/20230407200551.12660-12-michael.christie@oracle.com Reviewed-by: Chaitanya Kulkarni Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/nvme/host/pr.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/nvme.h | 4 +++ 2 files changed, 73 insertions(+) (limited to 'drivers/nvme') diff --git a/drivers/nvme/host/pr.c b/drivers/nvme/host/pr.c index cd93d2e5b340..0ee656404437 100644 --- a/drivers/nvme/host/pr.c +++ b/drivers/nvme/host/pr.c @@ -154,10 +154,79 @@ static int nvme_pr_release(struct block_device *bdev, u64 key, enum pr_type type return nvme_pr_command(bdev, cdw10, key, 0, nvme_cmd_resv_release); } +static int nvme_pr_resv_report(struct block_device *bdev, void *data, + u32 data_len, bool *eds) +{ + struct nvme_command c = { }; + int ret; + + c.common.opcode = nvme_cmd_resv_report; + c.common.cdw10 = cpu_to_le32(nvme_bytes_to_numd(data_len)); + c.common.cdw11 = cpu_to_le32(NVME_EXTENDED_DATA_STRUCT); + *eds = true; + +retry: + ret = nvme_send_pr_command(bdev, &c, data, data_len); + if (ret == NVME_SC_HOST_ID_INCONSIST && + c.common.cdw11 == cpu_to_le32(NVME_EXTENDED_DATA_STRUCT)) { + c.common.cdw11 = 0; + *eds = false; + goto retry; + } + + if (ret < 0) + return ret; + + return nvme_sc_to_pr_err(ret); +} + +static int nvme_pr_read_keys(struct block_device *bdev, + struct pr_keys *keys_info) +{ + u32 rse_len, num_keys = keys_info->num_keys; + struct nvme_reservation_status_ext *rse; + int ret, i; + bool eds; + + /* + * Assume we are using 128-bit host IDs and allocate a buffer large + * enough to get enough keys to fill the return keys buffer. + */ + rse_len = struct_size(rse, regctl_eds, num_keys); + rse = kzalloc(rse_len, GFP_KERNEL); + if (!rse) + return -ENOMEM; + + ret = nvme_pr_resv_report(bdev, rse, rse_len, &eds); + if (ret) + goto free_rse; + + keys_info->generation = le32_to_cpu(rse->gen); + keys_info->num_keys = get_unaligned_le16(&rse->regctl); + + num_keys = min(num_keys, keys_info->num_keys); + for (i = 0; i < num_keys; i++) { + if (eds) { + keys_info->keys[i] = + le64_to_cpu(rse->regctl_eds[i].rkey); + } else { + struct nvme_reservation_status *rs; + + rs = (struct nvme_reservation_status *)rse; + keys_info->keys[i] = le64_to_cpu(rs->regctl_ds[i].rkey); + } + } + +free_rse: + kfree(rse); + return ret; +} + const struct pr_ops nvme_pr_ops = { .pr_register = nvme_pr_register, .pr_reserve = nvme_pr_reserve, .pr_release = nvme_pr_release, .pr_preempt = nvme_pr_preempt, .pr_clear = nvme_pr_clear, + .pr_read_keys = nvme_pr_read_keys, }; diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 57b5b2b8d95b..a617e250d629 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -759,6 +759,10 @@ enum { NVME_LBART_ATTRIB_HIDE = 1 << 1, }; +enum nvme_eds { + NVME_EXTENDED_DATA_STRUCT = 0x1, +}; + struct nvme_registered_ctrl { __le16 cntlid; __u8 rcsts; -- cgit v1.2.3-70-g09d2 From be1a7cd2d0ed028ffdd60c65e3734e2a1d8b17df Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Fri, 7 Apr 2023 15:05:45 -0500 Subject: nvme: Add a nvme_pr_type enum The next patch adds support to report the reservation type, so we need to be able to convert from the NVMe PR value we get from the device to the linux block layer PR value that will be returned to callers. To prepare for that, this patch adds a nvme_pr_type enum and renames the nvme_pr_type function. Signed-off-by: Mike Christie Link: https://lore.kernel.org/r/20230407200551.12660-13-michael.christie@oracle.com Reviewed-by: Chaitanya Kulkarni Reviewed-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/nvme/host/pr.c | 24 ++++++++++++------------ include/linux/nvme.h | 9 +++++++++ 2 files changed, 21 insertions(+), 12 deletions(-) (limited to 'drivers/nvme') diff --git a/drivers/nvme/host/pr.c b/drivers/nvme/host/pr.c index 0ee656404437..732c56b417c2 100644 --- a/drivers/nvme/host/pr.c +++ b/drivers/nvme/host/pr.c @@ -9,24 +9,24 @@ #include "nvme.h" -static char nvme_pr_type(enum pr_type type) +static enum nvme_pr_type nvme_pr_type_from_blk(enum pr_type type) { switch (type) { case PR_WRITE_EXCLUSIVE: - return 1; + return NVME_PR_WRITE_EXCLUSIVE; case PR_EXCLUSIVE_ACCESS: - return 2; + return NVME_PR_EXCLUSIVE_ACCESS; case PR_WRITE_EXCLUSIVE_REG_ONLY: - return 3; + return NVME_PR_WRITE_EXCLUSIVE_REG_ONLY; case PR_EXCLUSIVE_ACCESS_REG_ONLY: - return 4; + return NVME_PR_EXCLUSIVE_ACCESS_REG_ONLY; case PR_WRITE_EXCLUSIVE_ALL_REGS: - return 5; + return NVME_PR_WRITE_EXCLUSIVE_ALL_REGS; case PR_EXCLUSIVE_ACCESS_ALL_REGS: - return 6; - default: - return 0; + return NVME_PR_EXCLUSIVE_ACCESS_ALL_REGS; } + + return 0; } static int nvme_send_ns_head_pr_command(struct block_device *bdev, @@ -127,7 +127,7 @@ static int nvme_pr_reserve(struct block_device *bdev, u64 key, if (flags & ~PR_FL_IGNORE_KEY) return -EOPNOTSUPP; - cdw10 = nvme_pr_type(type) << 8; + cdw10 = nvme_pr_type_from_blk(type) << 8; cdw10 |= ((flags & PR_FL_IGNORE_KEY) ? 1 << 3 : 0); return nvme_pr_command(bdev, cdw10, key, 0, nvme_cmd_resv_acquire); } @@ -135,7 +135,7 @@ static int nvme_pr_reserve(struct block_device *bdev, u64 key, static int nvme_pr_preempt(struct block_device *bdev, u64 old, u64 new, enum pr_type type, bool abort) { - u32 cdw10 = nvme_pr_type(type) << 8 | (abort ? 2 : 1); + u32 cdw10 = nvme_pr_type_from_blk(type) << 8 | (abort ? 2 : 1); return nvme_pr_command(bdev, cdw10, old, new, nvme_cmd_resv_acquire); } @@ -149,7 +149,7 @@ static int nvme_pr_clear(struct block_device *bdev, u64 key) static int nvme_pr_release(struct block_device *bdev, u64 key, enum pr_type type) { - u32 cdw10 = nvme_pr_type(type) << 8 | (key ? 0 : 1 << 3); + u32 cdw10 = nvme_pr_type_from_blk(type) << 8 | (key ? 0 : 1 << 3); return nvme_pr_command(bdev, cdw10, key, 0, nvme_cmd_resv_release); } diff --git a/include/linux/nvme.h b/include/linux/nvme.h index a617e250d629..4013abb86642 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -759,6 +759,15 @@ enum { NVME_LBART_ATTRIB_HIDE = 1 << 1, }; +enum nvme_pr_type { + NVME_PR_WRITE_EXCLUSIVE = 1, + NVME_PR_EXCLUSIVE_ACCESS = 2, + NVME_PR_WRITE_EXCLUSIVE_REG_ONLY = 3, + NVME_PR_EXCLUSIVE_ACCESS_REG_ONLY = 4, + NVME_PR_WRITE_EXCLUSIVE_ALL_REGS = 5, + NVME_PR_EXCLUSIVE_ACCESS_ALL_REGS = 6, +}; + enum nvme_eds { NVME_EXTENDED_DATA_STRUCT = 0x1, }; -- cgit v1.2.3-70-g09d2 From 28c97ba38ff9c00bc177887c2d8568b7115a44e0 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Fri, 7 Apr 2023 15:05:46 -0500 Subject: nvme: Add pr_ops read_reservation support This patch adds support for the pr_ops read_reservation callout by calling the NVMe Reservation Report helper. It then parses that info to detect if there is a reservation and if there is then convert the returned info to a pr_ops pr_held_reservation struct. Signed-off-by: Mike Christie Link: https://lore.kernel.org/r/20230407200551.12660-14-michael.christie@oracle.com Reviewed-by: Chaitanya Kulkarni Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/nvme/host/pr.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) (limited to 'drivers/nvme') diff --git a/drivers/nvme/host/pr.c b/drivers/nvme/host/pr.c index 732c56b417c2..391b1465ebfd 100644 --- a/drivers/nvme/host/pr.c +++ b/drivers/nvme/host/pr.c @@ -29,6 +29,26 @@ static enum nvme_pr_type nvme_pr_type_from_blk(enum pr_type type) return 0; } +static enum pr_type block_pr_type_from_nvme(enum nvme_pr_type type) +{ + switch (type) { + case NVME_PR_WRITE_EXCLUSIVE: + return PR_WRITE_EXCLUSIVE; + case NVME_PR_EXCLUSIVE_ACCESS: + return PR_EXCLUSIVE_ACCESS; + case NVME_PR_WRITE_EXCLUSIVE_REG_ONLY: + return PR_WRITE_EXCLUSIVE_REG_ONLY; + case NVME_PR_EXCLUSIVE_ACCESS_REG_ONLY: + return PR_EXCLUSIVE_ACCESS_REG_ONLY; + case NVME_PR_WRITE_EXCLUSIVE_ALL_REGS: + return PR_WRITE_EXCLUSIVE_ALL_REGS; + case NVME_PR_EXCLUSIVE_ACCESS_ALL_REGS: + return PR_EXCLUSIVE_ACCESS_ALL_REGS; + } + + return 0; +} + static int nvme_send_ns_head_pr_command(struct block_device *bdev, struct nvme_command *c, void *data, unsigned int data_len) { @@ -222,6 +242,68 @@ free_rse: return ret; } +static int nvme_pr_read_reservation(struct block_device *bdev, + struct pr_held_reservation *resv) +{ + struct nvme_reservation_status_ext tmp_rse, *rse; + int ret, i, num_regs; + u32 rse_len; + bool eds; + +get_num_regs: + /* + * Get the number of registrations so we know how big to allocate + * the response buffer. + */ + ret = nvme_pr_resv_report(bdev, &tmp_rse, sizeof(tmp_rse), &eds); + if (ret) + return ret; + + num_regs = get_unaligned_le16(&tmp_rse.regctl); + if (!num_regs) { + resv->generation = le32_to_cpu(tmp_rse.gen); + return 0; + } + + rse_len = struct_size(rse, regctl_eds, num_regs); + rse = kzalloc(rse_len, GFP_KERNEL); + if (!rse) + return -ENOMEM; + + ret = nvme_pr_resv_report(bdev, rse, rse_len, &eds); + if (ret) + goto free_rse; + + if (num_regs != get_unaligned_le16(&rse->regctl)) { + kfree(rse); + goto get_num_regs; + } + + resv->generation = le32_to_cpu(rse->gen); + resv->type = block_pr_type_from_nvme(rse->rtype); + + for (i = 0; i < num_regs; i++) { + if (eds) { + if (rse->regctl_eds[i].rcsts) { + resv->key = le64_to_cpu(rse->regctl_eds[i].rkey); + break; + } + } else { + struct nvme_reservation_status *rs; + + rs = (struct nvme_reservation_status *)rse; + if (rs->regctl_ds[i].rcsts) { + resv->key = le64_to_cpu(rs->regctl_ds[i].rkey); + break; + } + } + } + +free_rse: + kfree(rse); + return ret; +} + const struct pr_ops nvme_pr_ops = { .pr_register = nvme_pr_register, .pr_reserve = nvme_pr_reserve, @@ -229,4 +311,5 @@ const struct pr_ops nvme_pr_ops = { .pr_preempt = nvme_pr_preempt, .pr_clear = nvme_pr_clear, .pr_read_keys = nvme_pr_read_keys, + .pr_read_reservation = nvme_pr_read_reservation, }; -- cgit v1.2.3-70-g09d2