diff options
author | Matthew Wilcox <matthew.r.wilcox@intel.com> | 2011-01-26 17:05:50 -0500 |
---|---|---|
committer | Matthew Wilcox <matthew.r.wilcox@intel.com> | 2011-11-04 15:52:52 -0400 |
commit | 7fc3cdabba75c2516b8b645eb0ca7907aea70415 (patch) | |
tree | 67cf9f491410121bbf8c2418dcb05148258ad5cb /drivers/block/nvme.c | |
parent | bd38c5557cf482fc195e2264b32ea62eed60730a (diff) |
NVMe: Create nvme_map_user_pages() and nvme_unmap_user_pages()
These are generalisations of the code that was in
nvme_submit_user_admin_command().
Signed-off-by: Matthew Wilcox <matthew.r.wilcox@intel.com>
Diffstat (limited to 'drivers/block/nvme.c')
-rw-r--r-- | drivers/block/nvme.c | 68 |
1 files changed, 53 insertions, 15 deletions
diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index b28d188d10f8..f44d6cd87ea2 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c | |||
@@ -677,17 +677,22 @@ static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev) | |||
677 | return result; | 677 | return result; |
678 | } | 678 | } |
679 | 679 | ||
680 | static int nvme_submit_user_admin_command(struct nvme_dev *dev, unsigned long addr, | 680 | static int nvme_map_user_pages(struct nvme_dev *dev, int write, |
681 | unsigned length, struct nvme_command *cmd) | 681 | unsigned long addr, unsigned length, |
682 | struct scatterlist **sgp) | ||
682 | { | 683 | { |
683 | int i, err, count, nents, offset; | 684 | int i, err, count, nents, offset; |
684 | struct scatterlist sg[2]; | 685 | struct scatterlist *sg; |
685 | struct page *pages[2]; | 686 | struct page **pages; |
686 | 687 | ||
687 | if (addr & 3) | 688 | if (addr & 3) |
688 | return -EINVAL; | 689 | return -EINVAL; |
690 | if (!length) | ||
691 | return -EINVAL; | ||
692 | |||
689 | offset = offset_in_page(addr); | 693 | offset = offset_in_page(addr); |
690 | count = ((offset + length) > PAGE_SIZE) ? 2 : 1; | 694 | count = DIV_ROUND_UP(offset + length, PAGE_SIZE); |
695 | pages = kcalloc(count, sizeof(*pages), GFP_KERNEL); | ||
691 | 696 | ||
692 | err = get_user_pages_fast(addr, count, 1, pages); | 697 | err = get_user_pages_fast(addr, count, 1, pages); |
693 | if (err < count) { | 698 | if (err < count) { |
@@ -695,27 +700,60 @@ static int nvme_submit_user_admin_command(struct nvme_dev *dev, unsigned long ad | |||
695 | err = -EFAULT; | 700 | err = -EFAULT; |
696 | goto put_pages; | 701 | goto put_pages; |
697 | } | 702 | } |
703 | |||
704 | sg = kcalloc(count, sizeof(*sg), GFP_KERNEL); | ||
698 | sg_init_table(sg, count); | 705 | sg_init_table(sg, count); |
699 | sg_set_page(&sg[0], pages[0], PAGE_SIZE - offset, offset); | 706 | sg_set_page(&sg[0], pages[0], PAGE_SIZE - offset, offset); |
700 | if (count > 1) | 707 | length -= (PAGE_SIZE - offset); |
701 | sg_set_page(&sg[1], pages[1], offset, 0); | 708 | for (i = 1; i < count; i++) { |
702 | nents = dma_map_sg(&dev->pci_dev->dev, sg, count, DMA_FROM_DEVICE); | 709 | sg_set_page(&sg[i], pages[i], min_t(int, length, PAGE_SIZE), 0); |
710 | length -= PAGE_SIZE; | ||
711 | } | ||
712 | |||
713 | err = -ENOMEM; | ||
714 | nents = dma_map_sg(&dev->pci_dev->dev, sg, count, | ||
715 | write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); | ||
703 | if (!nents) | 716 | if (!nents) |
704 | goto put_pages; | 717 | goto put_pages; |
705 | 718 | ||
706 | nvme_setup_prps(&cmd->common, sg, length); | 719 | kfree(pages); |
720 | *sgp = sg; | ||
721 | return nents; | ||
707 | 722 | ||
708 | err = nvme_submit_admin_cmd(dev, cmd, NULL); | 723 | put_pages: |
724 | for (i = 0; i < count; i++) | ||
725 | put_page(pages[i]); | ||
726 | kfree(pages); | ||
727 | return err; | ||
728 | } | ||
709 | 729 | ||
710 | if (err) | 730 | static void nvme_unmap_user_pages(struct nvme_dev *dev, int write, |
711 | err = -EIO; | 731 | unsigned long addr, int length, |
732 | struct scatterlist *sg, int nents) | ||
733 | { | ||
734 | int i, count; | ||
712 | 735 | ||
736 | count = DIV_ROUND_UP(offset_in_page(addr) + length, PAGE_SIZE); | ||
713 | dma_unmap_sg(&dev->pci_dev->dev, sg, nents, DMA_FROM_DEVICE); | 737 | dma_unmap_sg(&dev->pci_dev->dev, sg, nents, DMA_FROM_DEVICE); |
714 | put_pages: | 738 | |
715 | for (i = 0; i < count; i++) | 739 | for (i = 0; i < count; i++) |
716 | put_page(pages[i]); | 740 | put_page(sg_page(&sg[i])); |
741 | } | ||
717 | 742 | ||
718 | return err; | 743 | static int nvme_submit_user_admin_command(struct nvme_dev *dev, |
744 | unsigned long addr, unsigned length, | ||
745 | struct nvme_command *cmd) | ||
746 | { | ||
747 | int err, nents; | ||
748 | struct scatterlist *sg; | ||
749 | |||
750 | nents = nvme_map_user_pages(dev, 0, addr, length, &sg); | ||
751 | if (nents < 0) | ||
752 | return nents; | ||
753 | nvme_setup_prps(&cmd->common, sg, length); | ||
754 | err = nvme_submit_admin_cmd(dev, cmd, NULL); | ||
755 | nvme_unmap_user_pages(dev, 0, addr, length, sg, nents); | ||
756 | return err ? -EIO : 0; | ||
719 | } | 757 | } |
720 | 758 | ||
721 | static int nvme_identify(struct nvme_ns *ns, unsigned long addr, int cns) | 759 | static int nvme_identify(struct nvme_ns *ns, unsigned long addr, int cns) |