Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xen/gntdev: add ioctl for grant copy

Add IOCTL_GNTDEV_GRANT_COPY to allow applications to copy between user
space buffers and grant references.

This interface is similar to the GNTTABOP_copy hypercall ABI except
the local buffers are provided using a virtual address (instead of a
GFN and offset). To avoid userspace from having to page align its
buffers the driver will use two or more ops if required.

If the ioctl returns 0, the application must check the status of each
segment with the segments status field. If the ioctl returns a -ve
error code (EINVAL or EFAULT), the status of individual ops is
undefined.

Signed-off-by: David Vrabel <david.vrabel@citrix.com>
Reviewed-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>

+253
+203
drivers/xen/gntdev.c
··· 748 748 return rc; 749 749 } 750 750 751 + #define GNTDEV_COPY_BATCH 24 752 + 753 + struct gntdev_copy_batch { 754 + struct gnttab_copy ops[GNTDEV_COPY_BATCH]; 755 + struct page *pages[GNTDEV_COPY_BATCH]; 756 + s16 __user *status[GNTDEV_COPY_BATCH]; 757 + unsigned int nr_ops; 758 + unsigned int nr_pages; 759 + }; 760 + 761 + static int gntdev_get_page(struct gntdev_copy_batch *batch, void __user *virt, 762 + bool writeable, unsigned long *gfn) 763 + { 764 + unsigned long addr = (unsigned long)virt; 765 + struct page *page; 766 + unsigned long xen_pfn; 767 + int ret; 768 + 769 + ret = get_user_pages_fast(addr, 1, writeable, &page); 770 + if (ret < 0) 771 + return ret; 772 + 773 + batch->pages[batch->nr_pages++] = page; 774 + 775 + xen_pfn = page_to_xen_pfn(page) + XEN_PFN_DOWN(addr & ~PAGE_MASK); 776 + *gfn = pfn_to_gfn(xen_pfn); 777 + 778 + return 0; 779 + } 780 + 781 + static void gntdev_put_pages(struct gntdev_copy_batch *batch) 782 + { 783 + unsigned int i; 784 + 785 + for (i = 0; i < batch->nr_pages; i++) 786 + put_page(batch->pages[i]); 787 + batch->nr_pages = 0; 788 + } 789 + 790 + static int gntdev_copy(struct gntdev_copy_batch *batch) 791 + { 792 + unsigned int i; 793 + 794 + gnttab_batch_copy(batch->ops, batch->nr_ops); 795 + gntdev_put_pages(batch); 796 + 797 + /* 798 + * For each completed op, update the status if the op failed 799 + * and all previous ops for the segment were successful. 800 + */ 801 + for (i = 0; i < batch->nr_ops; i++) { 802 + s16 status = batch->ops[i].status; 803 + s16 old_status; 804 + 805 + if (status == GNTST_okay) 806 + continue; 807 + 808 + if (__get_user(old_status, batch->status[i])) 809 + return -EFAULT; 810 + 811 + if (old_status != GNTST_okay) 812 + continue; 813 + 814 + if (__put_user(status, batch->status[i])) 815 + return -EFAULT; 816 + } 817 + 818 + batch->nr_ops = 0; 819 + return 0; 820 + } 821 + 822 + static int gntdev_grant_copy_seg(struct gntdev_copy_batch *batch, 823 + struct gntdev_grant_copy_segment *seg, 824 + s16 __user *status) 825 + { 826 + uint16_t copied = 0; 827 + 828 + /* 829 + * Disallow local -> local copies since there is only space in 830 + * batch->pages for one page per-op and this would be a very 831 + * expensive memcpy(). 832 + */ 833 + if (!(seg->flags & (GNTCOPY_source_gref | GNTCOPY_dest_gref))) 834 + return -EINVAL; 835 + 836 + /* Can't cross page if source/dest is a grant ref. */ 837 + if (seg->flags & GNTCOPY_source_gref) { 838 + if (seg->source.foreign.offset + seg->len > XEN_PAGE_SIZE) 839 + return -EINVAL; 840 + } 841 + if (seg->flags & GNTCOPY_dest_gref) { 842 + if (seg->dest.foreign.offset + seg->len > XEN_PAGE_SIZE) 843 + return -EINVAL; 844 + } 845 + 846 + if (put_user(GNTST_okay, status)) 847 + return -EFAULT; 848 + 849 + while (copied < seg->len) { 850 + struct gnttab_copy *op; 851 + void __user *virt; 852 + size_t len, off; 853 + unsigned long gfn; 854 + int ret; 855 + 856 + if (batch->nr_ops >= GNTDEV_COPY_BATCH) { 857 + ret = gntdev_copy(batch); 858 + if (ret < 0) 859 + return ret; 860 + } 861 + 862 + len = seg->len - copied; 863 + 864 + op = &batch->ops[batch->nr_ops]; 865 + op->flags = 0; 866 + 867 + if (seg->flags & GNTCOPY_source_gref) { 868 + op->source.u.ref = seg->source.foreign.ref; 869 + op->source.domid = seg->source.foreign.domid; 870 + op->source.offset = seg->source.foreign.offset + copied; 871 + op->flags |= GNTCOPY_source_gref; 872 + } else { 873 + virt = seg->source.virt + copied; 874 + off = (unsigned long)virt & ~XEN_PAGE_MASK; 875 + len = min(len, (size_t)XEN_PAGE_SIZE - off); 876 + 877 + ret = gntdev_get_page(batch, virt, false, &gfn); 878 + if (ret < 0) 879 + return ret; 880 + 881 + op->source.u.gmfn = gfn; 882 + op->source.domid = DOMID_SELF; 883 + op->source.offset = off; 884 + } 885 + 886 + if (seg->flags & GNTCOPY_dest_gref) { 887 + op->dest.u.ref = seg->dest.foreign.ref; 888 + op->dest.domid = seg->dest.foreign.domid; 889 + op->dest.offset = seg->dest.foreign.offset + copied; 890 + op->flags |= GNTCOPY_dest_gref; 891 + } else { 892 + virt = seg->dest.virt + copied; 893 + off = (unsigned long)virt & ~XEN_PAGE_MASK; 894 + len = min(len, (size_t)XEN_PAGE_SIZE - off); 895 + 896 + ret = gntdev_get_page(batch, virt, true, &gfn); 897 + if (ret < 0) 898 + return ret; 899 + 900 + op->dest.u.gmfn = gfn; 901 + op->dest.domid = DOMID_SELF; 902 + op->dest.offset = off; 903 + } 904 + 905 + op->len = len; 906 + copied += len; 907 + 908 + batch->status[batch->nr_ops] = status; 909 + batch->nr_ops++; 910 + } 911 + 912 + return 0; 913 + } 914 + 915 + static long gntdev_ioctl_grant_copy(struct gntdev_priv *priv, void __user *u) 916 + { 917 + struct ioctl_gntdev_grant_copy copy; 918 + struct gntdev_copy_batch batch; 919 + unsigned int i; 920 + int ret = 0; 921 + 922 + if (copy_from_user(&copy, u, sizeof(copy))) 923 + return -EFAULT; 924 + 925 + batch.nr_ops = 0; 926 + batch.nr_pages = 0; 927 + 928 + for (i = 0; i < copy.count; i++) { 929 + struct gntdev_grant_copy_segment seg; 930 + 931 + if (copy_from_user(&seg, &copy.segments[i], sizeof(seg))) { 932 + ret = -EFAULT; 933 + goto out; 934 + } 935 + 936 + ret = gntdev_grant_copy_seg(&batch, &seg, &copy.segments[i].status); 937 + if (ret < 0) 938 + goto out; 939 + 940 + cond_resched(); 941 + } 942 + if (batch.nr_ops) 943 + ret = gntdev_copy(&batch); 944 + return ret; 945 + 946 + out: 947 + gntdev_put_pages(&batch); 948 + return ret; 949 + } 950 + 751 951 static long gntdev_ioctl(struct file *flip, 752 952 unsigned int cmd, unsigned long arg) 753 953 { ··· 966 766 967 767 case IOCTL_GNTDEV_SET_UNMAP_NOTIFY: 968 768 return gntdev_ioctl_notify(priv, ptr); 769 + 770 + case IOCTL_GNTDEV_GRANT_COPY: 771 + return gntdev_ioctl_grant_copy(priv, ptr); 969 772 970 773 default: 971 774 pr_debug("priv %p, unknown cmd %x\n", priv, cmd);
+50
include/uapi/xen/gntdev.h
··· 144 144 __u32 event_channel_port; 145 145 }; 146 146 147 + struct gntdev_grant_copy_segment { 148 + union { 149 + void __user *virt; 150 + struct { 151 + grant_ref_t ref; 152 + __u16 offset; 153 + domid_t domid; 154 + } foreign; 155 + } source, dest; 156 + __u16 len; 157 + 158 + __u16 flags; /* GNTCOPY_* */ 159 + __s16 status; /* GNTST_* */ 160 + }; 161 + 162 + /* 163 + * Copy between grant references and local buffers. 164 + * 165 + * The copy is split into @count @segments, each of which can copy 166 + * to/from one grant reference. 167 + * 168 + * Each segment is similar to struct gnttab_copy in the hypervisor ABI 169 + * except the local buffer is specified using a virtual address 170 + * (instead of a GFN and offset). 171 + * 172 + * The local buffer may cross a Xen page boundary -- the driver will 173 + * split segments into multiple ops if required. 174 + * 175 + * Returns 0 if all segments have been processed and @status in each 176 + * segment is valid. Note that one or more segments may have failed 177 + * (status != GNTST_okay). 178 + * 179 + * If the driver had to split a segment into two or more ops, @status 180 + * includes the status of the first failed op for that segment (or 181 + * GNTST_okay if all ops were successful). 182 + * 183 + * If -1 is returned, the status of all segments is undefined. 184 + * 185 + * EINVAL: A segment has local buffers for both source and 186 + * destination. 187 + * EINVAL: A segment crosses the boundary of a foreign page. 188 + * EFAULT: A segment's local buffer is not accessible. 189 + */ 190 + #define IOCTL_GNTDEV_GRANT_COPY \ 191 + _IOC(_IOC_NONE, 'G', 8, sizeof(struct ioctl_gntdev_grant_copy)) 192 + struct ioctl_gntdev_grant_copy { 193 + unsigned int count; 194 + struct gntdev_grant_copy_segment __user *segments; 195 + }; 196 + 147 197 /* Clear (set to zero) the byte specified by index */ 148 198 #define UNMAP_NOTIFY_CLEAR_BYTE 0x1 149 199 /* Send an interrupt on the indicated event channel */