|
26 | 26 | #include <linux/bitops.h>
|
27 | 27 | #include <linux/lightnvm.h>
|
28 | 28 | #include <linux/vmalloc.h>
|
| 29 | +#include <linux/sched/sysctl.h> |
| 30 | +#include <uapi/linux/lightnvm.h> |
29 | 31 |
|
30 | 32 | enum nvme_nvm_admin_opcode {
|
31 | 33 | nvme_nvm_admin_identity = 0xe2,
|
@@ -583,6 +585,224 @@ static struct nvm_dev_ops nvme_nvm_dev_ops = {
|
583 | 585 | .max_phys_sect = 64,
|
584 | 586 | };
|
585 | 587 |
|
| 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 | + |
586 | 806 | int nvme_nvm_register(struct nvme_ns *ns, char *disk_name, int node)
|
587 | 807 | {
|
588 | 808 | struct request_queue *q = ns->queue;
|
|
0 commit comments