aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-06-28 15:32:13 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-06-28 15:32:13 -0400
commitb779157dd3db6199b50e7ad64678a1ceedbeebcf (patch)
treed00314f158869d072849f4635f70b80680a04638
parent4a10a91756ef381bced7b88cfb9232f660b92d93 (diff)
parenta714ea5fa41623c8d8c42bed0dfb38a4d653451d (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--MAINTAINERS6
-rw-r--r--drivers/vfio/pci/vfio_pci.c16
-rw-r--r--drivers/vfio/platform/Kconfig4
-rw-r--r--drivers/vfio/platform/Makefile2
-rw-r--r--drivers/vfio/platform/reset/Kconfig7
-rw-r--r--drivers/vfio/platform/reset/Makefile5
-rw-r--r--drivers/vfio/platform/reset/vfio_platform_calxedaxgmac.c86
-rw-r--r--drivers/vfio/platform/vfio_platform_common.c60
-rw-r--r--drivers/vfio/platform/vfio_platform_private.h7
-rw-r--r--drivers/vfio/vfio.c27
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/
10786F: include/linux/vfio.h 10786F: include/linux/vfio.h
10787F: include/uapi/linux/vfio.h 10787F: include/uapi/linux/vfio.h
10788 10788
10789VFIO PLATFORM DRIVER
10790M: Baptiste Reynal <b.reynal@virtualopensystems.com>
10791L: kvm@vger.kernel.org
10792S: Maintained
10793F: drivers/vfio/platform/
10794
10789VIDEOBUF2 FRAMEWORK 10795VIDEOBUF2 FRAMEWORK
10790M: Pawel Osciak <pawel@osciak.com> 10796M: Pawel Osciak <pawel@osciak.com>
10791M: Marek Szyprowski <m.szyprowski@samsung.com> 10797M: 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 {
1056static int vfio_pci_get_devs(struct pci_dev *pdev, void *data) 1056static 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 @@
1config VFIO_PLATFORM 1config 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
22source "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 @@
2vfio-platform-y := vfio_platform.o vfio_platform_common.o vfio_platform_irq.o 2vfio-platform-y := vfio_platform.o vfio_platform_common.o vfio_platform_irq.o
3 3
4obj-$(CONFIG_VFIO_PLATFORM) += vfio-platform.o 4obj-$(CONFIG_VFIO_PLATFORM) += vfio-platform.o
5obj-$(CONFIG_VFIO_PLATFORM) += reset/
5 6
6vfio-amba-y := vfio_amba.o 7vfio-amba-y := vfio_amba.o
7 8
8obj-$(CONFIG_VFIO_AMBA) += vfio-amba.o 9obj-$(CONFIG_VFIO_AMBA) += vfio-amba.o
10obj-$(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 @@
1config 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 @@
1vfio-platform-calxedaxgmac-y := vfio_platform_calxedaxgmac.o
2
3ccflags-y += -Idrivers/vfio/platform
4
5obj-$(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
50static 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
62int 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}
81EXPORT_SYMBOL_GPL(vfio_platform_calxedaxgmac_reset);
82
83MODULE_VERSION(DRIVER_VERSION);
84MODULE_LICENSE("GPL v2");
85MODULE_AUTHOR(DRIVER_AUTHOR);
86MODULE_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
26static DEFINE_MUTEX(driver_lock); 26static DEFINE_MUTEX(driver_lock);
27 27
28static 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
36static 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
60static void vfio_platform_put_reset(struct vfio_platform_device *vdev)
61{
62 if (vdev->reset)
63 symbol_put_addr(vdev->reset);
64}
65
28static int vfio_platform_regions_init(struct vfio_platform_device *vdev) 66static 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
73struct vfio_platform_reset_combo {
74 const char *compat;
75 const char *reset_function_name;
76 const char *module_name;
70}; 77};
71 78
72extern int vfio_platform_probe_common(struct vfio_platform_device *vdev, 79extern 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,
661EXPORT_SYMBOL_GPL(vfio_add_group_dev); 661EXPORT_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 */
671struct vfio_device *vfio_device_get_from_dev(struct device *dev) 670struct 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}