Commit b779157d authored by Linus Torvalds's avatar Linus Torvalds

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
parents 4a10a917 a714ea5f
......@@ -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;
}
......
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/
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.
vfio-platform-calxedaxgmac-y := vfio_platform_calxedaxgmac.o
ccflags-y += -Idrivers/vfio/platform
obj-$(CONFIG_VFIO_PLATFORM_CALXEDAXGMAC_RESET) += vfio-platform-calxedaxgmac.o
/*
* 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)
} 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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment