Skip to content

Commit 84d4add

Browse files
Matias Bjørlingaxboe
authored andcommitted
lightnvm: add ioctls for vector I/Os
Enable user-space to issue vector I/O commands through ioctls. To issue a vector I/O, the ppa list with addresses is also required and must be mapped for the controller to access. For each ioctl, the result and status bits are returned as well, such that user-space can retrieve the open-channel SSD completion bits. The implementation covers the traditional use-cases of bad block management, and vectored read/write/erase. Signed-off-by: Matias Bjørling <[email protected]> Metadata implementation, test, and fixes. Signed-off-by: Simon A.F. Lund <[email protected]> Signed-off-by: Matias Bjørling <[email protected]> Signed-off-by: Jens Axboe <[email protected]>
1 parent 19bd6fe commit 84d4add

File tree

4 files changed

+280
-0
lines changed

4 files changed

+280
-0
lines changed

drivers/nvme/host/core.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,10 @@ static int nvme_ioctl(struct block_device *bdev, fmode_t mode,
784784
return nvme_sg_io(ns, (void __user *)arg);
785785
#endif
786786
default:
787+
#ifdef CONFIG_NVM
788+
if (ns->ndev)
789+
return nvme_nvm_ioctl(ns, cmd, arg);
790+
#endif
787791
return -ENOTTY;
788792
}
789793
}

drivers/nvme/host/lightnvm.c

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include <linux/bitops.h>
2727
#include <linux/lightnvm.h>
2828
#include <linux/vmalloc.h>
29+
#include <linux/sched/sysctl.h>
30+
#include <uapi/linux/lightnvm.h>
2931

3032
enum nvme_nvm_admin_opcode {
3133
nvme_nvm_admin_identity = 0xe2,
@@ -583,6 +585,224 @@ static struct nvm_dev_ops nvme_nvm_dev_ops = {
583585
.max_phys_sect = 64,
584586
};
585587

588+
static void nvme_nvm_end_user_vio(struct request *rq, int error)
589+
{
590+
struct completion *waiting = rq->end_io_data;
591+
592+
complete(waiting);
593+
}
594+
595+
static int nvme_nvm_submit_user_cmd(struct request_queue *q,
596+
struct nvme_ns *ns,
597+
struct nvme_nvm_command *vcmd,
598+
void __user *ubuf, unsigned int bufflen,
599+
void __user *meta_buf, unsigned int meta_len,
600+
void __user *ppa_buf, unsigned int ppa_len,
601+
u32 *result, u64 *status, unsigned int timeout)
602+
{
603+
bool write = nvme_is_write((struct nvme_command *)vcmd);
604+
struct nvm_dev *dev = ns->ndev;
605+
struct gendisk *disk = ns->disk;
606+
struct request *rq;
607+
struct bio *bio = NULL;
608+
__le64 *ppa_list = NULL;
609+
dma_addr_t ppa_dma;
610+
__le64 *metadata = NULL;
611+
dma_addr_t metadata_dma;
612+
DECLARE_COMPLETION_ONSTACK(wait);
613+
int ret;
614+
615+
rq = nvme_alloc_request(q, (struct nvme_command *)vcmd, 0,
616+
NVME_QID_ANY);
617+
if (IS_ERR(rq)) {
618+
ret = -ENOMEM;
619+
goto err_cmd;
620+
}
621+
622+
rq->timeout = timeout ? timeout : ADMIN_TIMEOUT;
623+
624+
rq->cmd_flags &= ~REQ_FAILFAST_DRIVER;
625+
rq->end_io_data = &wait;
626+
627+
if (ppa_buf && ppa_len) {
628+
ppa_list = dma_pool_alloc(dev->dma_pool, GFP_KERNEL, &ppa_dma);
629+
if (!ppa_list) {
630+
ret = -ENOMEM;
631+
goto err_rq;
632+
}
633+
if (copy_from_user(ppa_list, (void __user *)ppa_buf,
634+
sizeof(u64) * (ppa_len + 1))) {
635+
ret = -EFAULT;
636+
goto err_ppa;
637+
}
638+
vcmd->ph_rw.spba = cpu_to_le64(ppa_dma);
639+
} else {
640+
vcmd->ph_rw.spba = cpu_to_le64((uintptr_t)ppa_buf);
641+
}
642+
643+
if (ubuf && bufflen) {
644+
ret = blk_rq_map_user(q, rq, NULL, ubuf, bufflen, GFP_KERNEL);
645+
if (ret)
646+
goto err_ppa;
647+
bio = rq->bio;
648+
649+
if (meta_buf && meta_len) {
650+
metadata = dma_pool_alloc(dev->dma_pool, GFP_KERNEL,
651+
&metadata_dma);
652+
if (!metadata) {
653+
ret = -ENOMEM;
654+
goto err_map;
655+
}
656+
657+
if (write) {
658+
if (copy_from_user(metadata,
659+
(void __user *)meta_buf,
660+
meta_len)) {
661+
ret = -EFAULT;
662+
goto err_meta;
663+
}
664+
}
665+
vcmd->ph_rw.metadata = cpu_to_le64(metadata_dma);
666+
}
667+
668+
if (!disk)
669+
goto submit;
670+
671+
bio->bi_bdev = bdget_disk(disk, 0);
672+
if (!bio->bi_bdev) {
673+
ret = -ENODEV;
674+
goto err_meta;
675+
}
676+
}
677+
678+
submit:
679+
blk_execute_rq_nowait(q, NULL, rq, 0, nvme_nvm_end_user_vio);
680+
681+
wait_for_completion_io(&wait);
682+
683+
ret = nvme_error_status(rq->errors);
684+
if (result)
685+
*result = rq->errors & 0x7ff;
686+
if (status)
687+
*status = le64_to_cpu(nvme_req(rq)->result.u64);
688+
689+
if (metadata && !ret && !write) {
690+
if (copy_to_user(meta_buf, (void *)metadata, meta_len))
691+
ret = -EFAULT;
692+
}
693+
err_meta:
694+
if (meta_buf && meta_len)
695+
dma_pool_free(dev->dma_pool, metadata, metadata_dma);
696+
err_map:
697+
if (bio) {
698+
if (disk && bio->bi_bdev)
699+
bdput(bio->bi_bdev);
700+
blk_rq_unmap_user(bio);
701+
}
702+
err_ppa:
703+
if (ppa_buf && ppa_len)
704+
dma_pool_free(dev->dma_pool, ppa_list, ppa_dma);
705+
err_rq:
706+
blk_mq_free_request(rq);
707+
err_cmd:
708+
return ret;
709+
}
710+
711+
static int nvme_nvm_submit_vio(struct nvme_ns *ns,
712+
struct nvm_user_vio __user *uvio)
713+
{
714+
struct nvm_user_vio vio;
715+
struct nvme_nvm_command c;
716+
unsigned int length;
717+
int ret;
718+
719+
if (copy_from_user(&vio, uvio, sizeof(vio)))
720+
return -EFAULT;
721+
if (vio.flags)
722+
return -EINVAL;
723+
724+
memset(&c, 0, sizeof(c));
725+
c.ph_rw.opcode = vio.opcode;
726+
c.ph_rw.nsid = cpu_to_le32(ns->ns_id);
727+
c.ph_rw.control = cpu_to_le16(vio.control);
728+
c.ph_rw.length = cpu_to_le16(vio.nppas);
729+
730+
length = (vio.nppas + 1) << ns->lba_shift;
731+
732+
ret = nvme_nvm_submit_user_cmd(ns->queue, ns, &c,
733+
(void __user *)(uintptr_t)vio.addr, length,
734+
(void __user *)(uintptr_t)vio.metadata,
735+
vio.metadata_len,
736+
(void __user *)(uintptr_t)vio.ppa_list, vio.nppas,
737+
&vio.result, &vio.status, 0);
738+
739+
if (ret && copy_to_user(uvio, &vio, sizeof(vio)))
740+
return -EFAULT;
741+
742+
return ret;
743+
}
744+
745+
static int nvme_nvm_user_vcmd(struct nvme_ns *ns, int admin,
746+
struct nvm_passthru_vio __user *uvcmd)
747+
{
748+
struct nvm_passthru_vio vcmd;
749+
struct nvme_nvm_command c;
750+
struct request_queue *q;
751+
unsigned int timeout = 0;
752+
int ret;
753+
754+
if (copy_from_user(&vcmd, uvcmd, sizeof(vcmd)))
755+
return -EFAULT;
756+
if ((vcmd.opcode != 0xF2) && (!capable(CAP_SYS_ADMIN)))
757+
return -EACCES;
758+
if (vcmd.flags)
759+
return -EINVAL;
760+
761+
memset(&c, 0, sizeof(c));
762+
c.common.opcode = vcmd.opcode;
763+
c.common.nsid = cpu_to_le32(ns->ns_id);
764+
c.common.cdw2[0] = cpu_to_le32(vcmd.cdw2);
765+
c.common.cdw2[1] = cpu_to_le32(vcmd.cdw3);
766+
/* cdw11-12 */
767+
c.ph_rw.length = cpu_to_le16(vcmd.nppas);
768+
c.ph_rw.control = cpu_to_le32(vcmd.control);
769+
c.common.cdw10[3] = cpu_to_le32(vcmd.cdw13);
770+
c.common.cdw10[4] = cpu_to_le32(vcmd.cdw14);
771+
c.common.cdw10[5] = cpu_to_le32(vcmd.cdw15);
772+
773+
if (vcmd.timeout_ms)
774+
timeout = msecs_to_jiffies(vcmd.timeout_ms);
775+
776+
q = admin ? ns->ctrl->admin_q : ns->queue;
777+
778+
ret = nvme_nvm_submit_user_cmd(q, ns,
779+
(struct nvme_nvm_command *)&c,
780+
(void __user *)(uintptr_t)vcmd.addr, vcmd.data_len,
781+
(void __user *)(uintptr_t)vcmd.metadata,
782+
vcmd.metadata_len,
783+
(void __user *)(uintptr_t)vcmd.ppa_list, vcmd.nppas,
784+
&vcmd.result, &vcmd.status, timeout);
785+
786+
if (ret && copy_to_user(uvcmd, &vcmd, sizeof(vcmd)))
787+
return -EFAULT;
788+
789+
return ret;
790+
}
791+
792+
int nvme_nvm_ioctl(struct nvme_ns *ns, unsigned int cmd, unsigned long arg)
793+
{
794+
switch (cmd) {
795+
case NVME_NVM_IOCTL_ADMIN_VIO:
796+
return nvme_nvm_user_vcmd(ns, 1, (void __user *)arg);
797+
case NVME_NVM_IOCTL_IO_VIO:
798+
return nvme_nvm_user_vcmd(ns, 0, (void __user *)arg);
799+
case NVME_NVM_IOCTL_SUBMIT_VIO:
800+
return nvme_nvm_submit_vio(ns, (void __user *)arg);
801+
default:
802+
return -ENOTTY;
803+
}
804+
}
805+
586806
int nvme_nvm_register(struct nvme_ns *ns, char *disk_name, int node)
587807
{
588808
struct request_queue *q = ns->queue;

drivers/nvme/host/nvme.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ int nvme_nvm_register(struct nvme_ns *ns, char *disk_name, int node);
326326
void nvme_nvm_unregister(struct nvme_ns *ns);
327327
int nvme_nvm_register_sysfs(struct nvme_ns *ns);
328328
void nvme_nvm_unregister_sysfs(struct nvme_ns *ns);
329+
int nvme_nvm_ioctl(struct nvme_ns *ns, unsigned int cmd, unsigned long arg);
329330
#else
330331
static inline int nvme_nvm_register(struct nvme_ns *ns, char *disk_name,
331332
int node)
@@ -343,6 +344,11 @@ static inline int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *i
343344
{
344345
return 0;
345346
}
347+
static inline int nvme_nvm_ioctl(struct nvme_ns *ns, unsigned int cmd,
348+
unsigned long arg)
349+
{
350+
return -ENOTTY;
351+
}
346352
#endif /* CONFIG_NVM */
347353

348354
static inline struct nvme_ns *nvme_get_ns_from_dev(struct device *dev)

include/uapi/linux/lightnvm.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,44 @@ struct nvm_ioctl_dev_factory {
122122
__u32 flags;
123123
};
124124

125+
struct nvm_user_vio {
126+
__u8 opcode;
127+
__u8 flags;
128+
__u16 control;
129+
__u16 nppas;
130+
__u16 rsvd;
131+
__u64 metadata;
132+
__u64 addr;
133+
__u64 ppa_list;
134+
__u32 metadata_len;
135+
__u32 data_len;
136+
__u64 status;
137+
__u32 result;
138+
__u32 rsvd3[3];
139+
};
140+
141+
struct nvm_passthru_vio {
142+
__u8 opcode;
143+
__u8 flags;
144+
__u8 rsvd[2];
145+
__u32 nsid;
146+
__u32 cdw2;
147+
__u32 cdw3;
148+
__u64 metadata;
149+
__u64 addr;
150+
__u32 metadata_len;
151+
__u32 data_len;
152+
__u64 ppa_list;
153+
__u16 nppas;
154+
__u16 control;
155+
__u32 cdw13;
156+
__u32 cdw14;
157+
__u32 cdw15;
158+
__u64 status;
159+
__u32 result;
160+
__u32 timeout_ms;
161+
};
162+
125163
/* The ioctl type, 'L', 0x20 - 0x2F documented in ioctl-number.txt */
126164
enum {
127165
/* top level cmds */
@@ -137,6 +175,11 @@ enum {
137175

138176
/* Factory reset device */
139177
NVM_DEV_FACTORY_CMD,
178+
179+
/* Vector user I/O */
180+
NVM_DEV_VIO_ADMIN_CMD = 0x41,
181+
NVM_DEV_VIO_CMD = 0x42,
182+
NVM_DEV_VIO_USER_CMD = 0x43,
140183
};
141184

142185
#define NVM_IOCTL 'L' /* 0x4c */
@@ -154,6 +197,13 @@ enum {
154197
#define NVM_DEV_FACTORY _IOW(NVM_IOCTL, NVM_DEV_FACTORY_CMD, \
155198
struct nvm_ioctl_dev_factory)
156199

200+
#define NVME_NVM_IOCTL_IO_VIO _IOWR(NVM_IOCTL, NVM_DEV_VIO_USER_CMD, \
201+
struct nvm_passthru_vio)
202+
#define NVME_NVM_IOCTL_ADMIN_VIO _IOWR(NVM_IOCTL, NVM_DEV_VIO_ADMIN_CMD,\
203+
struct nvm_passthru_vio)
204+
#define NVME_NVM_IOCTL_SUBMIT_VIO _IOWR(NVM_IOCTL, NVM_DEV_VIO_CMD,\
205+
struct nvm_user_vio)
206+
157207
#define NVM_VERSION_MAJOR 1
158208
#define NVM_VERSION_MINOR 0
159209
#define NVM_VERSION_PATCHLEVEL 0

0 commit comments

Comments
 (0)