Create a new driver igd_vfio_pci.ko that will be responsible for providing special extensions for INTEL Graphics card (GVT-d). Also preserve backward compatibility with vfio_pci.ko vendor specific extensions. Signed-off-by: Max Gurtovoy <mgurtovoy@xxxxxxxxxx> --- drivers/vfio/pci/Kconfig | 5 +- drivers/vfio/pci/Makefile | 4 +- .../pci/{vfio_pci_igd.c => igd_vfio_pci.c} | 147 +++++++++++++++++- drivers/vfio/pci/igd_vfio_pci.h | 24 +++ drivers/vfio/pci/vfio_pci.c | 4 + drivers/vfio/pci/vfio_pci_core.c | 15 -- drivers/vfio/pci/vfio_pci_core.h | 9 -- 7 files changed, 176 insertions(+), 32 deletions(-) rename drivers/vfio/pci/{vfio_pci_igd.c => igd_vfio_pci.c} (62%) create mode 100644 drivers/vfio/pci/igd_vfio_pci.h diff --git a/drivers/vfio/pci/Kconfig b/drivers/vfio/pci/Kconfig index 88c89863a205..09d85ba3e5b2 100644 --- a/drivers/vfio/pci/Kconfig +++ b/drivers/vfio/pci/Kconfig @@ -37,17 +37,14 @@ config VFIO_PCI_INTX def_bool y if !S390 config VFIO_PCI_IGD - bool "VFIO PCI extensions for Intel graphics (GVT-d)" + tristate "VFIO PCI extensions for Intel graphics (GVT-d)" depends on VFIO_PCI_CORE && X86 - default y help Support for Intel IGD specific extensions to enable direct assignment to virtual machines. This includes exposing an IGD specific firmware table and read-only copies of the host bridge and LPC bridge config space. - To enable Intel IGD assignment through vfio-pci, say Y. - config VFIO_PCI_NVLINK2GPU tristate "VFIO support for NVIDIA NVLINK2 GPUs" depends on VFIO_PCI_CORE && PPC_POWERNV diff --git a/drivers/vfio/pci/Makefile b/drivers/vfio/pci/Makefile index 86fb62e271fc..298b2fb3f075 100644 --- a/drivers/vfio/pci/Makefile +++ b/drivers/vfio/pci/Makefile @@ -4,9 +4,9 @@ obj-$(CONFIG_VFIO_PCI_CORE) += vfio-pci-core.o obj-$(CONFIG_VFIO_PCI) += vfio-pci.o obj-$(CONFIG_VFIO_PCI_NPU2) += npu2-vfio-pci.o obj-$(CONFIG_VFIO_PCI_NVLINK2GPU) += nvlink2gpu-vfio-pci.o +obj-$(CONFIG_VFIO_PCI_IGD) += igd-vfio-pci.o vfio-pci-core-y := vfio_pci_core.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o -vfio-pci-core-$(CONFIG_VFIO_PCI_IGD) += vfio_pci_igd.o vfio-pci-core-$(CONFIG_S390) += vfio_pci_zdev.o vfio-pci-y := vfio_pci.o @@ -14,3 +14,5 @@ vfio-pci-y := vfio_pci.o npu2-vfio-pci-y := npu2_vfio_pci.o nvlink2gpu-vfio-pci-y := nvlink2gpu_vfio_pci.o + +igd-vfio-pci-y := igd_vfio_pci.o diff --git a/drivers/vfio/pci/vfio_pci_igd.c b/drivers/vfio/pci/igd_vfio_pci.c similarity index 62% rename from drivers/vfio/pci/vfio_pci_igd.c rename to drivers/vfio/pci/igd_vfio_pci.c index 2388c9722ed8..bbbc432bca82 100644 --- a/drivers/vfio/pci/vfio_pci_igd.c +++ b/drivers/vfio/pci/igd_vfio_pci.c @@ -10,19 +10,32 @@ * address is also virtualized to prevent user modification. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> #include <linux/io.h> #include <linux/pci.h> +#include <linux/list.h> #include <linux/uaccess.h> #include <linux/vfio.h> #include "vfio_pci_core.h" +#include "igd_vfio_pci.h" #define OPREGION_SIGNATURE "IntelGraphicsMem" #define OPREGION_SIZE (8 * 1024) #define OPREGION_PCI_ADDR 0xfc -static size_t vfio_pci_igd_rw(struct vfio_pci_core_device *vdev, char __user *buf, - size_t count, loff_t *ppos, bool iswrite) +#define DRIVER_VERSION "0.1" +#define DRIVER_AUTHOR "Alex Williamson <alex.williamson@xxxxxxxxxx>" +#define DRIVER_DESC "IGD VFIO PCI - User Level meta-driver for Intel Graphics Processing Unit" + +struct igd_vfio_pci_device { + struct vfio_pci_core_device vdev; +}; + +static size_t vfio_pci_igd_rw(struct vfio_pci_core_device *vdev, + char __user *buf, size_t count, loff_t *ppos, bool iswrite) { unsigned int i = VFIO_PCI_OFFSET_TO_INDEX(*ppos) - VFIO_PCI_NUM_REGIONS; void *base = vdev->region[i].data; @@ -261,7 +274,7 @@ static int vfio_pci_igd_cfg_init(struct vfio_pci_core_device *vdev) return 0; } -int vfio_pci_igd_init(struct vfio_pci_core_device *vdev) +static int vfio_pci_igd_init(struct vfio_pci_core_device *vdev) { int ret; @@ -275,3 +288,131 @@ int vfio_pci_igd_init(struct vfio_pci_core_device *vdev) return 0; } + +static void igd_vfio_pci_release(void *device_data) +{ + struct vfio_pci_core_device *vdev = device_data; + + mutex_lock(&vdev->reflck->lock); + if (!(--vdev->refcnt)) { + vfio_pci_vf_token_user_add(vdev, -1); + vfio_pci_core_spapr_eeh_release(vdev); + vfio_pci_core_disable(vdev); + } + mutex_unlock(&vdev->reflck->lock); + + module_put(THIS_MODULE); +} + +static int igd_vfio_pci_open(void *device_data) +{ + struct vfio_pci_core_device *vdev = device_data; + int ret = 0; + + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + mutex_lock(&vdev->reflck->lock); + + if (!vdev->refcnt) { + ret = vfio_pci_core_enable(vdev); + if (ret) + goto error; + + ret = vfio_pci_igd_init(vdev); + if (ret && ret != -ENODEV) { + pci_warn(vdev->pdev, "Failed to setup Intel IGD regions\n"); + vfio_pci_core_disable(vdev); + goto error; + } + ret = 0; + vfio_pci_probe_mmaps(vdev); + vfio_pci_core_spapr_eeh_open(vdev); + vfio_pci_vf_token_user_add(vdev, 1); + } + vdev->refcnt++; +error: + mutex_unlock(&vdev->reflck->lock); + if (ret) + module_put(THIS_MODULE); + return ret; +} + +static const struct vfio_device_ops igd_vfio_pci_ops = { + .name = "igd-vfio-pci", + .open = igd_vfio_pci_open, + .release = igd_vfio_pci_release, + .ioctl = vfio_pci_core_ioctl, + .read = vfio_pci_core_read, + .write = vfio_pci_core_write, + .mmap = vfio_pci_core_mmap, + .request = vfio_pci_core_request, + .match = vfio_pci_core_match, +}; + +static int igd_vfio_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct igd_vfio_pci_device *igvdev; + int ret; + + igvdev = kzalloc(sizeof(*igvdev), GFP_KERNEL); + if (!igvdev) + return -ENOMEM; + + ret = vfio_pci_core_register_device(&igvdev->vdev, pdev, + &igd_vfio_pci_ops); + if (ret) + goto out_free; + + return 0; + +out_free: + kfree(igvdev); + return ret; +} + +static void igd_vfio_pci_remove(struct pci_dev *pdev) +{ + struct vfio_device *vdev = dev_get_drvdata(&pdev->dev); + struct vfio_pci_core_device *core_vpdev = vfio_device_data(vdev); + struct igd_vfio_pci_device *igvdev; + + igvdev = container_of(core_vpdev, struct igd_vfio_pci_device, vdev); + + vfio_pci_core_unregister_device(core_vpdev); + kfree(igvdev); +} + +static const struct pci_device_id igd_vfio_pci_table[] = { + { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, 0xff0000, 0 }, + { 0, } +}; + +static struct pci_driver igd_vfio_pci_driver = { + .name = "igd-vfio-pci", + .id_table = igd_vfio_pci_table, + .probe = igd_vfio_pci_probe, + .remove = igd_vfio_pci_remove, +#ifdef CONFIG_PCI_IOV + .sriov_configure = vfio_pci_core_sriov_configure, +#endif + .err_handler = &vfio_pci_core_err_handlers, +}; + +#ifdef CONFIG_VFIO_PCI_DRIVER_COMPAT +struct pci_driver *get_igd_vfio_pci_driver(struct pci_dev *pdev) +{ + if (pci_match_id(igd_vfio_pci_driver.id_table, pdev)) + return &igd_vfio_pci_driver; + return NULL; +} +EXPORT_SYMBOL_GPL(get_igd_vfio_pci_driver); +#endif + +module_pci_driver(igd_vfio_pci_driver); + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/vfio/pci/igd_vfio_pci.h b/drivers/vfio/pci/igd_vfio_pci.h new file mode 100644 index 000000000000..859aeca354cb --- /dev/null +++ b/drivers/vfio/pci/igd_vfio_pci.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020, Mellanox Technologies, Ltd. All rights reserved. + * Author: Max Gurtovoy <mgurtovoy@xxxxxxxxxx> + */ + +#ifndef IGD_VFIO_PCI_H +#define IGD_VFIO_PCI_H + +#include <linux/pci.h> +#include <linux/module.h> + +#ifdef CONFIG_VFIO_PCI_DRIVER_COMPAT +#if defined(CONFIG_VFIO_PCI_IGD) || defined(CONFIG_VFIO_PCI_IGD_MODULE) +struct pci_driver *get_igd_vfio_pci_driver(struct pci_dev *pdev); +#else +struct pci_driver *get_igd_vfio_pci_driver(struct pci_dev *pdev) +{ + return NULL; +} +#endif +#endif + +#endif /* IGD_VFIO_PCI_H */ diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 8e81ea039f31..1c2f6d55a243 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -30,6 +30,7 @@ #ifdef CONFIG_VFIO_PCI_DRIVER_COMPAT #include "npu2_vfio_pci.h" #include "nvlink2gpu_vfio_pci.h" +#include "igd_vfio_pci.h" #endif #define DRIVER_VERSION "0.2" @@ -170,6 +171,9 @@ static struct pci_driver *vfio_pci_get_compat_driver(struct pci_dev *pdev) default: return NULL; } + case PCI_VENDOR_ID_INTEL: + if (pdev->class == PCI_CLASS_DISPLAY_VGA << 8) + return get_igd_vfio_pci_driver(pdev); } return NULL; diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index f9b39abe54cb..59c9d0d56a0b 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -343,22 +343,7 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev) if (!vfio_vga_disabled() && vfio_pci_is_vga(pdev)) vdev->has_vga = true; - - if (vfio_pci_is_vga(pdev) && - pdev->vendor == PCI_VENDOR_ID_INTEL && - IS_ENABLED(CONFIG_VFIO_PCI_IGD)) { - ret = vfio_pci_igd_init(vdev); - if (ret && ret != -ENODEV) { - pci_warn(pdev, "Failed to setup Intel IGD regions\n"); - goto disable_exit; - } - } - return 0; - -disable_exit: - vfio_pci_disable(vdev); - return ret; } EXPORT_SYMBOL_GPL(vfio_pci_core_enable); diff --git a/drivers/vfio/pci/vfio_pci_core.h b/drivers/vfio/pci/vfio_pci_core.h index 31f3836e606e..2b5ea0db9284 100644 --- a/drivers/vfio/pci/vfio_pci_core.h +++ b/drivers/vfio/pci/vfio_pci_core.h @@ -196,15 +196,6 @@ extern u16 vfio_pci_memory_lock_and_enable(struct vfio_pci_core_device *vdev); extern void vfio_pci_memory_unlock_and_restore(struct vfio_pci_core_device *vdev, u16 cmd); -#ifdef CONFIG_VFIO_PCI_IGD -extern int vfio_pci_igd_init(struct vfio_pci_core_device *vdev); -#else -static inline int vfio_pci_igd_init(struct vfio_pci_core_device *vdev) -{ - return -ENODEV; -} -#endif - #ifdef CONFIG_S390 extern int vfio_pci_info_zdev_add_caps(struct vfio_pci_core_device *vdev, struct vfio_info_cap *caps); -- 2.25.4