diff options
Diffstat (limited to 'drivers/vfio/vfio.c')
-rw-r--r-- | drivers/vfio/vfio.c | 117 |
1 files changed, 90 insertions, 27 deletions
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index fcc12f3e60a3..acb7121a9316 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c | |||
@@ -24,8 +24,10 @@ | |||
24 | #include <linux/list.h> | 24 | #include <linux/list.h> |
25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
26 | #include <linux/mutex.h> | 26 | #include <linux/mutex.h> |
27 | #include <linux/rwsem.h> | ||
27 | #include <linux/sched.h> | 28 | #include <linux/sched.h> |
28 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
30 | #include <linux/stat.h> | ||
29 | #include <linux/string.h> | 31 | #include <linux/string.h> |
30 | #include <linux/uaccess.h> | 32 | #include <linux/uaccess.h> |
31 | #include <linux/vfio.h> | 33 | #include <linux/vfio.h> |
@@ -57,7 +59,7 @@ struct vfio_iommu_driver { | |||
57 | struct vfio_container { | 59 | struct vfio_container { |
58 | struct kref kref; | 60 | struct kref kref; |
59 | struct list_head group_list; | 61 | struct list_head group_list; |
60 | struct mutex group_lock; | 62 | struct rw_semaphore group_lock; |
61 | struct vfio_iommu_driver *iommu_driver; | 63 | struct vfio_iommu_driver *iommu_driver; |
62 | void *iommu_data; | 64 | void *iommu_data; |
63 | }; | 65 | }; |
@@ -392,12 +394,13 @@ static void vfio_device_release(struct kref *kref) | |||
392 | } | 394 | } |
393 | 395 | ||
394 | /* Device reference always implies a group reference */ | 396 | /* Device reference always implies a group reference */ |
395 | static void vfio_device_put(struct vfio_device *device) | 397 | void vfio_device_put(struct vfio_device *device) |
396 | { | 398 | { |
397 | struct vfio_group *group = device->group; | 399 | struct vfio_group *group = device->group; |
398 | kref_put_mutex(&device->kref, vfio_device_release, &group->device_lock); | 400 | kref_put_mutex(&device->kref, vfio_device_release, &group->device_lock); |
399 | vfio_group_put(group); | 401 | vfio_group_put(group); |
400 | } | 402 | } |
403 | EXPORT_SYMBOL_GPL(vfio_device_put); | ||
401 | 404 | ||
402 | static void vfio_device_get(struct vfio_device *device) | 405 | static void vfio_device_get(struct vfio_device *device) |
403 | { | 406 | { |
@@ -627,6 +630,33 @@ int vfio_add_group_dev(struct device *dev, | |||
627 | } | 630 | } |
628 | EXPORT_SYMBOL_GPL(vfio_add_group_dev); | 631 | EXPORT_SYMBOL_GPL(vfio_add_group_dev); |
629 | 632 | ||
633 | /** | ||
634 | * Get a reference to the vfio_device for a device that is known to | ||
635 | * be bound to a vfio driver. The driver implicitly holds a | ||
636 | * vfio_device reference between vfio_add_group_dev and | ||
637 | * vfio_del_group_dev. We can therefore use drvdata to increment | ||
638 | * that reference from the struct device. This additional | ||
639 | * reference must be released by calling vfio_device_put. | ||
640 | */ | ||
641 | struct vfio_device *vfio_device_get_from_dev(struct device *dev) | ||
642 | { | ||
643 | struct vfio_device *device = dev_get_drvdata(dev); | ||
644 | |||
645 | vfio_device_get(device); | ||
646 | |||
647 | return device; | ||
648 | } | ||
649 | EXPORT_SYMBOL_GPL(vfio_device_get_from_dev); | ||
650 | |||
651 | /* | ||
652 | * Caller must hold a reference to the vfio_device | ||
653 | */ | ||
654 | void *vfio_device_data(struct vfio_device *device) | ||
655 | { | ||
656 | return device->device_data; | ||
657 | } | ||
658 | EXPORT_SYMBOL_GPL(vfio_device_data); | ||
659 | |||
630 | /* Given a referenced group, check if it contains the device */ | 660 | /* Given a referenced group, check if it contains the device */ |
631 | static bool vfio_dev_present(struct vfio_group *group, struct device *dev) | 661 | static bool vfio_dev_present(struct vfio_group *group, struct device *dev) |
632 | { | 662 | { |
@@ -675,9 +705,13 @@ EXPORT_SYMBOL_GPL(vfio_del_group_dev); | |||
675 | static long vfio_ioctl_check_extension(struct vfio_container *container, | 705 | static long vfio_ioctl_check_extension(struct vfio_container *container, |
676 | unsigned long arg) | 706 | unsigned long arg) |
677 | { | 707 | { |
678 | struct vfio_iommu_driver *driver = container->iommu_driver; | 708 | struct vfio_iommu_driver *driver; |
679 | long ret = 0; | 709 | long ret = 0; |
680 | 710 | ||
711 | down_read(&container->group_lock); | ||
712 | |||
713 | driver = container->iommu_driver; | ||
714 | |||
681 | switch (arg) { | 715 | switch (arg) { |
682 | /* No base extensions yet */ | 716 | /* No base extensions yet */ |
683 | default: | 717 | default: |
@@ -707,10 +741,12 @@ static long vfio_ioctl_check_extension(struct vfio_container *container, | |||
707 | VFIO_CHECK_EXTENSION, arg); | 741 | VFIO_CHECK_EXTENSION, arg); |
708 | } | 742 | } |
709 | 743 | ||
744 | up_read(&container->group_lock); | ||
745 | |||
710 | return ret; | 746 | return ret; |
711 | } | 747 | } |
712 | 748 | ||
713 | /* hold container->group_lock */ | 749 | /* hold write lock on container->group_lock */ |
714 | static int __vfio_container_attach_groups(struct vfio_container *container, | 750 | static int __vfio_container_attach_groups(struct vfio_container *container, |
715 | struct vfio_iommu_driver *driver, | 751 | struct vfio_iommu_driver *driver, |
716 | void *data) | 752 | void *data) |
@@ -741,7 +777,7 @@ static long vfio_ioctl_set_iommu(struct vfio_container *container, | |||
741 | struct vfio_iommu_driver *driver; | 777 | struct vfio_iommu_driver *driver; |
742 | long ret = -ENODEV; | 778 | long ret = -ENODEV; |
743 | 779 | ||
744 | mutex_lock(&container->group_lock); | 780 | down_write(&container->group_lock); |
745 | 781 | ||
746 | /* | 782 | /* |
747 | * The container is designed to be an unprivileged interface while | 783 | * The container is designed to be an unprivileged interface while |
@@ -752,7 +788,7 @@ static long vfio_ioctl_set_iommu(struct vfio_container *container, | |||
752 | * the container is deprivileged and returns to an unset state. | 788 | * the container is deprivileged and returns to an unset state. |
753 | */ | 789 | */ |
754 | if (list_empty(&container->group_list) || container->iommu_driver) { | 790 | if (list_empty(&container->group_list) || container->iommu_driver) { |
755 | mutex_unlock(&container->group_lock); | 791 | up_write(&container->group_lock); |
756 | return -EINVAL; | 792 | return -EINVAL; |
757 | } | 793 | } |
758 | 794 | ||
@@ -799,7 +835,7 @@ static long vfio_ioctl_set_iommu(struct vfio_container *container, | |||
799 | 835 | ||
800 | mutex_unlock(&vfio.iommu_drivers_lock); | 836 | mutex_unlock(&vfio.iommu_drivers_lock); |
801 | skip_drivers_unlock: | 837 | skip_drivers_unlock: |
802 | mutex_unlock(&container->group_lock); | 838 | up_write(&container->group_lock); |
803 | 839 | ||
804 | return ret; | 840 | return ret; |
805 | } | 841 | } |
@@ -815,9 +851,6 @@ static long vfio_fops_unl_ioctl(struct file *filep, | |||
815 | if (!container) | 851 | if (!container) |
816 | return ret; | 852 | return ret; |
817 | 853 | ||
818 | driver = container->iommu_driver; | ||
819 | data = container->iommu_data; | ||
820 | |||
821 | switch (cmd) { | 854 | switch (cmd) { |
822 | case VFIO_GET_API_VERSION: | 855 | case VFIO_GET_API_VERSION: |
823 | ret = VFIO_API_VERSION; | 856 | ret = VFIO_API_VERSION; |
@@ -829,8 +862,15 @@ static long vfio_fops_unl_ioctl(struct file *filep, | |||
829 | ret = vfio_ioctl_set_iommu(container, arg); | 862 | ret = vfio_ioctl_set_iommu(container, arg); |
830 | break; | 863 | break; |
831 | default: | 864 | default: |
865 | down_read(&container->group_lock); | ||
866 | |||
867 | driver = container->iommu_driver; | ||
868 | data = container->iommu_data; | ||
869 | |||
832 | if (driver) /* passthrough all unrecognized ioctls */ | 870 | if (driver) /* passthrough all unrecognized ioctls */ |
833 | ret = driver->ops->ioctl(data, cmd, arg); | 871 | ret = driver->ops->ioctl(data, cmd, arg); |
872 | |||
873 | up_read(&container->group_lock); | ||
834 | } | 874 | } |
835 | 875 | ||
836 | return ret; | 876 | return ret; |
@@ -854,7 +894,7 @@ static int vfio_fops_open(struct inode *inode, struct file *filep) | |||
854 | return -ENOMEM; | 894 | return -ENOMEM; |
855 | 895 | ||
856 | INIT_LIST_HEAD(&container->group_list); | 896 | INIT_LIST_HEAD(&container->group_list); |
857 | mutex_init(&container->group_lock); | 897 | init_rwsem(&container->group_lock); |
858 | kref_init(&container->kref); | 898 | kref_init(&container->kref); |
859 | 899 | ||
860 | filep->private_data = container; | 900 | filep->private_data = container; |
@@ -881,35 +921,55 @@ static ssize_t vfio_fops_read(struct file *filep, char __user *buf, | |||
881 | size_t count, loff_t *ppos) | 921 | size_t count, loff_t *ppos) |
882 | { | 922 | { |
883 | struct vfio_container *container = filep->private_data; | 923 | struct vfio_container *container = filep->private_data; |
884 | struct vfio_iommu_driver *driver = container->iommu_driver; | 924 | struct vfio_iommu_driver *driver; |
925 | ssize_t ret = -EINVAL; | ||
885 | 926 | ||
886 | if (unlikely(!driver || !driver->ops->read)) | 927 | down_read(&container->group_lock); |
887 | return -EINVAL; | ||
888 | 928 | ||
889 | return driver->ops->read(container->iommu_data, buf, count, ppos); | 929 | driver = container->iommu_driver; |
930 | if (likely(driver && driver->ops->read)) | ||
931 | ret = driver->ops->read(container->iommu_data, | ||
932 | buf, count, ppos); | ||
933 | |||
934 | up_read(&container->group_lock); | ||
935 | |||
936 | return ret; | ||
890 | } | 937 | } |
891 | 938 | ||
892 | static ssize_t vfio_fops_write(struct file *filep, const char __user *buf, | 939 | static ssize_t vfio_fops_write(struct file *filep, const char __user *buf, |
893 | size_t count, loff_t *ppos) | 940 | size_t count, loff_t *ppos) |
894 | { | 941 | { |
895 | struct vfio_container *container = filep->private_data; | 942 | struct vfio_container *container = filep->private_data; |
896 | struct vfio_iommu_driver *driver = container->iommu_driver; | 943 | struct vfio_iommu_driver *driver; |
944 | ssize_t ret = -EINVAL; | ||
897 | 945 | ||
898 | if (unlikely(!driver || !driver->ops->write)) | 946 | down_read(&container->group_lock); |
899 | return -EINVAL; | ||
900 | 947 | ||
901 | return driver->ops->write(container->iommu_data, buf, count, ppos); | 948 | driver = container->iommu_driver; |
949 | if (likely(driver && driver->ops->write)) | ||
950 | ret = driver->ops->write(container->iommu_data, | ||
951 | buf, count, ppos); | ||
952 | |||
953 | up_read(&container->group_lock); | ||
954 | |||
955 | return ret; | ||
902 | } | 956 | } |
903 | 957 | ||
904 | static int vfio_fops_mmap(struct file *filep, struct vm_area_struct *vma) | 958 | static int vfio_fops_mmap(struct file *filep, struct vm_area_struct *vma) |
905 | { | 959 | { |
906 | struct vfio_container *container = filep->private_data; | 960 | struct vfio_container *container = filep->private_data; |
907 | struct vfio_iommu_driver *driver = container->iommu_driver; | 961 | struct vfio_iommu_driver *driver; |
962 | int ret = -EINVAL; | ||
908 | 963 | ||
909 | if (unlikely(!driver || !driver->ops->mmap)) | 964 | down_read(&container->group_lock); |
910 | return -EINVAL; | ||
911 | 965 | ||
912 | return driver->ops->mmap(container->iommu_data, vma); | 966 | driver = container->iommu_driver; |
967 | if (likely(driver && driver->ops->mmap)) | ||
968 | ret = driver->ops->mmap(container->iommu_data, vma); | ||
969 | |||
970 | up_read(&container->group_lock); | ||
971 | |||
972 | return ret; | ||
913 | } | 973 | } |
914 | 974 | ||
915 | static const struct file_operations vfio_fops = { | 975 | static const struct file_operations vfio_fops = { |
@@ -933,7 +993,7 @@ static void __vfio_group_unset_container(struct vfio_group *group) | |||
933 | struct vfio_container *container = group->container; | 993 | struct vfio_container *container = group->container; |
934 | struct vfio_iommu_driver *driver; | 994 | struct vfio_iommu_driver *driver; |
935 | 995 | ||
936 | mutex_lock(&container->group_lock); | 996 | down_write(&container->group_lock); |
937 | 997 | ||
938 | driver = container->iommu_driver; | 998 | driver = container->iommu_driver; |
939 | if (driver) | 999 | if (driver) |
@@ -951,7 +1011,7 @@ static void __vfio_group_unset_container(struct vfio_group *group) | |||
951 | container->iommu_data = NULL; | 1011 | container->iommu_data = NULL; |
952 | } | 1012 | } |
953 | 1013 | ||
954 | mutex_unlock(&container->group_lock); | 1014 | up_write(&container->group_lock); |
955 | 1015 | ||
956 | vfio_container_put(container); | 1016 | vfio_container_put(container); |
957 | } | 1017 | } |
@@ -1011,7 +1071,7 @@ static int vfio_group_set_container(struct vfio_group *group, int container_fd) | |||
1011 | container = f.file->private_data; | 1071 | container = f.file->private_data; |
1012 | WARN_ON(!container); /* fget ensures we don't race vfio_release */ | 1072 | WARN_ON(!container); /* fget ensures we don't race vfio_release */ |
1013 | 1073 | ||
1014 | mutex_lock(&container->group_lock); | 1074 | down_write(&container->group_lock); |
1015 | 1075 | ||
1016 | driver = container->iommu_driver; | 1076 | driver = container->iommu_driver; |
1017 | if (driver) { | 1077 | if (driver) { |
@@ -1029,7 +1089,7 @@ static int vfio_group_set_container(struct vfio_group *group, int container_fd) | |||
1029 | atomic_inc(&group->container_users); | 1089 | atomic_inc(&group->container_users); |
1030 | 1090 | ||
1031 | unlock_out: | 1091 | unlock_out: |
1032 | mutex_unlock(&container->group_lock); | 1092 | up_write(&container->group_lock); |
1033 | fdput(f); | 1093 | fdput(f); |
1034 | return ret; | 1094 | return ret; |
1035 | } | 1095 | } |
@@ -1300,6 +1360,9 @@ static const struct file_operations vfio_device_fops = { | |||
1300 | */ | 1360 | */ |
1301 | static char *vfio_devnode(struct device *dev, umode_t *mode) | 1361 | static char *vfio_devnode(struct device *dev, umode_t *mode) |
1302 | { | 1362 | { |
1363 | if (MINOR(dev->devt) == 0) | ||
1364 | *mode = S_IRUGO | S_IWUGO; | ||
1365 | |||
1303 | return kasprintf(GFP_KERNEL, "vfio/%s", dev_name(dev)); | 1366 | return kasprintf(GFP_KERNEL, "vfio/%s", dev_name(dev)); |
1304 | } | 1367 | } |
1305 | 1368 | ||