diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-28 15:32:13 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-28 15:32:13 -0400 |
commit | b779157dd3db6199b50e7ad64678a1ceedbeebcf (patch) | |
tree | d00314f158869d072849f4635f70b80680a04638 | |
parent | 4a10a91756ef381bced7b88cfb9232f660b92d93 (diff) | |
parent | a714ea5fa41623c8d8c42bed0dfb38a4d653451d (diff) |
Merge tag 'vfio-v4.2-rc1' of git://github.com/awilliam/linux-vfio
Pull VFIO updates from Alex Williamson:
- fix race with device reference versus driver release (Alex Williamson)
- add reset hooks and Calxeda xgmac reset for vfio-platform (Eric Auger)
- enable vfio-platform for ARM64 (Eric Auger)
- tag Baptiste Reynal as vfio-platform sub-maintainer (Alex Williamson)
* tag 'vfio-v4.2-rc1' of git://github.com/awilliam/linux-vfio:
MAINTAINERS: Add vfio-platform sub-maintainer
VFIO: platform: enable ARM64 build
VFIO: platform: Calxeda xgmac reset module
VFIO: platform: populate the reset function on probe
VFIO: platform: add reset callback
VFIO: platform: add reset struct and lookup table
vfio/pci: Fix racy vfio_device_get_from_dev() call
-rw-r--r-- | MAINTAINERS | 6 | ||||
-rw-r--r-- | drivers/vfio/pci/vfio_pci.c | 16 | ||||
-rw-r--r-- | drivers/vfio/platform/Kconfig | 4 | ||||
-rw-r--r-- | drivers/vfio/platform/Makefile | 2 | ||||
-rw-r--r-- | drivers/vfio/platform/reset/Kconfig | 7 | ||||
-rw-r--r-- | drivers/vfio/platform/reset/Makefile | 5 | ||||
-rw-r--r-- | drivers/vfio/platform/reset/vfio_platform_calxedaxgmac.c | 86 | ||||
-rw-r--r-- | drivers/vfio/platform/vfio_platform_common.c | 60 | ||||
-rw-r--r-- | drivers/vfio/platform/vfio_platform_private.h | 7 | ||||
-rw-r--r-- | drivers/vfio/vfio.c | 27 |
10 files changed, 201 insertions, 19 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 6aedd5072323..2c7e07c99765 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -10786,6 +10786,12 @@ F: drivers/vfio/ | |||
10786 | F: include/linux/vfio.h | 10786 | F: include/linux/vfio.h |
10787 | F: include/uapi/linux/vfio.h | 10787 | F: include/uapi/linux/vfio.h |
10788 | 10788 | ||
10789 | VFIO PLATFORM DRIVER | ||
10790 | M: Baptiste Reynal <b.reynal@virtualopensystems.com> | ||
10791 | L: kvm@vger.kernel.org | ||
10792 | S: Maintained | ||
10793 | F: drivers/vfio/platform/ | ||
10794 | |||
10789 | VIDEOBUF2 FRAMEWORK | 10795 | VIDEOBUF2 FRAMEWORK |
10790 | M: Pawel Osciak <pawel@osciak.com> | 10796 | M: Pawel Osciak <pawel@osciak.com> |
10791 | M: Marek Szyprowski <m.szyprowski@samsung.com> | 10797 | M: Marek Szyprowski <m.szyprowski@samsung.com> |
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index e9851add6f4e..964ad572aaee 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c | |||
@@ -1056,19 +1056,21 @@ struct vfio_devices { | |||
1056 | static int vfio_pci_get_devs(struct pci_dev *pdev, void *data) | 1056 | static int vfio_pci_get_devs(struct pci_dev *pdev, void *data) |
1057 | { | 1057 | { |
1058 | struct vfio_devices *devs = data; | 1058 | struct vfio_devices *devs = data; |
1059 | struct pci_driver *pci_drv = ACCESS_ONCE(pdev->driver); | 1059 | struct vfio_device *device; |
1060 | |||
1061 | if (pci_drv != &vfio_pci_driver) | ||
1062 | return -EBUSY; | ||
1063 | 1060 | ||
1064 | if (devs->cur_index == devs->max_index) | 1061 | if (devs->cur_index == devs->max_index) |
1065 | return -ENOSPC; | 1062 | return -ENOSPC; |
1066 | 1063 | ||
1067 | devs->devices[devs->cur_index] = vfio_device_get_from_dev(&pdev->dev); | 1064 | device = vfio_device_get_from_dev(&pdev->dev); |
1068 | if (!devs->devices[devs->cur_index]) | 1065 | if (!device) |
1069 | return -EINVAL; | 1066 | return -EINVAL; |
1070 | 1067 | ||
1071 | devs->cur_index++; | 1068 | if (pci_dev_driver(pdev) != &vfio_pci_driver) { |
1069 | vfio_device_put(device); | ||
1070 | return -EBUSY; | ||
1071 | } | ||
1072 | |||
1073 | devs->devices[devs->cur_index++] = device; | ||
1072 | return 0; | 1074 | return 0; |
1073 | } | 1075 | } |
1074 | 1076 | ||
diff --git a/drivers/vfio/platform/Kconfig b/drivers/vfio/platform/Kconfig index 9a4403e2a36c..bb30128782aa 100644 --- a/drivers/vfio/platform/Kconfig +++ b/drivers/vfio/platform/Kconfig | |||
@@ -1,6 +1,6 @@ | |||
1 | config VFIO_PLATFORM | 1 | config VFIO_PLATFORM |
2 | tristate "VFIO support for platform devices" | 2 | tristate "VFIO support for platform devices" |
3 | depends on VFIO && EVENTFD && ARM | 3 | depends on VFIO && EVENTFD && (ARM || ARM64) |
4 | select VFIO_VIRQFD | 4 | select VFIO_VIRQFD |
5 | help | 5 | help |
6 | Support for platform devices with VFIO. This is required to make | 6 | Support for platform devices with VFIO. This is required to make |
@@ -18,3 +18,5 @@ config VFIO_AMBA | |||
18 | framework. | 18 | framework. |
19 | 19 | ||
20 | If you don't know what to do here, say N. | 20 | If you don't know what to do here, say N. |
21 | |||
22 | source "drivers/vfio/platform/reset/Kconfig" | ||
diff --git a/drivers/vfio/platform/Makefile b/drivers/vfio/platform/Makefile index 81de144c0eaa..9ce8afe28450 100644 --- a/drivers/vfio/platform/Makefile +++ b/drivers/vfio/platform/Makefile | |||
@@ -2,7 +2,9 @@ | |||
2 | vfio-platform-y := vfio_platform.o vfio_platform_common.o vfio_platform_irq.o | 2 | vfio-platform-y := vfio_platform.o vfio_platform_common.o vfio_platform_irq.o |
3 | 3 | ||
4 | obj-$(CONFIG_VFIO_PLATFORM) += vfio-platform.o | 4 | obj-$(CONFIG_VFIO_PLATFORM) += vfio-platform.o |
5 | obj-$(CONFIG_VFIO_PLATFORM) += reset/ | ||
5 | 6 | ||
6 | vfio-amba-y := vfio_amba.o | 7 | vfio-amba-y := vfio_amba.o |
7 | 8 | ||
8 | obj-$(CONFIG_VFIO_AMBA) += vfio-amba.o | 9 | obj-$(CONFIG_VFIO_AMBA) += vfio-amba.o |
10 | obj-$(CONFIG_VFIO_AMBA) += reset/ | ||
diff --git a/drivers/vfio/platform/reset/Kconfig b/drivers/vfio/platform/reset/Kconfig new file mode 100644 index 000000000000..746b96b0003b --- /dev/null +++ b/drivers/vfio/platform/reset/Kconfig | |||
@@ -0,0 +1,7 @@ | |||
1 | config VFIO_PLATFORM_CALXEDAXGMAC_RESET | ||
2 | tristate "VFIO support for calxeda xgmac reset" | ||
3 | depends on VFIO_PLATFORM | ||
4 | help | ||
5 | Enables the VFIO platform driver to handle reset for Calxeda xgmac | ||
6 | |||
7 | If you don't know what to do here, say N. | ||
diff --git a/drivers/vfio/platform/reset/Makefile b/drivers/vfio/platform/reset/Makefile new file mode 100644 index 000000000000..2a486af9f8fa --- /dev/null +++ b/drivers/vfio/platform/reset/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | vfio-platform-calxedaxgmac-y := vfio_platform_calxedaxgmac.o | ||
2 | |||
3 | ccflags-y += -Idrivers/vfio/platform | ||
4 | |||
5 | obj-$(CONFIG_VFIO_PLATFORM_CALXEDAXGMAC_RESET) += vfio-platform-calxedaxgmac.o | ||
diff --git a/drivers/vfio/platform/reset/vfio_platform_calxedaxgmac.c b/drivers/vfio/platform/reset/vfio_platform_calxedaxgmac.c new file mode 100644 index 000000000000..619dc7d22082 --- /dev/null +++ b/drivers/vfio/platform/reset/vfio_platform_calxedaxgmac.c | |||
@@ -0,0 +1,86 @@ | |||
1 | /* | ||
2 | * VFIO platform driver specialized for Calxeda xgmac reset | ||
3 | * reset code is inherited from calxeda xgmac native driver | ||
4 | * | ||
5 | * Copyright 2010-2011 Calxeda, Inc. | ||
6 | * Copyright (c) 2015 Linaro Ltd. | ||
7 | * www.linaro.org | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms and conditions of the GNU General Public License, | ||
11 | * version 2, as published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
16 | * more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License along with | ||
19 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/io.h> | ||
26 | |||
27 | #include "vfio_platform_private.h" | ||
28 | |||
29 | #define DRIVER_VERSION "0.1" | ||
30 | #define DRIVER_AUTHOR "Eric Auger <eric.auger@linaro.org>" | ||
31 | #define DRIVER_DESC "Reset support for Calxeda xgmac vfio platform device" | ||
32 | |||
33 | #define CALXEDAXGMAC_COMPAT "calxeda,hb-xgmac" | ||
34 | |||
35 | /* XGMAC Register definitions */ | ||
36 | #define XGMAC_CONTROL 0x00000000 /* MAC Configuration */ | ||
37 | |||
38 | /* DMA Control and Status Registers */ | ||
39 | #define XGMAC_DMA_CONTROL 0x00000f18 /* Ctrl (Operational Mode) */ | ||
40 | #define XGMAC_DMA_INTR_ENA 0x00000f1c /* Interrupt Enable */ | ||
41 | |||
42 | /* DMA Control registe defines */ | ||
43 | #define DMA_CONTROL_ST 0x00002000 /* Start/Stop Transmission */ | ||
44 | #define DMA_CONTROL_SR 0x00000002 /* Start/Stop Receive */ | ||
45 | |||
46 | /* Common MAC defines */ | ||
47 | #define MAC_ENABLE_TX 0x00000008 /* Transmitter Enable */ | ||
48 | #define MAC_ENABLE_RX 0x00000004 /* Receiver Enable */ | ||
49 | |||
50 | static inline void xgmac_mac_disable(void __iomem *ioaddr) | ||
51 | { | ||
52 | u32 value = readl(ioaddr + XGMAC_DMA_CONTROL); | ||
53 | |||
54 | value &= ~(DMA_CONTROL_ST | DMA_CONTROL_SR); | ||
55 | writel(value, ioaddr + XGMAC_DMA_CONTROL); | ||
56 | |||
57 | value = readl(ioaddr + XGMAC_CONTROL); | ||
58 | value &= ~(MAC_ENABLE_TX | MAC_ENABLE_RX); | ||
59 | writel(value, ioaddr + XGMAC_CONTROL); | ||
60 | } | ||
61 | |||
62 | int vfio_platform_calxedaxgmac_reset(struct vfio_platform_device *vdev) | ||
63 | { | ||
64 | struct vfio_platform_region reg = vdev->regions[0]; | ||
65 | |||
66 | if (!reg.ioaddr) { | ||
67 | reg.ioaddr = | ||
68 | ioremap_nocache(reg.addr, reg.size); | ||
69 | if (!reg.ioaddr) | ||
70 | return -ENOMEM; | ||
71 | } | ||
72 | |||
73 | /* disable IRQ */ | ||
74 | writel(0, reg.ioaddr + XGMAC_DMA_INTR_ENA); | ||
75 | |||
76 | /* Disable the MAC core */ | ||
77 | xgmac_mac_disable(reg.ioaddr); | ||
78 | |||
79 | return 0; | ||
80 | } | ||
81 | EXPORT_SYMBOL_GPL(vfio_platform_calxedaxgmac_reset); | ||
82 | |||
83 | MODULE_VERSION(DRIVER_VERSION); | ||
84 | MODULE_LICENSE("GPL v2"); | ||
85 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
86 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
diff --git a/drivers/vfio/platform/vfio_platform_common.c b/drivers/vfio/platform/vfio_platform_common.c index abcff7a1aa66..e43efb5e92bf 100644 --- a/drivers/vfio/platform/vfio_platform_common.c +++ b/drivers/vfio/platform/vfio_platform_common.c | |||
@@ -25,6 +25,44 @@ | |||
25 | 25 | ||
26 | static DEFINE_MUTEX(driver_lock); | 26 | static DEFINE_MUTEX(driver_lock); |
27 | 27 | ||
28 | static const struct vfio_platform_reset_combo reset_lookup_table[] = { | ||
29 | { | ||
30 | .compat = "calxeda,hb-xgmac", | ||
31 | .reset_function_name = "vfio_platform_calxedaxgmac_reset", | ||
32 | .module_name = "vfio-platform-calxedaxgmac", | ||
33 | }, | ||
34 | }; | ||
35 | |||
36 | static void vfio_platform_get_reset(struct vfio_platform_device *vdev, | ||
37 | struct device *dev) | ||
38 | { | ||
39 | const char *compat; | ||
40 | int (*reset)(struct vfio_platform_device *); | ||
41 | int ret, i; | ||
42 | |||
43 | ret = device_property_read_string(dev, "compatible", &compat); | ||
44 | if (ret) | ||
45 | return; | ||
46 | |||
47 | for (i = 0 ; i < ARRAY_SIZE(reset_lookup_table); i++) { | ||
48 | if (!strcmp(reset_lookup_table[i].compat, compat)) { | ||
49 | request_module(reset_lookup_table[i].module_name); | ||
50 | reset = __symbol_get( | ||
51 | reset_lookup_table[i].reset_function_name); | ||
52 | if (reset) { | ||
53 | vdev->reset = reset; | ||
54 | return; | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | |||
60 | static void vfio_platform_put_reset(struct vfio_platform_device *vdev) | ||
61 | { | ||
62 | if (vdev->reset) | ||
63 | symbol_put_addr(vdev->reset); | ||
64 | } | ||
65 | |||
28 | static int vfio_platform_regions_init(struct vfio_platform_device *vdev) | 66 | static int vfio_platform_regions_init(struct vfio_platform_device *vdev) |
29 | { | 67 | { |
30 | int cnt = 0, i; | 68 | int cnt = 0, i; |
@@ -100,6 +138,8 @@ static void vfio_platform_release(void *device_data) | |||
100 | mutex_lock(&driver_lock); | 138 | mutex_lock(&driver_lock); |
101 | 139 | ||
102 | if (!(--vdev->refcnt)) { | 140 | if (!(--vdev->refcnt)) { |
141 | if (vdev->reset) | ||
142 | vdev->reset(vdev); | ||
103 | vfio_platform_regions_cleanup(vdev); | 143 | vfio_platform_regions_cleanup(vdev); |
104 | vfio_platform_irq_cleanup(vdev); | 144 | vfio_platform_irq_cleanup(vdev); |
105 | } | 145 | } |
@@ -127,6 +167,9 @@ static int vfio_platform_open(void *device_data) | |||
127 | ret = vfio_platform_irq_init(vdev); | 167 | ret = vfio_platform_irq_init(vdev); |
128 | if (ret) | 168 | if (ret) |
129 | goto err_irq; | 169 | goto err_irq; |
170 | |||
171 | if (vdev->reset) | ||
172 | vdev->reset(vdev); | ||
130 | } | 173 | } |
131 | 174 | ||
132 | vdev->refcnt++; | 175 | vdev->refcnt++; |
@@ -159,6 +202,8 @@ static long vfio_platform_ioctl(void *device_data, | |||
159 | if (info.argsz < minsz) | 202 | if (info.argsz < minsz) |
160 | return -EINVAL; | 203 | return -EINVAL; |
161 | 204 | ||
205 | if (vdev->reset) | ||
206 | vdev->flags |= VFIO_DEVICE_FLAGS_RESET; | ||
162 | info.flags = vdev->flags; | 207 | info.flags = vdev->flags; |
163 | info.num_regions = vdev->num_regions; | 208 | info.num_regions = vdev->num_regions; |
164 | info.num_irqs = vdev->num_irqs; | 209 | info.num_irqs = vdev->num_irqs; |
@@ -252,8 +297,12 @@ static long vfio_platform_ioctl(void *device_data, | |||
252 | 297 | ||
253 | return ret; | 298 | return ret; |
254 | 299 | ||
255 | } else if (cmd == VFIO_DEVICE_RESET) | 300 | } else if (cmd == VFIO_DEVICE_RESET) { |
256 | return -EINVAL; | 301 | if (vdev->reset) |
302 | return vdev->reset(vdev); | ||
303 | else | ||
304 | return -EINVAL; | ||
305 | } | ||
257 | 306 | ||
258 | return -ENOTTY; | 307 | return -ENOTTY; |
259 | } | 308 | } |
@@ -502,6 +551,8 @@ int vfio_platform_probe_common(struct vfio_platform_device *vdev, | |||
502 | return ret; | 551 | return ret; |
503 | } | 552 | } |
504 | 553 | ||
554 | vfio_platform_get_reset(vdev, dev); | ||
555 | |||
505 | mutex_init(&vdev->igate); | 556 | mutex_init(&vdev->igate); |
506 | 557 | ||
507 | return 0; | 558 | return 0; |
@@ -513,8 +564,11 @@ struct vfio_platform_device *vfio_platform_remove_common(struct device *dev) | |||
513 | struct vfio_platform_device *vdev; | 564 | struct vfio_platform_device *vdev; |
514 | 565 | ||
515 | vdev = vfio_del_group_dev(dev); | 566 | vdev = vfio_del_group_dev(dev); |
516 | if (vdev) | 567 | |
568 | if (vdev) { | ||
569 | vfio_platform_put_reset(vdev); | ||
517 | iommu_group_put(dev->iommu_group); | 570 | iommu_group_put(dev->iommu_group); |
571 | } | ||
518 | 572 | ||
519 | return vdev; | 573 | return vdev; |
520 | } | 574 | } |
diff --git a/drivers/vfio/platform/vfio_platform_private.h b/drivers/vfio/platform/vfio_platform_private.h index 5d31e0473406..1c9b3d59543c 100644 --- a/drivers/vfio/platform/vfio_platform_private.h +++ b/drivers/vfio/platform/vfio_platform_private.h | |||
@@ -67,6 +67,13 @@ struct vfio_platform_device { | |||
67 | struct resource* | 67 | struct resource* |
68 | (*get_resource)(struct vfio_platform_device *vdev, int i); | 68 | (*get_resource)(struct vfio_platform_device *vdev, int i); |
69 | int (*get_irq)(struct vfio_platform_device *vdev, int i); | 69 | int (*get_irq)(struct vfio_platform_device *vdev, int i); |
70 | int (*reset)(struct vfio_platform_device *vdev); | ||
71 | }; | ||
72 | |||
73 | struct vfio_platform_reset_combo { | ||
74 | const char *compat; | ||
75 | const char *reset_function_name; | ||
76 | const char *module_name; | ||
70 | }; | 77 | }; |
71 | 78 | ||
72 | extern int vfio_platform_probe_common(struct vfio_platform_device *vdev, | 79 | extern int vfio_platform_probe_common(struct vfio_platform_device *vdev, |
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index e1278fe04b1e..2fb29dfeffbd 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c | |||
@@ -661,18 +661,29 @@ int vfio_add_group_dev(struct device *dev, | |||
661 | EXPORT_SYMBOL_GPL(vfio_add_group_dev); | 661 | EXPORT_SYMBOL_GPL(vfio_add_group_dev); |
662 | 662 | ||
663 | /** | 663 | /** |
664 | * Get a reference to the vfio_device for a device that is known to | 664 | * Get a reference to the vfio_device for a device. Even if the |
665 | * be bound to a vfio driver. The driver implicitly holds a | 665 | * caller thinks they own the device, they could be racing with a |
666 | * vfio_device reference between vfio_add_group_dev and | 666 | * release call path, so we can't trust drvdata for the shortcut. |
667 | * vfio_del_group_dev. We can therefore use drvdata to increment | 667 | * Go the long way around, from the iommu_group to the vfio_group |
668 | * that reference from the struct device. This additional | 668 | * to the vfio_device. |
669 | * reference must be released by calling vfio_device_put. | ||
670 | */ | 669 | */ |
671 | struct vfio_device *vfio_device_get_from_dev(struct device *dev) | 670 | struct vfio_device *vfio_device_get_from_dev(struct device *dev) |
672 | { | 671 | { |
673 | struct vfio_device *device = dev_get_drvdata(dev); | 672 | struct iommu_group *iommu_group; |
673 | struct vfio_group *group; | ||
674 | struct vfio_device *device; | ||
675 | |||
676 | iommu_group = iommu_group_get(dev); | ||
677 | if (!iommu_group) | ||
678 | return NULL; | ||
674 | 679 | ||
675 | vfio_device_get(device); | 680 | group = vfio_group_get_from_iommu(iommu_group); |
681 | iommu_group_put(iommu_group); | ||
682 | if (!group) | ||
683 | return NULL; | ||
684 | |||
685 | device = vfio_group_get_device(group, dev); | ||
686 | vfio_group_put(group); | ||
676 | 687 | ||
677 | return device; | 688 | return device; |
678 | } | 689 | } |