diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-14 21:06:47 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-14 21:06:47 -0400 |
commit | 8c194f3bd322a8bd44d079092d870549b8ae62d1 (patch) | |
tree | c194690d0a01b6e5313d6af133e394a056a3015f | |
parent | 07e492eb8921a8aa53fd2bf637bee3da94cc03fe (diff) | |
parent | 5a0ff17741c1785b27229a16b5ab77470d71b170 (diff) |
Merge tag 'vfio-v4.1-rc1' of git://github.com/awilliam/linux-vfio
Pull VFIO updates from Alex Williamson:
- VFIO platform bus driver support (Baptiste Reynal, Antonios Motakis,
testing and review by Eric Auger)
- Split VFIO irqfd support to separate module (Alex Williamson)
- vfio-pci VGA arbiter client (Alex Williamson)
- New vfio-pci.ids= module option (Alex Williamson)
- vfio-pci D3 power state support for idle devices (Alex Williamson)
* tag 'vfio-v4.1-rc1' of git://github.com/awilliam/linux-vfio: (30 commits)
vfio-pci: Fix use after free
vfio-pci: Move idle devices to D3hot power state
vfio-pci: Remove warning if try-reset fails
vfio-pci: Allow PCI IDs to be specified as module options
vfio-pci: Add VGA arbiter client
vfio-pci: Add module option to disable VGA region access
vgaarb: Stub vga_set_legacy_decoding()
vfio: Split virqfd into a separate module for vfio bus drivers
vfio: virqfd_lock can be static
vfio: put off the allocation of "minor" in vfio_create_group
vfio/platform: implement IRQ masking/unmasking via an eventfd
vfio: initialize the virqfd workqueue in VFIO generic code
vfio: move eventfd support code for VFIO_PCI to a separate file
vfio: pass an opaque pointer on virqfd initialization
vfio: add local lock for virqfd instead of depending on VFIO PCI
vfio: virqfd: rename vfio_pci_virqfd_init and vfio_pci_virqfd_exit
vfio: add a vfio_ prefix to virqfd_enable and virqfd_disable and export
vfio/platform: support for level sensitive interrupts
vfio/platform: trigger an interrupt via eventfd
vfio/platform: initial interrupts support code
...
-rw-r--r-- | drivers/vfio/Kconfig | 6 | ||||
-rw-r--r-- | drivers/vfio/Makefile | 4 | ||||
-rw-r--r-- | drivers/vfio/pci/Kconfig | 1 | ||||
-rw-r--r-- | drivers/vfio/pci/vfio_pci.c | 188 | ||||
-rw-r--r-- | drivers/vfio/pci/vfio_pci_intrs.c | 238 | ||||
-rw-r--r-- | drivers/vfio/pci/vfio_pci_private.h | 3 | ||||
-rw-r--r-- | drivers/vfio/platform/Kconfig | 20 | ||||
-rw-r--r-- | drivers/vfio/platform/Makefile | 8 | ||||
-rw-r--r-- | drivers/vfio/platform/vfio_amba.c | 115 | ||||
-rw-r--r-- | drivers/vfio/platform/vfio_platform.c | 103 | ||||
-rw-r--r-- | drivers/vfio/platform/vfio_platform_common.c | 521 | ||||
-rw-r--r-- | drivers/vfio/platform/vfio_platform_irq.c | 336 | ||||
-rw-r--r-- | drivers/vfio/platform/vfio_platform_private.h | 85 | ||||
-rw-r--r-- | drivers/vfio/vfio.c | 13 | ||||
-rw-r--r-- | drivers/vfio/virqfd.c | 226 | ||||
-rw-r--r-- | include/linux/vfio.h | 25 | ||||
-rw-r--r-- | include/linux/vgaarb.h | 5 | ||||
-rw-r--r-- | include/uapi/linux/vfio.h | 2 |
18 files changed, 1640 insertions, 259 deletions
diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig index 14e27ab32456..7d092ddc8119 100644 --- a/drivers/vfio/Kconfig +++ b/drivers/vfio/Kconfig | |||
@@ -13,6 +13,11 @@ config VFIO_SPAPR_EEH | |||
13 | depends on EEH && VFIO_IOMMU_SPAPR_TCE | 13 | depends on EEH && VFIO_IOMMU_SPAPR_TCE |
14 | default n | 14 | default n |
15 | 15 | ||
16 | config VFIO_VIRQFD | ||
17 | tristate | ||
18 | depends on VFIO && EVENTFD | ||
19 | default n | ||
20 | |||
16 | menuconfig VFIO | 21 | menuconfig VFIO |
17 | tristate "VFIO Non-Privileged userspace driver framework" | 22 | tristate "VFIO Non-Privileged userspace driver framework" |
18 | depends on IOMMU_API | 23 | depends on IOMMU_API |
@@ -27,3 +32,4 @@ menuconfig VFIO | |||
27 | If you don't know what to do here, say N. | 32 | If you don't know what to do here, say N. |
28 | 33 | ||
29 | source "drivers/vfio/pci/Kconfig" | 34 | source "drivers/vfio/pci/Kconfig" |
35 | source "drivers/vfio/platform/Kconfig" | ||
diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile index 0b035b12600a..7b8a31f63fea 100644 --- a/drivers/vfio/Makefile +++ b/drivers/vfio/Makefile | |||
@@ -1,5 +1,9 @@ | |||
1 | vfio_virqfd-y := virqfd.o | ||
2 | |||
1 | obj-$(CONFIG_VFIO) += vfio.o | 3 | obj-$(CONFIG_VFIO) += vfio.o |
4 | obj-$(CONFIG_VFIO_VIRQFD) += vfio_virqfd.o | ||
2 | obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o | 5 | obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o |
3 | obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o | 6 | obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o |
4 | obj-$(CONFIG_VFIO_SPAPR_EEH) += vfio_spapr_eeh.o | 7 | obj-$(CONFIG_VFIO_SPAPR_EEH) += vfio_spapr_eeh.o |
5 | obj-$(CONFIG_VFIO_PCI) += pci/ | 8 | obj-$(CONFIG_VFIO_PCI) += pci/ |
9 | obj-$(CONFIG_VFIO_PLATFORM) += platform/ | ||
diff --git a/drivers/vfio/pci/Kconfig b/drivers/vfio/pci/Kconfig index c6bb5da2d2a7..579d83bf5358 100644 --- a/drivers/vfio/pci/Kconfig +++ b/drivers/vfio/pci/Kconfig | |||
@@ -1,6 +1,7 @@ | |||
1 | config VFIO_PCI | 1 | config VFIO_PCI |
2 | tristate "VFIO support for PCI devices" | 2 | tristate "VFIO support for PCI devices" |
3 | depends on VFIO && PCI && EVENTFD | 3 | depends on VFIO && PCI && EVENTFD |
4 | select VFIO_VIRQFD | ||
4 | help | 5 | help |
5 | Support for the PCI VFIO bus driver. This is required to make | 6 | Support for the PCI VFIO bus driver. This is required to make |
6 | use of PCI drivers using the VFIO framework. | 7 | use of PCI drivers using the VFIO framework. |
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index f8a186381ae8..69fab0fd15ae 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c | |||
@@ -11,6 +11,8 @@ | |||
11 | * Author: Tom Lyon, pugs@cisco.com | 11 | * Author: Tom Lyon, pugs@cisco.com |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
15 | |||
14 | #include <linux/device.h> | 16 | #include <linux/device.h> |
15 | #include <linux/eventfd.h> | 17 | #include <linux/eventfd.h> |
16 | #include <linux/file.h> | 18 | #include <linux/file.h> |
@@ -25,6 +27,7 @@ | |||
25 | #include <linux/types.h> | 27 | #include <linux/types.h> |
26 | #include <linux/uaccess.h> | 28 | #include <linux/uaccess.h> |
27 | #include <linux/vfio.h> | 29 | #include <linux/vfio.h> |
30 | #include <linux/vgaarb.h> | ||
28 | 31 | ||
29 | #include "vfio_pci_private.h" | 32 | #include "vfio_pci_private.h" |
30 | 33 | ||
@@ -32,13 +35,81 @@ | |||
32 | #define DRIVER_AUTHOR "Alex Williamson <alex.williamson@redhat.com>" | 35 | #define DRIVER_AUTHOR "Alex Williamson <alex.williamson@redhat.com>" |
33 | #define DRIVER_DESC "VFIO PCI - User Level meta-driver" | 36 | #define DRIVER_DESC "VFIO PCI - User Level meta-driver" |
34 | 37 | ||
38 | static char ids[1024] __initdata; | ||
39 | module_param_string(ids, ids, sizeof(ids), 0); | ||
40 | MODULE_PARM_DESC(ids, "Initial PCI IDs to add to the vfio driver, format is \"vendor:device[:subvendor[:subdevice[:class[:class_mask]]]]\" and multiple comma separated entries can be specified"); | ||
41 | |||
35 | static bool nointxmask; | 42 | static bool nointxmask; |
36 | module_param_named(nointxmask, nointxmask, bool, S_IRUGO | S_IWUSR); | 43 | module_param_named(nointxmask, nointxmask, bool, S_IRUGO | S_IWUSR); |
37 | MODULE_PARM_DESC(nointxmask, | 44 | MODULE_PARM_DESC(nointxmask, |
38 | "Disable support for PCI 2.3 style INTx masking. If this resolves problems for specific devices, report lspci -vvvxxx to linux-pci@vger.kernel.org so the device can be fixed automatically via the broken_intx_masking flag."); | 45 | "Disable support for PCI 2.3 style INTx masking. If this resolves problems for specific devices, report lspci -vvvxxx to linux-pci@vger.kernel.org so the device can be fixed automatically via the broken_intx_masking flag."); |
39 | 46 | ||
47 | #ifdef CONFIG_VFIO_PCI_VGA | ||
48 | static bool disable_vga; | ||
49 | module_param(disable_vga, bool, S_IRUGO); | ||
50 | MODULE_PARM_DESC(disable_vga, "Disable VGA resource access through vfio-pci"); | ||
51 | #endif | ||
52 | |||
53 | static bool disable_idle_d3; | ||
54 | module_param(disable_idle_d3, bool, S_IRUGO | S_IWUSR); | ||
55 | MODULE_PARM_DESC(disable_idle_d3, | ||
56 | "Disable using the PCI D3 low power state for idle, unused devices"); | ||
57 | |||
40 | static DEFINE_MUTEX(driver_lock); | 58 | static DEFINE_MUTEX(driver_lock); |
41 | 59 | ||
60 | static inline bool vfio_vga_disabled(void) | ||
61 | { | ||
62 | #ifdef CONFIG_VFIO_PCI_VGA | ||
63 | return disable_vga; | ||
64 | #else | ||
65 | return true; | ||
66 | #endif | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * Our VGA arbiter participation is limited since we don't know anything | ||
71 | * about the device itself. However, if the device is the only VGA device | ||
72 | * downstream of a bridge and VFIO VGA support is disabled, then we can | ||
73 | * safely return legacy VGA IO and memory as not decoded since the user | ||
74 | * has no way to get to it and routing can be disabled externally at the | ||
75 | * bridge. | ||
76 | */ | ||
77 | static unsigned int vfio_pci_set_vga_decode(void *opaque, bool single_vga) | ||
78 | { | ||
79 | struct vfio_pci_device *vdev = opaque; | ||
80 | struct pci_dev *tmp = NULL, *pdev = vdev->pdev; | ||
81 | unsigned char max_busnr; | ||
82 | unsigned int decodes; | ||
83 | |||
84 | if (single_vga || !vfio_vga_disabled() || pci_is_root_bus(pdev->bus)) | ||
85 | return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM | | ||
86 | VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM; | ||
87 | |||
88 | max_busnr = pci_bus_max_busnr(pdev->bus); | ||
89 | decodes = VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; | ||
90 | |||
91 | while ((tmp = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, tmp)) != NULL) { | ||
92 | if (tmp == pdev || | ||
93 | pci_domain_nr(tmp->bus) != pci_domain_nr(pdev->bus) || | ||
94 | pci_is_root_bus(tmp->bus)) | ||
95 | continue; | ||
96 | |||
97 | if (tmp->bus->number >= pdev->bus->number && | ||
98 | tmp->bus->number <= max_busnr) { | ||
99 | pci_dev_put(tmp); | ||
100 | decodes |= VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM; | ||
101 | break; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | return decodes; | ||
106 | } | ||
107 | |||
108 | static inline bool vfio_pci_is_vga(struct pci_dev *pdev) | ||
109 | { | ||
110 | return (pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA; | ||
111 | } | ||
112 | |||
42 | static void vfio_pci_try_bus_reset(struct vfio_pci_device *vdev); | 113 | static void vfio_pci_try_bus_reset(struct vfio_pci_device *vdev); |
43 | 114 | ||
44 | static int vfio_pci_enable(struct vfio_pci_device *vdev) | 115 | static int vfio_pci_enable(struct vfio_pci_device *vdev) |
@@ -48,6 +119,8 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) | |||
48 | u16 cmd; | 119 | u16 cmd; |
49 | u8 msix_pos; | 120 | u8 msix_pos; |
50 | 121 | ||
122 | pci_set_power_state(pdev, PCI_D0); | ||
123 | |||
51 | /* Don't allow our initial saved state to include busmaster */ | 124 | /* Don't allow our initial saved state to include busmaster */ |
52 | pci_clear_master(pdev); | 125 | pci_clear_master(pdev); |
53 | 126 | ||
@@ -93,10 +166,8 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) | |||
93 | } else | 166 | } else |
94 | vdev->msix_bar = 0xFF; | 167 | vdev->msix_bar = 0xFF; |
95 | 168 | ||
96 | #ifdef CONFIG_VFIO_PCI_VGA | 169 | if (!vfio_vga_disabled() && vfio_pci_is_vga(pdev)) |
97 | if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) | ||
98 | vdev->has_vga = true; | 170 | vdev->has_vga = true; |
99 | #endif | ||
100 | 171 | ||
101 | return 0; | 172 | return 0; |
102 | } | 173 | } |
@@ -153,20 +224,17 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev) | |||
153 | * Try to reset the device. The success of this is dependent on | 224 | * Try to reset the device. The success of this is dependent on |
154 | * being able to lock the device, which is not always possible. | 225 | * being able to lock the device, which is not always possible. |
155 | */ | 226 | */ |
156 | if (vdev->reset_works) { | 227 | if (vdev->reset_works && !pci_try_reset_function(pdev)) |
157 | int ret = pci_try_reset_function(pdev); | 228 | vdev->needs_reset = false; |
158 | if (ret) | ||
159 | pr_warn("%s: Failed to reset device %s (%d)\n", | ||
160 | __func__, dev_name(&pdev->dev), ret); | ||
161 | else | ||
162 | vdev->needs_reset = false; | ||
163 | } | ||
164 | 229 | ||
165 | pci_restore_state(pdev); | 230 | pci_restore_state(pdev); |
166 | out: | 231 | out: |
167 | pci_disable_device(pdev); | 232 | pci_disable_device(pdev); |
168 | 233 | ||
169 | vfio_pci_try_bus_reset(vdev); | 234 | vfio_pci_try_bus_reset(vdev); |
235 | |||
236 | if (!disable_idle_d3) | ||
237 | pci_set_power_state(pdev, PCI_D3hot); | ||
170 | } | 238 | } |
171 | 239 | ||
172 | static void vfio_pci_release(void *device_data) | 240 | static void vfio_pci_release(void *device_data) |
@@ -885,6 +953,27 @@ static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |||
885 | if (ret) { | 953 | if (ret) { |
886 | iommu_group_put(group); | 954 | iommu_group_put(group); |
887 | kfree(vdev); | 955 | kfree(vdev); |
956 | return ret; | ||
957 | } | ||
958 | |||
959 | if (vfio_pci_is_vga(pdev)) { | ||
960 | vga_client_register(pdev, vdev, NULL, vfio_pci_set_vga_decode); | ||
961 | vga_set_legacy_decoding(pdev, | ||
962 | vfio_pci_set_vga_decode(vdev, false)); | ||
963 | } | ||
964 | |||
965 | if (!disable_idle_d3) { | ||
966 | /* | ||
967 | * pci-core sets the device power state to an unknown value at | ||
968 | * bootup and after being removed from a driver. The only | ||
969 | * transition it allows from this unknown state is to D0, which | ||
970 | * typically happens when a driver calls pci_enable_device(). | ||
971 | * We're not ready to enable the device yet, but we do want to | ||
972 | * be able to get to D3. Therefore first do a D0 transition | ||
973 | * before going to D3. | ||
974 | */ | ||
975 | pci_set_power_state(pdev, PCI_D0); | ||
976 | pci_set_power_state(pdev, PCI_D3hot); | ||
888 | } | 977 | } |
889 | 978 | ||
890 | return ret; | 979 | return ret; |
@@ -895,10 +984,21 @@ static void vfio_pci_remove(struct pci_dev *pdev) | |||
895 | struct vfio_pci_device *vdev; | 984 | struct vfio_pci_device *vdev; |
896 | 985 | ||
897 | vdev = vfio_del_group_dev(&pdev->dev); | 986 | vdev = vfio_del_group_dev(&pdev->dev); |
898 | if (vdev) { | 987 | if (!vdev) |
899 | iommu_group_put(pdev->dev.iommu_group); | 988 | return; |
900 | kfree(vdev); | 989 | |
990 | iommu_group_put(pdev->dev.iommu_group); | ||
991 | kfree(vdev); | ||
992 | |||
993 | if (vfio_pci_is_vga(pdev)) { | ||
994 | vga_client_register(pdev, NULL, NULL, NULL); | ||
995 | vga_set_legacy_decoding(pdev, | ||
996 | VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM | | ||
997 | VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM); | ||
901 | } | 998 | } |
999 | |||
1000 | if (!disable_idle_d3) | ||
1001 | pci_set_power_state(pdev, PCI_D0); | ||
902 | } | 1002 | } |
903 | 1003 | ||
904 | static pci_ers_result_t vfio_pci_aer_err_detected(struct pci_dev *pdev, | 1004 | static pci_ers_result_t vfio_pci_aer_err_detected(struct pci_dev *pdev, |
@@ -1017,10 +1117,13 @@ static void vfio_pci_try_bus_reset(struct vfio_pci_device *vdev) | |||
1017 | 1117 | ||
1018 | put_devs: | 1118 | put_devs: |
1019 | for (i = 0; i < devs.cur_index; i++) { | 1119 | for (i = 0; i < devs.cur_index; i++) { |
1020 | if (!ret) { | 1120 | tmp = vfio_device_data(devs.devices[i]); |
1021 | tmp = vfio_device_data(devs.devices[i]); | 1121 | if (!ret) |
1022 | tmp->needs_reset = false; | 1122 | tmp->needs_reset = false; |
1023 | } | 1123 | |
1124 | if (!tmp->refcnt && !disable_idle_d3) | ||
1125 | pci_set_power_state(tmp->pdev, PCI_D3hot); | ||
1126 | |||
1024 | vfio_device_put(devs.devices[i]); | 1127 | vfio_device_put(devs.devices[i]); |
1025 | } | 1128 | } |
1026 | 1129 | ||
@@ -1030,10 +1133,50 @@ put_devs: | |||
1030 | static void __exit vfio_pci_cleanup(void) | 1133 | static void __exit vfio_pci_cleanup(void) |
1031 | { | 1134 | { |
1032 | pci_unregister_driver(&vfio_pci_driver); | 1135 | pci_unregister_driver(&vfio_pci_driver); |
1033 | vfio_pci_virqfd_exit(); | ||
1034 | vfio_pci_uninit_perm_bits(); | 1136 | vfio_pci_uninit_perm_bits(); |
1035 | } | 1137 | } |
1036 | 1138 | ||
1139 | static void __init vfio_pci_fill_ids(void) | ||
1140 | { | ||
1141 | char *p, *id; | ||
1142 | int rc; | ||
1143 | |||
1144 | /* no ids passed actually */ | ||
1145 | if (ids[0] == '\0') | ||
1146 | return; | ||
1147 | |||
1148 | /* add ids specified in the module parameter */ | ||
1149 | p = ids; | ||
1150 | while ((id = strsep(&p, ","))) { | ||
1151 | unsigned int vendor, device, subvendor = PCI_ANY_ID, | ||
1152 | subdevice = PCI_ANY_ID, class = 0, class_mask = 0; | ||
1153 | int fields; | ||
1154 | |||
1155 | if (!strlen(id)) | ||
1156 | continue; | ||
1157 | |||
1158 | fields = sscanf(id, "%x:%x:%x:%x:%x:%x", | ||
1159 | &vendor, &device, &subvendor, &subdevice, | ||
1160 | &class, &class_mask); | ||
1161 | |||
1162 | if (fields < 2) { | ||
1163 | pr_warn("invalid id string \"%s\"\n", id); | ||
1164 | continue; | ||
1165 | } | ||
1166 | |||
1167 | rc = pci_add_dynid(&vfio_pci_driver, vendor, device, | ||
1168 | subvendor, subdevice, class, class_mask, 0); | ||
1169 | if (rc) | ||
1170 | pr_warn("failed to add dynamic id [%04hx:%04hx[%04hx:%04hx]] class %#08x/%08x (%d)\n", | ||
1171 | vendor, device, subvendor, subdevice, | ||
1172 | class, class_mask, rc); | ||
1173 | else | ||
1174 | pr_info("add [%04hx:%04hx[%04hx:%04hx]] class %#08x/%08x\n", | ||
1175 | vendor, device, subvendor, subdevice, | ||
1176 | class, class_mask); | ||
1177 | } | ||
1178 | } | ||
1179 | |||
1037 | static int __init vfio_pci_init(void) | 1180 | static int __init vfio_pci_init(void) |
1038 | { | 1181 | { |
1039 | int ret; | 1182 | int ret; |
@@ -1043,21 +1186,16 @@ static int __init vfio_pci_init(void) | |||
1043 | if (ret) | 1186 | if (ret) |
1044 | return ret; | 1187 | return ret; |
1045 | 1188 | ||
1046 | /* Start the virqfd cleanup handler */ | ||
1047 | ret = vfio_pci_virqfd_init(); | ||
1048 | if (ret) | ||
1049 | goto out_virqfd; | ||
1050 | |||
1051 | /* Register and scan for devices */ | 1189 | /* Register and scan for devices */ |
1052 | ret = pci_register_driver(&vfio_pci_driver); | 1190 | ret = pci_register_driver(&vfio_pci_driver); |
1053 | if (ret) | 1191 | if (ret) |
1054 | goto out_driver; | 1192 | goto out_driver; |
1055 | 1193 | ||
1194 | vfio_pci_fill_ids(); | ||
1195 | |||
1056 | return 0; | 1196 | return 0; |
1057 | 1197 | ||
1058 | out_driver: | 1198 | out_driver: |
1059 | vfio_pci_virqfd_exit(); | ||
1060 | out_virqfd: | ||
1061 | vfio_pci_uninit_perm_bits(); | 1199 | vfio_pci_uninit_perm_bits(); |
1062 | return ret; | 1200 | return ret; |
1063 | } | 1201 | } |
diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c index 2027a27546ef..1f577b4ac126 100644 --- a/drivers/vfio/pci/vfio_pci_intrs.c +++ b/drivers/vfio/pci/vfio_pci_intrs.c | |||
@@ -19,230 +19,19 @@ | |||
19 | #include <linux/msi.h> | 19 | #include <linux/msi.h> |
20 | #include <linux/pci.h> | 20 | #include <linux/pci.h> |
21 | #include <linux/file.h> | 21 | #include <linux/file.h> |
22 | #include <linux/poll.h> | ||
23 | #include <linux/vfio.h> | 22 | #include <linux/vfio.h> |
24 | #include <linux/wait.h> | 23 | #include <linux/wait.h> |
25 | #include <linux/workqueue.h> | ||
26 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
27 | 25 | ||
28 | #include "vfio_pci_private.h" | 26 | #include "vfio_pci_private.h" |
29 | 27 | ||
30 | /* | 28 | /* |
31 | * IRQfd - generic | ||
32 | */ | ||
33 | struct virqfd { | ||
34 | struct vfio_pci_device *vdev; | ||
35 | struct eventfd_ctx *eventfd; | ||
36 | int (*handler)(struct vfio_pci_device *, void *); | ||
37 | void (*thread)(struct vfio_pci_device *, void *); | ||
38 | void *data; | ||
39 | struct work_struct inject; | ||
40 | wait_queue_t wait; | ||
41 | poll_table pt; | ||
42 | struct work_struct shutdown; | ||
43 | struct virqfd **pvirqfd; | ||
44 | }; | ||
45 | |||
46 | static struct workqueue_struct *vfio_irqfd_cleanup_wq; | ||
47 | |||
48 | int __init vfio_pci_virqfd_init(void) | ||
49 | { | ||
50 | vfio_irqfd_cleanup_wq = | ||
51 | create_singlethread_workqueue("vfio-irqfd-cleanup"); | ||
52 | if (!vfio_irqfd_cleanup_wq) | ||
53 | return -ENOMEM; | ||
54 | |||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | void vfio_pci_virqfd_exit(void) | ||
59 | { | ||
60 | destroy_workqueue(vfio_irqfd_cleanup_wq); | ||
61 | } | ||
62 | |||
63 | static void virqfd_deactivate(struct virqfd *virqfd) | ||
64 | { | ||
65 | queue_work(vfio_irqfd_cleanup_wq, &virqfd->shutdown); | ||
66 | } | ||
67 | |||
68 | static int virqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key) | ||
69 | { | ||
70 | struct virqfd *virqfd = container_of(wait, struct virqfd, wait); | ||
71 | unsigned long flags = (unsigned long)key; | ||
72 | |||
73 | if (flags & POLLIN) { | ||
74 | /* An event has been signaled, call function */ | ||
75 | if ((!virqfd->handler || | ||
76 | virqfd->handler(virqfd->vdev, virqfd->data)) && | ||
77 | virqfd->thread) | ||
78 | schedule_work(&virqfd->inject); | ||
79 | } | ||
80 | |||
81 | if (flags & POLLHUP) { | ||
82 | unsigned long flags; | ||
83 | spin_lock_irqsave(&virqfd->vdev->irqlock, flags); | ||
84 | |||
85 | /* | ||
86 | * The eventfd is closing, if the virqfd has not yet been | ||
87 | * queued for release, as determined by testing whether the | ||
88 | * vdev pointer to it is still valid, queue it now. As | ||
89 | * with kvm irqfds, we know we won't race against the virqfd | ||
90 | * going away because we hold wqh->lock to get here. | ||
91 | */ | ||
92 | if (*(virqfd->pvirqfd) == virqfd) { | ||
93 | *(virqfd->pvirqfd) = NULL; | ||
94 | virqfd_deactivate(virqfd); | ||
95 | } | ||
96 | |||
97 | spin_unlock_irqrestore(&virqfd->vdev->irqlock, flags); | ||
98 | } | ||
99 | |||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | static void virqfd_ptable_queue_proc(struct file *file, | ||
104 | wait_queue_head_t *wqh, poll_table *pt) | ||
105 | { | ||
106 | struct virqfd *virqfd = container_of(pt, struct virqfd, pt); | ||
107 | add_wait_queue(wqh, &virqfd->wait); | ||
108 | } | ||
109 | |||
110 | static void virqfd_shutdown(struct work_struct *work) | ||
111 | { | ||
112 | struct virqfd *virqfd = container_of(work, struct virqfd, shutdown); | ||
113 | u64 cnt; | ||
114 | |||
115 | eventfd_ctx_remove_wait_queue(virqfd->eventfd, &virqfd->wait, &cnt); | ||
116 | flush_work(&virqfd->inject); | ||
117 | eventfd_ctx_put(virqfd->eventfd); | ||
118 | |||
119 | kfree(virqfd); | ||
120 | } | ||
121 | |||
122 | static void virqfd_inject(struct work_struct *work) | ||
123 | { | ||
124 | struct virqfd *virqfd = container_of(work, struct virqfd, inject); | ||
125 | if (virqfd->thread) | ||
126 | virqfd->thread(virqfd->vdev, virqfd->data); | ||
127 | } | ||
128 | |||
129 | static int virqfd_enable(struct vfio_pci_device *vdev, | ||
130 | int (*handler)(struct vfio_pci_device *, void *), | ||
131 | void (*thread)(struct vfio_pci_device *, void *), | ||
132 | void *data, struct virqfd **pvirqfd, int fd) | ||
133 | { | ||
134 | struct fd irqfd; | ||
135 | struct eventfd_ctx *ctx; | ||
136 | struct virqfd *virqfd; | ||
137 | int ret = 0; | ||
138 | unsigned int events; | ||
139 | |||
140 | virqfd = kzalloc(sizeof(*virqfd), GFP_KERNEL); | ||
141 | if (!virqfd) | ||
142 | return -ENOMEM; | ||
143 | |||
144 | virqfd->pvirqfd = pvirqfd; | ||
145 | virqfd->vdev = vdev; | ||
146 | virqfd->handler = handler; | ||
147 | virqfd->thread = thread; | ||
148 | virqfd->data = data; | ||
149 | |||
150 | INIT_WORK(&virqfd->shutdown, virqfd_shutdown); | ||
151 | INIT_WORK(&virqfd->inject, virqfd_inject); | ||
152 | |||
153 | irqfd = fdget(fd); | ||
154 | if (!irqfd.file) { | ||
155 | ret = -EBADF; | ||
156 | goto err_fd; | ||
157 | } | ||
158 | |||
159 | ctx = eventfd_ctx_fileget(irqfd.file); | ||
160 | if (IS_ERR(ctx)) { | ||
161 | ret = PTR_ERR(ctx); | ||
162 | goto err_ctx; | ||
163 | } | ||
164 | |||
165 | virqfd->eventfd = ctx; | ||
166 | |||
167 | /* | ||
168 | * virqfds can be released by closing the eventfd or directly | ||
169 | * through ioctl. These are both done through a workqueue, so | ||
170 | * we update the pointer to the virqfd under lock to avoid | ||
171 | * pushing multiple jobs to release the same virqfd. | ||
172 | */ | ||
173 | spin_lock_irq(&vdev->irqlock); | ||
174 | |||
175 | if (*pvirqfd) { | ||
176 | spin_unlock_irq(&vdev->irqlock); | ||
177 | ret = -EBUSY; | ||
178 | goto err_busy; | ||
179 | } | ||
180 | *pvirqfd = virqfd; | ||
181 | |||
182 | spin_unlock_irq(&vdev->irqlock); | ||
183 | |||
184 | /* | ||
185 | * Install our own custom wake-up handling so we are notified via | ||
186 | * a callback whenever someone signals the underlying eventfd. | ||
187 | */ | ||
188 | init_waitqueue_func_entry(&virqfd->wait, virqfd_wakeup); | ||
189 | init_poll_funcptr(&virqfd->pt, virqfd_ptable_queue_proc); | ||
190 | |||
191 | events = irqfd.file->f_op->poll(irqfd.file, &virqfd->pt); | ||
192 | |||
193 | /* | ||
194 | * Check if there was an event already pending on the eventfd | ||
195 | * before we registered and trigger it as if we didn't miss it. | ||
196 | */ | ||
197 | if (events & POLLIN) { | ||
198 | if ((!handler || handler(vdev, data)) && thread) | ||
199 | schedule_work(&virqfd->inject); | ||
200 | } | ||
201 | |||
202 | /* | ||
203 | * Do not drop the file until the irqfd is fully initialized, | ||
204 | * otherwise we might race against the POLLHUP. | ||
205 | */ | ||
206 | fdput(irqfd); | ||
207 | |||
208 | return 0; | ||
209 | err_busy: | ||
210 | eventfd_ctx_put(ctx); | ||
211 | err_ctx: | ||
212 | fdput(irqfd); | ||
213 | err_fd: | ||
214 | kfree(virqfd); | ||
215 | |||
216 | return ret; | ||
217 | } | ||
218 | |||
219 | static void virqfd_disable(struct vfio_pci_device *vdev, | ||
220 | struct virqfd **pvirqfd) | ||
221 | { | ||
222 | unsigned long flags; | ||
223 | |||
224 | spin_lock_irqsave(&vdev->irqlock, flags); | ||
225 | |||
226 | if (*pvirqfd) { | ||
227 | virqfd_deactivate(*pvirqfd); | ||
228 | *pvirqfd = NULL; | ||
229 | } | ||
230 | |||
231 | spin_unlock_irqrestore(&vdev->irqlock, flags); | ||
232 | |||
233 | /* | ||
234 | * Block until we know all outstanding shutdown jobs have completed. | ||
235 | * Even if we don't queue the job, flush the wq to be sure it's | ||
236 | * been released. | ||
237 | */ | ||
238 | flush_workqueue(vfio_irqfd_cleanup_wq); | ||
239 | } | ||
240 | |||
241 | /* | ||
242 | * INTx | 29 | * INTx |
243 | */ | 30 | */ |
244 | static void vfio_send_intx_eventfd(struct vfio_pci_device *vdev, void *unused) | 31 | static void vfio_send_intx_eventfd(void *opaque, void *unused) |
245 | { | 32 | { |
33 | struct vfio_pci_device *vdev = opaque; | ||
34 | |||
246 | if (likely(is_intx(vdev) && !vdev->virq_disabled)) | 35 | if (likely(is_intx(vdev) && !vdev->virq_disabled)) |
247 | eventfd_signal(vdev->ctx[0].trigger, 1); | 36 | eventfd_signal(vdev->ctx[0].trigger, 1); |
248 | } | 37 | } |
@@ -285,9 +74,9 @@ void vfio_pci_intx_mask(struct vfio_pci_device *vdev) | |||
285 | * a signal is necessary, which can then be handled via a work queue | 74 | * a signal is necessary, which can then be handled via a work queue |
286 | * or directly depending on the caller. | 75 | * or directly depending on the caller. |
287 | */ | 76 | */ |
288 | static int vfio_pci_intx_unmask_handler(struct vfio_pci_device *vdev, | 77 | static int vfio_pci_intx_unmask_handler(void *opaque, void *unused) |
289 | void *unused) | ||
290 | { | 78 | { |
79 | struct vfio_pci_device *vdev = opaque; | ||
291 | struct pci_dev *pdev = vdev->pdev; | 80 | struct pci_dev *pdev = vdev->pdev; |
292 | unsigned long flags; | 81 | unsigned long flags; |
293 | int ret = 0; | 82 | int ret = 0; |
@@ -440,8 +229,8 @@ static int vfio_intx_set_signal(struct vfio_pci_device *vdev, int fd) | |||
440 | static void vfio_intx_disable(struct vfio_pci_device *vdev) | 229 | static void vfio_intx_disable(struct vfio_pci_device *vdev) |
441 | { | 230 | { |
442 | vfio_intx_set_signal(vdev, -1); | 231 | vfio_intx_set_signal(vdev, -1); |
443 | virqfd_disable(vdev, &vdev->ctx[0].unmask); | 232 | vfio_virqfd_disable(&vdev->ctx[0].unmask); |
444 | virqfd_disable(vdev, &vdev->ctx[0].mask); | 233 | vfio_virqfd_disable(&vdev->ctx[0].mask); |
445 | vdev->irq_type = VFIO_PCI_NUM_IRQS; | 234 | vdev->irq_type = VFIO_PCI_NUM_IRQS; |
446 | vdev->num_ctx = 0; | 235 | vdev->num_ctx = 0; |
447 | kfree(vdev->ctx); | 236 | kfree(vdev->ctx); |
@@ -605,8 +394,8 @@ static void vfio_msi_disable(struct vfio_pci_device *vdev, bool msix) | |||
605 | vfio_msi_set_block(vdev, 0, vdev->num_ctx, NULL, msix); | 394 | vfio_msi_set_block(vdev, 0, vdev->num_ctx, NULL, msix); |
606 | 395 | ||
607 | for (i = 0; i < vdev->num_ctx; i++) { | 396 | for (i = 0; i < vdev->num_ctx; i++) { |
608 | virqfd_disable(vdev, &vdev->ctx[i].unmask); | 397 | vfio_virqfd_disable(&vdev->ctx[i].unmask); |
609 | virqfd_disable(vdev, &vdev->ctx[i].mask); | 398 | vfio_virqfd_disable(&vdev->ctx[i].mask); |
610 | } | 399 | } |
611 | 400 | ||
612 | if (msix) { | 401 | if (msix) { |
@@ -639,11 +428,12 @@ static int vfio_pci_set_intx_unmask(struct vfio_pci_device *vdev, | |||
639 | } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { | 428 | } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { |
640 | int32_t fd = *(int32_t *)data; | 429 | int32_t fd = *(int32_t *)data; |
641 | if (fd >= 0) | 430 | if (fd >= 0) |
642 | return virqfd_enable(vdev, vfio_pci_intx_unmask_handler, | 431 | return vfio_virqfd_enable((void *) vdev, |
643 | vfio_send_intx_eventfd, NULL, | 432 | vfio_pci_intx_unmask_handler, |
644 | &vdev->ctx[0].unmask, fd); | 433 | vfio_send_intx_eventfd, NULL, |
434 | &vdev->ctx[0].unmask, fd); | ||
645 | 435 | ||
646 | virqfd_disable(vdev, &vdev->ctx[0].unmask); | 436 | vfio_virqfd_disable(&vdev->ctx[0].unmask); |
647 | } | 437 | } |
648 | 438 | ||
649 | return 0; | 439 | return 0; |
diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h index c9f9b323f152..ae0e1b4c1711 100644 --- a/drivers/vfio/pci/vfio_pci_private.h +++ b/drivers/vfio/pci/vfio_pci_private.h | |||
@@ -87,9 +87,6 @@ extern ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf, | |||
87 | extern int vfio_pci_init_perm_bits(void); | 87 | extern int vfio_pci_init_perm_bits(void); |
88 | extern void vfio_pci_uninit_perm_bits(void); | 88 | extern void vfio_pci_uninit_perm_bits(void); |
89 | 89 | ||
90 | extern int vfio_pci_virqfd_init(void); | ||
91 | extern void vfio_pci_virqfd_exit(void); | ||
92 | |||
93 | extern int vfio_config_init(struct vfio_pci_device *vdev); | 90 | extern int vfio_config_init(struct vfio_pci_device *vdev); |
94 | extern void vfio_config_free(struct vfio_pci_device *vdev); | 91 | extern void vfio_config_free(struct vfio_pci_device *vdev); |
95 | #endif /* VFIO_PCI_PRIVATE_H */ | 92 | #endif /* VFIO_PCI_PRIVATE_H */ |
diff --git a/drivers/vfio/platform/Kconfig b/drivers/vfio/platform/Kconfig new file mode 100644 index 000000000000..9a4403e2a36c --- /dev/null +++ b/drivers/vfio/platform/Kconfig | |||
@@ -0,0 +1,20 @@ | |||
1 | config VFIO_PLATFORM | ||
2 | tristate "VFIO support for platform devices" | ||
3 | depends on VFIO && EVENTFD && ARM | ||
4 | select VFIO_VIRQFD | ||
5 | help | ||
6 | Support for platform devices with VFIO. This is required to make | ||
7 | use of platform devices present on the system using the VFIO | ||
8 | framework. | ||
9 | |||
10 | If you don't know what to do here, say N. | ||
11 | |||
12 | config VFIO_AMBA | ||
13 | tristate "VFIO support for AMBA devices" | ||
14 | depends on VFIO_PLATFORM && ARM_AMBA | ||
15 | help | ||
16 | Support for ARM AMBA devices with VFIO. This is required to make | ||
17 | use of ARM AMBA devices present on the system using the VFIO | ||
18 | framework. | ||
19 | |||
20 | If you don't know what to do here, say N. | ||
diff --git a/drivers/vfio/platform/Makefile b/drivers/vfio/platform/Makefile new file mode 100644 index 000000000000..81de144c0eaa --- /dev/null +++ b/drivers/vfio/platform/Makefile | |||
@@ -0,0 +1,8 @@ | |||
1 | |||
2 | vfio-platform-y := vfio_platform.o vfio_platform_common.o vfio_platform_irq.o | ||
3 | |||
4 | obj-$(CONFIG_VFIO_PLATFORM) += vfio-platform.o | ||
5 | |||
6 | vfio-amba-y := vfio_amba.o | ||
7 | |||
8 | obj-$(CONFIG_VFIO_AMBA) += vfio-amba.o | ||
diff --git a/drivers/vfio/platform/vfio_amba.c b/drivers/vfio/platform/vfio_amba.c new file mode 100644 index 000000000000..ff0331f72526 --- /dev/null +++ b/drivers/vfio/platform/vfio_amba.c | |||
@@ -0,0 +1,115 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 - Virtual Open Systems | ||
3 | * Author: Antonios Motakis <a.motakis@virtualopensystems.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License, version 2, as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/vfio.h> | ||
18 | #include <linux/amba/bus.h> | ||
19 | |||
20 | #include "vfio_platform_private.h" | ||
21 | |||
22 | #define DRIVER_VERSION "0.10" | ||
23 | #define DRIVER_AUTHOR "Antonios Motakis <a.motakis@virtualopensystems.com>" | ||
24 | #define DRIVER_DESC "VFIO for AMBA devices - User Level meta-driver" | ||
25 | |||
26 | /* probing devices from the AMBA bus */ | ||
27 | |||
28 | static struct resource *get_amba_resource(struct vfio_platform_device *vdev, | ||
29 | int i) | ||
30 | { | ||
31 | struct amba_device *adev = (struct amba_device *) vdev->opaque; | ||
32 | |||
33 | if (i == 0) | ||
34 | return &adev->res; | ||
35 | |||
36 | return NULL; | ||
37 | } | ||
38 | |||
39 | static int get_amba_irq(struct vfio_platform_device *vdev, int i) | ||
40 | { | ||
41 | struct amba_device *adev = (struct amba_device *) vdev->opaque; | ||
42 | int ret = 0; | ||
43 | |||
44 | if (i < AMBA_NR_IRQS) | ||
45 | ret = adev->irq[i]; | ||
46 | |||
47 | /* zero is an unset IRQ for AMBA devices */ | ||
48 | return ret ? ret : -ENXIO; | ||
49 | } | ||
50 | |||
51 | static int vfio_amba_probe(struct amba_device *adev, const struct amba_id *id) | ||
52 | { | ||
53 | struct vfio_platform_device *vdev; | ||
54 | int ret; | ||
55 | |||
56 | vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); | ||
57 | if (!vdev) | ||
58 | return -ENOMEM; | ||
59 | |||
60 | vdev->name = kasprintf(GFP_KERNEL, "vfio-amba-%08x", adev->periphid); | ||
61 | if (!vdev->name) { | ||
62 | kfree(vdev); | ||
63 | return -ENOMEM; | ||
64 | } | ||
65 | |||
66 | vdev->opaque = (void *) adev; | ||
67 | vdev->flags = VFIO_DEVICE_FLAGS_AMBA; | ||
68 | vdev->get_resource = get_amba_resource; | ||
69 | vdev->get_irq = get_amba_irq; | ||
70 | |||
71 | ret = vfio_platform_probe_common(vdev, &adev->dev); | ||
72 | if (ret) { | ||
73 | kfree(vdev->name); | ||
74 | kfree(vdev); | ||
75 | } | ||
76 | |||
77 | return ret; | ||
78 | } | ||
79 | |||
80 | static int vfio_amba_remove(struct amba_device *adev) | ||
81 | { | ||
82 | struct vfio_platform_device *vdev; | ||
83 | |||
84 | vdev = vfio_platform_remove_common(&adev->dev); | ||
85 | if (vdev) { | ||
86 | kfree(vdev->name); | ||
87 | kfree(vdev); | ||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | return -EINVAL; | ||
92 | } | ||
93 | |||
94 | static struct amba_id pl330_ids[] = { | ||
95 | { 0, 0 }, | ||
96 | }; | ||
97 | |||
98 | MODULE_DEVICE_TABLE(amba, pl330_ids); | ||
99 | |||
100 | static struct amba_driver vfio_amba_driver = { | ||
101 | .probe = vfio_amba_probe, | ||
102 | .remove = vfio_amba_remove, | ||
103 | .id_table = pl330_ids, | ||
104 | .drv = { | ||
105 | .name = "vfio-amba", | ||
106 | .owner = THIS_MODULE, | ||
107 | }, | ||
108 | }; | ||
109 | |||
110 | module_amba_driver(vfio_amba_driver); | ||
111 | |||
112 | MODULE_VERSION(DRIVER_VERSION); | ||
113 | MODULE_LICENSE("GPL v2"); | ||
114 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
115 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
diff --git a/drivers/vfio/platform/vfio_platform.c b/drivers/vfio/platform/vfio_platform.c new file mode 100644 index 000000000000..cef645c83996 --- /dev/null +++ b/drivers/vfio/platform/vfio_platform.c | |||
@@ -0,0 +1,103 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 - Virtual Open Systems | ||
3 | * Author: Antonios Motakis <a.motakis@virtualopensystems.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License, version 2, as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/vfio.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | |||
20 | #include "vfio_platform_private.h" | ||
21 | |||
22 | #define DRIVER_VERSION "0.10" | ||
23 | #define DRIVER_AUTHOR "Antonios Motakis <a.motakis@virtualopensystems.com>" | ||
24 | #define DRIVER_DESC "VFIO for platform devices - User Level meta-driver" | ||
25 | |||
26 | /* probing devices from the linux platform bus */ | ||
27 | |||
28 | static struct resource *get_platform_resource(struct vfio_platform_device *vdev, | ||
29 | int num) | ||
30 | { | ||
31 | struct platform_device *dev = (struct platform_device *) vdev->opaque; | ||
32 | int i; | ||
33 | |||
34 | for (i = 0; i < dev->num_resources; i++) { | ||
35 | struct resource *r = &dev->resource[i]; | ||
36 | |||
37 | if (resource_type(r) & (IORESOURCE_MEM|IORESOURCE_IO)) { | ||
38 | if (!num) | ||
39 | return r; | ||
40 | |||
41 | num--; | ||
42 | } | ||
43 | } | ||
44 | return NULL; | ||
45 | } | ||
46 | |||
47 | static int get_platform_irq(struct vfio_platform_device *vdev, int i) | ||
48 | { | ||
49 | struct platform_device *pdev = (struct platform_device *) vdev->opaque; | ||
50 | |||
51 | return platform_get_irq(pdev, i); | ||
52 | } | ||
53 | |||
54 | static int vfio_platform_probe(struct platform_device *pdev) | ||
55 | { | ||
56 | struct vfio_platform_device *vdev; | ||
57 | int ret; | ||
58 | |||
59 | vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); | ||
60 | if (!vdev) | ||
61 | return -ENOMEM; | ||
62 | |||
63 | vdev->opaque = (void *) pdev; | ||
64 | vdev->name = pdev->name; | ||
65 | vdev->flags = VFIO_DEVICE_FLAGS_PLATFORM; | ||
66 | vdev->get_resource = get_platform_resource; | ||
67 | vdev->get_irq = get_platform_irq; | ||
68 | |||
69 | ret = vfio_platform_probe_common(vdev, &pdev->dev); | ||
70 | if (ret) | ||
71 | kfree(vdev); | ||
72 | |||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | static int vfio_platform_remove(struct platform_device *pdev) | ||
77 | { | ||
78 | struct vfio_platform_device *vdev; | ||
79 | |||
80 | vdev = vfio_platform_remove_common(&pdev->dev); | ||
81 | if (vdev) { | ||
82 | kfree(vdev); | ||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | return -EINVAL; | ||
87 | } | ||
88 | |||
89 | static struct platform_driver vfio_platform_driver = { | ||
90 | .probe = vfio_platform_probe, | ||
91 | .remove = vfio_platform_remove, | ||
92 | .driver = { | ||
93 | .name = "vfio-platform", | ||
94 | .owner = THIS_MODULE, | ||
95 | }, | ||
96 | }; | ||
97 | |||
98 | module_platform_driver(vfio_platform_driver); | ||
99 | |||
100 | MODULE_VERSION(DRIVER_VERSION); | ||
101 | MODULE_LICENSE("GPL v2"); | ||
102 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
103 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
diff --git a/drivers/vfio/platform/vfio_platform_common.c b/drivers/vfio/platform/vfio_platform_common.c new file mode 100644 index 000000000000..abcff7a1aa66 --- /dev/null +++ b/drivers/vfio/platform/vfio_platform_common.c | |||
@@ -0,0 +1,521 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 - Virtual Open Systems | ||
3 | * Author: Antonios Motakis <a.motakis@virtualopensystems.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License, version 2, as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/device.h> | ||
16 | #include <linux/iommu.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/mutex.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/types.h> | ||
21 | #include <linux/uaccess.h> | ||
22 | #include <linux/vfio.h> | ||
23 | |||
24 | #include "vfio_platform_private.h" | ||
25 | |||
26 | static DEFINE_MUTEX(driver_lock); | ||
27 | |||
28 | static int vfio_platform_regions_init(struct vfio_platform_device *vdev) | ||
29 | { | ||
30 | int cnt = 0, i; | ||
31 | |||
32 | while (vdev->get_resource(vdev, cnt)) | ||
33 | cnt++; | ||
34 | |||
35 | vdev->regions = kcalloc(cnt, sizeof(struct vfio_platform_region), | ||
36 | GFP_KERNEL); | ||
37 | if (!vdev->regions) | ||
38 | return -ENOMEM; | ||
39 | |||
40 | for (i = 0; i < cnt; i++) { | ||
41 | struct resource *res = | ||
42 | vdev->get_resource(vdev, i); | ||
43 | |||
44 | if (!res) | ||
45 | goto err; | ||
46 | |||
47 | vdev->regions[i].addr = res->start; | ||
48 | vdev->regions[i].size = resource_size(res); | ||
49 | vdev->regions[i].flags = 0; | ||
50 | |||
51 | switch (resource_type(res)) { | ||
52 | case IORESOURCE_MEM: | ||
53 | vdev->regions[i].type = VFIO_PLATFORM_REGION_TYPE_MMIO; | ||
54 | vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_READ; | ||
55 | if (!(res->flags & IORESOURCE_READONLY)) | ||
56 | vdev->regions[i].flags |= | ||
57 | VFIO_REGION_INFO_FLAG_WRITE; | ||
58 | |||
59 | /* | ||
60 | * Only regions addressed with PAGE granularity may be | ||
61 | * MMAPed securely. | ||
62 | */ | ||
63 | if (!(vdev->regions[i].addr & ~PAGE_MASK) && | ||
64 | !(vdev->regions[i].size & ~PAGE_MASK)) | ||
65 | vdev->regions[i].flags |= | ||
66 | VFIO_REGION_INFO_FLAG_MMAP; | ||
67 | |||
68 | break; | ||
69 | case IORESOURCE_IO: | ||
70 | vdev->regions[i].type = VFIO_PLATFORM_REGION_TYPE_PIO; | ||
71 | break; | ||
72 | default: | ||
73 | goto err; | ||
74 | } | ||
75 | } | ||
76 | |||
77 | vdev->num_regions = cnt; | ||
78 | |||
79 | return 0; | ||
80 | err: | ||
81 | kfree(vdev->regions); | ||
82 | return -EINVAL; | ||
83 | } | ||
84 | |||
85 | static void vfio_platform_regions_cleanup(struct vfio_platform_device *vdev) | ||
86 | { | ||
87 | int i; | ||
88 | |||
89 | for (i = 0; i < vdev->num_regions; i++) | ||
90 | iounmap(vdev->regions[i].ioaddr); | ||
91 | |||
92 | vdev->num_regions = 0; | ||
93 | kfree(vdev->regions); | ||
94 | } | ||
95 | |||
96 | static void vfio_platform_release(void *device_data) | ||
97 | { | ||
98 | struct vfio_platform_device *vdev = device_data; | ||
99 | |||
100 | mutex_lock(&driver_lock); | ||
101 | |||
102 | if (!(--vdev->refcnt)) { | ||
103 | vfio_platform_regions_cleanup(vdev); | ||
104 | vfio_platform_irq_cleanup(vdev); | ||
105 | } | ||
106 | |||
107 | mutex_unlock(&driver_lock); | ||
108 | |||
109 | module_put(THIS_MODULE); | ||
110 | } | ||
111 | |||
112 | static int vfio_platform_open(void *device_data) | ||
113 | { | ||
114 | struct vfio_platform_device *vdev = device_data; | ||
115 | int ret; | ||
116 | |||
117 | if (!try_module_get(THIS_MODULE)) | ||
118 | return -ENODEV; | ||
119 | |||
120 | mutex_lock(&driver_lock); | ||
121 | |||
122 | if (!vdev->refcnt) { | ||
123 | ret = vfio_platform_regions_init(vdev); | ||
124 | if (ret) | ||
125 | goto err_reg; | ||
126 | |||
127 | ret = vfio_platform_irq_init(vdev); | ||
128 | if (ret) | ||
129 | goto err_irq; | ||
130 | } | ||
131 | |||
132 | vdev->refcnt++; | ||
133 | |||
134 | mutex_unlock(&driver_lock); | ||
135 | return 0; | ||
136 | |||
137 | err_irq: | ||
138 | vfio_platform_regions_cleanup(vdev); | ||
139 | err_reg: | ||
140 | mutex_unlock(&driver_lock); | ||
141 | module_put(THIS_MODULE); | ||
142 | return ret; | ||
143 | } | ||
144 | |||
145 | static long vfio_platform_ioctl(void *device_data, | ||
146 | unsigned int cmd, unsigned long arg) | ||
147 | { | ||
148 | struct vfio_platform_device *vdev = device_data; | ||
149 | unsigned long minsz; | ||
150 | |||
151 | if (cmd == VFIO_DEVICE_GET_INFO) { | ||
152 | struct vfio_device_info info; | ||
153 | |||
154 | minsz = offsetofend(struct vfio_device_info, num_irqs); | ||
155 | |||
156 | if (copy_from_user(&info, (void __user *)arg, minsz)) | ||
157 | return -EFAULT; | ||
158 | |||
159 | if (info.argsz < minsz) | ||
160 | return -EINVAL; | ||
161 | |||
162 | info.flags = vdev->flags; | ||
163 | info.num_regions = vdev->num_regions; | ||
164 | info.num_irqs = vdev->num_irqs; | ||
165 | |||
166 | return copy_to_user((void __user *)arg, &info, minsz); | ||
167 | |||
168 | } else if (cmd == VFIO_DEVICE_GET_REGION_INFO) { | ||
169 | struct vfio_region_info info; | ||
170 | |||
171 | minsz = offsetofend(struct vfio_region_info, offset); | ||
172 | |||
173 | if (copy_from_user(&info, (void __user *)arg, minsz)) | ||
174 | return -EFAULT; | ||
175 | |||
176 | if (info.argsz < minsz) | ||
177 | return -EINVAL; | ||
178 | |||
179 | if (info.index >= vdev->num_regions) | ||
180 | return -EINVAL; | ||
181 | |||
182 | /* map offset to the physical address */ | ||
183 | info.offset = VFIO_PLATFORM_INDEX_TO_OFFSET(info.index); | ||
184 | info.size = vdev->regions[info.index].size; | ||
185 | info.flags = vdev->regions[info.index].flags; | ||
186 | |||
187 | return copy_to_user((void __user *)arg, &info, minsz); | ||
188 | |||
189 | } else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) { | ||
190 | struct vfio_irq_info info; | ||
191 | |||
192 | minsz = offsetofend(struct vfio_irq_info, count); | ||
193 | |||
194 | if (copy_from_user(&info, (void __user *)arg, minsz)) | ||
195 | return -EFAULT; | ||
196 | |||
197 | if (info.argsz < minsz) | ||
198 | return -EINVAL; | ||
199 | |||
200 | if (info.index >= vdev->num_irqs) | ||
201 | return -EINVAL; | ||
202 | |||
203 | info.flags = vdev->irqs[info.index].flags; | ||
204 | info.count = vdev->irqs[info.index].count; | ||
205 | |||
206 | return copy_to_user((void __user *)arg, &info, minsz); | ||
207 | |||
208 | } else if (cmd == VFIO_DEVICE_SET_IRQS) { | ||
209 | struct vfio_irq_set hdr; | ||
210 | u8 *data = NULL; | ||
211 | int ret = 0; | ||
212 | |||
213 | minsz = offsetofend(struct vfio_irq_set, count); | ||
214 | |||
215 | if (copy_from_user(&hdr, (void __user *)arg, minsz)) | ||
216 | return -EFAULT; | ||
217 | |||
218 | if (hdr.argsz < minsz) | ||
219 | return -EINVAL; | ||
220 | |||
221 | if (hdr.index >= vdev->num_irqs) | ||
222 | return -EINVAL; | ||
223 | |||
224 | if (hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK | | ||
225 | VFIO_IRQ_SET_ACTION_TYPE_MASK)) | ||
226 | return -EINVAL; | ||
227 | |||
228 | if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) { | ||
229 | size_t size; | ||
230 | |||
231 | if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL) | ||
232 | size = sizeof(uint8_t); | ||
233 | else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD) | ||
234 | size = sizeof(int32_t); | ||
235 | else | ||
236 | return -EINVAL; | ||
237 | |||
238 | if (hdr.argsz - minsz < size) | ||
239 | return -EINVAL; | ||
240 | |||
241 | data = memdup_user((void __user *)(arg + minsz), size); | ||
242 | if (IS_ERR(data)) | ||
243 | return PTR_ERR(data); | ||
244 | } | ||
245 | |||
246 | mutex_lock(&vdev->igate); | ||
247 | |||
248 | ret = vfio_platform_set_irqs_ioctl(vdev, hdr.flags, hdr.index, | ||
249 | hdr.start, hdr.count, data); | ||
250 | mutex_unlock(&vdev->igate); | ||
251 | kfree(data); | ||
252 | |||
253 | return ret; | ||
254 | |||
255 | } else if (cmd == VFIO_DEVICE_RESET) | ||
256 | return -EINVAL; | ||
257 | |||
258 | return -ENOTTY; | ||
259 | } | ||
260 | |||
261 | static ssize_t vfio_platform_read_mmio(struct vfio_platform_region reg, | ||
262 | char __user *buf, size_t count, | ||
263 | loff_t off) | ||
264 | { | ||
265 | unsigned int done = 0; | ||
266 | |||
267 | if (!reg.ioaddr) { | ||
268 | reg.ioaddr = | ||
269 | ioremap_nocache(reg.addr, reg.size); | ||
270 | |||
271 | if (!reg.ioaddr) | ||
272 | return -ENOMEM; | ||
273 | } | ||
274 | |||
275 | while (count) { | ||
276 | size_t filled; | ||
277 | |||
278 | if (count >= 4 && !(off % 4)) { | ||
279 | u32 val; | ||
280 | |||
281 | val = ioread32(reg.ioaddr + off); | ||
282 | if (copy_to_user(buf, &val, 4)) | ||
283 | goto err; | ||
284 | |||
285 | filled = 4; | ||
286 | } else if (count >= 2 && !(off % 2)) { | ||
287 | u16 val; | ||
288 | |||
289 | val = ioread16(reg.ioaddr + off); | ||
290 | if (copy_to_user(buf, &val, 2)) | ||
291 | goto err; | ||
292 | |||
293 | filled = 2; | ||
294 | } else { | ||
295 | u8 val; | ||
296 | |||
297 | val = ioread8(reg.ioaddr + off); | ||
298 | if (copy_to_user(buf, &val, 1)) | ||
299 | goto err; | ||
300 | |||
301 | filled = 1; | ||
302 | } | ||
303 | |||
304 | |||
305 | count -= filled; | ||
306 | done += filled; | ||
307 | off += filled; | ||
308 | buf += filled; | ||
309 | } | ||
310 | |||
311 | return done; | ||
312 | err: | ||
313 | return -EFAULT; | ||
314 | } | ||
315 | |||
316 | static ssize_t vfio_platform_read(void *device_data, char __user *buf, | ||
317 | size_t count, loff_t *ppos) | ||
318 | { | ||
319 | struct vfio_platform_device *vdev = device_data; | ||
320 | unsigned int index = VFIO_PLATFORM_OFFSET_TO_INDEX(*ppos); | ||
321 | loff_t off = *ppos & VFIO_PLATFORM_OFFSET_MASK; | ||
322 | |||
323 | if (index >= vdev->num_regions) | ||
324 | return -EINVAL; | ||
325 | |||
326 | if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_READ)) | ||
327 | return -EINVAL; | ||
328 | |||
329 | if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO) | ||
330 | return vfio_platform_read_mmio(vdev->regions[index], | ||
331 | buf, count, off); | ||
332 | else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO) | ||
333 | return -EINVAL; /* not implemented */ | ||
334 | |||
335 | return -EINVAL; | ||
336 | } | ||
337 | |||
338 | static ssize_t vfio_platform_write_mmio(struct vfio_platform_region reg, | ||
339 | const char __user *buf, size_t count, | ||
340 | loff_t off) | ||
341 | { | ||
342 | unsigned int done = 0; | ||
343 | |||
344 | if (!reg.ioaddr) { | ||
345 | reg.ioaddr = | ||
346 | ioremap_nocache(reg.addr, reg.size); | ||
347 | |||
348 | if (!reg.ioaddr) | ||
349 | return -ENOMEM; | ||
350 | } | ||
351 | |||
352 | while (count) { | ||
353 | size_t filled; | ||
354 | |||
355 | if (count >= 4 && !(off % 4)) { | ||
356 | u32 val; | ||
357 | |||
358 | if (copy_from_user(&val, buf, 4)) | ||
359 | goto err; | ||
360 | iowrite32(val, reg.ioaddr + off); | ||
361 | |||
362 | filled = 4; | ||
363 | } else if (count >= 2 && !(off % 2)) { | ||
364 | u16 val; | ||
365 | |||
366 | if (copy_from_user(&val, buf, 2)) | ||
367 | goto err; | ||
368 | iowrite16(val, reg.ioaddr + off); | ||
369 | |||
370 | filled = 2; | ||
371 | } else { | ||
372 | u8 val; | ||
373 | |||
374 | if (copy_from_user(&val, buf, 1)) | ||
375 | goto err; | ||
376 | iowrite8(val, reg.ioaddr + off); | ||
377 | |||
378 | filled = 1; | ||
379 | } | ||
380 | |||
381 | count -= filled; | ||
382 | done += filled; | ||
383 | off += filled; | ||
384 | buf += filled; | ||
385 | } | ||
386 | |||
387 | return done; | ||
388 | err: | ||
389 | return -EFAULT; | ||
390 | } | ||
391 | |||
392 | static ssize_t vfio_platform_write(void *device_data, const char __user *buf, | ||
393 | size_t count, loff_t *ppos) | ||
394 | { | ||
395 | struct vfio_platform_device *vdev = device_data; | ||
396 | unsigned int index = VFIO_PLATFORM_OFFSET_TO_INDEX(*ppos); | ||
397 | loff_t off = *ppos & VFIO_PLATFORM_OFFSET_MASK; | ||
398 | |||
399 | if (index >= vdev->num_regions) | ||
400 | return -EINVAL; | ||
401 | |||
402 | if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_WRITE)) | ||
403 | return -EINVAL; | ||
404 | |||
405 | if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO) | ||
406 | return vfio_platform_write_mmio(vdev->regions[index], | ||
407 | buf, count, off); | ||
408 | else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO) | ||
409 | return -EINVAL; /* not implemented */ | ||
410 | |||
411 | return -EINVAL; | ||
412 | } | ||
413 | |||
414 | static int vfio_platform_mmap_mmio(struct vfio_platform_region region, | ||
415 | struct vm_area_struct *vma) | ||
416 | { | ||
417 | u64 req_len, pgoff, req_start; | ||
418 | |||
419 | req_len = vma->vm_end - vma->vm_start; | ||
420 | pgoff = vma->vm_pgoff & | ||
421 | ((1U << (VFIO_PLATFORM_OFFSET_SHIFT - PAGE_SHIFT)) - 1); | ||
422 | req_start = pgoff << PAGE_SHIFT; | ||
423 | |||
424 | if (region.size < PAGE_SIZE || req_start + req_len > region.size) | ||
425 | return -EINVAL; | ||
426 | |||
427 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | ||
428 | vma->vm_pgoff = (region.addr >> PAGE_SHIFT) + pgoff; | ||
429 | |||
430 | return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, | ||
431 | req_len, vma->vm_page_prot); | ||
432 | } | ||
433 | |||
434 | static int vfio_platform_mmap(void *device_data, struct vm_area_struct *vma) | ||
435 | { | ||
436 | struct vfio_platform_device *vdev = device_data; | ||
437 | unsigned int index; | ||
438 | |||
439 | index = vma->vm_pgoff >> (VFIO_PLATFORM_OFFSET_SHIFT - PAGE_SHIFT); | ||
440 | |||
441 | if (vma->vm_end < vma->vm_start) | ||
442 | return -EINVAL; | ||
443 | if (!(vma->vm_flags & VM_SHARED)) | ||
444 | return -EINVAL; | ||
445 | if (index >= vdev->num_regions) | ||
446 | return -EINVAL; | ||
447 | if (vma->vm_start & ~PAGE_MASK) | ||
448 | return -EINVAL; | ||
449 | if (vma->vm_end & ~PAGE_MASK) | ||
450 | return -EINVAL; | ||
451 | |||
452 | if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_MMAP)) | ||
453 | return -EINVAL; | ||
454 | |||
455 | if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_READ) | ||
456 | && (vma->vm_flags & VM_READ)) | ||
457 | return -EINVAL; | ||
458 | |||
459 | if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_WRITE) | ||
460 | && (vma->vm_flags & VM_WRITE)) | ||
461 | return -EINVAL; | ||
462 | |||
463 | vma->vm_private_data = vdev; | ||
464 | |||
465 | if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO) | ||
466 | return vfio_platform_mmap_mmio(vdev->regions[index], vma); | ||
467 | |||
468 | else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO) | ||
469 | return -EINVAL; /* not implemented */ | ||
470 | |||
471 | return -EINVAL; | ||
472 | } | ||
473 | |||
474 | static const struct vfio_device_ops vfio_platform_ops = { | ||
475 | .name = "vfio-platform", | ||
476 | .open = vfio_platform_open, | ||
477 | .release = vfio_platform_release, | ||
478 | .ioctl = vfio_platform_ioctl, | ||
479 | .read = vfio_platform_read, | ||
480 | .write = vfio_platform_write, | ||
481 | .mmap = vfio_platform_mmap, | ||
482 | }; | ||
483 | |||
484 | int vfio_platform_probe_common(struct vfio_platform_device *vdev, | ||
485 | struct device *dev) | ||
486 | { | ||
487 | struct iommu_group *group; | ||
488 | int ret; | ||
489 | |||
490 | if (!vdev) | ||
491 | return -EINVAL; | ||
492 | |||
493 | group = iommu_group_get(dev); | ||
494 | if (!group) { | ||
495 | pr_err("VFIO: No IOMMU group for device %s\n", vdev->name); | ||
496 | return -EINVAL; | ||
497 | } | ||
498 | |||
499 | ret = vfio_add_group_dev(dev, &vfio_platform_ops, vdev); | ||
500 | if (ret) { | ||
501 | iommu_group_put(group); | ||
502 | return ret; | ||
503 | } | ||
504 | |||
505 | mutex_init(&vdev->igate); | ||
506 | |||
507 | return 0; | ||
508 | } | ||
509 | EXPORT_SYMBOL_GPL(vfio_platform_probe_common); | ||
510 | |||
511 | struct vfio_platform_device *vfio_platform_remove_common(struct device *dev) | ||
512 | { | ||
513 | struct vfio_platform_device *vdev; | ||
514 | |||
515 | vdev = vfio_del_group_dev(dev); | ||
516 | if (vdev) | ||
517 | iommu_group_put(dev->iommu_group); | ||
518 | |||
519 | return vdev; | ||
520 | } | ||
521 | EXPORT_SYMBOL_GPL(vfio_platform_remove_common); | ||
diff --git a/drivers/vfio/platform/vfio_platform_irq.c b/drivers/vfio/platform/vfio_platform_irq.c new file mode 100644 index 000000000000..88bba57b30a8 --- /dev/null +++ b/drivers/vfio/platform/vfio_platform_irq.c | |||
@@ -0,0 +1,336 @@ | |||
1 | /* | ||
2 | * VFIO platform devices interrupt handling | ||
3 | * | ||
4 | * Copyright (C) 2013 - Virtual Open Systems | ||
5 | * Author: Antonios Motakis <a.motakis@virtualopensystems.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License, version 2, as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | */ | ||
16 | |||
17 | #include <linux/eventfd.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/types.h> | ||
21 | #include <linux/vfio.h> | ||
22 | #include <linux/irq.h> | ||
23 | |||
24 | #include "vfio_platform_private.h" | ||
25 | |||
26 | static void vfio_platform_mask(struct vfio_platform_irq *irq_ctx) | ||
27 | { | ||
28 | unsigned long flags; | ||
29 | |||
30 | spin_lock_irqsave(&irq_ctx->lock, flags); | ||
31 | |||
32 | if (!irq_ctx->masked) { | ||
33 | disable_irq_nosync(irq_ctx->hwirq); | ||
34 | irq_ctx->masked = true; | ||
35 | } | ||
36 | |||
37 | spin_unlock_irqrestore(&irq_ctx->lock, flags); | ||
38 | } | ||
39 | |||
40 | static int vfio_platform_mask_handler(void *opaque, void *unused) | ||
41 | { | ||
42 | struct vfio_platform_irq *irq_ctx = opaque; | ||
43 | |||
44 | vfio_platform_mask(irq_ctx); | ||
45 | |||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | static int vfio_platform_set_irq_mask(struct vfio_platform_device *vdev, | ||
50 | unsigned index, unsigned start, | ||
51 | unsigned count, uint32_t flags, | ||
52 | void *data) | ||
53 | { | ||
54 | if (start != 0 || count != 1) | ||
55 | return -EINVAL; | ||
56 | |||
57 | if (!(vdev->irqs[index].flags & VFIO_IRQ_INFO_MASKABLE)) | ||
58 | return -EINVAL; | ||
59 | |||
60 | if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { | ||
61 | int32_t fd = *(int32_t *)data; | ||
62 | |||
63 | if (fd >= 0) | ||
64 | return vfio_virqfd_enable((void *) &vdev->irqs[index], | ||
65 | vfio_platform_mask_handler, | ||
66 | NULL, NULL, | ||
67 | &vdev->irqs[index].mask, fd); | ||
68 | |||
69 | vfio_virqfd_disable(&vdev->irqs[index].mask); | ||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | if (flags & VFIO_IRQ_SET_DATA_NONE) { | ||
74 | vfio_platform_mask(&vdev->irqs[index]); | ||
75 | |||
76 | } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { | ||
77 | uint8_t mask = *(uint8_t *)data; | ||
78 | |||
79 | if (mask) | ||
80 | vfio_platform_mask(&vdev->irqs[index]); | ||
81 | } | ||
82 | |||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | static void vfio_platform_unmask(struct vfio_platform_irq *irq_ctx) | ||
87 | { | ||
88 | unsigned long flags; | ||
89 | |||
90 | spin_lock_irqsave(&irq_ctx->lock, flags); | ||
91 | |||
92 | if (irq_ctx->masked) { | ||
93 | enable_irq(irq_ctx->hwirq); | ||
94 | irq_ctx->masked = false; | ||
95 | } | ||
96 | |||
97 | spin_unlock_irqrestore(&irq_ctx->lock, flags); | ||
98 | } | ||
99 | |||
100 | static int vfio_platform_unmask_handler(void *opaque, void *unused) | ||
101 | { | ||
102 | struct vfio_platform_irq *irq_ctx = opaque; | ||
103 | |||
104 | vfio_platform_unmask(irq_ctx); | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static int vfio_platform_set_irq_unmask(struct vfio_platform_device *vdev, | ||
110 | unsigned index, unsigned start, | ||
111 | unsigned count, uint32_t flags, | ||
112 | void *data) | ||
113 | { | ||
114 | if (start != 0 || count != 1) | ||
115 | return -EINVAL; | ||
116 | |||
117 | if (!(vdev->irqs[index].flags & VFIO_IRQ_INFO_MASKABLE)) | ||
118 | return -EINVAL; | ||
119 | |||
120 | if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { | ||
121 | int32_t fd = *(int32_t *)data; | ||
122 | |||
123 | if (fd >= 0) | ||
124 | return vfio_virqfd_enable((void *) &vdev->irqs[index], | ||
125 | vfio_platform_unmask_handler, | ||
126 | NULL, NULL, | ||
127 | &vdev->irqs[index].unmask, | ||
128 | fd); | ||
129 | |||
130 | vfio_virqfd_disable(&vdev->irqs[index].unmask); | ||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | if (flags & VFIO_IRQ_SET_DATA_NONE) { | ||
135 | vfio_platform_unmask(&vdev->irqs[index]); | ||
136 | |||
137 | } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { | ||
138 | uint8_t unmask = *(uint8_t *)data; | ||
139 | |||
140 | if (unmask) | ||
141 | vfio_platform_unmask(&vdev->irqs[index]); | ||
142 | } | ||
143 | |||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | static irqreturn_t vfio_automasked_irq_handler(int irq, void *dev_id) | ||
148 | { | ||
149 | struct vfio_platform_irq *irq_ctx = dev_id; | ||
150 | unsigned long flags; | ||
151 | int ret = IRQ_NONE; | ||
152 | |||
153 | spin_lock_irqsave(&irq_ctx->lock, flags); | ||
154 | |||
155 | if (!irq_ctx->masked) { | ||
156 | ret = IRQ_HANDLED; | ||
157 | |||
158 | /* automask maskable interrupts */ | ||
159 | disable_irq_nosync(irq_ctx->hwirq); | ||
160 | irq_ctx->masked = true; | ||
161 | } | ||
162 | |||
163 | spin_unlock_irqrestore(&irq_ctx->lock, flags); | ||
164 | |||
165 | if (ret == IRQ_HANDLED) | ||
166 | eventfd_signal(irq_ctx->trigger, 1); | ||
167 | |||
168 | return ret; | ||
169 | } | ||
170 | |||
171 | static irqreturn_t vfio_irq_handler(int irq, void *dev_id) | ||
172 | { | ||
173 | struct vfio_platform_irq *irq_ctx = dev_id; | ||
174 | |||
175 | eventfd_signal(irq_ctx->trigger, 1); | ||
176 | |||
177 | return IRQ_HANDLED; | ||
178 | } | ||
179 | |||
180 | static int vfio_set_trigger(struct vfio_platform_device *vdev, int index, | ||
181 | int fd, irq_handler_t handler) | ||
182 | { | ||
183 | struct vfio_platform_irq *irq = &vdev->irqs[index]; | ||
184 | struct eventfd_ctx *trigger; | ||
185 | int ret; | ||
186 | |||
187 | if (irq->trigger) { | ||
188 | free_irq(irq->hwirq, irq); | ||
189 | kfree(irq->name); | ||
190 | eventfd_ctx_put(irq->trigger); | ||
191 | irq->trigger = NULL; | ||
192 | } | ||
193 | |||
194 | if (fd < 0) /* Disable only */ | ||
195 | return 0; | ||
196 | |||
197 | irq->name = kasprintf(GFP_KERNEL, "vfio-irq[%d](%s)", | ||
198 | irq->hwirq, vdev->name); | ||
199 | if (!irq->name) | ||
200 | return -ENOMEM; | ||
201 | |||
202 | trigger = eventfd_ctx_fdget(fd); | ||
203 | if (IS_ERR(trigger)) { | ||
204 | kfree(irq->name); | ||
205 | return PTR_ERR(trigger); | ||
206 | } | ||
207 | |||
208 | irq->trigger = trigger; | ||
209 | |||
210 | irq_set_status_flags(irq->hwirq, IRQ_NOAUTOEN); | ||
211 | ret = request_irq(irq->hwirq, handler, 0, irq->name, irq); | ||
212 | if (ret) { | ||
213 | kfree(irq->name); | ||
214 | eventfd_ctx_put(trigger); | ||
215 | irq->trigger = NULL; | ||
216 | return ret; | ||
217 | } | ||
218 | |||
219 | if (!irq->masked) | ||
220 | enable_irq(irq->hwirq); | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int vfio_platform_set_irq_trigger(struct vfio_platform_device *vdev, | ||
226 | unsigned index, unsigned start, | ||
227 | unsigned count, uint32_t flags, | ||
228 | void *data) | ||
229 | { | ||
230 | struct vfio_platform_irq *irq = &vdev->irqs[index]; | ||
231 | irq_handler_t handler; | ||
232 | |||
233 | if (vdev->irqs[index].flags & VFIO_IRQ_INFO_AUTOMASKED) | ||
234 | handler = vfio_automasked_irq_handler; | ||
235 | else | ||
236 | handler = vfio_irq_handler; | ||
237 | |||
238 | if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) | ||
239 | return vfio_set_trigger(vdev, index, -1, handler); | ||
240 | |||
241 | if (start != 0 || count != 1) | ||
242 | return -EINVAL; | ||
243 | |||
244 | if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { | ||
245 | int32_t fd = *(int32_t *)data; | ||
246 | |||
247 | return vfio_set_trigger(vdev, index, fd, handler); | ||
248 | } | ||
249 | |||
250 | if (flags & VFIO_IRQ_SET_DATA_NONE) { | ||
251 | handler(irq->hwirq, irq); | ||
252 | |||
253 | } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { | ||
254 | uint8_t trigger = *(uint8_t *)data; | ||
255 | |||
256 | if (trigger) | ||
257 | handler(irq->hwirq, irq); | ||
258 | } | ||
259 | |||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | int vfio_platform_set_irqs_ioctl(struct vfio_platform_device *vdev, | ||
264 | uint32_t flags, unsigned index, unsigned start, | ||
265 | unsigned count, void *data) | ||
266 | { | ||
267 | int (*func)(struct vfio_platform_device *vdev, unsigned index, | ||
268 | unsigned start, unsigned count, uint32_t flags, | ||
269 | void *data) = NULL; | ||
270 | |||
271 | switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { | ||
272 | case VFIO_IRQ_SET_ACTION_MASK: | ||
273 | func = vfio_platform_set_irq_mask; | ||
274 | break; | ||
275 | case VFIO_IRQ_SET_ACTION_UNMASK: | ||
276 | func = vfio_platform_set_irq_unmask; | ||
277 | break; | ||
278 | case VFIO_IRQ_SET_ACTION_TRIGGER: | ||
279 | func = vfio_platform_set_irq_trigger; | ||
280 | break; | ||
281 | } | ||
282 | |||
283 | if (!func) | ||
284 | return -ENOTTY; | ||
285 | |||
286 | return func(vdev, index, start, count, flags, data); | ||
287 | } | ||
288 | |||
289 | int vfio_platform_irq_init(struct vfio_platform_device *vdev) | ||
290 | { | ||
291 | int cnt = 0, i; | ||
292 | |||
293 | while (vdev->get_irq(vdev, cnt) >= 0) | ||
294 | cnt++; | ||
295 | |||
296 | vdev->irqs = kcalloc(cnt, sizeof(struct vfio_platform_irq), GFP_KERNEL); | ||
297 | if (!vdev->irqs) | ||
298 | return -ENOMEM; | ||
299 | |||
300 | for (i = 0; i < cnt; i++) { | ||
301 | int hwirq = vdev->get_irq(vdev, i); | ||
302 | |||
303 | if (hwirq < 0) | ||
304 | goto err; | ||
305 | |||
306 | spin_lock_init(&vdev->irqs[i].lock); | ||
307 | |||
308 | vdev->irqs[i].flags = VFIO_IRQ_INFO_EVENTFD; | ||
309 | |||
310 | if (irq_get_trigger_type(hwirq) & IRQ_TYPE_LEVEL_MASK) | ||
311 | vdev->irqs[i].flags |= VFIO_IRQ_INFO_MASKABLE | ||
312 | | VFIO_IRQ_INFO_AUTOMASKED; | ||
313 | |||
314 | vdev->irqs[i].count = 1; | ||
315 | vdev->irqs[i].hwirq = hwirq; | ||
316 | vdev->irqs[i].masked = false; | ||
317 | } | ||
318 | |||
319 | vdev->num_irqs = cnt; | ||
320 | |||
321 | return 0; | ||
322 | err: | ||
323 | kfree(vdev->irqs); | ||
324 | return -EINVAL; | ||
325 | } | ||
326 | |||
327 | void vfio_platform_irq_cleanup(struct vfio_platform_device *vdev) | ||
328 | { | ||
329 | int i; | ||
330 | |||
331 | for (i = 0; i < vdev->num_irqs; i++) | ||
332 | vfio_set_trigger(vdev, i, -1, NULL); | ||
333 | |||
334 | vdev->num_irqs = 0; | ||
335 | kfree(vdev->irqs); | ||
336 | } | ||
diff --git a/drivers/vfio/platform/vfio_platform_private.h b/drivers/vfio/platform/vfio_platform_private.h new file mode 100644 index 000000000000..5d31e0473406 --- /dev/null +++ b/drivers/vfio/platform/vfio_platform_private.h | |||
@@ -0,0 +1,85 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 - Virtual Open Systems | ||
3 | * Author: Antonios Motakis <a.motakis@virtualopensystems.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License, version 2, as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | */ | ||
14 | |||
15 | #ifndef VFIO_PLATFORM_PRIVATE_H | ||
16 | #define VFIO_PLATFORM_PRIVATE_H | ||
17 | |||
18 | #include <linux/types.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | |||
21 | #define VFIO_PLATFORM_OFFSET_SHIFT 40 | ||
22 | #define VFIO_PLATFORM_OFFSET_MASK (((u64)(1) << VFIO_PLATFORM_OFFSET_SHIFT) - 1) | ||
23 | |||
24 | #define VFIO_PLATFORM_OFFSET_TO_INDEX(off) \ | ||
25 | (off >> VFIO_PLATFORM_OFFSET_SHIFT) | ||
26 | |||
27 | #define VFIO_PLATFORM_INDEX_TO_OFFSET(index) \ | ||
28 | ((u64)(index) << VFIO_PLATFORM_OFFSET_SHIFT) | ||
29 | |||
30 | struct vfio_platform_irq { | ||
31 | u32 flags; | ||
32 | u32 count; | ||
33 | int hwirq; | ||
34 | char *name; | ||
35 | struct eventfd_ctx *trigger; | ||
36 | bool masked; | ||
37 | spinlock_t lock; | ||
38 | struct virqfd *unmask; | ||
39 | struct virqfd *mask; | ||
40 | }; | ||
41 | |||
42 | struct vfio_platform_region { | ||
43 | u64 addr; | ||
44 | resource_size_t size; | ||
45 | u32 flags; | ||
46 | u32 type; | ||
47 | #define VFIO_PLATFORM_REGION_TYPE_MMIO 1 | ||
48 | #define VFIO_PLATFORM_REGION_TYPE_PIO 2 | ||
49 | void __iomem *ioaddr; | ||
50 | }; | ||
51 | |||
52 | struct vfio_platform_device { | ||
53 | struct vfio_platform_region *regions; | ||
54 | u32 num_regions; | ||
55 | struct vfio_platform_irq *irqs; | ||
56 | u32 num_irqs; | ||
57 | int refcnt; | ||
58 | struct mutex igate; | ||
59 | |||
60 | /* | ||
61 | * These fields should be filled by the bus specific binder | ||
62 | */ | ||
63 | void *opaque; | ||
64 | const char *name; | ||
65 | uint32_t flags; | ||
66 | /* callbacks to discover device resources */ | ||
67 | struct resource* | ||
68 | (*get_resource)(struct vfio_platform_device *vdev, int i); | ||
69 | int (*get_irq)(struct vfio_platform_device *vdev, int i); | ||
70 | }; | ||
71 | |||
72 | extern int vfio_platform_probe_common(struct vfio_platform_device *vdev, | ||
73 | struct device *dev); | ||
74 | extern struct vfio_platform_device *vfio_platform_remove_common | ||
75 | (struct device *dev); | ||
76 | |||
77 | extern int vfio_platform_irq_init(struct vfio_platform_device *vdev); | ||
78 | extern void vfio_platform_irq_cleanup(struct vfio_platform_device *vdev); | ||
79 | |||
80 | extern int vfio_platform_set_irqs_ioctl(struct vfio_platform_device *vdev, | ||
81 | uint32_t flags, unsigned index, | ||
82 | unsigned start, unsigned count, | ||
83 | void *data); | ||
84 | |||
85 | #endif /* VFIO_PLATFORM_PRIVATE_H */ | ||
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 4cde85501444..0d336625ac71 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c | |||
@@ -234,22 +234,21 @@ static struct vfio_group *vfio_create_group(struct iommu_group *iommu_group) | |||
234 | 234 | ||
235 | mutex_lock(&vfio.group_lock); | 235 | mutex_lock(&vfio.group_lock); |
236 | 236 | ||
237 | minor = vfio_alloc_group_minor(group); | ||
238 | if (minor < 0) { | ||
239 | vfio_group_unlock_and_free(group); | ||
240 | return ERR_PTR(minor); | ||
241 | } | ||
242 | |||
243 | /* Did we race creating this group? */ | 237 | /* Did we race creating this group? */ |
244 | list_for_each_entry(tmp, &vfio.group_list, vfio_next) { | 238 | list_for_each_entry(tmp, &vfio.group_list, vfio_next) { |
245 | if (tmp->iommu_group == iommu_group) { | 239 | if (tmp->iommu_group == iommu_group) { |
246 | vfio_group_get(tmp); | 240 | vfio_group_get(tmp); |
247 | vfio_free_group_minor(minor); | ||
248 | vfio_group_unlock_and_free(group); | 241 | vfio_group_unlock_and_free(group); |
249 | return tmp; | 242 | return tmp; |
250 | } | 243 | } |
251 | } | 244 | } |
252 | 245 | ||
246 | minor = vfio_alloc_group_minor(group); | ||
247 | if (minor < 0) { | ||
248 | vfio_group_unlock_and_free(group); | ||
249 | return ERR_PTR(minor); | ||
250 | } | ||
251 | |||
253 | dev = device_create(vfio.class, NULL, | 252 | dev = device_create(vfio.class, NULL, |
254 | MKDEV(MAJOR(vfio.group_devt), minor), | 253 | MKDEV(MAJOR(vfio.group_devt), minor), |
255 | group, "%d", iommu_group_id(iommu_group)); | 254 | group, "%d", iommu_group_id(iommu_group)); |
diff --git a/drivers/vfio/virqfd.c b/drivers/vfio/virqfd.c new file mode 100644 index 000000000000..27c89cd5d70b --- /dev/null +++ b/drivers/vfio/virqfd.c | |||
@@ -0,0 +1,226 @@ | |||
1 | /* | ||
2 | * VFIO generic eventfd code for IRQFD support. | ||
3 | * Derived from drivers/vfio/pci/vfio_pci_intrs.c | ||
4 | * | ||
5 | * Copyright (C) 2012 Red Hat, Inc. All rights reserved. | ||
6 | * Author: Alex Williamson <alex.williamson@redhat.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/vfio.h> | ||
14 | #include <linux/eventfd.h> | ||
15 | #include <linux/file.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/slab.h> | ||
18 | |||
19 | #define DRIVER_VERSION "0.1" | ||
20 | #define DRIVER_AUTHOR "Alex Williamson <alex.williamson@redhat.com>" | ||
21 | #define DRIVER_DESC "IRQFD support for VFIO bus drivers" | ||
22 | |||
23 | static struct workqueue_struct *vfio_irqfd_cleanup_wq; | ||
24 | static DEFINE_SPINLOCK(virqfd_lock); | ||
25 | |||
26 | static int __init vfio_virqfd_init(void) | ||
27 | { | ||
28 | vfio_irqfd_cleanup_wq = | ||
29 | create_singlethread_workqueue("vfio-irqfd-cleanup"); | ||
30 | if (!vfio_irqfd_cleanup_wq) | ||
31 | return -ENOMEM; | ||
32 | |||
33 | return 0; | ||
34 | } | ||
35 | |||
36 | static void __exit vfio_virqfd_exit(void) | ||
37 | { | ||
38 | destroy_workqueue(vfio_irqfd_cleanup_wq); | ||
39 | } | ||
40 | |||
41 | static void virqfd_deactivate(struct virqfd *virqfd) | ||
42 | { | ||
43 | queue_work(vfio_irqfd_cleanup_wq, &virqfd->shutdown); | ||
44 | } | ||
45 | |||
46 | static int virqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key) | ||
47 | { | ||
48 | struct virqfd *virqfd = container_of(wait, struct virqfd, wait); | ||
49 | unsigned long flags = (unsigned long)key; | ||
50 | |||
51 | if (flags & POLLIN) { | ||
52 | /* An event has been signaled, call function */ | ||
53 | if ((!virqfd->handler || | ||
54 | virqfd->handler(virqfd->opaque, virqfd->data)) && | ||
55 | virqfd->thread) | ||
56 | schedule_work(&virqfd->inject); | ||
57 | } | ||
58 | |||
59 | if (flags & POLLHUP) { | ||
60 | unsigned long flags; | ||
61 | spin_lock_irqsave(&virqfd_lock, flags); | ||
62 | |||
63 | /* | ||
64 | * The eventfd is closing, if the virqfd has not yet been | ||
65 | * queued for release, as determined by testing whether the | ||
66 | * virqfd pointer to it is still valid, queue it now. As | ||
67 | * with kvm irqfds, we know we won't race against the virqfd | ||
68 | * going away because we hold the lock to get here. | ||
69 | */ | ||
70 | if (*(virqfd->pvirqfd) == virqfd) { | ||
71 | *(virqfd->pvirqfd) = NULL; | ||
72 | virqfd_deactivate(virqfd); | ||
73 | } | ||
74 | |||
75 | spin_unlock_irqrestore(&virqfd_lock, flags); | ||
76 | } | ||
77 | |||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | static void virqfd_ptable_queue_proc(struct file *file, | ||
82 | wait_queue_head_t *wqh, poll_table *pt) | ||
83 | { | ||
84 | struct virqfd *virqfd = container_of(pt, struct virqfd, pt); | ||
85 | add_wait_queue(wqh, &virqfd->wait); | ||
86 | } | ||
87 | |||
88 | static void virqfd_shutdown(struct work_struct *work) | ||
89 | { | ||
90 | struct virqfd *virqfd = container_of(work, struct virqfd, shutdown); | ||
91 | u64 cnt; | ||
92 | |||
93 | eventfd_ctx_remove_wait_queue(virqfd->eventfd, &virqfd->wait, &cnt); | ||
94 | flush_work(&virqfd->inject); | ||
95 | eventfd_ctx_put(virqfd->eventfd); | ||
96 | |||
97 | kfree(virqfd); | ||
98 | } | ||
99 | |||
100 | static void virqfd_inject(struct work_struct *work) | ||
101 | { | ||
102 | struct virqfd *virqfd = container_of(work, struct virqfd, inject); | ||
103 | if (virqfd->thread) | ||
104 | virqfd->thread(virqfd->opaque, virqfd->data); | ||
105 | } | ||
106 | |||
107 | int vfio_virqfd_enable(void *opaque, | ||
108 | int (*handler)(void *, void *), | ||
109 | void (*thread)(void *, void *), | ||
110 | void *data, struct virqfd **pvirqfd, int fd) | ||
111 | { | ||
112 | struct fd irqfd; | ||
113 | struct eventfd_ctx *ctx; | ||
114 | struct virqfd *virqfd; | ||
115 | int ret = 0; | ||
116 | unsigned int events; | ||
117 | |||
118 | virqfd = kzalloc(sizeof(*virqfd), GFP_KERNEL); | ||
119 | if (!virqfd) | ||
120 | return -ENOMEM; | ||
121 | |||
122 | virqfd->pvirqfd = pvirqfd; | ||
123 | virqfd->opaque = opaque; | ||
124 | virqfd->handler = handler; | ||
125 | virqfd->thread = thread; | ||
126 | virqfd->data = data; | ||
127 | |||
128 | INIT_WORK(&virqfd->shutdown, virqfd_shutdown); | ||
129 | INIT_WORK(&virqfd->inject, virqfd_inject); | ||
130 | |||
131 | irqfd = fdget(fd); | ||
132 | if (!irqfd.file) { | ||
133 | ret = -EBADF; | ||
134 | goto err_fd; | ||
135 | } | ||
136 | |||
137 | ctx = eventfd_ctx_fileget(irqfd.file); | ||
138 | if (IS_ERR(ctx)) { | ||
139 | ret = PTR_ERR(ctx); | ||
140 | goto err_ctx; | ||
141 | } | ||
142 | |||
143 | virqfd->eventfd = ctx; | ||
144 | |||
145 | /* | ||
146 | * virqfds can be released by closing the eventfd or directly | ||
147 | * through ioctl. These are both done through a workqueue, so | ||
148 | * we update the pointer to the virqfd under lock to avoid | ||
149 | * pushing multiple jobs to release the same virqfd. | ||
150 | */ | ||
151 | spin_lock_irq(&virqfd_lock); | ||
152 | |||
153 | if (*pvirqfd) { | ||
154 | spin_unlock_irq(&virqfd_lock); | ||
155 | ret = -EBUSY; | ||
156 | goto err_busy; | ||
157 | } | ||
158 | *pvirqfd = virqfd; | ||
159 | |||
160 | spin_unlock_irq(&virqfd_lock); | ||
161 | |||
162 | /* | ||
163 | * Install our own custom wake-up handling so we are notified via | ||
164 | * a callback whenever someone signals the underlying eventfd. | ||
165 | */ | ||
166 | init_waitqueue_func_entry(&virqfd->wait, virqfd_wakeup); | ||
167 | init_poll_funcptr(&virqfd->pt, virqfd_ptable_queue_proc); | ||
168 | |||
169 | events = irqfd.file->f_op->poll(irqfd.file, &virqfd->pt); | ||
170 | |||
171 | /* | ||
172 | * Check if there was an event already pending on the eventfd | ||
173 | * before we registered and trigger it as if we didn't miss it. | ||
174 | */ | ||
175 | if (events & POLLIN) { | ||
176 | if ((!handler || handler(opaque, data)) && thread) | ||
177 | schedule_work(&virqfd->inject); | ||
178 | } | ||
179 | |||
180 | /* | ||
181 | * Do not drop the file until the irqfd is fully initialized, | ||
182 | * otherwise we might race against the POLLHUP. | ||
183 | */ | ||
184 | fdput(irqfd); | ||
185 | |||
186 | return 0; | ||
187 | err_busy: | ||
188 | eventfd_ctx_put(ctx); | ||
189 | err_ctx: | ||
190 | fdput(irqfd); | ||
191 | err_fd: | ||
192 | kfree(virqfd); | ||
193 | |||
194 | return ret; | ||
195 | } | ||
196 | EXPORT_SYMBOL_GPL(vfio_virqfd_enable); | ||
197 | |||
198 | void vfio_virqfd_disable(struct virqfd **pvirqfd) | ||
199 | { | ||
200 | unsigned long flags; | ||
201 | |||
202 | spin_lock_irqsave(&virqfd_lock, flags); | ||
203 | |||
204 | if (*pvirqfd) { | ||
205 | virqfd_deactivate(*pvirqfd); | ||
206 | *pvirqfd = NULL; | ||
207 | } | ||
208 | |||
209 | spin_unlock_irqrestore(&virqfd_lock, flags); | ||
210 | |||
211 | /* | ||
212 | * Block until we know all outstanding shutdown jobs have completed. | ||
213 | * Even if we don't queue the job, flush the wq to be sure it's | ||
214 | * been released. | ||
215 | */ | ||
216 | flush_workqueue(vfio_irqfd_cleanup_wq); | ||
217 | } | ||
218 | EXPORT_SYMBOL_GPL(vfio_virqfd_disable); | ||
219 | |||
220 | module_init(vfio_virqfd_init); | ||
221 | module_exit(vfio_virqfd_exit); | ||
222 | |||
223 | MODULE_VERSION(DRIVER_VERSION); | ||
224 | MODULE_LICENSE("GPL v2"); | ||
225 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
226 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
diff --git a/include/linux/vfio.h b/include/linux/vfio.h index 049b2f497bc7..ddb440975382 100644 --- a/include/linux/vfio.h +++ b/include/linux/vfio.h | |||
@@ -14,6 +14,8 @@ | |||
14 | 14 | ||
15 | #include <linux/iommu.h> | 15 | #include <linux/iommu.h> |
16 | #include <linux/mm.h> | 16 | #include <linux/mm.h> |
17 | #include <linux/workqueue.h> | ||
18 | #include <linux/poll.h> | ||
17 | #include <uapi/linux/vfio.h> | 19 | #include <uapi/linux/vfio.h> |
18 | 20 | ||
19 | /** | 21 | /** |
@@ -110,4 +112,27 @@ static inline long vfio_spapr_iommu_eeh_ioctl(struct iommu_group *group, | |||
110 | return -ENOTTY; | 112 | return -ENOTTY; |
111 | } | 113 | } |
112 | #endif /* CONFIG_EEH */ | 114 | #endif /* CONFIG_EEH */ |
115 | |||
116 | /* | ||
117 | * IRQfd - generic | ||
118 | */ | ||
119 | struct virqfd { | ||
120 | void *opaque; | ||
121 | struct eventfd_ctx *eventfd; | ||
122 | int (*handler)(void *, void *); | ||
123 | void (*thread)(void *, void *); | ||
124 | void *data; | ||
125 | struct work_struct inject; | ||
126 | wait_queue_t wait; | ||
127 | poll_table pt; | ||
128 | struct work_struct shutdown; | ||
129 | struct virqfd **pvirqfd; | ||
130 | }; | ||
131 | |||
132 | extern int vfio_virqfd_enable(void *opaque, | ||
133 | int (*handler)(void *, void *), | ||
134 | void (*thread)(void *, void *), | ||
135 | void *data, struct virqfd **pvirqfd, int fd); | ||
136 | extern void vfio_virqfd_disable(struct virqfd **pvirqfd); | ||
137 | |||
113 | #endif /* VFIO_H */ | 138 | #endif /* VFIO_H */ |
diff --git a/include/linux/vgaarb.h b/include/linux/vgaarb.h index c37bd4d06739..8c3b412d84df 100644 --- a/include/linux/vgaarb.h +++ b/include/linux/vgaarb.h | |||
@@ -65,8 +65,13 @@ struct pci_dev; | |||
65 | * out of the arbitration process (and can be safe to take | 65 | * out of the arbitration process (and can be safe to take |
66 | * interrupts at any time. | 66 | * interrupts at any time. |
67 | */ | 67 | */ |
68 | #if defined(CONFIG_VGA_ARB) | ||
68 | extern void vga_set_legacy_decoding(struct pci_dev *pdev, | 69 | extern void vga_set_legacy_decoding(struct pci_dev *pdev, |
69 | unsigned int decodes); | 70 | unsigned int decodes); |
71 | #else | ||
72 | static inline void vga_set_legacy_decoding(struct pci_dev *pdev, | ||
73 | unsigned int decodes) { }; | ||
74 | #endif | ||
70 | 75 | ||
71 | /** | 76 | /** |
72 | * vga_get - acquire & locks VGA resources | 77 | * vga_get - acquire & locks VGA resources |
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h index 82889c30f4f5..b57b750c222f 100644 --- a/include/uapi/linux/vfio.h +++ b/include/uapi/linux/vfio.h | |||
@@ -160,6 +160,8 @@ struct vfio_device_info { | |||
160 | __u32 flags; | 160 | __u32 flags; |
161 | #define VFIO_DEVICE_FLAGS_RESET (1 << 0) /* Device supports reset */ | 161 | #define VFIO_DEVICE_FLAGS_RESET (1 << 0) /* Device supports reset */ |
162 | #define VFIO_DEVICE_FLAGS_PCI (1 << 1) /* vfio-pci device */ | 162 | #define VFIO_DEVICE_FLAGS_PCI (1 << 1) /* vfio-pci device */ |
163 | #define VFIO_DEVICE_FLAGS_PLATFORM (1 << 2) /* vfio-platform device */ | ||
164 | #define VFIO_DEVICE_FLAGS_AMBA (1 << 3) /* vfio-amba device */ | ||
163 | __u32 num_regions; /* Max region index + 1 */ | 165 | __u32 num_regions; /* Max region index + 1 */ |
164 | __u32 num_irqs; /* Max IRQ index + 1 */ | 166 | __u32 num_irqs; /* Max IRQ index + 1 */ |
165 | }; | 167 | }; |