2021-04-10 06:42:03 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2011-2014, Intel Corporation.
|
|
|
|
* Copyright (c) 2017-2021 Christoph Hellwig.
|
|
|
|
*/
|
2024-07-02 15:10:19 +00:00
|
|
|
#include <linux/bio-integrity.h>
|
2021-04-10 06:42:03 +00:00
|
|
|
#include <linux/ptrace.h> /* for force_successful_syscall_return */
|
|
|
|
#include <linux/nvme_ioctl.h>
|
2023-12-01 00:57:35 +00:00
|
|
|
#include <linux/io_uring/cmd.h>
|
2021-04-10 06:42:03 +00:00
|
|
|
#include "nvme.h"
|
|
|
|
|
2023-01-08 06:53:03 +00:00
|
|
|
enum {
|
|
|
|
NVME_IOCTL_VEC = (1 << 0),
|
2023-01-08 06:56:54 +00:00
|
|
|
NVME_IOCTL_PARTITION = (1 << 1),
|
2023-01-08 06:53:03 +00:00
|
|
|
};
|
|
|
|
|
2022-10-31 16:23:50 +00:00
|
|
|
static bool nvme_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c,
|
2023-06-08 11:02:49 +00:00
|
|
|
unsigned int flags, bool open_for_write)
|
2022-10-31 16:23:50 +00:00
|
|
|
{
|
2022-12-13 15:13:38 +00:00
|
|
|
u32 effects;
|
|
|
|
|
2023-01-08 06:56:54 +00:00
|
|
|
/*
|
|
|
|
* Do not allow unprivileged passthrough on partitions, as that allows an
|
|
|
|
* escape from the containment of the partition.
|
|
|
|
*/
|
|
|
|
if (flags & NVME_IOCTL_PARTITION)
|
2023-05-02 18:43:41 +00:00
|
|
|
goto admin;
|
2023-01-08 06:56:54 +00:00
|
|
|
|
2022-10-31 16:23:50 +00:00
|
|
|
/*
|
|
|
|
* Do not allow unprivileged processes to send vendor specific or fabrics
|
|
|
|
* commands as we can't be sure about their effects.
|
|
|
|
*/
|
|
|
|
if (c->common.opcode >= nvme_cmd_vendor_start ||
|
|
|
|
c->common.opcode == nvme_fabrics_command)
|
2023-05-02 18:43:41 +00:00
|
|
|
goto admin;
|
2022-10-31 16:23:50 +00:00
|
|
|
|
2022-11-01 09:43:07 +00:00
|
|
|
/*
|
|
|
|
* Do not allow unprivileged passthrough of admin commands except
|
|
|
|
* for a subset of identify commands that contain information required
|
|
|
|
* to form proper I/O commands in userspace and do not expose any
|
|
|
|
* potentially sensitive information.
|
|
|
|
*/
|
|
|
|
if (!ns) {
|
|
|
|
if (c->common.opcode == nvme_admin_identify) {
|
|
|
|
switch (c->identify.cns) {
|
|
|
|
case NVME_ID_CNS_NS:
|
|
|
|
case NVME_ID_CNS_CS_NS:
|
|
|
|
case NVME_ID_CNS_NS_CS_INDEP:
|
2022-12-05 14:27:46 +00:00
|
|
|
case NVME_ID_CNS_CS_CTRL:
|
|
|
|
case NVME_ID_CNS_CTRL:
|
2022-11-01 09:43:07 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2023-05-02 18:43:41 +00:00
|
|
|
goto admin;
|
2022-11-01 09:43:07 +00:00
|
|
|
}
|
2022-10-31 16:23:50 +00:00
|
|
|
|
|
|
|
/*
|
2022-12-13 15:13:38 +00:00
|
|
|
* Check if the controller provides a Commands Supported and Effects log
|
|
|
|
* and marks this command as supported. If not reject unprivileged
|
|
|
|
* passthrough.
|
|
|
|
*/
|
|
|
|
effects = nvme_command_effects(ns->ctrl, ns, c->common.opcode);
|
|
|
|
if (!(effects & NVME_CMD_EFFECTS_CSUPP))
|
2023-05-02 18:43:41 +00:00
|
|
|
goto admin;
|
2022-12-13 15:13:38 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Don't allow passthrough for command that have intrusive (or unknown)
|
|
|
|
* effects.
|
|
|
|
*/
|
|
|
|
if (effects & ~(NVME_CMD_EFFECTS_CSUPP | NVME_CMD_EFFECTS_LBCC |
|
|
|
|
NVME_CMD_EFFECTS_UUID_SEL |
|
|
|
|
NVME_CMD_EFFECTS_SCOPE_MASK))
|
2023-05-02 18:43:41 +00:00
|
|
|
goto admin;
|
2022-12-13 15:13:38 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Only allow I/O commands that transfer data to the controller or that
|
|
|
|
* change the logical block contents if the file descriptor is open for
|
|
|
|
* writing.
|
2022-10-31 16:23:50 +00:00
|
|
|
*/
|
2023-05-02 18:43:41 +00:00
|
|
|
if ((nvme_is_write(c) || (effects & NVME_CMD_EFFECTS_LBCC)) &&
|
|
|
|
!open_for_write)
|
|
|
|
goto admin;
|
|
|
|
|
2022-10-31 16:23:50 +00:00
|
|
|
return true;
|
2023-05-02 18:43:41 +00:00
|
|
|
admin:
|
|
|
|
return capable(CAP_SYS_ADMIN);
|
2022-10-31 16:23:50 +00:00
|
|
|
}
|
|
|
|
|
2021-04-10 06:42:03 +00:00
|
|
|
/*
|
|
|
|
* Convert integer values from ioctl structures to user pointers, silently
|
|
|
|
* ignoring the upper bits in the compat case to match behaviour of 32-bit
|
|
|
|
* kernels.
|
|
|
|
*/
|
|
|
|
static void __user *nvme_to_user_ptr(uintptr_t ptrval)
|
|
|
|
{
|
|
|
|
if (in_compat_syscall())
|
|
|
|
ptrval = (compat_uptr_t)ptrval;
|
|
|
|
return (void __user *)ptrval;
|
|
|
|
}
|
|
|
|
|
2022-05-11 05:47:47 +00:00
|
|
|
static struct request *nvme_alloc_user_request(struct request_queue *q,
|
2022-09-30 06:27:44 +00:00
|
|
|
struct nvme_command *cmd, blk_opf_t rq_flags,
|
|
|
|
blk_mq_req_flags_t blk_flags)
|
2021-04-10 06:42:03 +00:00
|
|
|
{
|
|
|
|
struct request *req;
|
|
|
|
|
2022-05-11 05:47:48 +00:00
|
|
|
req = blk_mq_alloc_request(q, nvme_req_op(cmd) | rq_flags, blk_flags);
|
2021-04-10 06:42:03 +00:00
|
|
|
if (IS_ERR(req))
|
2022-05-11 05:47:47 +00:00
|
|
|
return req;
|
2022-03-15 14:53:59 +00:00
|
|
|
nvme_init_request(req, cmd);
|
2021-04-10 06:42:03 +00:00
|
|
|
nvme_req(req)->flags |= NVME_REQ_USERCMD;
|
2022-09-30 06:27:44 +00:00
|
|
|
return req;
|
|
|
|
}
|
2021-04-10 06:42:03 +00:00
|
|
|
|
2022-09-30 06:27:48 +00:00
|
|
|
static int nvme_map_user_request(struct request *req, u64 ubuffer,
|
2022-09-30 06:27:44 +00:00
|
|
|
unsigned bufflen, void __user *meta_buffer, unsigned meta_len,
|
2023-11-30 21:53:07 +00:00
|
|
|
u32 meta_seed, struct io_uring_cmd *ioucmd, unsigned int flags)
|
2022-09-30 06:27:44 +00:00
|
|
|
{
|
|
|
|
struct request_queue *q = req->q;
|
|
|
|
struct nvme_ns *ns = q->queuedata;
|
|
|
|
struct block_device *bdev = ns ? ns->disk->part0 : NULL;
|
|
|
|
struct bio *bio = NULL;
|
|
|
|
int ret;
|
|
|
|
|
2022-09-30 06:27:49 +00:00
|
|
|
if (ioucmd && (ioucmd->flags & IORING_URING_CMD_FIXED)) {
|
|
|
|
struct iov_iter iter;
|
|
|
|
|
|
|
|
/* fixedbufs is only for non-vectored io */
|
2023-01-08 06:53:03 +00:00
|
|
|
if (WARN_ON_ONCE(flags & NVME_IOCTL_VEC))
|
2022-09-30 06:27:49 +00:00
|
|
|
return -EINVAL;
|
|
|
|
ret = io_uring_cmd_import_fixed(ubuffer, bufflen,
|
|
|
|
rq_data_dir(req), &iter, ioucmd);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
ret = blk_rq_map_user_iov(q, req, NULL, &iter, GFP_KERNEL);
|
|
|
|
} else {
|
|
|
|
ret = blk_rq_map_user_io(req, NULL, nvme_to_user_ptr(ubuffer),
|
2023-01-08 06:53:03 +00:00
|
|
|
bufflen, GFP_KERNEL, flags & NVME_IOCTL_VEC, 0,
|
|
|
|
0, rq_data_dir(req));
|
2022-09-30 06:27:49 +00:00
|
|
|
}
|
2022-09-30 06:27:44 +00:00
|
|
|
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
2023-11-30 21:53:07 +00:00
|
|
|
|
2022-09-30 06:27:44 +00:00
|
|
|
bio = req->bio;
|
2023-11-30 21:53:07 +00:00
|
|
|
if (bdev) {
|
2022-09-30 06:27:44 +00:00
|
|
|
bio_set_dev(bio, bdev);
|
2023-11-30 21:53:07 +00:00
|
|
|
if (meta_buffer && meta_len) {
|
|
|
|
ret = bio_integrity_map_user(bio, meta_buffer, meta_len,
|
|
|
|
meta_seed);
|
|
|
|
if (ret)
|
|
|
|
goto out_unmap;
|
|
|
|
req->cmd_flags |= REQ_INTEGRITY;
|
2021-04-10 06:42:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-30 06:27:44 +00:00
|
|
|
return ret;
|
2022-05-11 05:47:47 +00:00
|
|
|
|
|
|
|
out_unmap:
|
|
|
|
if (bio)
|
2024-07-02 15:10:22 +00:00
|
|
|
blk_rq_unmap_user(bio);
|
2022-05-11 05:47:47 +00:00
|
|
|
out:
|
|
|
|
blk_mq_free_request(req);
|
2022-09-30 06:27:44 +00:00
|
|
|
return ret;
|
2022-05-11 05:47:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nvme_submit_user_cmd(struct request_queue *q,
|
2023-01-08 06:53:03 +00:00
|
|
|
struct nvme_command *cmd, u64 ubuffer, unsigned bufflen,
|
|
|
|
void __user *meta_buffer, unsigned meta_len, u32 meta_seed,
|
|
|
|
u64 *result, unsigned timeout, unsigned int flags)
|
2022-05-11 05:47:47 +00:00
|
|
|
{
|
2022-12-14 09:13:16 +00:00
|
|
|
struct nvme_ns *ns = q->queuedata;
|
2022-09-19 19:36:46 +00:00
|
|
|
struct nvme_ctrl *ctrl;
|
2022-05-11 05:47:47 +00:00
|
|
|
struct request *req;
|
|
|
|
struct bio *bio;
|
2022-09-19 19:36:46 +00:00
|
|
|
u32 effects;
|
2022-05-11 05:47:47 +00:00
|
|
|
int ret;
|
|
|
|
|
2022-09-30 06:27:44 +00:00
|
|
|
req = nvme_alloc_user_request(q, cmd, 0, 0);
|
2022-05-11 05:47:47 +00:00
|
|
|
if (IS_ERR(req))
|
|
|
|
return PTR_ERR(req);
|
|
|
|
|
2022-09-30 06:27:44 +00:00
|
|
|
req->timeout = timeout;
|
|
|
|
if (ubuffer && bufflen) {
|
|
|
|
ret = nvme_map_user_request(req, ubuffer, bufflen, meta_buffer,
|
2023-11-30 21:53:07 +00:00
|
|
|
meta_len, meta_seed, NULL, flags);
|
2022-09-30 06:27:44 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-05-11 05:47:47 +00:00
|
|
|
bio = req->bio;
|
2022-09-19 19:36:46 +00:00
|
|
|
ctrl = nvme_req(req)->ctrl;
|
2022-05-11 05:47:47 +00:00
|
|
|
|
2022-12-14 09:13:16 +00:00
|
|
|
effects = nvme_passthru_start(ctrl, ns, cmd->common.opcode);
|
|
|
|
ret = nvme_execute_rq(req, false);
|
2021-04-10 06:42:03 +00:00
|
|
|
if (result)
|
|
|
|
*result = le64_to_cpu(nvme_req(req)->result.u64);
|
|
|
|
if (bio)
|
2024-07-02 15:10:22 +00:00
|
|
|
blk_rq_unmap_user(bio);
|
2021-04-10 06:42:03 +00:00
|
|
|
blk_mq_free_request(req);
|
2022-09-19 19:36:46 +00:00
|
|
|
|
|
|
|
if (effects)
|
2023-05-26 17:06:56 +00:00
|
|
|
nvme_passthru_end(ctrl, ns, effects, cmd, ret);
|
2022-09-19 19:36:46 +00:00
|
|
|
|
2021-04-10 06:42:03 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
|
|
|
|
{
|
|
|
|
struct nvme_user_io io;
|
|
|
|
struct nvme_command c;
|
|
|
|
unsigned length, meta_len;
|
|
|
|
void __user *metadata;
|
|
|
|
|
|
|
|
if (copy_from_user(&io, uio, sizeof(io)))
|
|
|
|
return -EFAULT;
|
|
|
|
if (io.flags)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
switch (io.opcode) {
|
|
|
|
case nvme_cmd_write:
|
|
|
|
case nvme_cmd_read:
|
|
|
|
case nvme_cmd_compare:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2023-12-18 16:59:49 +00:00
|
|
|
length = (io.nblocks + 1) << ns->head->lba_shift;
|
2021-04-10 06:42:03 +00:00
|
|
|
|
|
|
|
if ((io.control & NVME_RW_PRINFO_PRACT) &&
|
2024-02-06 23:34:17 +00:00
|
|
|
(ns->head->ms == ns->head->pi_size)) {
|
2021-04-10 06:42:03 +00:00
|
|
|
/*
|
|
|
|
* Protection information is stripped/inserted by the
|
|
|
|
* controller.
|
|
|
|
*/
|
|
|
|
if (nvme_to_user_ptr(io.metadata))
|
|
|
|
return -EINVAL;
|
|
|
|
meta_len = 0;
|
|
|
|
metadata = NULL;
|
|
|
|
} else {
|
2023-12-18 16:59:49 +00:00
|
|
|
meta_len = (io.nblocks + 1) * ns->head->ms;
|
2021-04-10 06:42:03 +00:00
|
|
|
metadata = nvme_to_user_ptr(io.metadata);
|
|
|
|
}
|
|
|
|
|
2023-12-18 16:59:49 +00:00
|
|
|
if (ns->head->features & NVME_NS_EXT_LBAS) {
|
2021-04-10 06:42:03 +00:00
|
|
|
length += meta_len;
|
|
|
|
meta_len = 0;
|
|
|
|
} else if (meta_len) {
|
|
|
|
if ((io.metadata & 3) || !io.metadata)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&c, 0, sizeof(c));
|
|
|
|
c.rw.opcode = io.opcode;
|
|
|
|
c.rw.flags = io.flags;
|
|
|
|
c.rw.nsid = cpu_to_le32(ns->head->ns_id);
|
|
|
|
c.rw.slba = cpu_to_le64(io.slba);
|
|
|
|
c.rw.length = cpu_to_le16(io.nblocks);
|
|
|
|
c.rw.control = cpu_to_le16(io.control);
|
|
|
|
c.rw.dsmgmt = cpu_to_le32(io.dsmgmt);
|
|
|
|
c.rw.reftag = cpu_to_le32(io.reftag);
|
|
|
|
c.rw.apptag = cpu_to_le16(io.apptag);
|
|
|
|
c.rw.appmask = cpu_to_le16(io.appmask);
|
|
|
|
|
2023-01-08 06:53:03 +00:00
|
|
|
return nvme_submit_user_cmd(ns->queue, &c, io.addr, length, metadata,
|
|
|
|
meta_len, lower_32_bits(io.slba), NULL, 0, 0);
|
2021-04-10 06:42:03 +00:00
|
|
|
}
|
|
|
|
|
2021-06-07 19:54:50 +00:00
|
|
|
static bool nvme_validate_passthru_nsid(struct nvme_ctrl *ctrl,
|
|
|
|
struct nvme_ns *ns, __u32 nsid)
|
|
|
|
{
|
|
|
|
if (ns && nsid != ns->head->ns_id) {
|
|
|
|
dev_err(ctrl->device,
|
|
|
|
"%s: nsid (%u) in cmd does not match nsid (%u)"
|
|
|
|
"of namespace\n",
|
|
|
|
current->comm, nsid, ns->head->ns_id);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-04-10 06:42:03 +00:00
|
|
|
static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
|
2023-01-08 06:56:54 +00:00
|
|
|
struct nvme_passthru_cmd __user *ucmd, unsigned int flags,
|
2023-06-08 11:02:49 +00:00
|
|
|
bool open_for_write)
|
2021-04-10 06:42:03 +00:00
|
|
|
{
|
|
|
|
struct nvme_passthru_cmd cmd;
|
|
|
|
struct nvme_command c;
|
|
|
|
unsigned timeout = 0;
|
|
|
|
u64 result;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
if (copy_from_user(&cmd, ucmd, sizeof(cmd)))
|
|
|
|
return -EFAULT;
|
|
|
|
if (cmd.flags)
|
|
|
|
return -EINVAL;
|
2021-06-07 19:54:50 +00:00
|
|
|
if (!nvme_validate_passthru_nsid(ctrl, ns, cmd.nsid))
|
2021-04-10 06:42:03 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
memset(&c, 0, sizeof(c));
|
|
|
|
c.common.opcode = cmd.opcode;
|
|
|
|
c.common.flags = cmd.flags;
|
|
|
|
c.common.nsid = cpu_to_le32(cmd.nsid);
|
|
|
|
c.common.cdw2[0] = cpu_to_le32(cmd.cdw2);
|
|
|
|
c.common.cdw2[1] = cpu_to_le32(cmd.cdw3);
|
|
|
|
c.common.cdw10 = cpu_to_le32(cmd.cdw10);
|
|
|
|
c.common.cdw11 = cpu_to_le32(cmd.cdw11);
|
|
|
|
c.common.cdw12 = cpu_to_le32(cmd.cdw12);
|
|
|
|
c.common.cdw13 = cpu_to_le32(cmd.cdw13);
|
|
|
|
c.common.cdw14 = cpu_to_le32(cmd.cdw14);
|
|
|
|
c.common.cdw15 = cpu_to_le32(cmd.cdw15);
|
|
|
|
|
2023-06-08 11:02:49 +00:00
|
|
|
if (!nvme_cmd_allowed(ns, &c, 0, open_for_write))
|
2022-10-31 16:23:50 +00:00
|
|
|
return -EACCES;
|
|
|
|
|
2021-04-10 06:42:03 +00:00
|
|
|
if (cmd.timeout_ms)
|
|
|
|
timeout = msecs_to_jiffies(cmd.timeout_ms);
|
|
|
|
|
|
|
|
status = nvme_submit_user_cmd(ns ? ns->queue : ctrl->admin_q, &c,
|
2023-01-08 06:53:03 +00:00
|
|
|
cmd.addr, cmd.data_len, nvme_to_user_ptr(cmd.metadata),
|
|
|
|
cmd.metadata_len, 0, &result, timeout, 0);
|
2021-04-10 06:42:03 +00:00
|
|
|
|
|
|
|
if (status >= 0) {
|
|
|
|
if (put_user(result, &ucmd->result))
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nvme_user_cmd64(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
|
2023-01-08 06:53:03 +00:00
|
|
|
struct nvme_passthru_cmd64 __user *ucmd, unsigned int flags,
|
2023-06-08 11:02:49 +00:00
|
|
|
bool open_for_write)
|
2021-04-10 06:42:03 +00:00
|
|
|
{
|
|
|
|
struct nvme_passthru_cmd64 cmd;
|
|
|
|
struct nvme_command c;
|
|
|
|
unsigned timeout = 0;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
if (copy_from_user(&cmd, ucmd, sizeof(cmd)))
|
|
|
|
return -EFAULT;
|
|
|
|
if (cmd.flags)
|
|
|
|
return -EINVAL;
|
2021-06-07 19:54:50 +00:00
|
|
|
if (!nvme_validate_passthru_nsid(ctrl, ns, cmd.nsid))
|
2021-04-10 06:42:03 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
memset(&c, 0, sizeof(c));
|
|
|
|
c.common.opcode = cmd.opcode;
|
|
|
|
c.common.flags = cmd.flags;
|
|
|
|
c.common.nsid = cpu_to_le32(cmd.nsid);
|
|
|
|
c.common.cdw2[0] = cpu_to_le32(cmd.cdw2);
|
|
|
|
c.common.cdw2[1] = cpu_to_le32(cmd.cdw3);
|
|
|
|
c.common.cdw10 = cpu_to_le32(cmd.cdw10);
|
|
|
|
c.common.cdw11 = cpu_to_le32(cmd.cdw11);
|
|
|
|
c.common.cdw12 = cpu_to_le32(cmd.cdw12);
|
|
|
|
c.common.cdw13 = cpu_to_le32(cmd.cdw13);
|
|
|
|
c.common.cdw14 = cpu_to_le32(cmd.cdw14);
|
|
|
|
c.common.cdw15 = cpu_to_le32(cmd.cdw15);
|
|
|
|
|
2023-06-08 11:02:49 +00:00
|
|
|
if (!nvme_cmd_allowed(ns, &c, flags, open_for_write))
|
2022-10-31 16:23:50 +00:00
|
|
|
return -EACCES;
|
|
|
|
|
2021-04-10 06:42:03 +00:00
|
|
|
if (cmd.timeout_ms)
|
|
|
|
timeout = msecs_to_jiffies(cmd.timeout_ms);
|
|
|
|
|
|
|
|
status = nvme_submit_user_cmd(ns ? ns->queue : ctrl->admin_q, &c,
|
2023-01-08 06:53:03 +00:00
|
|
|
cmd.addr, cmd.data_len, nvme_to_user_ptr(cmd.metadata),
|
|
|
|
cmd.metadata_len, 0, &cmd.result, timeout, flags);
|
2021-04-10 06:42:03 +00:00
|
|
|
|
|
|
|
if (status >= 0) {
|
|
|
|
if (put_user(cmd.result, &ucmd->result))
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2022-05-11 05:47:48 +00:00
|
|
|
struct nvme_uring_data {
|
|
|
|
__u64 metadata;
|
|
|
|
__u64 addr;
|
|
|
|
__u32 data_len;
|
|
|
|
__u32 metadata_len;
|
|
|
|
__u32 timeout_ms;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This overlays struct io_uring_cmd pdu.
|
|
|
|
* Expect build errors if this grows larger than that.
|
|
|
|
*/
|
|
|
|
struct nvme_uring_cmd_pdu {
|
2023-11-30 21:53:07 +00:00
|
|
|
struct request *req;
|
|
|
|
struct bio *bio;
|
|
|
|
u64 result;
|
|
|
|
int status;
|
2022-05-11 05:47:48 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static inline struct nvme_uring_cmd_pdu *nvme_uring_cmd_pdu(
|
|
|
|
struct io_uring_cmd *ioucmd)
|
|
|
|
{
|
|
|
|
return (struct nvme_uring_cmd_pdu *)&ioucmd->pdu;
|
|
|
|
}
|
|
|
|
|
2023-03-21 02:01:25 +00:00
|
|
|
static void nvme_uring_task_cb(struct io_uring_cmd *ioucmd,
|
|
|
|
unsigned issue_flags)
|
2022-09-21 14:26:26 +00:00
|
|
|
{
|
|
|
|
struct nvme_uring_cmd_pdu *pdu = nvme_uring_cmd_pdu(ioucmd);
|
|
|
|
|
|
|
|
if (pdu->bio)
|
2024-07-02 15:10:22 +00:00
|
|
|
blk_rq_unmap_user(pdu->bio);
|
2023-11-30 21:53:07 +00:00
|
|
|
io_uring_cmd_done(ioucmd, pdu->status, pdu->result, issue_flags);
|
2022-09-21 14:26:26 +00:00
|
|
|
}
|
|
|
|
|
2022-09-21 21:19:54 +00:00
|
|
|
static enum rq_end_io_ret nvme_uring_cmd_end_io(struct request *req,
|
|
|
|
blk_status_t err)
|
2022-05-11 05:47:48 +00:00
|
|
|
{
|
|
|
|
struct io_uring_cmd *ioucmd = req->end_io_data;
|
|
|
|
struct nvme_uring_cmd_pdu *pdu = nvme_uring_cmd_pdu(ioucmd);
|
|
|
|
|
2023-11-30 21:53:07 +00:00
|
|
|
if (nvme_req(req)->flags & NVME_REQ_CANCELLED)
|
|
|
|
pdu->status = -EINTR;
|
|
|
|
else
|
|
|
|
pdu->status = nvme_req(req)->status;
|
|
|
|
pdu->result = le64_to_cpu(nvme_req(req)->result.u64);
|
2022-08-23 16:14:43 +00:00
|
|
|
|
|
|
|
/*
|
2024-03-19 23:10:50 +00:00
|
|
|
* For iopoll, complete it directly. Note that using the uring_cmd
|
|
|
|
* helper for this is safe only because we check blk_rq_is_poll().
|
|
|
|
* As that returns false if we're NOT on a polled queue, then it's
|
|
|
|
* safe to use the polled completion helper.
|
|
|
|
*
|
2022-08-23 16:14:43 +00:00
|
|
|
* Otherwise, move the completion to task work.
|
|
|
|
*/
|
2024-03-19 23:10:50 +00:00
|
|
|
if (blk_rq_is_poll(req)) {
|
|
|
|
if (pdu->bio)
|
2024-07-02 15:10:22 +00:00
|
|
|
blk_rq_unmap_user(pdu->bio);
|
2024-03-19 23:10:50 +00:00
|
|
|
io_uring_cmd_iopoll_done(ioucmd, pdu->result, pdu->status);
|
|
|
|
} else {
|
2023-05-15 12:54:43 +00:00
|
|
|
io_uring_cmd_do_in_task_lazy(ioucmd, nvme_uring_task_cb);
|
2024-03-19 23:10:50 +00:00
|
|
|
}
|
2022-09-21 21:19:54 +00:00
|
|
|
|
2022-09-22 17:41:51 +00:00
|
|
|
return RQ_END_IO_FREE;
|
2022-09-21 14:26:26 +00:00
|
|
|
}
|
|
|
|
|
2022-05-11 05:47:48 +00:00
|
|
|
static int nvme_uring_cmd_io(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
|
2022-05-11 05:47:49 +00:00
|
|
|
struct io_uring_cmd *ioucmd, unsigned int issue_flags, bool vec)
|
2022-05-11 05:47:48 +00:00
|
|
|
{
|
|
|
|
struct nvme_uring_cmd_pdu *pdu = nvme_uring_cmd_pdu(ioucmd);
|
io_uring: Pass whole sqe to commands
Currently uring CMD operation relies on having large SQEs, but future
operations might want to use normal SQE.
The io_uring_cmd currently only saves the payload (cmd) part of the SQE,
but, for commands that use normal SQE size, it might be necessary to
access the initial SQE fields outside of the payload/cmd block. So,
saves the whole SQE other than just the pdu.
This changes slightly how the io_uring_cmd works, since the cmd
structures and callbacks are not opaque to io_uring anymore. I.e, the
callbacks can look at the SQE entries, not only, in the cmd structure.
The main advantage is that we don't need to create custom structures for
simple commands.
Creates io_uring_sqe_cmd() that returns the cmd private data as a null
pointer and avoids casting in the callee side.
Also, make most of ublk_drv's sqe->cmd priv structure into const, and use
io_uring_sqe_cmd() to get the private structure, removing the unwanted
cast. (There is one case where the cast is still needed since the
header->{len,addr} is updated in the private structure)
Suggested-by: Pavel Begunkov <asml.silence@gmail.com>
Signed-off-by: Breno Leitao <leitao@debian.org>
Reviewed-by: Keith Busch <kbusch@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Pavel Begunkov <asml.silence@gmail.com>
Link: https://lore.kernel.org/r/20230504121856.904491-3-leitao@debian.org
Signed-off-by: Jens Axboe <axboe@kernel.dk>
2023-05-04 12:18:55 +00:00
|
|
|
const struct nvme_uring_cmd *cmd = io_uring_sqe_cmd(ioucmd->sqe);
|
2022-05-11 05:47:48 +00:00
|
|
|
struct request_queue *q = ns ? ns->queue : ctrl->admin_q;
|
|
|
|
struct nvme_uring_data d;
|
|
|
|
struct nvme_command c;
|
|
|
|
struct request *req;
|
2023-01-17 12:06:37 +00:00
|
|
|
blk_opf_t rq_flags = REQ_ALLOC_CACHE;
|
2022-05-11 05:47:48 +00:00
|
|
|
blk_mq_req_flags_t blk_flags = 0;
|
2022-09-30 06:27:44 +00:00
|
|
|
int ret;
|
2022-05-11 05:47:48 +00:00
|
|
|
|
|
|
|
c.common.opcode = READ_ONCE(cmd->opcode);
|
|
|
|
c.common.flags = READ_ONCE(cmd->flags);
|
|
|
|
if (c.common.flags)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
c.common.command_id = 0;
|
|
|
|
c.common.nsid = cpu_to_le32(cmd->nsid);
|
|
|
|
if (!nvme_validate_passthru_nsid(ctrl, ns, le32_to_cpu(c.common.nsid)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
c.common.cdw2[0] = cpu_to_le32(READ_ONCE(cmd->cdw2));
|
|
|
|
c.common.cdw2[1] = cpu_to_le32(READ_ONCE(cmd->cdw3));
|
|
|
|
c.common.metadata = 0;
|
|
|
|
c.common.dptr.prp1 = c.common.dptr.prp2 = 0;
|
|
|
|
c.common.cdw10 = cpu_to_le32(READ_ONCE(cmd->cdw10));
|
|
|
|
c.common.cdw11 = cpu_to_le32(READ_ONCE(cmd->cdw11));
|
|
|
|
c.common.cdw12 = cpu_to_le32(READ_ONCE(cmd->cdw12));
|
|
|
|
c.common.cdw13 = cpu_to_le32(READ_ONCE(cmd->cdw13));
|
|
|
|
c.common.cdw14 = cpu_to_le32(READ_ONCE(cmd->cdw14));
|
|
|
|
c.common.cdw15 = cpu_to_le32(READ_ONCE(cmd->cdw15));
|
|
|
|
|
2023-06-08 11:02:49 +00:00
|
|
|
if (!nvme_cmd_allowed(ns, &c, 0, ioucmd->file->f_mode & FMODE_WRITE))
|
2022-10-31 16:23:50 +00:00
|
|
|
return -EACCES;
|
|
|
|
|
2022-05-11 05:47:48 +00:00
|
|
|
d.metadata = READ_ONCE(cmd->metadata);
|
|
|
|
d.addr = READ_ONCE(cmd->addr);
|
|
|
|
d.data_len = READ_ONCE(cmd->data_len);
|
|
|
|
d.metadata_len = READ_ONCE(cmd->metadata_len);
|
|
|
|
d.timeout_ms = READ_ONCE(cmd->timeout_ms);
|
|
|
|
|
|
|
|
if (issue_flags & IO_URING_F_NONBLOCK) {
|
2023-01-17 12:06:37 +00:00
|
|
|
rq_flags |= REQ_NOWAIT;
|
2022-05-11 05:47:48 +00:00
|
|
|
blk_flags = BLK_MQ_REQ_NOWAIT;
|
|
|
|
}
|
2022-08-23 16:14:43 +00:00
|
|
|
if (issue_flags & IO_URING_F_IOPOLL)
|
|
|
|
rq_flags |= REQ_POLLED;
|
2022-05-11 05:47:48 +00:00
|
|
|
|
2022-09-30 06:27:44 +00:00
|
|
|
req = nvme_alloc_user_request(q, &c, rq_flags, blk_flags);
|
2022-05-11 05:47:48 +00:00
|
|
|
if (IS_ERR(req))
|
|
|
|
return PTR_ERR(req);
|
2022-09-30 06:27:44 +00:00
|
|
|
req->timeout = d.timeout_ms ? msecs_to_jiffies(d.timeout_ms) : 0;
|
|
|
|
|
|
|
|
if (d.addr && d.data_len) {
|
2022-09-30 06:27:48 +00:00
|
|
|
ret = nvme_map_user_request(req, d.addr,
|
2022-09-30 06:27:44 +00:00
|
|
|
d.data_len, nvme_to_user_ptr(d.metadata),
|
2023-11-30 21:53:07 +00:00
|
|
|
d.metadata_len, 0, ioucmd, vec);
|
2022-09-30 06:27:44 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
2022-05-11 05:47:48 +00:00
|
|
|
|
|
|
|
/* to free bio on completion, as req->bio will be null at that time */
|
|
|
|
pdu->bio = req->bio;
|
2023-11-30 21:53:07 +00:00
|
|
|
pdu->req = req;
|
2022-09-21 14:26:26 +00:00
|
|
|
req->end_io_data = ioucmd;
|
2023-11-30 21:53:07 +00:00
|
|
|
req->end_io = nvme_uring_cmd_end_io;
|
2022-05-24 12:15:30 +00:00
|
|
|
blk_execute_rq_nowait(req, false);
|
2022-05-11 05:47:48 +00:00
|
|
|
return -EIOCBQUEUED;
|
|
|
|
}
|
|
|
|
|
2021-04-10 06:42:03 +00:00
|
|
|
static bool is_ctrl_ioctl(unsigned int cmd)
|
|
|
|
{
|
|
|
|
if (cmd == NVME_IOCTL_ADMIN_CMD || cmd == NVME_IOCTL_ADMIN64_CMD)
|
|
|
|
return true;
|
|
|
|
if (is_sed_ioctl(cmd))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nvme_ctrl_ioctl(struct nvme_ctrl *ctrl, unsigned int cmd,
|
2023-06-08 11:02:49 +00:00
|
|
|
void __user *argp, bool open_for_write)
|
2021-04-10 06:42:03 +00:00
|
|
|
{
|
|
|
|
switch (cmd) {
|
|
|
|
case NVME_IOCTL_ADMIN_CMD:
|
2023-06-08 11:02:49 +00:00
|
|
|
return nvme_user_cmd(ctrl, NULL, argp, 0, open_for_write);
|
2021-04-10 06:42:03 +00:00
|
|
|
case NVME_IOCTL_ADMIN64_CMD:
|
2023-06-08 11:02:49 +00:00
|
|
|
return nvme_user_cmd64(ctrl, NULL, argp, 0, open_for_write);
|
2021-04-10 06:42:03 +00:00
|
|
|
default:
|
|
|
|
return sed_ioctl(ctrl->opal_dev, cmd, argp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef COMPAT_FOR_U64_ALIGNMENT
|
|
|
|
struct nvme_user_io32 {
|
|
|
|
__u8 opcode;
|
|
|
|
__u8 flags;
|
|
|
|
__u16 control;
|
|
|
|
__u16 nblocks;
|
|
|
|
__u16 rsvd;
|
|
|
|
__u64 metadata;
|
|
|
|
__u64 addr;
|
|
|
|
__u64 slba;
|
|
|
|
__u32 dsmgmt;
|
|
|
|
__u32 reftag;
|
|
|
|
__u16 apptag;
|
|
|
|
__u16 appmask;
|
|
|
|
} __attribute__((__packed__));
|
|
|
|
#define NVME_IOCTL_SUBMIT_IO32 _IOW('N', 0x42, struct nvme_user_io32)
|
|
|
|
#endif /* COMPAT_FOR_U64_ALIGNMENT */
|
|
|
|
|
|
|
|
static int nvme_ns_ioctl(struct nvme_ns *ns, unsigned int cmd,
|
2023-06-08 11:02:49 +00:00
|
|
|
void __user *argp, unsigned int flags, bool open_for_write)
|
2021-04-10 06:42:03 +00:00
|
|
|
{
|
|
|
|
switch (cmd) {
|
|
|
|
case NVME_IOCTL_ID:
|
|
|
|
force_successful_syscall_return();
|
|
|
|
return ns->head->ns_id;
|
|
|
|
case NVME_IOCTL_IO_CMD:
|
2023-06-08 11:02:49 +00:00
|
|
|
return nvme_user_cmd(ns->ctrl, ns, argp, flags, open_for_write);
|
2021-04-10 06:42:03 +00:00
|
|
|
/*
|
|
|
|
* struct nvme_user_io can have different padding on some 32-bit ABIs.
|
|
|
|
* Just accept the compat version as all fields that are used are the
|
|
|
|
* same size and at the same offset.
|
|
|
|
*/
|
|
|
|
#ifdef COMPAT_FOR_U64_ALIGNMENT
|
|
|
|
case NVME_IOCTL_SUBMIT_IO32:
|
|
|
|
#endif
|
|
|
|
case NVME_IOCTL_SUBMIT_IO:
|
|
|
|
return nvme_submit_io(ns, argp);
|
2022-02-10 05:37:55 +00:00
|
|
|
case NVME_IOCTL_IO64_CMD_VEC:
|
2023-01-08 06:53:03 +00:00
|
|
|
flags |= NVME_IOCTL_VEC;
|
|
|
|
fallthrough;
|
|
|
|
case NVME_IOCTL_IO64_CMD:
|
2023-06-08 11:02:49 +00:00
|
|
|
return nvme_user_cmd64(ns->ctrl, ns, argp, flags,
|
|
|
|
open_for_write);
|
2021-04-10 06:42:03 +00:00
|
|
|
default:
|
2021-08-12 13:23:08 +00:00
|
|
|
return -ENOTTY;
|
2021-04-10 06:42:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-08 11:02:55 +00:00
|
|
|
int nvme_ioctl(struct block_device *bdev, blk_mode_t mode,
|
2021-04-10 06:42:03 +00:00
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
struct nvme_ns *ns = bdev->bd_disk->private_data;
|
2023-06-08 11:02:55 +00:00
|
|
|
bool open_for_write = mode & BLK_OPEN_WRITE;
|
2023-01-08 06:45:30 +00:00
|
|
|
void __user *argp = (void __user *)arg;
|
2023-01-08 06:56:54 +00:00
|
|
|
unsigned int flags = 0;
|
|
|
|
|
|
|
|
if (bdev_is_partition(bdev))
|
|
|
|
flags |= NVME_IOCTL_PARTITION;
|
2021-04-10 06:42:03 +00:00
|
|
|
|
2023-01-08 06:45:30 +00:00
|
|
|
if (is_ctrl_ioctl(cmd))
|
2023-06-08 11:02:49 +00:00
|
|
|
return nvme_ctrl_ioctl(ns->ctrl, cmd, argp, open_for_write);
|
|
|
|
return nvme_ns_ioctl(ns, cmd, argp, flags, open_for_write);
|
2021-04-21 07:45:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
long nvme_ns_chr_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
struct nvme_ns *ns =
|
|
|
|
container_of(file_inode(file)->i_cdev, struct nvme_ns, cdev);
|
2023-06-08 11:02:49 +00:00
|
|
|
bool open_for_write = file->f_mode & FMODE_WRITE;
|
2023-01-08 06:45:30 +00:00
|
|
|
void __user *argp = (void __user *)arg;
|
2021-04-21 07:45:04 +00:00
|
|
|
|
2023-01-08 06:45:30 +00:00
|
|
|
if (is_ctrl_ioctl(cmd))
|
2023-06-08 11:02:49 +00:00
|
|
|
return nvme_ctrl_ioctl(ns->ctrl, cmd, argp, open_for_write);
|
|
|
|
return nvme_ns_ioctl(ns, cmd, argp, 0, open_for_write);
|
2021-04-10 06:42:03 +00:00
|
|
|
}
|
|
|
|
|
2022-05-20 09:06:29 +00:00
|
|
|
static int nvme_uring_cmd_checks(unsigned int issue_flags)
|
2022-05-11 05:47:48 +00:00
|
|
|
{
|
|
|
|
|
2022-05-20 09:06:29 +00:00
|
|
|
/* NVMe passthrough requires big SQE/CQE support */
|
2022-05-11 05:47:48 +00:00
|
|
|
if ((issue_flags & (IO_URING_F_SQE128|IO_URING_F_CQE32)) !=
|
|
|
|
(IO_URING_F_SQE128|IO_URING_F_CQE32))
|
|
|
|
return -EOPNOTSUPP;
|
2022-05-20 09:06:29 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nvme_ns_uring_cmd(struct nvme_ns *ns, struct io_uring_cmd *ioucmd,
|
|
|
|
unsigned int issue_flags)
|
|
|
|
{
|
|
|
|
struct nvme_ctrl *ctrl = ns->ctrl;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
BUILD_BUG_ON(sizeof(struct nvme_uring_cmd_pdu) > sizeof(ioucmd->pdu));
|
|
|
|
|
|
|
|
ret = nvme_uring_cmd_checks(issue_flags);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2022-05-11 05:47:48 +00:00
|
|
|
|
|
|
|
switch (ioucmd->cmd_op) {
|
|
|
|
case NVME_URING_CMD_IO:
|
2022-05-11 05:47:49 +00:00
|
|
|
ret = nvme_uring_cmd_io(ctrl, ns, ioucmd, issue_flags, false);
|
|
|
|
break;
|
|
|
|
case NVME_URING_CMD_IO_VEC:
|
|
|
|
ret = nvme_uring_cmd_io(ctrl, ns, ioucmd, issue_flags, true);
|
2022-05-11 05:47:48 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = -ENOTTY;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int nvme_ns_chr_uring_cmd(struct io_uring_cmd *ioucmd, unsigned int issue_flags)
|
|
|
|
{
|
|
|
|
struct nvme_ns *ns = container_of(file_inode(ioucmd->file)->i_cdev,
|
|
|
|
struct nvme_ns, cdev);
|
|
|
|
|
|
|
|
return nvme_ns_uring_cmd(ns, ioucmd, issue_flags);
|
|
|
|
}
|
|
|
|
|
2022-09-02 21:18:05 +00:00
|
|
|
int nvme_ns_chr_uring_cmd_iopoll(struct io_uring_cmd *ioucmd,
|
|
|
|
struct io_comp_batch *iob,
|
|
|
|
unsigned int poll_flags)
|
2022-08-23 16:14:43 +00:00
|
|
|
{
|
2023-11-30 21:53:07 +00:00
|
|
|
struct nvme_uring_cmd_pdu *pdu = nvme_uring_cmd_pdu(ioucmd);
|
|
|
|
struct request *req = pdu->req;
|
2022-08-23 16:14:43 +00:00
|
|
|
|
2023-06-12 19:03:43 +00:00
|
|
|
if (req && blk_rq_is_poll(req))
|
2023-11-30 21:53:07 +00:00
|
|
|
return blk_rq_poll(req, iob, poll_flags);
|
|
|
|
return 0;
|
2022-08-23 16:14:43 +00:00
|
|
|
}
|
2021-04-10 06:42:03 +00:00
|
|
|
#ifdef CONFIG_NVME_MULTIPATH
|
2021-04-22 08:04:07 +00:00
|
|
|
static int nvme_ns_head_ctrl_ioctl(struct nvme_ns *ns, unsigned int cmd,
|
2022-10-31 16:23:50 +00:00
|
|
|
void __user *argp, struct nvme_ns_head *head, int srcu_idx,
|
2023-06-08 11:02:49 +00:00
|
|
|
bool open_for_write)
|
2021-05-19 07:09:56 +00:00
|
|
|
__releases(&head->srcu)
|
2021-04-10 06:42:03 +00:00
|
|
|
{
|
2021-04-22 08:04:07 +00:00
|
|
|
struct nvme_ctrl *ctrl = ns->ctrl;
|
2021-04-10 06:42:03 +00:00
|
|
|
int ret;
|
|
|
|
|
2021-04-22 08:04:07 +00:00
|
|
|
nvme_get_ctrl(ns->ctrl);
|
2021-05-19 07:08:41 +00:00
|
|
|
srcu_read_unlock(&head->srcu, srcu_idx);
|
2023-06-08 11:02:49 +00:00
|
|
|
ret = nvme_ctrl_ioctl(ns->ctrl, cmd, argp, open_for_write);
|
2021-04-10 06:42:03 +00:00
|
|
|
|
2021-04-22 08:04:07 +00:00
|
|
|
nvme_put_ctrl(ctrl);
|
2021-04-10 06:42:03 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-06-08 11:02:55 +00:00
|
|
|
int nvme_ns_head_ioctl(struct block_device *bdev, blk_mode_t mode,
|
2021-04-10 06:42:03 +00:00
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
2021-05-19 07:04:26 +00:00
|
|
|
struct nvme_ns_head *head = bdev->bd_disk->private_data;
|
2023-06-08 11:02:55 +00:00
|
|
|
bool open_for_write = mode & BLK_OPEN_WRITE;
|
2021-04-21 07:45:04 +00:00
|
|
|
void __user *argp = (void __user *)arg;
|
2021-04-22 08:04:07 +00:00
|
|
|
struct nvme_ns *ns;
|
2021-05-19 07:04:26 +00:00
|
|
|
int srcu_idx, ret = -EWOULDBLOCK;
|
2023-01-08 06:56:54 +00:00
|
|
|
unsigned int flags = 0;
|
|
|
|
|
|
|
|
if (bdev_is_partition(bdev))
|
|
|
|
flags |= NVME_IOCTL_PARTITION;
|
2021-04-22 08:04:07 +00:00
|
|
|
|
2021-05-19 07:04:26 +00:00
|
|
|
srcu_idx = srcu_read_lock(&head->srcu);
|
|
|
|
ns = nvme_find_path(head);
|
|
|
|
if (!ns)
|
|
|
|
goto out_unlock;
|
2021-04-21 07:45:04 +00:00
|
|
|
|
2021-04-22 08:04:07 +00:00
|
|
|
/*
|
|
|
|
* Handle ioctls that apply to the controller instead of the namespace
|
|
|
|
* seperately and drop the ns SRCU reference early. This avoids a
|
|
|
|
* deadlock when deleting namespaces using the passthrough interface.
|
|
|
|
*/
|
2021-04-21 07:45:04 +00:00
|
|
|
if (is_ctrl_ioctl(cmd))
|
2022-10-31 16:23:50 +00:00
|
|
|
return nvme_ns_head_ctrl_ioctl(ns, cmd, argp, head, srcu_idx,
|
2023-06-08 11:02:49 +00:00
|
|
|
open_for_write);
|
2021-04-22 08:04:07 +00:00
|
|
|
|
2023-06-08 11:02:49 +00:00
|
|
|
ret = nvme_ns_ioctl(ns, cmd, argp, flags, open_for_write);
|
2021-05-19 07:04:26 +00:00
|
|
|
out_unlock:
|
|
|
|
srcu_read_unlock(&head->srcu, srcu_idx);
|
2021-04-22 08:04:07 +00:00
|
|
|
return ret;
|
2021-04-21 07:45:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
long nvme_ns_head_chr_ioctl(struct file *file, unsigned int cmd,
|
|
|
|
unsigned long arg)
|
|
|
|
{
|
2023-06-08 11:02:49 +00:00
|
|
|
bool open_for_write = file->f_mode & FMODE_WRITE;
|
2021-04-21 07:45:04 +00:00
|
|
|
struct cdev *cdev = file_inode(file)->i_cdev;
|
|
|
|
struct nvme_ns_head *head =
|
|
|
|
container_of(cdev, struct nvme_ns_head, cdev);
|
|
|
|
void __user *argp = (void __user *)arg;
|
2021-04-22 08:04:07 +00:00
|
|
|
struct nvme_ns *ns;
|
2021-05-19 07:02:59 +00:00
|
|
|
int srcu_idx, ret = -EWOULDBLOCK;
|
2021-04-22 08:04:07 +00:00
|
|
|
|
|
|
|
srcu_idx = srcu_read_lock(&head->srcu);
|
|
|
|
ns = nvme_find_path(head);
|
2021-05-19 07:02:59 +00:00
|
|
|
if (!ns)
|
|
|
|
goto out_unlock;
|
2021-04-10 06:42:03 +00:00
|
|
|
|
|
|
|
if (is_ctrl_ioctl(cmd))
|
2022-10-31 16:23:50 +00:00
|
|
|
return nvme_ns_head_ctrl_ioctl(ns, cmd, argp, head, srcu_idx,
|
2023-06-08 11:02:49 +00:00
|
|
|
open_for_write);
|
2021-04-22 08:04:07 +00:00
|
|
|
|
2023-06-08 11:02:49 +00:00
|
|
|
ret = nvme_ns_ioctl(ns, cmd, argp, 0, open_for_write);
|
2021-05-19 07:02:59 +00:00
|
|
|
out_unlock:
|
|
|
|
srcu_read_unlock(&head->srcu, srcu_idx);
|
2021-04-22 08:04:07 +00:00
|
|
|
return ret;
|
2021-04-10 06:42:03 +00:00
|
|
|
}
|
2022-05-11 05:47:48 +00:00
|
|
|
|
|
|
|
int nvme_ns_head_chr_uring_cmd(struct io_uring_cmd *ioucmd,
|
|
|
|
unsigned int issue_flags)
|
|
|
|
{
|
|
|
|
struct cdev *cdev = file_inode(ioucmd->file)->i_cdev;
|
|
|
|
struct nvme_ns_head *head = container_of(cdev, struct nvme_ns_head, cdev);
|
|
|
|
int srcu_idx = srcu_read_lock(&head->srcu);
|
|
|
|
struct nvme_ns *ns = nvme_find_path(head);
|
|
|
|
int ret = -EINVAL;
|
|
|
|
|
|
|
|
if (ns)
|
|
|
|
ret = nvme_ns_uring_cmd(ns, ioucmd, issue_flags);
|
|
|
|
srcu_read_unlock(&head->srcu, srcu_idx);
|
|
|
|
return ret;
|
|
|
|
}
|
2021-04-10 06:42:03 +00:00
|
|
|
#endif /* CONFIG_NVME_MULTIPATH */
|
|
|
|
|
2022-05-20 09:06:30 +00:00
|
|
|
int nvme_dev_uring_cmd(struct io_uring_cmd *ioucmd, unsigned int issue_flags)
|
|
|
|
{
|
|
|
|
struct nvme_ctrl *ctrl = ioucmd->file->private_data;
|
|
|
|
int ret;
|
|
|
|
|
2022-08-23 16:14:43 +00:00
|
|
|
/* IOPOLL not supported yet */
|
|
|
|
if (issue_flags & IO_URING_F_IOPOLL)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2022-05-20 09:06:30 +00:00
|
|
|
ret = nvme_uring_cmd_checks(issue_flags);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
switch (ioucmd->cmd_op) {
|
|
|
|
case NVME_URING_CMD_ADMIN:
|
|
|
|
ret = nvme_uring_cmd_io(ctrl, NULL, ioucmd, issue_flags, false);
|
|
|
|
break;
|
|
|
|
case NVME_URING_CMD_ADMIN_VEC:
|
|
|
|
ret = nvme_uring_cmd_io(ctrl, NULL, ioucmd, issue_flags, true);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = -ENOTTY;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-10-31 16:23:50 +00:00
|
|
|
static int nvme_dev_user_cmd(struct nvme_ctrl *ctrl, void __user *argp,
|
2023-06-08 11:02:49 +00:00
|
|
|
bool open_for_write)
|
2021-04-10 06:42:03 +00:00
|
|
|
{
|
|
|
|
struct nvme_ns *ns;
|
2024-05-21 13:41:45 +00:00
|
|
|
int ret, srcu_idx;
|
2021-04-10 06:42:03 +00:00
|
|
|
|
2024-05-21 13:41:45 +00:00
|
|
|
srcu_idx = srcu_read_lock(&ctrl->srcu);
|
2021-04-10 06:42:03 +00:00
|
|
|
if (list_empty(&ctrl->namespaces)) {
|
|
|
|
ret = -ENOTTY;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2024-05-21 13:41:45 +00:00
|
|
|
ns = list_first_or_null_rcu(&ctrl->namespaces, struct nvme_ns, list);
|
2021-04-10 06:42:03 +00:00
|
|
|
if (ns != list_last_entry(&ctrl->namespaces, struct nvme_ns, list)) {
|
|
|
|
dev_warn(ctrl->device,
|
|
|
|
"NVME_IOCTL_IO_CMD not supported when multiple namespaces present!\n");
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_warn(ctrl->device,
|
|
|
|
"using deprecated NVME_IOCTL_IO_CMD ioctl on the char device!\n");
|
2024-05-21 13:41:45 +00:00
|
|
|
if (!nvme_get_ns(ns)) {
|
|
|
|
ret = -ENXIO;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
srcu_read_unlock(&ctrl->srcu, srcu_idx);
|
2021-04-10 06:42:03 +00:00
|
|
|
|
2023-06-08 11:02:49 +00:00
|
|
|
ret = nvme_user_cmd(ctrl, ns, argp, 0, open_for_write);
|
2021-04-10 06:42:03 +00:00
|
|
|
nvme_put_ns(ns);
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
out_unlock:
|
2024-05-21 13:41:45 +00:00
|
|
|
srcu_read_unlock(&ctrl->srcu, srcu_idx);
|
2021-04-10 06:42:03 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
long nvme_dev_ioctl(struct file *file, unsigned int cmd,
|
|
|
|
unsigned long arg)
|
|
|
|
{
|
2023-06-08 11:02:49 +00:00
|
|
|
bool open_for_write = file->f_mode & FMODE_WRITE;
|
2021-04-10 06:42:03 +00:00
|
|
|
struct nvme_ctrl *ctrl = file->private_data;
|
|
|
|
void __user *argp = (void __user *)arg;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case NVME_IOCTL_ADMIN_CMD:
|
2023-06-08 11:02:49 +00:00
|
|
|
return nvme_user_cmd(ctrl, NULL, argp, 0, open_for_write);
|
2021-04-10 06:42:03 +00:00
|
|
|
case NVME_IOCTL_ADMIN64_CMD:
|
2023-06-08 11:02:49 +00:00
|
|
|
return nvme_user_cmd64(ctrl, NULL, argp, 0, open_for_write);
|
2021-04-10 06:42:03 +00:00
|
|
|
case NVME_IOCTL_IO_CMD:
|
2023-06-08 11:02:49 +00:00
|
|
|
return nvme_dev_user_cmd(ctrl, argp, open_for_write);
|
2021-04-10 06:42:03 +00:00
|
|
|
case NVME_IOCTL_RESET:
|
2022-09-22 14:54:06 +00:00
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EACCES;
|
2021-04-10 06:42:03 +00:00
|
|
|
dev_warn(ctrl->device, "resetting controller\n");
|
|
|
|
return nvme_reset_ctrl_sync(ctrl);
|
|
|
|
case NVME_IOCTL_SUBSYS_RESET:
|
2022-09-22 14:54:06 +00:00
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EACCES;
|
2021-04-10 06:42:03 +00:00
|
|
|
return nvme_reset_subsystem(ctrl);
|
|
|
|
case NVME_IOCTL_RESCAN:
|
2022-09-22 14:54:06 +00:00
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EACCES;
|
2021-04-10 06:42:03 +00:00
|
|
|
nvme_queue_scan(ctrl);
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return -ENOTTY;
|
|
|
|
}
|
|
|
|
}
|