mirror of
https://git.proxmox.com/git/mirror_ubuntu-kernels.git
synced 2025-12-06 07:31:49 +00:00
-----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;
|
|
}
|