VFIO updates for v4.2
- 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) -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJVjaiUAAoJECObm247sIsiRJQP/RiqcYg9uDGbBt8Gi+Qi2BmG KrVqG/ty18CZgyis01GZa5O8bL2ViQFAwfpnIJ6l5v4IQAYUUPlfy6Gy9ZgotQir U5ot4ABT9KGqJyjkDixvfUmg5M1gj1euO/xC+KVMpnxN0tC1wbymtAahzd3/RXyq 5cef/cran5bwnh2RtAN/rFkbjZBuqppVZJJV7uyOd69HXhktrZw5skQssL/61+vT znww9H6WT/Qk60haQrcD+SVqAaPv6Tx8bu1LVZ1Zc/rWVxJ2HisOp5dTxGeRnaSu PtMfOndgtYwe5fBaYyNjWluJLpBzY7b5UvHtsZ52hPXvlgkKSMrfCJxM4BQQSekW txmeV6LpQsm+Nq0+sIOFph8h/LzZaebvMsc5YnLamixGb/mAxbbr31yGjokTuS4F ndKUR+9/mFrdcqeBT4UD1Yt45YHl7bRZ9AWKJIC1TstrTW+1rEZ49r71PLea6y4f SJJ+eCvhYr/eebvrsfePqzeo1X+S8HNTgCXrSlqZdohS21qtnsh4g/CaOxI2UVt8 zaZosxNQLzpNK/Z8s7MZpCMenWZsq2frcckROeEgNKdPntwkbVT4MG3LBrWvtuBU HGAKfWCGAjfIIN8ZBUSFtIqua/kXCcec99CcYYQOtv16UbmhadAPoUlMur3HI9T9 S0vWtIKTsRa8ymyrGBDp =/gWA -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
b779157dd3
|
@ -10786,6 +10786,12 @@ F: drivers/vfio/
|
|||
F: include/linux/vfio.h
|
||||
F: include/uapi/linux/vfio.h
|
||||
|
||||
VFIO PLATFORM DRIVER
|
||||
M: Baptiste Reynal <b.reynal@virtualopensystems.com>
|
||||
L: kvm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/vfio/platform/
|
||||
|
||||
VIDEOBUF2 FRAMEWORK
|
||||
M: Pawel Osciak <pawel@osciak.com>
|
||||
M: Marek Szyprowski <m.szyprowski@samsung.com>
|
||||
|
|
|
@ -1056,19 +1056,21 @@ struct vfio_devices {
|
|||
static int vfio_pci_get_devs(struct pci_dev *pdev, void *data)
|
||||
{
|
||||
struct vfio_devices *devs = data;
|
||||
struct pci_driver *pci_drv = ACCESS_ONCE(pdev->driver);
|
||||
|
||||
if (pci_drv != &vfio_pci_driver)
|
||||
return -EBUSY;
|
||||
struct vfio_device *device;
|
||||
|
||||
if (devs->cur_index == devs->max_index)
|
||||
return -ENOSPC;
|
||||
|
||||
devs->devices[devs->cur_index] = vfio_device_get_from_dev(&pdev->dev);
|
||||
if (!devs->devices[devs->cur_index])
|
||||
device = vfio_device_get_from_dev(&pdev->dev);
|
||||
if (!device)
|
||||
return -EINVAL;
|
||||
|
||||
devs->cur_index++;
|
||||
if (pci_dev_driver(pdev) != &vfio_pci_driver) {
|
||||
vfio_device_put(device);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
devs->devices[devs->cur_index++] = device;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
config VFIO_PLATFORM
|
||||
tristate "VFIO support for platform devices"
|
||||
depends on VFIO && EVENTFD && ARM
|
||||
depends on VFIO && EVENTFD && (ARM || ARM64)
|
||||
select VFIO_VIRQFD
|
||||
help
|
||||
Support for platform devices with VFIO. This is required to make
|
||||
|
@ -18,3 +18,5 @@ config VFIO_AMBA
|
|||
framework.
|
||||
|
||||
If you don't know what to do here, say N.
|
||||
|
||||
source "drivers/vfio/platform/reset/Kconfig"
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
vfio-platform-y := vfio_platform.o vfio_platform_common.o vfio_platform_irq.o
|
||||
|
||||
obj-$(CONFIG_VFIO_PLATFORM) += vfio-platform.o
|
||||
obj-$(CONFIG_VFIO_PLATFORM) += reset/
|
||||
|
||||
vfio-amba-y := vfio_amba.o
|
||||
|
||||
obj-$(CONFIG_VFIO_AMBA) += vfio-amba.o
|
||||
obj-$(CONFIG_VFIO_AMBA) += reset/
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
config VFIO_PLATFORM_CALXEDAXGMAC_RESET
|
||||
tristate "VFIO support for calxeda xgmac reset"
|
||||
depends on VFIO_PLATFORM
|
||||
help
|
||||
Enables the VFIO platform driver to handle reset for Calxeda xgmac
|
||||
|
||||
If you don't know what to do here, say N.
|
|
@ -0,0 +1,5 @@
|
|||
vfio-platform-calxedaxgmac-y := vfio_platform_calxedaxgmac.o
|
||||
|
||||
ccflags-y += -Idrivers/vfio/platform
|
||||
|
||||
obj-$(CONFIG_VFIO_PLATFORM_CALXEDAXGMAC_RESET) += vfio-platform-calxedaxgmac.o
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* VFIO platform driver specialized for Calxeda xgmac reset
|
||||
* reset code is inherited from calxeda xgmac native driver
|
||||
*
|
||||
* Copyright 2010-2011 Calxeda, Inc.
|
||||
* Copyright (c) 2015 Linaro Ltd.
|
||||
* www.linaro.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "vfio_platform_private.h"
|
||||
|
||||
#define DRIVER_VERSION "0.1"
|
||||
#define DRIVER_AUTHOR "Eric Auger <eric.auger@linaro.org>"
|
||||
#define DRIVER_DESC "Reset support for Calxeda xgmac vfio platform device"
|
||||
|
||||
#define CALXEDAXGMAC_COMPAT "calxeda,hb-xgmac"
|
||||
|
||||
/* XGMAC Register definitions */
|
||||
#define XGMAC_CONTROL 0x00000000 /* MAC Configuration */
|
||||
|
||||
/* DMA Control and Status Registers */
|
||||
#define XGMAC_DMA_CONTROL 0x00000f18 /* Ctrl (Operational Mode) */
|
||||
#define XGMAC_DMA_INTR_ENA 0x00000f1c /* Interrupt Enable */
|
||||
|
||||
/* DMA Control registe defines */
|
||||
#define DMA_CONTROL_ST 0x00002000 /* Start/Stop Transmission */
|
||||
#define DMA_CONTROL_SR 0x00000002 /* Start/Stop Receive */
|
||||
|
||||
/* Common MAC defines */
|
||||
#define MAC_ENABLE_TX 0x00000008 /* Transmitter Enable */
|
||||
#define MAC_ENABLE_RX 0x00000004 /* Receiver Enable */
|
||||
|
||||
static inline void xgmac_mac_disable(void __iomem *ioaddr)
|
||||
{
|
||||
u32 value = readl(ioaddr + XGMAC_DMA_CONTROL);
|
||||
|
||||
value &= ~(DMA_CONTROL_ST | DMA_CONTROL_SR);
|
||||
writel(value, ioaddr + XGMAC_DMA_CONTROL);
|
||||
|
||||
value = readl(ioaddr + XGMAC_CONTROL);
|
||||
value &= ~(MAC_ENABLE_TX | MAC_ENABLE_RX);
|
||||
writel(value, ioaddr + XGMAC_CONTROL);
|
||||
}
|
||||
|
||||
int vfio_platform_calxedaxgmac_reset(struct vfio_platform_device *vdev)
|
||||
{
|
||||
struct vfio_platform_region reg = vdev->regions[0];
|
||||
|
||||
if (!reg.ioaddr) {
|
||||
reg.ioaddr =
|
||||
ioremap_nocache(reg.addr, reg.size);
|
||||
if (!reg.ioaddr)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* disable IRQ */
|
||||
writel(0, reg.ioaddr + XGMAC_DMA_INTR_ENA);
|
||||
|
||||
/* Disable the MAC core */
|
||||
xgmac_mac_disable(reg.ioaddr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vfio_platform_calxedaxgmac_reset);
|
||||
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
|
@ -25,6 +25,44 @@
|
|||
|
||||
static DEFINE_MUTEX(driver_lock);
|
||||
|
||||
static const struct vfio_platform_reset_combo reset_lookup_table[] = {
|
||||
{
|
||||
.compat = "calxeda,hb-xgmac",
|
||||
.reset_function_name = "vfio_platform_calxedaxgmac_reset",
|
||||
.module_name = "vfio-platform-calxedaxgmac",
|
||||
},
|
||||
};
|
||||
|
||||
static void vfio_platform_get_reset(struct vfio_platform_device *vdev,
|
||||
struct device *dev)
|
||||
{
|
||||
const char *compat;
|
||||
int (*reset)(struct vfio_platform_device *);
|
||||
int ret, i;
|
||||
|
||||
ret = device_property_read_string(dev, "compatible", &compat);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
for (i = 0 ; i < ARRAY_SIZE(reset_lookup_table); i++) {
|
||||
if (!strcmp(reset_lookup_table[i].compat, compat)) {
|
||||
request_module(reset_lookup_table[i].module_name);
|
||||
reset = __symbol_get(
|
||||
reset_lookup_table[i].reset_function_name);
|
||||
if (reset) {
|
||||
vdev->reset = reset;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void vfio_platform_put_reset(struct vfio_platform_device *vdev)
|
||||
{
|
||||
if (vdev->reset)
|
||||
symbol_put_addr(vdev->reset);
|
||||
}
|
||||
|
||||
static int vfio_platform_regions_init(struct vfio_platform_device *vdev)
|
||||
{
|
||||
int cnt = 0, i;
|
||||
|
@ -100,6 +138,8 @@ static void vfio_platform_release(void *device_data)
|
|||
mutex_lock(&driver_lock);
|
||||
|
||||
if (!(--vdev->refcnt)) {
|
||||
if (vdev->reset)
|
||||
vdev->reset(vdev);
|
||||
vfio_platform_regions_cleanup(vdev);
|
||||
vfio_platform_irq_cleanup(vdev);
|
||||
}
|
||||
|
@ -127,6 +167,9 @@ static int vfio_platform_open(void *device_data)
|
|||
ret = vfio_platform_irq_init(vdev);
|
||||
if (ret)
|
||||
goto err_irq;
|
||||
|
||||
if (vdev->reset)
|
||||
vdev->reset(vdev);
|
||||
}
|
||||
|
||||
vdev->refcnt++;
|
||||
|
@ -159,6 +202,8 @@ static long vfio_platform_ioctl(void *device_data,
|
|||
if (info.argsz < minsz)
|
||||
return -EINVAL;
|
||||
|
||||
if (vdev->reset)
|
||||
vdev->flags |= VFIO_DEVICE_FLAGS_RESET;
|
||||
info.flags = vdev->flags;
|
||||
info.num_regions = vdev->num_regions;
|
||||
info.num_irqs = vdev->num_irqs;
|
||||
|
@ -252,8 +297,12 @@ static long vfio_platform_ioctl(void *device_data,
|
|||
|
||||
return ret;
|
||||
|
||||
} else if (cmd == VFIO_DEVICE_RESET)
|
||||
return -EINVAL;
|
||||
} else if (cmd == VFIO_DEVICE_RESET) {
|
||||
if (vdev->reset)
|
||||
return vdev->reset(vdev);
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
@ -502,6 +551,8 @@ int vfio_platform_probe_common(struct vfio_platform_device *vdev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
vfio_platform_get_reset(vdev, dev);
|
||||
|
||||
mutex_init(&vdev->igate);
|
||||
|
||||
return 0;
|
||||
|
@ -513,8 +564,11 @@ struct vfio_platform_device *vfio_platform_remove_common(struct device *dev)
|
|||
struct vfio_platform_device *vdev;
|
||||
|
||||
vdev = vfio_del_group_dev(dev);
|
||||
if (vdev)
|
||||
|
||||
if (vdev) {
|
||||
vfio_platform_put_reset(vdev);
|
||||
iommu_group_put(dev->iommu_group);
|
||||
}
|
||||
|
||||
return vdev;
|
||||
}
|
||||
|
|
|
@ -67,6 +67,13 @@ struct vfio_platform_device {
|
|||
struct resource*
|
||||
(*get_resource)(struct vfio_platform_device *vdev, int i);
|
||||
int (*get_irq)(struct vfio_platform_device *vdev, int i);
|
||||
int (*reset)(struct vfio_platform_device *vdev);
|
||||
};
|
||||
|
||||
struct vfio_platform_reset_combo {
|
||||
const char *compat;
|
||||
const char *reset_function_name;
|
||||
const char *module_name;
|
||||
};
|
||||
|
||||
extern int vfio_platform_probe_common(struct vfio_platform_device *vdev,
|
||||
|
|
|
@ -661,18 +661,29 @@ int vfio_add_group_dev(struct device *dev,
|
|||
EXPORT_SYMBOL_GPL(vfio_add_group_dev);
|
||||
|
||||
/**
|
||||
* Get a reference to the vfio_device for a device that is known to
|
||||
* be bound to a vfio driver. The driver implicitly holds a
|
||||
* vfio_device reference between vfio_add_group_dev and
|
||||
* vfio_del_group_dev. We can therefore use drvdata to increment
|
||||
* that reference from the struct device. This additional
|
||||
* reference must be released by calling vfio_device_put.
|
||||
* Get a reference to the vfio_device for a device. Even if the
|
||||
* caller thinks they own the device, they could be racing with a
|
||||
* release call path, so we can't trust drvdata for the shortcut.
|
||||
* Go the long way around, from the iommu_group to the vfio_group
|
||||
* to the vfio_device.
|
||||
*/
|
||||
struct vfio_device *vfio_device_get_from_dev(struct device *dev)
|
||||
{
|
||||
struct vfio_device *device = dev_get_drvdata(dev);
|
||||
struct iommu_group *iommu_group;
|
||||
struct vfio_group *group;
|
||||
struct vfio_device *device;
|
||||
|
||||
vfio_device_get(device);
|
||||
iommu_group = iommu_group_get(dev);
|
||||
if (!iommu_group)
|
||||
return NULL;
|
||||
|
||||
group = vfio_group_get_from_iommu(iommu_group);
|
||||
iommu_group_put(iommu_group);
|
||||
if (!group)
|
||||
return NULL;
|
||||
|
||||
device = vfio_group_get_device(group, dev);
|
||||
vfio_group_put(group);
|
||||
|
||||
return device;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue