mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
3e78198862
-----BEGIN PGP SIGNATURE----- iQJEBAABCAAuFiEEwPw5LcreJtl1+l5K99NY+ylx4KYFAmaOTd8QHGF4Ym9lQGtl cm5lbC5kawAKCRD301j7KXHgppqIEACUr8Vv2FtezvT3OfVSlYWHHLXzkRhwEG5s vdk0o7Ow6U54sMjfymbHTgLD0ZOJf3uJ6BI95FQuW41jPzDFVbx4Hy8QzqonMkw9 1D/YQ4zrVL2mOKBzATbKpoGJzMOzGeoXEueFZ1AYPAX7RrDtP4xPQNfrcfkdE2zF LycJN70Vp6lrZZMuI9yb9ts1tf7TFzK0HJANxOAKTgSiPmBmxesjkJlhrdUrgkAU qDVyjj7u/ssndBJAb9i6Bl95Do8s9t4DeJq5/6wgKqtf5hClMXzPVB8Wy084gr6E rTRsCEhOug3qEZSqfAgAxnd3XFRNc/p2KMUe5YZ4mAqux4hpSmIQQDM/5X5K9vEv f4MNqUGlqyqntZx+KPyFpf7kLHFYS1qK4ub0FojWJEY4GrbBPNjjncLJ9+ozR0c8 kNDaFjMNAjalBee1FxNNH8LdVcd28rrCkPxRLEfO/gvBMUmvJf4ZyKmSED0v5DhY vZqKlBqG+wg0EXvdiWEHMDh9Y+q/2XBIkS6NN/Bhh61HNu+XzC838ts1X7lR+4o2 AM5Vapw+v0q6kFBMRP3IcJI/c0UcIU8EQU7axMyzWtvhog8kx8x01hIj1L4UyYYr rUdWrkugBVXJbywFuH/QIJxWxS/z4JdSw5VjASJLIrXy+aANmmG9Wonv95eyhpUv 5iv+EdRSNA== =wVi8 -----END PGP SIGNATURE----- Merge tag 'for-6.11/block-20240710' of git://git.kernel.dk/linux Pull block updates from Jens Axboe: - NVMe updates via Keith: - Device initialization memory leak fixes (Keith) - More constants defined (Weiwen) - Target debugfs support (Hannes) - PCIe subsystem reset enhancements (Keith) - Queue-depth multipath policy (Redhat and PureStorage) - Implement get_unique_id (Christoph) - Authentication error fixes (Gaosheng) - MD updates via Song - sync_action fix and refactoring (Yu Kuai) - Various small fixes (Christoph Hellwig, Li Nan, and Ofir Gal, Yu Kuai, Benjamin Marzinski, Christophe JAILLET, Yang Li) - Fix loop detach/open race (Gulam) - Fix lower control limit for blk-throttle (Yu) - Add module descriptions to various drivers (Jeff) - Add support for atomic writes for block devices, and statx reporting for same. Includes SCSI and NVMe (John, Prasad, Alan) - Add IO priority information to block trace points (Dongliang) - Various zone improvements and tweaks (Damien) - mq-deadline tag reservation improvements (Bart) - Ignore direct reclaim swap writes in writeback throttling (Baokun) - Block integrity improvements and fixes (Anuj) - Add basic support for rust based block drivers. Has a dummy null_blk variant for now (Andreas) - Series converting driver settings to queue limits, and cleanups and fixes related to that (Christoph) - Cleanup for poking too deeply into the bvec internals, in preparation for DMA mapping API changes (Christoph) - Various minor tweaks and fixes (Jiapeng, John, Kanchan, Mikulas, Ming, Zhu, Damien, Christophe, Chaitanya) * tag 'for-6.11/block-20240710' of git://git.kernel.dk/linux: (206 commits) floppy: add missing MODULE_DESCRIPTION() macro loop: add missing MODULE_DESCRIPTION() macro ublk_drv: add missing MODULE_DESCRIPTION() macro xen/blkback: add missing MODULE_DESCRIPTION() macro block/rnbd: Constify struct kobj_type block: take offset into account in blk_bvec_map_sg again block: fix get_max_segment_size() warning loop: Don't bother validating blocksize virtio_blk: Don't bother validating blocksize null_blk: Don't bother validating blocksize block: Validate logical block size in blk_validate_limits() virtio_blk: Fix default logical block size fallback nvmet-auth: fix nvmet_auth hash error handling nvme: implement ->get_unique_id block: pass a phys_addr_t to get_max_segment_size block: add a bvec_phys helper blk-lib: check for kill signal in ioctl BLKZEROOUT block: limit the Write Zeroes to manually writing zeroes fallback block: refacto blkdev_issue_zeroout block: move read-only and supported checks into (__)blkdev_issue_zeroout ...
368 lines
9.3 KiB
C
368 lines
9.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* NVMe Fabrics command implementation.
|
|
* Copyright (c) 2015-2016 HGST, a Western Digital Company.
|
|
*/
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
#include <linux/blkdev.h>
|
|
#include "nvmet.h"
|
|
|
|
static void nvmet_execute_prop_set(struct nvmet_req *req)
|
|
{
|
|
u64 val = le64_to_cpu(req->cmd->prop_set.value);
|
|
u16 status = 0;
|
|
|
|
if (!nvmet_check_transfer_len(req, 0))
|
|
return;
|
|
|
|
if (req->cmd->prop_set.attrib & 1) {
|
|
req->error_loc =
|
|
offsetof(struct nvmf_property_set_command, attrib);
|
|
status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
|
|
goto out;
|
|
}
|
|
|
|
switch (le32_to_cpu(req->cmd->prop_set.offset)) {
|
|
case NVME_REG_CC:
|
|
nvmet_update_cc(req->sq->ctrl, val);
|
|
break;
|
|
default:
|
|
req->error_loc =
|
|
offsetof(struct nvmf_property_set_command, offset);
|
|
status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
|
|
}
|
|
out:
|
|
nvmet_req_complete(req, status);
|
|
}
|
|
|
|
static void nvmet_execute_prop_get(struct nvmet_req *req)
|
|
{
|
|
struct nvmet_ctrl *ctrl = req->sq->ctrl;
|
|
u16 status = 0;
|
|
u64 val = 0;
|
|
|
|
if (!nvmet_check_transfer_len(req, 0))
|
|
return;
|
|
|
|
if (req->cmd->prop_get.attrib & 1) {
|
|
switch (le32_to_cpu(req->cmd->prop_get.offset)) {
|
|
case NVME_REG_CAP:
|
|
val = ctrl->cap;
|
|
break;
|
|
default:
|
|
status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
|
|
break;
|
|
}
|
|
} else {
|
|
switch (le32_to_cpu(req->cmd->prop_get.offset)) {
|
|
case NVME_REG_VS:
|
|
val = ctrl->subsys->ver;
|
|
break;
|
|
case NVME_REG_CC:
|
|
val = ctrl->cc;
|
|
break;
|
|
case NVME_REG_CSTS:
|
|
val = ctrl->csts;
|
|
break;
|
|
default:
|
|
status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (status && req->cmd->prop_get.attrib & 1) {
|
|
req->error_loc =
|
|
offsetof(struct nvmf_property_get_command, offset);
|
|
} else {
|
|
req->error_loc =
|
|
offsetof(struct nvmf_property_get_command, attrib);
|
|
}
|
|
|
|
req->cqe->result.u64 = cpu_to_le64(val);
|
|
nvmet_req_complete(req, status);
|
|
}
|
|
|
|
u16 nvmet_parse_fabrics_admin_cmd(struct nvmet_req *req)
|
|
{
|
|
struct nvme_command *cmd = req->cmd;
|
|
|
|
switch (cmd->fabrics.fctype) {
|
|
case nvme_fabrics_type_property_set:
|
|
req->execute = nvmet_execute_prop_set;
|
|
break;
|
|
case nvme_fabrics_type_property_get:
|
|
req->execute = nvmet_execute_prop_get;
|
|
break;
|
|
#ifdef CONFIG_NVME_TARGET_AUTH
|
|
case nvme_fabrics_type_auth_send:
|
|
req->execute = nvmet_execute_auth_send;
|
|
break;
|
|
case nvme_fabrics_type_auth_receive:
|
|
req->execute = nvmet_execute_auth_receive;
|
|
break;
|
|
#endif
|
|
default:
|
|
pr_debug("received unknown capsule type 0x%x\n",
|
|
cmd->fabrics.fctype);
|
|
req->error_loc = offsetof(struct nvmf_common_command, fctype);
|
|
return NVME_SC_INVALID_OPCODE | NVME_STATUS_DNR;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
u16 nvmet_parse_fabrics_io_cmd(struct nvmet_req *req)
|
|
{
|
|
struct nvme_command *cmd = req->cmd;
|
|
|
|
switch (cmd->fabrics.fctype) {
|
|
#ifdef CONFIG_NVME_TARGET_AUTH
|
|
case nvme_fabrics_type_auth_send:
|
|
req->execute = nvmet_execute_auth_send;
|
|
break;
|
|
case nvme_fabrics_type_auth_receive:
|
|
req->execute = nvmet_execute_auth_receive;
|
|
break;
|
|
#endif
|
|
default:
|
|
pr_debug("received unknown capsule type 0x%x\n",
|
|
cmd->fabrics.fctype);
|
|
req->error_loc = offsetof(struct nvmf_common_command, fctype);
|
|
return NVME_SC_INVALID_OPCODE | NVME_STATUS_DNR;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static u16 nvmet_install_queue(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
|
|
{
|
|
struct nvmf_connect_command *c = &req->cmd->connect;
|
|
u16 qid = le16_to_cpu(c->qid);
|
|
u16 sqsize = le16_to_cpu(c->sqsize);
|
|
struct nvmet_ctrl *old;
|
|
u16 mqes = NVME_CAP_MQES(ctrl->cap);
|
|
u16 ret;
|
|
|
|
if (!sqsize) {
|
|
pr_warn("queue size zero!\n");
|
|
req->error_loc = offsetof(struct nvmf_connect_command, sqsize);
|
|
req->cqe->result.u32 = IPO_IATTR_CONNECT_SQE(sqsize);
|
|
ret = NVME_SC_CONNECT_INVALID_PARAM | NVME_STATUS_DNR;
|
|
goto err;
|
|
}
|
|
|
|
if (ctrl->sqs[qid] != NULL) {
|
|
pr_warn("qid %u has already been created\n", qid);
|
|
req->error_loc = offsetof(struct nvmf_connect_command, qid);
|
|
return NVME_SC_CMD_SEQ_ERROR | NVME_STATUS_DNR;
|
|
}
|
|
|
|
/* for fabrics, this value applies to only the I/O Submission Queues */
|
|
if (qid && sqsize > mqes) {
|
|
pr_warn("sqsize %u is larger than MQES supported %u cntlid %d\n",
|
|
sqsize, mqes, ctrl->cntlid);
|
|
req->error_loc = offsetof(struct nvmf_connect_command, sqsize);
|
|
req->cqe->result.u32 = IPO_IATTR_CONNECT_SQE(sqsize);
|
|
return NVME_SC_CONNECT_INVALID_PARAM | NVME_STATUS_DNR;
|
|
}
|
|
|
|
old = cmpxchg(&req->sq->ctrl, NULL, ctrl);
|
|
if (old) {
|
|
pr_warn("queue already connected!\n");
|
|
req->error_loc = offsetof(struct nvmf_connect_command, opcode);
|
|
return NVME_SC_CONNECT_CTRL_BUSY | NVME_STATUS_DNR;
|
|
}
|
|
|
|
/* note: convert queue size from 0's-based value to 1's-based value */
|
|
nvmet_cq_setup(ctrl, req->cq, qid, sqsize + 1);
|
|
nvmet_sq_setup(ctrl, req->sq, qid, sqsize + 1);
|
|
|
|
if (c->cattr & NVME_CONNECT_DISABLE_SQFLOW) {
|
|
req->sq->sqhd_disabled = true;
|
|
req->cqe->sq_head = cpu_to_le16(0xffff);
|
|
}
|
|
|
|
if (ctrl->ops->install_queue) {
|
|
ret = ctrl->ops->install_queue(req->sq);
|
|
if (ret) {
|
|
pr_err("failed to install queue %d cntlid %d ret %x\n",
|
|
qid, ctrl->cntlid, ret);
|
|
ctrl->sqs[qid] = NULL;
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
err:
|
|
req->sq->ctrl = NULL;
|
|
return ret;
|
|
}
|
|
|
|
static u32 nvmet_connect_result(struct nvmet_ctrl *ctrl)
|
|
{
|
|
return (u32)ctrl->cntlid |
|
|
(nvmet_has_auth(ctrl) ? NVME_CONNECT_AUTHREQ_ATR : 0);
|
|
}
|
|
|
|
static void nvmet_execute_admin_connect(struct nvmet_req *req)
|
|
{
|
|
struct nvmf_connect_command *c = &req->cmd->connect;
|
|
struct nvmf_connect_data *d;
|
|
struct nvmet_ctrl *ctrl = NULL;
|
|
u16 status;
|
|
u8 dhchap_status;
|
|
|
|
if (!nvmet_check_transfer_len(req, sizeof(struct nvmf_connect_data)))
|
|
return;
|
|
|
|
d = kmalloc(sizeof(*d), GFP_KERNEL);
|
|
if (!d) {
|
|
status = NVME_SC_INTERNAL;
|
|
goto complete;
|
|
}
|
|
|
|
status = nvmet_copy_from_sgl(req, 0, d, sizeof(*d));
|
|
if (status)
|
|
goto out;
|
|
|
|
if (c->recfmt != 0) {
|
|
pr_warn("invalid connect version (%d).\n",
|
|
le16_to_cpu(c->recfmt));
|
|
req->error_loc = offsetof(struct nvmf_connect_command, recfmt);
|
|
status = NVME_SC_CONNECT_FORMAT | NVME_STATUS_DNR;
|
|
goto out;
|
|
}
|
|
|
|
if (unlikely(d->cntlid != cpu_to_le16(0xffff))) {
|
|
pr_warn("connect attempt for invalid controller ID %#x\n",
|
|
d->cntlid);
|
|
status = NVME_SC_CONNECT_INVALID_PARAM | NVME_STATUS_DNR;
|
|
req->cqe->result.u32 = IPO_IATTR_CONNECT_DATA(cntlid);
|
|
goto out;
|
|
}
|
|
|
|
d->subsysnqn[NVMF_NQN_FIELD_LEN - 1] = '\0';
|
|
d->hostnqn[NVMF_NQN_FIELD_LEN - 1] = '\0';
|
|
status = nvmet_alloc_ctrl(d->subsysnqn, d->hostnqn, req,
|
|
le32_to_cpu(c->kato), &ctrl);
|
|
if (status)
|
|
goto out;
|
|
|
|
uuid_copy(&ctrl->hostid, &d->hostid);
|
|
|
|
dhchap_status = nvmet_setup_auth(ctrl);
|
|
if (dhchap_status) {
|
|
pr_err("Failed to setup authentication, dhchap status %u\n",
|
|
dhchap_status);
|
|
nvmet_ctrl_put(ctrl);
|
|
if (dhchap_status == NVME_AUTH_DHCHAP_FAILURE_FAILED)
|
|
status = (NVME_SC_CONNECT_INVALID_HOST | NVME_STATUS_DNR);
|
|
else
|
|
status = NVME_SC_INTERNAL;
|
|
goto out;
|
|
}
|
|
|
|
status = nvmet_install_queue(ctrl, req);
|
|
if (status) {
|
|
nvmet_ctrl_put(ctrl);
|
|
goto out;
|
|
}
|
|
|
|
pr_info("creating %s controller %d for subsystem %s for NQN %s%s%s.\n",
|
|
nvmet_is_disc_subsys(ctrl->subsys) ? "discovery" : "nvm",
|
|
ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn,
|
|
ctrl->pi_support ? " T10-PI is enabled" : "",
|
|
nvmet_has_auth(ctrl) ? " with DH-HMAC-CHAP" : "");
|
|
req->cqe->result.u32 = cpu_to_le32(nvmet_connect_result(ctrl));
|
|
out:
|
|
kfree(d);
|
|
complete:
|
|
nvmet_req_complete(req, status);
|
|
}
|
|
|
|
static void nvmet_execute_io_connect(struct nvmet_req *req)
|
|
{
|
|
struct nvmf_connect_command *c = &req->cmd->connect;
|
|
struct nvmf_connect_data *d;
|
|
struct nvmet_ctrl *ctrl;
|
|
u16 qid = le16_to_cpu(c->qid);
|
|
u16 status;
|
|
|
|
if (!nvmet_check_transfer_len(req, sizeof(struct nvmf_connect_data)))
|
|
return;
|
|
|
|
d = kmalloc(sizeof(*d), GFP_KERNEL);
|
|
if (!d) {
|
|
status = NVME_SC_INTERNAL;
|
|
goto complete;
|
|
}
|
|
|
|
status = nvmet_copy_from_sgl(req, 0, d, sizeof(*d));
|
|
if (status)
|
|
goto out;
|
|
|
|
if (c->recfmt != 0) {
|
|
pr_warn("invalid connect version (%d).\n",
|
|
le16_to_cpu(c->recfmt));
|
|
status = NVME_SC_CONNECT_FORMAT | NVME_STATUS_DNR;
|
|
goto out;
|
|
}
|
|
|
|
d->subsysnqn[NVMF_NQN_FIELD_LEN - 1] = '\0';
|
|
d->hostnqn[NVMF_NQN_FIELD_LEN - 1] = '\0';
|
|
ctrl = nvmet_ctrl_find_get(d->subsysnqn, d->hostnqn,
|
|
le16_to_cpu(d->cntlid), req);
|
|
if (!ctrl) {
|
|
status = NVME_SC_CONNECT_INVALID_PARAM | NVME_STATUS_DNR;
|
|
goto out;
|
|
}
|
|
|
|
if (unlikely(qid > ctrl->subsys->max_qid)) {
|
|
pr_warn("invalid queue id (%d)\n", qid);
|
|
status = NVME_SC_CONNECT_INVALID_PARAM | NVME_STATUS_DNR;
|
|
req->cqe->result.u32 = IPO_IATTR_CONNECT_SQE(qid);
|
|
goto out_ctrl_put;
|
|
}
|
|
|
|
status = nvmet_install_queue(ctrl, req);
|
|
if (status)
|
|
goto out_ctrl_put;
|
|
|
|
pr_debug("adding queue %d to ctrl %d.\n", qid, ctrl->cntlid);
|
|
req->cqe->result.u32 = cpu_to_le32(nvmet_connect_result(ctrl));
|
|
out:
|
|
kfree(d);
|
|
complete:
|
|
nvmet_req_complete(req, status);
|
|
return;
|
|
|
|
out_ctrl_put:
|
|
nvmet_ctrl_put(ctrl);
|
|
goto out;
|
|
}
|
|
|
|
u16 nvmet_parse_connect_cmd(struct nvmet_req *req)
|
|
{
|
|
struct nvme_command *cmd = req->cmd;
|
|
|
|
if (!nvme_is_fabrics(cmd)) {
|
|
pr_debug("invalid command 0x%x on unconnected queue.\n",
|
|
cmd->fabrics.opcode);
|
|
req->error_loc = offsetof(struct nvme_common_command, opcode);
|
|
return NVME_SC_INVALID_OPCODE | NVME_STATUS_DNR;
|
|
}
|
|
if (cmd->fabrics.fctype != nvme_fabrics_type_connect) {
|
|
pr_debug("invalid capsule type 0x%x on unconnected queue.\n",
|
|
cmd->fabrics.fctype);
|
|
req->error_loc = offsetof(struct nvmf_common_command, fctype);
|
|
return NVME_SC_INVALID_OPCODE | NVME_STATUS_DNR;
|
|
}
|
|
|
|
if (cmd->connect.qid == 0)
|
|
req->execute = nvmet_execute_admin_connect;
|
|
else
|
|
req->execute = nvmet_execute_io_connect;
|
|
return 0;
|
|
}
|