Skip to content

Commit

Permalink
Merge tag 'vfio-v4.2-rc1' of git://github.com/awilliam/linux-vfio
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Linus Torvalds committed Jun 28, 2015
2 parents 4a10a91 + a714ea5 commit b779157
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 19 deletions.
6 changes: 6 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
16 changes: 9 additions & 7 deletions drivers/vfio/pci/vfio_pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
4 changes: 3 additions & 1 deletion drivers/vfio/platform/Kconfig
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 changes: 2 additions & 0 deletions drivers/vfio/platform/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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/
7 changes: 7 additions & 0 deletions drivers/vfio/platform/reset/Kconfig
Original file line number Diff line number Diff line change
@@ -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.
5 changes: 5 additions & 0 deletions drivers/vfio/platform/reset/Makefile
Original file line number Diff line number Diff line change
@@ -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
86 changes: 86 additions & 0 deletions drivers/vfio/platform/reset/vfio_platform_calxedaxgmac.c
Original file line number Diff line number Diff line change
@@ -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);
60 changes: 57 additions & 3 deletions drivers/vfio/platform/vfio_platform_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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++;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down
7 changes: 7 additions & 0 deletions drivers/vfio/platform/vfio_platform_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
27 changes: 19 additions & 8 deletions drivers/vfio/vfio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

iommu_group = iommu_group_get(dev);
if (!iommu_group)
return NULL;

vfio_device_get(device);
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;
}
Expand Down

0 comments on commit b779157

Please sign in to comment.