Merge tag 'vfio-v5.15-rc1' of git://github.com/awilliam/linux-vfio
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 2 Sep 2021 20:41:33 +0000 (13:41 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 2 Sep 2021 20:41:33 +0000 (13:41 -0700)
Pull VFIO updates from Alex Williamson:

 - Fix dma-valid return WAITED implementation (Anthony Yznaga)

 - SPDX license cleanups (Cai Huoqing)

 - Split vfio-pci-core from vfio-pci and enhance PCI driver matching to
   support future vendor provided vfio-pci variants (Yishai Hadas, Max
   Gurtovoy, Jason Gunthorpe)

 - Replace duplicated reflck with core support for managing first open,
   last close, and device sets (Jason Gunthorpe, Max Gurtovoy, Yishai
   Hadas)

 - Fix non-modular mdev support and don't nag about request callback
   support (Christoph Hellwig)

 - Add semaphore to protect instruction intercept handler and replace
   open-coded locks in vfio-ap driver (Tony Krowiak)

 - Convert vfio-ap to vfio_register_group_dev() API (Jason Gunthorpe)

* tag 'vfio-v5.15-rc1' of git://github.com/awilliam/linux-vfio: (37 commits)
  vfio/pci: Introduce vfio_pci_core.ko
  vfio: Use kconfig if XX/endif blocks instead of repeating 'depends on'
  vfio: Use select for eventfd
  PCI / VFIO: Add 'override_only' support for VFIO PCI sub system
  PCI: Add 'override_only' field to struct pci_device_id
  vfio/pci: Move module parameters to vfio_pci.c
  vfio/pci: Move igd initialization to vfio_pci.c
  vfio/pci: Split the pci_driver code out of vfio_pci_core.c
  vfio/pci: Include vfio header in vfio_pci_core.h
  vfio/pci: Rename ops functions to fit core namings
  vfio/pci: Rename vfio_pci_device to vfio_pci_core_device
  vfio/pci: Rename vfio_pci_private.h to vfio_pci_core.h
  vfio/pci: Rename vfio_pci.c to vfio_pci_core.c
  vfio/ap_ops: Convert to use vfio_register_group_dev()
  s390/vfio-ap: replace open coded locks for VFIO_GROUP_NOTIFY_SET_KVM notification
  s390/vfio-ap: r/w lock for PQAP interception handler function pointer
  vfio/type1: Fix vfio_find_dma_valid return
  vfio-pci/zdev: Remove repeated verbose license text
  vfio: platform: reset: Convert to SPDX identifier
  vfio: Remove struct vfio_device_ops open/release
  ...

46 files changed:
Documentation/PCI/pci.rst
Documentation/driver-api/vfio.rst
MAINTAINERS
arch/s390/include/asm/kvm_host.h
arch/s390/kvm/kvm-s390.c
arch/s390/kvm/priv.c
drivers/gpu/drm/i915/gvt/kvmgt.c
drivers/pci/pci-driver.c
drivers/s390/cio/vfio_ccw_ops.c
drivers/s390/crypto/vfio_ap_ops.c
drivers/s390/crypto/vfio_ap_private.h
drivers/vfio/Kconfig
drivers/vfio/fsl-mc/Kconfig
drivers/vfio/fsl-mc/vfio_fsl_mc.c
drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c
drivers/vfio/fsl-mc/vfio_fsl_mc_private.h
drivers/vfio/mdev/Kconfig
drivers/vfio/mdev/mdev_core.c
drivers/vfio/mdev/vfio_mdev.c
drivers/vfio/pci/Kconfig
drivers/vfio/pci/Makefile
drivers/vfio/pci/vfio_pci.c
drivers/vfio/pci/vfio_pci_config.c
drivers/vfio/pci/vfio_pci_core.c [new file with mode: 0644]
drivers/vfio/pci/vfio_pci_igd.c
drivers/vfio/pci/vfio_pci_intrs.c
drivers/vfio/pci/vfio_pci_private.h [deleted file]
drivers/vfio/pci/vfio_pci_rdwr.c
drivers/vfio/pci/vfio_pci_zdev.c
drivers/vfio/platform/Kconfig
drivers/vfio/platform/reset/Kconfig
drivers/vfio/platform/reset/vfio_platform_bcmflexrm.c
drivers/vfio/platform/vfio_platform_common.c
drivers/vfio/platform/vfio_platform_private.h
drivers/vfio/vfio.c
drivers/vfio/vfio_iommu_type1.c
include/linux/mdev.h
include/linux/mod_devicetable.h
include/linux/pci.h
include/linux/vfio.h
include/linux/vfio_pci_core.h [new file with mode: 0644]
samples/vfio-mdev/mbochs.c
samples/vfio-mdev/mdpy.c
samples/vfio-mdev/mtty.c
scripts/mod/devicetable-offsets.c
scripts/mod/file2alias.c

index fa651e2..87c6f4a 100644 (file)
@@ -103,6 +103,7 @@ need pass only as many optional fields as necessary:
   - subvendor and subdevice fields default to PCI_ANY_ID (FFFFFFFF)
   - class and classmask fields default to 0
   - driver_data defaults to 0UL.
+  - override_only field defaults to 0.
 
 Note that driver_data must match the value used by any of the pci_device_id
 entries defined in the driver. This makes the driver_data field mandatory
index 606eed8..c663b6f 100644 (file)
@@ -255,11 +255,13 @@ vfio_unregister_group_dev() respectively::
        void vfio_init_group_dev(struct vfio_device *device,
                                struct device *dev,
                                const struct vfio_device_ops *ops);
+       void vfio_uninit_group_dev(struct vfio_device *device);
        int vfio_register_group_dev(struct vfio_device *device);
        void vfio_unregister_group_dev(struct vfio_device *device);
 
 The driver should embed the vfio_device in its own structure and call
-vfio_init_group_dev() to pre-configure it before going to registration.
+vfio_init_group_dev() to pre-configure it before going to registration
+and call vfio_uninit_group_dev() after completing the un-registration.
 vfio_register_group_dev() indicates to the core to begin tracking the
 iommu_group of the specified dev and register the dev as owned by a VFIO bus
 driver. Once vfio_register_group_dev() returns it is possible for userspace to
index 2f29842..2c83051 100644 (file)
@@ -19607,6 +19607,7 @@ T:      git git://github.com/awilliam/linux-vfio.git
 F:     Documentation/driver-api/vfio.rst
 F:     drivers/vfio/
 F:     include/linux/vfio.h
+F:     include/linux/vfio_pci_core.h
 F:     include/uapi/linux/vfio.h
 
 VFIO FSL-MC DRIVER
index 161a9e1..d681ae4 100644 (file)
@@ -798,14 +798,12 @@ struct kvm_s390_cpu_model {
        unsigned short ibc;
 };
 
-struct kvm_s390_module_hook {
-       int (*hook)(struct kvm_vcpu *vcpu);
-       struct module *owner;
-};
+typedef int (*crypto_hook)(struct kvm_vcpu *vcpu);
 
 struct kvm_s390_crypto {
        struct kvm_s390_crypto_cb *crycb;
-       struct kvm_s390_module_hook *pqap_hook;
+       struct rw_semaphore pqap_hook_rwsem;
+       crypto_hook *pqap_hook;
        __u32 crycbd;
        __u8 aes_kw;
        __u8 dea_kw;
index 4527ac7..efda061 100644 (file)
@@ -2559,12 +2559,26 @@ static void kvm_s390_set_crycb_format(struct kvm *kvm)
                kvm->arch.crypto.crycbd |= CRYCB_FORMAT1;
 }
 
+/*
+ * kvm_arch_crypto_set_masks
+ *
+ * @kvm: pointer to the target guest's KVM struct containing the crypto masks
+ *      to be set.
+ * @apm: the mask identifying the accessible AP adapters
+ * @aqm: the mask identifying the accessible AP domains
+ * @adm: the mask identifying the accessible AP control domains
+ *
+ * Set the masks that identify the adapters, domains and control domains to
+ * which the KVM guest is granted access.
+ *
+ * Note: The kvm->lock mutex must be locked by the caller before invoking this
+ *      function.
+ */
 void kvm_arch_crypto_set_masks(struct kvm *kvm, unsigned long *apm,
                               unsigned long *aqm, unsigned long *adm)
 {
        struct kvm_s390_crypto_cb *crycb = kvm->arch.crypto.crycb;
 
-       mutex_lock(&kvm->lock);
        kvm_s390_vcpu_block_all(kvm);
 
        switch (kvm->arch.crypto.crycbd & CRYCB_FORMAT_MASK) {
@@ -2595,13 +2609,23 @@ void kvm_arch_crypto_set_masks(struct kvm *kvm, unsigned long *apm,
        /* recreate the shadow crycb for each vcpu */
        kvm_s390_sync_request_broadcast(kvm, KVM_REQ_VSIE_RESTART);
        kvm_s390_vcpu_unblock_all(kvm);
-       mutex_unlock(&kvm->lock);
 }
 EXPORT_SYMBOL_GPL(kvm_arch_crypto_set_masks);
 
+/*
+ * kvm_arch_crypto_clear_masks
+ *
+ * @kvm: pointer to the target guest's KVM struct containing the crypto masks
+ *      to be cleared.
+ *
+ * Clear the masks that identify the adapters, domains and control domains to
+ * which the KVM guest is granted access.
+ *
+ * Note: The kvm->lock mutex must be locked by the caller before invoking this
+ *      function.
+ */
 void kvm_arch_crypto_clear_masks(struct kvm *kvm)
 {
-       mutex_lock(&kvm->lock);
        kvm_s390_vcpu_block_all(kvm);
 
        memset(&kvm->arch.crypto.crycb->apcb0, 0,
@@ -2613,7 +2637,6 @@ void kvm_arch_crypto_clear_masks(struct kvm *kvm)
        /* recreate the shadow crycb for each vcpu */
        kvm_s390_sync_request_broadcast(kvm, KVM_REQ_VSIE_RESTART);
        kvm_s390_vcpu_unblock_all(kvm);
-       mutex_unlock(&kvm->lock);
 }
 EXPORT_SYMBOL_GPL(kvm_arch_crypto_clear_masks);
 
@@ -2630,6 +2653,7 @@ static void kvm_s390_crypto_init(struct kvm *kvm)
 {
        kvm->arch.crypto.crycb = &kvm->arch.sie_page2->crycb;
        kvm_s390_set_crycb_format(kvm);
+       init_rwsem(&kvm->arch.crypto.pqap_hook_rwsem);
 
        if (!test_kvm_facility(kvm, 76))
                return;
index 9928f78..53da4ce 100644 (file)
@@ -610,6 +610,7 @@ static int handle_io_inst(struct kvm_vcpu *vcpu)
 static int handle_pqap(struct kvm_vcpu *vcpu)
 {
        struct ap_queue_status status = {};
+       crypto_hook pqap_hook;
        unsigned long reg0;
        int ret;
        uint8_t fc;
@@ -654,18 +655,20 @@ static int handle_pqap(struct kvm_vcpu *vcpu)
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 
        /*
-        * Verify that the hook callback is registered, lock the owner
-        * and call the hook.
+        * If the hook callback is registered, there will be a pointer to the
+        * hook function pointer in the kvm_s390_crypto structure. Lock the
+        * owner, retrieve the hook function pointer and call the hook.
         */
+       down_read(&vcpu->kvm->arch.crypto.pqap_hook_rwsem);
        if (vcpu->kvm->arch.crypto.pqap_hook) {
-               if (!try_module_get(vcpu->kvm->arch.crypto.pqap_hook->owner))
-                       return -EOPNOTSUPP;
-               ret = vcpu->kvm->arch.crypto.pqap_hook->hook(vcpu);
-               module_put(vcpu->kvm->arch.crypto.pqap_hook->owner);
+               pqap_hook = *vcpu->kvm->arch.crypto.pqap_hook;
+               ret = pqap_hook(vcpu);
                if (!ret && vcpu->run->s.regs.gprs[1] & 0x00ff0000)
                        kvm_s390_set_psw_cc(vcpu, 3);
+               up_read(&vcpu->kvm->arch.crypto.pqap_hook_rwsem);
                return ret;
        }
+       up_read(&vcpu->kvm->arch.crypto.pqap_hook_rwsem);
        /*
         * A vfio_driver must register a hook.
         * No hook means no driver to enable the SIE CRYCB and no queues.
index 1ac98f8..7efa386 100644 (file)
@@ -885,7 +885,7 @@ static int intel_vgpu_group_notifier(struct notifier_block *nb,
        return NOTIFY_OK;
 }
 
-static int intel_vgpu_open(struct mdev_device *mdev)
+static int intel_vgpu_open_device(struct mdev_device *mdev)
 {
        struct intel_vgpu *vgpu = mdev_get_drvdata(mdev);
        struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
@@ -1004,7 +1004,7 @@ static void __intel_vgpu_release(struct intel_vgpu *vgpu)
        vgpu->handle = 0;
 }
 
-static void intel_vgpu_release(struct mdev_device *mdev)
+static void intel_vgpu_close_device(struct mdev_device *mdev)
 {
        struct intel_vgpu *vgpu = mdev_get_drvdata(mdev);
 
@@ -1753,8 +1753,8 @@ static struct mdev_parent_ops intel_vgpu_ops = {
        .create                 = intel_vgpu_create,
        .remove                 = intel_vgpu_remove,
 
-       .open                   = intel_vgpu_open,
-       .release                = intel_vgpu_release,
+       .open_device            = intel_vgpu_open_device,
+       .close_device           = intel_vgpu_close_device,
 
        .read                   = intel_vgpu_read,
        .write                  = intel_vgpu_write,
index a061539..2761ab8 100644 (file)
@@ -136,7 +136,7 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv,
                                                    struct pci_dev *dev)
 {
        struct pci_dynid *dynid;
-       const struct pci_device_id *found_id = NULL;
+       const struct pci_device_id *found_id = NULL, *ids;
 
        /* When driver_override is set, only bind to the matching driver */
        if (dev->driver_override && strcmp(dev->driver_override, drv->name))
@@ -152,14 +152,28 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv,
        }
        spin_unlock(&drv->dynids.lock);
 
-       if (!found_id)
-               found_id = pci_match_id(drv->id_table, dev);
+       if (found_id)
+               return found_id;
 
-       /* driver_override will always match, send a dummy id */
-       if (!found_id && dev->driver_override)
-               found_id = &pci_device_id_any;
+       for (ids = drv->id_table; (found_id = pci_match_id(ids, dev));
+            ids = found_id + 1) {
+               /*
+                * The match table is split based on driver_override.
+                * In case override_only was set, enforce driver_override
+                * matching.
+                */
+               if (found_id->override_only) {
+                       if (dev->driver_override)
+                               return found_id;
+               } else {
+                       return found_id;
+               }
+       }
 
-       return found_id;
+       /* driver_override will always match, send a dummy id */
+       if (dev->driver_override)
+               return &pci_device_id_any;
+       return NULL;
 }
 
 /**
index c57d2a7..7f540ad 100644 (file)
@@ -159,7 +159,7 @@ static int vfio_ccw_mdev_remove(struct mdev_device *mdev)
        return 0;
 }
 
-static int vfio_ccw_mdev_open(struct mdev_device *mdev)
+static int vfio_ccw_mdev_open_device(struct mdev_device *mdev)
 {
        struct vfio_ccw_private *private =
                dev_get_drvdata(mdev_parent_dev(mdev));
@@ -194,7 +194,7 @@ out_unregister:
        return ret;
 }
 
-static void vfio_ccw_mdev_release(struct mdev_device *mdev)
+static void vfio_ccw_mdev_close_device(struct mdev_device *mdev)
 {
        struct vfio_ccw_private *private =
                dev_get_drvdata(mdev_parent_dev(mdev));
@@ -638,8 +638,8 @@ static const struct mdev_parent_ops vfio_ccw_mdev_ops = {
        .supported_type_groups  = mdev_type_groups,
        .create                 = vfio_ccw_mdev_create,
        .remove                 = vfio_ccw_mdev_remove,
-       .open                   = vfio_ccw_mdev_open,
-       .release                = vfio_ccw_mdev_release,
+       .open_device            = vfio_ccw_mdev_open_device,
+       .close_device           = vfio_ccw_mdev_close_device,
        .read                   = vfio_ccw_mdev_read,
        .write                  = vfio_ccw_mdev_write,
        .ioctl                  = vfio_ccw_mdev_ioctl,
index 67f1455..118939a 100644 (file)
@@ -24,8 +24,9 @@
 #define VFIO_AP_MDEV_TYPE_HWVIRT "passthrough"
 #define VFIO_AP_MDEV_NAME_HWVIRT "VFIO AP Passthrough Device"
 
-static int vfio_ap_mdev_reset_queues(struct mdev_device *mdev);
+static int vfio_ap_mdev_reset_queues(struct ap_matrix_mdev *matrix_mdev);
 static struct vfio_ap_queue *vfio_ap_find_queue(int apqn);
+static const struct vfio_device_ops vfio_ap_matrix_dev_ops;
 
 static int match_apqn(struct device *dev, const void *data)
 {
@@ -295,15 +296,6 @@ static int handle_pqap(struct kvm_vcpu *vcpu)
        matrix_mdev = container_of(vcpu->kvm->arch.crypto.pqap_hook,
                                   struct ap_matrix_mdev, pqap_hook);
 
-       /*
-        * If the KVM pointer is in the process of being set, wait until the
-        * process has completed.
-        */
-       wait_event_cmd(matrix_mdev->wait_for_kvm,
-                      !matrix_mdev->kvm_busy,
-                      mutex_unlock(&matrix_dev->lock),
-                      mutex_lock(&matrix_dev->lock));
-
        /* If the there is no guest using the mdev, there is nothing to do */
        if (!matrix_mdev->kvm)
                goto out_unlock;
@@ -336,45 +328,57 @@ static void vfio_ap_matrix_init(struct ap_config_info *info,
        matrix->adm_max = info->apxa ? info->Nd : 15;
 }
 
-static int vfio_ap_mdev_create(struct mdev_device *mdev)
+static int vfio_ap_mdev_probe(struct mdev_device *mdev)
 {
        struct ap_matrix_mdev *matrix_mdev;
+       int ret;
 
        if ((atomic_dec_if_positive(&matrix_dev->available_instances) < 0))
                return -EPERM;
 
        matrix_mdev = kzalloc(sizeof(*matrix_mdev), GFP_KERNEL);
        if (!matrix_mdev) {
-               atomic_inc(&matrix_dev->available_instances);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto err_dec_available;
        }
+       vfio_init_group_dev(&matrix_mdev->vdev, &mdev->dev,
+                           &vfio_ap_matrix_dev_ops);
 
        matrix_mdev->mdev = mdev;
        vfio_ap_matrix_init(&matrix_dev->info, &matrix_mdev->matrix);
-       init_waitqueue_head(&matrix_mdev->wait_for_kvm);
-       mdev_set_drvdata(mdev, matrix_mdev);
-       matrix_mdev->pqap_hook.hook = handle_pqap;
-       matrix_mdev->pqap_hook.owner = THIS_MODULE;
+       matrix_mdev->pqap_hook = handle_pqap;
        mutex_lock(&matrix_dev->lock);
        list_add(&matrix_mdev->node, &matrix_dev->mdev_list);
        mutex_unlock(&matrix_dev->lock);
 
+       ret = vfio_register_group_dev(&matrix_mdev->vdev);
+       if (ret)
+               goto err_list;
+       dev_set_drvdata(&mdev->dev, matrix_mdev);
        return 0;
+
+err_list:
+       mutex_lock(&matrix_dev->lock);
+       list_del(&matrix_mdev->node);
+       mutex_unlock(&matrix_dev->lock);
+       kfree(matrix_mdev);
+err_dec_available:
+       atomic_inc(&matrix_dev->available_instances);
+       return ret;
 }
 
-static int vfio_ap_mdev_remove(struct mdev_device *mdev)
+static void vfio_ap_mdev_remove(struct mdev_device *mdev)
 {
-       struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+       struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(&mdev->dev);
+
+       vfio_unregister_group_dev(&matrix_mdev->vdev);
 
        mutex_lock(&matrix_dev->lock);
-       vfio_ap_mdev_reset_queues(mdev);
+       vfio_ap_mdev_reset_queues(matrix_mdev);
        list_del(&matrix_mdev->node);
        kfree(matrix_mdev);
-       mdev_set_drvdata(mdev, NULL);
        atomic_inc(&matrix_dev->available_instances);
        mutex_unlock(&matrix_dev->lock);
-
-       return 0;
 }
 
 static ssize_t name_show(struct mdev_type *mtype,
@@ -614,16 +618,12 @@ static ssize_t assign_adapter_store(struct device *dev,
 {
        int ret;
        unsigned long apid;
-       struct mdev_device *mdev = mdev_from_dev(dev);
-       struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+       struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
 
        mutex_lock(&matrix_dev->lock);
 
-       /*
-        * If the KVM pointer is in flux or the guest is running, disallow
-        * un-assignment of adapter
-        */
-       if (matrix_mdev->kvm_busy || matrix_mdev->kvm) {
+       /* If the KVM guest is running, disallow assignment of adapter */
+       if (matrix_mdev->kvm) {
                ret = -EBUSY;
                goto done;
        }
@@ -685,16 +685,12 @@ static ssize_t unassign_adapter_store(struct device *dev,
 {
        int ret;
        unsigned long apid;
-       struct mdev_device *mdev = mdev_from_dev(dev);
-       struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+       struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
 
        mutex_lock(&matrix_dev->lock);
 
-       /*
-        * If the KVM pointer is in flux or the guest is running, disallow
-        * un-assignment of adapter
-        */
-       if (matrix_mdev->kvm_busy || matrix_mdev->kvm) {
+       /* If the KVM guest is running, disallow unassignment of adapter */
+       if (matrix_mdev->kvm) {
                ret = -EBUSY;
                goto done;
        }
@@ -773,17 +769,13 @@ static ssize_t assign_domain_store(struct device *dev,
 {
        int ret;
        unsigned long apqi;
-       struct mdev_device *mdev = mdev_from_dev(dev);
-       struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+       struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
        unsigned long max_apqi = matrix_mdev->matrix.aqm_max;
 
        mutex_lock(&matrix_dev->lock);
 
-       /*
-        * If the KVM pointer is in flux or the guest is running, disallow
-        * assignment of domain
-        */
-       if (matrix_mdev->kvm_busy || matrix_mdev->kvm) {
+       /* If the KVM guest is running, disallow assignment of domain */
+       if (matrix_mdev->kvm) {
                ret = -EBUSY;
                goto done;
        }
@@ -840,16 +832,12 @@ static ssize_t unassign_domain_store(struct device *dev,
 {
        int ret;
        unsigned long apqi;
-       struct mdev_device *mdev = mdev_from_dev(dev);
-       struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+       struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
 
        mutex_lock(&matrix_dev->lock);
 
-       /*
-        * If the KVM pointer is in flux or the guest is running, disallow
-        * un-assignment of domain
-        */
-       if (matrix_mdev->kvm_busy || matrix_mdev->kvm) {
+       /* If the KVM guest is running, disallow unassignment of domain */
+       if (matrix_mdev->kvm) {
                ret = -EBUSY;
                goto done;
        }
@@ -893,16 +881,12 @@ static ssize_t assign_control_domain_store(struct device *dev,
 {
        int ret;
        unsigned long id;
-       struct mdev_device *mdev = mdev_from_dev(dev);
-       struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+       struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
 
        mutex_lock(&matrix_dev->lock);
 
-       /*
-        * If the KVM pointer is in flux or the guest is running, disallow
-        * assignment of control domain.
-        */
-       if (matrix_mdev->kvm_busy || matrix_mdev->kvm) {
+       /* If the KVM guest is running, disallow assignment of control domain */
+       if (matrix_mdev->kvm) {
                ret = -EBUSY;
                goto done;
        }
@@ -949,17 +933,13 @@ static ssize_t unassign_control_domain_store(struct device *dev,
 {
        int ret;
        unsigned long domid;
-       struct mdev_device *mdev = mdev_from_dev(dev);
-       struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+       struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
        unsigned long max_domid =  matrix_mdev->matrix.adm_max;
 
        mutex_lock(&matrix_dev->lock);
 
-       /*
-        * If the KVM pointer is in flux or the guest is running, disallow
-        * un-assignment of control domain.
-        */
-       if (matrix_mdev->kvm_busy || matrix_mdev->kvm) {
+       /* If a KVM guest is running, disallow unassignment of control domain */
+       if (matrix_mdev->kvm) {
                ret = -EBUSY;
                goto done;
        }
@@ -988,8 +968,7 @@ static ssize_t control_domains_show(struct device *dev,
        int nchars = 0;
        int n;
        char *bufpos = buf;
-       struct mdev_device *mdev = mdev_from_dev(dev);
-       struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+       struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
        unsigned long max_domid = matrix_mdev->matrix.adm_max;
 
        mutex_lock(&matrix_dev->lock);
@@ -1007,8 +986,7 @@ static DEVICE_ATTR_RO(control_domains);
 static ssize_t matrix_show(struct device *dev, struct device_attribute *attr,
                           char *buf)
 {
-       struct mdev_device *mdev = mdev_from_dev(dev);
-       struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+       struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
        char *bufpos = buf;
        unsigned long apid;
        unsigned long apqi;
@@ -1098,23 +1076,30 @@ static int vfio_ap_mdev_set_kvm(struct ap_matrix_mdev *matrix_mdev,
        struct ap_matrix_mdev *m;
 
        if (kvm->arch.crypto.crycbd) {
+               down_write(&kvm->arch.crypto.pqap_hook_rwsem);
+               kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook;
+               up_write(&kvm->arch.crypto.pqap_hook_rwsem);
+
+               mutex_lock(&kvm->lock);
+               mutex_lock(&matrix_dev->lock);
+
                list_for_each_entry(m, &matrix_dev->mdev_list, node) {
-                       if (m != matrix_mdev && m->kvm == kvm)
+                       if (m != matrix_mdev && m->kvm == kvm) {
+                               mutex_unlock(&kvm->lock);
+                               mutex_unlock(&matrix_dev->lock);
                                return -EPERM;
+                       }
                }
 
                kvm_get_kvm(kvm);
-               matrix_mdev->kvm_busy = true;
-               mutex_unlock(&matrix_dev->lock);
+               matrix_mdev->kvm = kvm;
                kvm_arch_crypto_set_masks(kvm,
                                          matrix_mdev->matrix.apm,
                                          matrix_mdev->matrix.aqm,
                                          matrix_mdev->matrix.adm);
-               mutex_lock(&matrix_dev->lock);
-               kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook;
-               matrix_mdev->kvm = kvm;
-               matrix_mdev->kvm_busy = false;
-               wake_up_all(&matrix_mdev->wait_for_kvm);
+
+               mutex_unlock(&kvm->lock);
+               mutex_unlock(&matrix_dev->lock);
        }
 
        return 0;
@@ -1163,28 +1148,24 @@ static int vfio_ap_mdev_iommu_notifier(struct notifier_block *nb,
  * certain circumstances, will result in a circular lock dependency if this is
  * done under the @matrix_mdev->lock.
  */
-static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
+static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev,
+                                  struct kvm *kvm)
 {
-       /*
-        * If the KVM pointer is in the process of being set, wait until the
-        * process has completed.
-        */
-       wait_event_cmd(matrix_mdev->wait_for_kvm,
-                      !matrix_mdev->kvm_busy,
-                      mutex_unlock(&matrix_dev->lock),
-                      mutex_lock(&matrix_dev->lock));
+       if (kvm && kvm->arch.crypto.crycbd) {
+               down_write(&kvm->arch.crypto.pqap_hook_rwsem);
+               kvm->arch.crypto.pqap_hook = NULL;
+               up_write(&kvm->arch.crypto.pqap_hook_rwsem);
 
-       if (matrix_mdev->kvm) {
-               matrix_mdev->kvm_busy = true;
-               mutex_unlock(&matrix_dev->lock);
-               kvm_arch_crypto_clear_masks(matrix_mdev->kvm);
+               mutex_lock(&kvm->lock);
                mutex_lock(&matrix_dev->lock);
-               vfio_ap_mdev_reset_queues(matrix_mdev->mdev);
-               matrix_mdev->kvm->arch.crypto.pqap_hook = NULL;
-               kvm_put_kvm(matrix_mdev->kvm);
+
+               kvm_arch_crypto_clear_masks(kvm);
+               vfio_ap_mdev_reset_queues(matrix_mdev);
+               kvm_put_kvm(kvm);
                matrix_mdev->kvm = NULL;
-               matrix_mdev->kvm_busy = false;
-               wake_up_all(&matrix_mdev->wait_for_kvm);
+
+               mutex_unlock(&kvm->lock);
+               mutex_unlock(&matrix_dev->lock);
        }
 }
 
@@ -1197,16 +1178,13 @@ static int vfio_ap_mdev_group_notifier(struct notifier_block *nb,
        if (action != VFIO_GROUP_NOTIFY_SET_KVM)
                return NOTIFY_OK;
 
-       mutex_lock(&matrix_dev->lock);
        matrix_mdev = container_of(nb, struct ap_matrix_mdev, group_notifier);
 
        if (!data)
-               vfio_ap_mdev_unset_kvm(matrix_mdev);
+               vfio_ap_mdev_unset_kvm(matrix_mdev, matrix_mdev->kvm);
        else if (vfio_ap_mdev_set_kvm(matrix_mdev, data))
                notify_rc = NOTIFY_DONE;
 
-       mutex_unlock(&matrix_dev->lock);
-
        return notify_rc;
 }
 
@@ -1276,13 +1254,12 @@ free_resources:
        return ret;
 }
 
-static int vfio_ap_mdev_reset_queues(struct mdev_device *mdev)
+static int vfio_ap_mdev_reset_queues(struct ap_matrix_mdev *matrix_mdev)
 {
        int ret;
        int rc = 0;
        unsigned long apid, apqi;
        struct vfio_ap_queue *q;
-       struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
 
        for_each_set_bit_inv(apid, matrix_mdev->matrix.apm,
                             matrix_mdev->matrix.apm_max + 1) {
@@ -1303,52 +1280,45 @@ static int vfio_ap_mdev_reset_queues(struct mdev_device *mdev)
        return rc;
 }
 
-static int vfio_ap_mdev_open(struct mdev_device *mdev)
+static int vfio_ap_mdev_open_device(struct vfio_device *vdev)
 {
-       struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+       struct ap_matrix_mdev *matrix_mdev =
+               container_of(vdev, struct ap_matrix_mdev, vdev);
        unsigned long events;
        int ret;
 
-
-       if (!try_module_get(THIS_MODULE))
-               return -ENODEV;
-
        matrix_mdev->group_notifier.notifier_call = vfio_ap_mdev_group_notifier;
        events = VFIO_GROUP_NOTIFY_SET_KVM;
 
-       ret = vfio_register_notifier(mdev_dev(mdev), VFIO_GROUP_NOTIFY,
+       ret = vfio_register_notifier(vdev->dev, VFIO_GROUP_NOTIFY,
                                     &events, &matrix_mdev->group_notifier);
-       if (ret) {
-               module_put(THIS_MODULE);
+       if (ret)
                return ret;
-       }
 
        matrix_mdev->iommu_notifier.notifier_call = vfio_ap_mdev_iommu_notifier;
        events = VFIO_IOMMU_NOTIFY_DMA_UNMAP;
-       ret = vfio_register_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
+       ret = vfio_register_notifier(vdev->dev, VFIO_IOMMU_NOTIFY,
                                     &events, &matrix_mdev->iommu_notifier);
-       if (!ret)
-               return ret;
+       if (ret)
+               goto out_unregister_group;
+       return 0;
 
-       vfio_unregister_notifier(mdev_dev(mdev), VFIO_GROUP_NOTIFY,
+out_unregister_group:
+       vfio_unregister_notifier(vdev->dev, VFIO_GROUP_NOTIFY,
                                 &matrix_mdev->group_notifier);
-       module_put(THIS_MODULE);
        return ret;
 }
 
-static void vfio_ap_mdev_release(struct mdev_device *mdev)
+static void vfio_ap_mdev_close_device(struct vfio_device *vdev)
 {
-       struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
-
-       mutex_lock(&matrix_dev->lock);
-       vfio_ap_mdev_unset_kvm(matrix_mdev);
-       mutex_unlock(&matrix_dev->lock);
+       struct ap_matrix_mdev *matrix_mdev =
+               container_of(vdev, struct ap_matrix_mdev, vdev);
 
-       vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
+       vfio_unregister_notifier(vdev->dev, VFIO_IOMMU_NOTIFY,
                                 &matrix_mdev->iommu_notifier);
-       vfio_unregister_notifier(mdev_dev(mdev), VFIO_GROUP_NOTIFY,
+       vfio_unregister_notifier(vdev->dev, VFIO_GROUP_NOTIFY,
                                 &matrix_mdev->group_notifier);
-       module_put(THIS_MODULE);
+       vfio_ap_mdev_unset_kvm(matrix_mdev, matrix_mdev->kvm);
 }
 
 static int vfio_ap_mdev_get_device_info(unsigned long arg)
@@ -1371,11 +1341,12 @@ static int vfio_ap_mdev_get_device_info(unsigned long arg)
        return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
 }
 
-static ssize_t vfio_ap_mdev_ioctl(struct mdev_device *mdev,
+static ssize_t vfio_ap_mdev_ioctl(struct vfio_device *vdev,
                                    unsigned int cmd, unsigned long arg)
 {
+       struct ap_matrix_mdev *matrix_mdev =
+               container_of(vdev, struct ap_matrix_mdev, vdev);
        int ret;
-       struct ap_matrix_mdev *matrix_mdev;
 
        mutex_lock(&matrix_dev->lock);
        switch (cmd) {
@@ -1383,22 +1354,7 @@ static ssize_t vfio_ap_mdev_ioctl(struct mdev_device *mdev,
                ret = vfio_ap_mdev_get_device_info(arg);
                break;
        case VFIO_DEVICE_RESET:
-               matrix_mdev = mdev_get_drvdata(mdev);
-               if (WARN(!matrix_mdev, "Driver data missing from mdev!!")) {
-                       ret = -EINVAL;
-                       break;
-               }
-
-               /*
-                * If the KVM pointer is in the process of being set, wait until
-                * the process has completed.
-                */
-               wait_event_cmd(matrix_mdev->wait_for_kvm,
-                              !matrix_mdev->kvm_busy,
-                              mutex_unlock(&matrix_dev->lock),
-                              mutex_lock(&matrix_dev->lock));
-
-               ret = vfio_ap_mdev_reset_queues(mdev);
+               ret = vfio_ap_mdev_reset_queues(matrix_mdev);
                break;
        default:
                ret = -EOPNOTSUPP;
@@ -1409,25 +1365,51 @@ static ssize_t vfio_ap_mdev_ioctl(struct mdev_device *mdev,
        return ret;
 }
 
+static const struct vfio_device_ops vfio_ap_matrix_dev_ops = {
+       .open_device = vfio_ap_mdev_open_device,
+       .close_device = vfio_ap_mdev_close_device,
+       .ioctl = vfio_ap_mdev_ioctl,
+};
+
+static struct mdev_driver vfio_ap_matrix_driver = {
+       .driver = {
+               .name = "vfio_ap_mdev",
+               .owner = THIS_MODULE,
+               .mod_name = KBUILD_MODNAME,
+               .dev_groups = vfio_ap_mdev_attr_groups,
+       },
+       .probe = vfio_ap_mdev_probe,
+       .remove = vfio_ap_mdev_remove,
+};
+
 static const struct mdev_parent_ops vfio_ap_matrix_ops = {
        .owner                  = THIS_MODULE,
+       .device_driver          = &vfio_ap_matrix_driver,
        .supported_type_groups  = vfio_ap_mdev_type_groups,
-       .mdev_attr_groups       = vfio_ap_mdev_attr_groups,
-       .create                 = vfio_ap_mdev_create,
-       .remove                 = vfio_ap_mdev_remove,
-       .open                   = vfio_ap_mdev_open,
-       .release                = vfio_ap_mdev_release,
-       .ioctl                  = vfio_ap_mdev_ioctl,
 };
 
 int vfio_ap_mdev_register(void)
 {
+       int ret;
+
        atomic_set(&matrix_dev->available_instances, MAX_ZDEV_ENTRIES_EXT);
 
-       return mdev_register_device(&matrix_dev->device, &vfio_ap_matrix_ops);
+       ret = mdev_register_driver(&vfio_ap_matrix_driver);
+       if (ret)
+               return ret;
+
+       ret = mdev_register_device(&matrix_dev->device, &vfio_ap_matrix_ops);
+       if (ret)
+               goto err_driver;
+       return 0;
+
+err_driver:
+       mdev_unregister_driver(&vfio_ap_matrix_driver);
+       return ret;
 }
 
 void vfio_ap_mdev_unregister(void)
 {
        mdev_unregister_device(&matrix_dev->device);
+       mdev_unregister_driver(&vfio_ap_matrix_driver);
 }
index f82a639..77760e2 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/delay.h>
 #include <linux/mutex.h>
 #include <linux/kvm_host.h>
+#include <linux/vfio.h>
 
 #include "ap_bus.h"
 
@@ -79,14 +80,13 @@ struct ap_matrix {
  * @kvm:       the struct holding guest's state
  */
 struct ap_matrix_mdev {
+       struct vfio_device vdev;
        struct list_head node;
        struct ap_matrix matrix;
        struct notifier_block group_notifier;
        struct notifier_block iommu_notifier;
-       bool kvm_busy;
-       wait_queue_head_t wait_for_kvm;
        struct kvm *kvm;
-       struct kvm_s390_module_hook pqap_hook;
+       crypto_hook pqap_hook;
        struct mdev_device *mdev;
 };
 
index 67d0bf4..6130d00 100644 (file)
@@ -1,12 +1,22 @@
 # SPDX-License-Identifier: GPL-2.0-only
+menuconfig VFIO
+       tristate "VFIO Non-Privileged userspace driver framework"
+       select IOMMU_API
+       select VFIO_IOMMU_TYPE1 if MMU && (X86 || S390 || ARM || ARM64)
+       help
+         VFIO provides a framework for secure userspace device drivers.
+         See Documentation/driver-api/vfio.rst for more details.
+
+         If you don't know what to do here, say N.
+
+if VFIO
 config VFIO_IOMMU_TYPE1
        tristate
-       depends on VFIO
        default n
 
 config VFIO_IOMMU_SPAPR_TCE
        tristate
-       depends on VFIO && SPAPR_TCE_IOMMU
+       depends on SPAPR_TCE_IOMMU
        default VFIO
 
 config VFIO_SPAPR_EEH
@@ -16,22 +26,11 @@ config VFIO_SPAPR_EEH
 
 config VFIO_VIRQFD
        tristate
-       depends on VFIO && EVENTFD
+       select EVENTFD
        default n
 
-menuconfig VFIO
-       tristate "VFIO Non-Privileged userspace driver framework"
-       select IOMMU_API
-       select VFIO_IOMMU_TYPE1 if MMU && (X86 || S390 || ARM || ARM64)
-       help
-         VFIO provides a framework for secure userspace device drivers.
-         See Documentation/driver-api/vfio.rst for more details.
-
-         If you don't know what to do here, say N.
-
-menuconfig VFIO_NOIOMMU
+config VFIO_NOIOMMU
        bool "VFIO No-IOMMU support"
-       depends on VFIO
        help
          VFIO is built on the ability to isolate devices using the IOMMU.
          Only with an IOMMU can userspace access to DMA capable devices be
@@ -48,4 +47,6 @@ source "drivers/vfio/pci/Kconfig"
 source "drivers/vfio/platform/Kconfig"
 source "drivers/vfio/mdev/Kconfig"
 source "drivers/vfio/fsl-mc/Kconfig"
+endif
+
 source "virt/lib/Kconfig"
index b1a527d..597d338 100644 (file)
@@ -1,6 +1,7 @@
 config VFIO_FSL_MC
        tristate "VFIO support for QorIQ DPAA2 fsl-mc bus devices"
-       depends on VFIO && FSL_MC_BUS && EVENTFD
+       depends on FSL_MC_BUS
+       select EVENTFD
        help
          Driver to enable support for the VFIO QorIQ DPAA2 fsl-mc
          (Management Complex) devices. This is required to passthrough
index 90cad10..0ead91b 100644 (file)
 
 static struct fsl_mc_driver vfio_fsl_mc_driver;
 
-static DEFINE_MUTEX(reflck_lock);
-
-static void vfio_fsl_mc_reflck_get(struct vfio_fsl_mc_reflck *reflck)
-{
-       kref_get(&reflck->kref);
-}
-
-static void vfio_fsl_mc_reflck_release(struct kref *kref)
-{
-       struct vfio_fsl_mc_reflck *reflck = container_of(kref,
-                                                     struct vfio_fsl_mc_reflck,
-                                                     kref);
-
-       mutex_destroy(&reflck->lock);
-       kfree(reflck);
-       mutex_unlock(&reflck_lock);
-}
-
-static void vfio_fsl_mc_reflck_put(struct vfio_fsl_mc_reflck *reflck)
-{
-       kref_put_mutex(&reflck->kref, vfio_fsl_mc_reflck_release, &reflck_lock);
-}
-
-static struct vfio_fsl_mc_reflck *vfio_fsl_mc_reflck_alloc(void)
-{
-       struct vfio_fsl_mc_reflck *reflck;
-
-       reflck = kzalloc(sizeof(*reflck), GFP_KERNEL);
-       if (!reflck)
-               return ERR_PTR(-ENOMEM);
-
-       kref_init(&reflck->kref);
-       mutex_init(&reflck->lock);
-
-       return reflck;
-}
-
-static int vfio_fsl_mc_reflck_attach(struct vfio_fsl_mc_device *vdev)
-{
-       int ret = 0;
-
-       mutex_lock(&reflck_lock);
-       if (is_fsl_mc_bus_dprc(vdev->mc_dev)) {
-               vdev->reflck = vfio_fsl_mc_reflck_alloc();
-               ret = PTR_ERR_OR_ZERO(vdev->reflck);
-       } else {
-               struct device *mc_cont_dev = vdev->mc_dev->dev.parent;
-               struct vfio_device *device;
-               struct vfio_fsl_mc_device *cont_vdev;
-
-               device = vfio_device_get_from_dev(mc_cont_dev);
-               if (!device) {
-                       ret = -ENODEV;
-                       goto unlock;
-               }
-
-               cont_vdev =
-                       container_of(device, struct vfio_fsl_mc_device, vdev);
-               if (!cont_vdev || !cont_vdev->reflck) {
-                       vfio_device_put(device);
-                       ret = -ENODEV;
-                       goto unlock;
-               }
-               vfio_fsl_mc_reflck_get(cont_vdev->reflck);
-               vdev->reflck = cont_vdev->reflck;
-               vfio_device_put(device);
-       }
-
-unlock:
-       mutex_unlock(&reflck_lock);
-       return ret;
-}
-
-static int vfio_fsl_mc_regions_init(struct vfio_fsl_mc_device *vdev)
+static int vfio_fsl_mc_open_device(struct vfio_device *core_vdev)
 {
+       struct vfio_fsl_mc_device *vdev =
+               container_of(core_vdev, struct vfio_fsl_mc_device, vdev);
        struct fsl_mc_device *mc_dev = vdev->mc_dev;
        int count = mc_dev->obj_desc.region_count;
        int i;
@@ -136,58 +65,30 @@ static void vfio_fsl_mc_regions_cleanup(struct vfio_fsl_mc_device *vdev)
        kfree(vdev->regions);
 }
 
-static int vfio_fsl_mc_open(struct vfio_device *core_vdev)
-{
-       struct vfio_fsl_mc_device *vdev =
-               container_of(core_vdev, struct vfio_fsl_mc_device, vdev);
-       int ret = 0;
-
-       mutex_lock(&vdev->reflck->lock);
-       if (!vdev->refcnt) {
-               ret = vfio_fsl_mc_regions_init(vdev);
-               if (ret)
-                       goto out;
-       }
-       vdev->refcnt++;
-out:
-       mutex_unlock(&vdev->reflck->lock);
-
-       return ret;
-}
 
-static void vfio_fsl_mc_release(struct vfio_device *core_vdev)
+static void vfio_fsl_mc_close_device(struct vfio_device *core_vdev)
 {
        struct vfio_fsl_mc_device *vdev =
                container_of(core_vdev, struct vfio_fsl_mc_device, vdev);
+       struct fsl_mc_device *mc_dev = vdev->mc_dev;
+       struct device *cont_dev = fsl_mc_cont_dev(&mc_dev->dev);
+       struct fsl_mc_device *mc_cont = to_fsl_mc_device(cont_dev);
        int ret;
 
-       mutex_lock(&vdev->reflck->lock);
+       vfio_fsl_mc_regions_cleanup(vdev);
 
-       if (!(--vdev->refcnt)) {
-               struct fsl_mc_device *mc_dev = vdev->mc_dev;
-               struct device *cont_dev = fsl_mc_cont_dev(&mc_dev->dev);
-               struct fsl_mc_device *mc_cont = to_fsl_mc_device(cont_dev);
+       /* reset the device before cleaning up the interrupts */
+       ret = dprc_reset_container(mc_cont->mc_io, 0, mc_cont->mc_handle,
+                                  mc_cont->obj_desc.id,
+                                  DPRC_RESET_OPTION_NON_RECURSIVE);
 
-               vfio_fsl_mc_regions_cleanup(vdev);
+       if (WARN_ON(ret))
+               dev_warn(&mc_cont->dev,
+                        "VFIO_FLS_MC: reset device has failed (%d)\n", ret);
 
-               /* reset the device before cleaning up the interrupts */
-               ret = dprc_reset_container(mc_cont->mc_io, 0,
-                     mc_cont->mc_handle,
-                         mc_cont->obj_desc.id,
-                         DPRC_RESET_OPTION_NON_RECURSIVE);
+       vfio_fsl_mc_irqs_cleanup(vdev);
 
-               if (ret) {
-                       dev_warn(&mc_cont->dev, "VFIO_FLS_MC: reset device has failed (%d)\n",
-                                ret);
-                       WARN_ON(1);
-               }
-
-               vfio_fsl_mc_irqs_cleanup(vdev);
-
-               fsl_mc_cleanup_irq_pool(mc_cont);
-       }
-
-       mutex_unlock(&vdev->reflck->lock);
+       fsl_mc_cleanup_irq_pool(mc_cont);
 }
 
 static long vfio_fsl_mc_ioctl(struct vfio_device *core_vdev,
@@ -504,8 +405,8 @@ static int vfio_fsl_mc_mmap(struct vfio_device *core_vdev,
 
 static const struct vfio_device_ops vfio_fsl_mc_ops = {
        .name           = "vfio-fsl-mc",
-       .open           = vfio_fsl_mc_open,
-       .release        = vfio_fsl_mc_release,
+       .open_device    = vfio_fsl_mc_open_device,
+       .close_device   = vfio_fsl_mc_close_device,
        .ioctl          = vfio_fsl_mc_ioctl,
        .read           = vfio_fsl_mc_read,
        .write          = vfio_fsl_mc_write,
@@ -625,13 +526,16 @@ static int vfio_fsl_mc_probe(struct fsl_mc_device *mc_dev)
        vdev->mc_dev = mc_dev;
        mutex_init(&vdev->igate);
 
-       ret = vfio_fsl_mc_reflck_attach(vdev);
+       if (is_fsl_mc_bus_dprc(mc_dev))
+               ret = vfio_assign_device_set(&vdev->vdev, &mc_dev->dev);
+       else
+               ret = vfio_assign_device_set(&vdev->vdev, mc_dev->dev.parent);
        if (ret)
-               goto out_kfree;
+               goto out_uninit;
 
        ret = vfio_fsl_mc_init_device(vdev);
        if (ret)
-               goto out_reflck;
+               goto out_uninit;
 
        ret = vfio_register_group_dev(&vdev->vdev);
        if (ret) {
@@ -639,12 +543,6 @@ static int vfio_fsl_mc_probe(struct fsl_mc_device *mc_dev)
                goto out_device;
        }
 
-       /*
-        * This triggers recursion into vfio_fsl_mc_probe() on another device
-        * and the vfio_fsl_mc_reflck_attach() must succeed, which relies on the
-        * vfio_add_group_dev() above. It has no impact on this vdev, so it is
-        * safe to be after the vfio device is made live.
-        */
        ret = vfio_fsl_mc_scan_container(mc_dev);
        if (ret)
                goto out_group_dev;
@@ -655,9 +553,8 @@ out_group_dev:
        vfio_unregister_group_dev(&vdev->vdev);
 out_device:
        vfio_fsl_uninit_device(vdev);
-out_reflck:
-       vfio_fsl_mc_reflck_put(vdev->reflck);
-out_kfree:
+out_uninit:
+       vfio_uninit_group_dev(&vdev->vdev);
        kfree(vdev);
 out_group_put:
        vfio_iommu_group_put(group, dev);
@@ -674,8 +571,8 @@ static int vfio_fsl_mc_remove(struct fsl_mc_device *mc_dev)
 
        dprc_remove_devices(mc_dev, NULL, 0);
        vfio_fsl_uninit_device(vdev);
-       vfio_fsl_mc_reflck_put(vdev->reflck);
 
+       vfio_uninit_group_dev(&vdev->vdev);
        kfree(vdev);
        vfio_iommu_group_put(mc_dev->dev.iommu_group, dev);
 
index 0d9f300..77e5840 100644 (file)
@@ -120,7 +120,7 @@ static int vfio_fsl_mc_set_irq_trigger(struct vfio_fsl_mc_device *vdev,
        if (start != 0 || count != 1)
                return -EINVAL;
 
-       mutex_lock(&vdev->reflck->lock);
+       mutex_lock(&vdev->vdev.dev_set->lock);
        ret = fsl_mc_populate_irq_pool(mc_cont,
                        FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
        if (ret)
@@ -129,7 +129,7 @@ static int vfio_fsl_mc_set_irq_trigger(struct vfio_fsl_mc_device *vdev,
        ret = vfio_fsl_mc_irqs_allocate(vdev);
        if (ret)
                goto unlock;
-       mutex_unlock(&vdev->reflck->lock);
+       mutex_unlock(&vdev->vdev.dev_set->lock);
 
        if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
                s32 fd = *(s32 *)data;
@@ -154,7 +154,7 @@ static int vfio_fsl_mc_set_irq_trigger(struct vfio_fsl_mc_device *vdev,
        return 0;
 
 unlock:
-       mutex_unlock(&vdev->reflck->lock);
+       mutex_unlock(&vdev->vdev.dev_set->lock);
        return ret;
 
 }
index 89700e0..4ad63ec 100644 (file)
@@ -22,11 +22,6 @@ struct vfio_fsl_mc_irq {
        char            *name;
 };
 
-struct vfio_fsl_mc_reflck {
-       struct kref             kref;
-       struct mutex            lock;
-};
-
 struct vfio_fsl_mc_region {
        u32                     flags;
        u32                     type;
@@ -39,9 +34,7 @@ struct vfio_fsl_mc_device {
        struct vfio_device              vdev;
        struct fsl_mc_device            *mc_dev;
        struct notifier_block        nb;
-       int                             refcnt;
        struct vfio_fsl_mc_region       *regions;
-       struct vfio_fsl_mc_reflck   *reflck;
        struct mutex         igate;
        struct vfio_fsl_mc_irq      *mc_irqs;
 };
index 763c877..646dbed 100644 (file)
@@ -2,7 +2,6 @@
 
 config VFIO_MDEV
        tristate "Mediated device driver framework"
-       depends on VFIO
        default n
        help
          Provides a framework to virtualize devices.
index e4581ec..b314101 100644 (file)
@@ -138,10 +138,6 @@ int mdev_register_device(struct device *dev, const struct mdev_parent_ops *ops)
        if (!dev)
                return -EINVAL;
 
-       /* Not mandatory, but its absence could be a problem */
-       if (!ops->request)
-               dev_info(dev, "Driver cannot be asked to release device\n");
-
        mutex_lock(&parent_list_lock);
 
        /* Check for duplicate */
@@ -398,7 +394,7 @@ static void __exit mdev_exit(void)
        mdev_bus_unregister();
 }
 
-module_init(mdev_init)
+subsys_initcall(mdev_init)
 module_exit(mdev_exit)
 
 MODULE_VERSION(DRIVER_VERSION);
index 39ef748..7a98830 100644 (file)
 
 #include "mdev_private.h"
 
-static int vfio_mdev_open(struct vfio_device *core_vdev)
+static int vfio_mdev_open_device(struct vfio_device *core_vdev)
 {
        struct mdev_device *mdev = to_mdev_device(core_vdev->dev);
        struct mdev_parent *parent = mdev->type->parent;
 
-       if (unlikely(!parent->ops->open))
-               return -EINVAL;
+       if (unlikely(!parent->ops->open_device))
+               return 0;
 
-       return parent->ops->open(mdev);
+       return parent->ops->open_device(mdev);
 }
 
-static void vfio_mdev_release(struct vfio_device *core_vdev)
+static void vfio_mdev_close_device(struct vfio_device *core_vdev)
 {
        struct mdev_device *mdev = to_mdev_device(core_vdev->dev);
        struct mdev_parent *parent = mdev->type->parent;
 
-       if (likely(parent->ops->release))
-               parent->ops->release(mdev);
+       if (likely(parent->ops->close_device))
+               parent->ops->close_device(mdev);
 }
 
 static long vfio_mdev_unlocked_ioctl(struct vfio_device *core_vdev,
@@ -44,7 +44,7 @@ static long vfio_mdev_unlocked_ioctl(struct vfio_device *core_vdev,
        struct mdev_parent *parent = mdev->type->parent;
 
        if (unlikely(!parent->ops->ioctl))
-               return -EINVAL;
+               return 0;
 
        return parent->ops->ioctl(mdev, cmd, arg);
 }
@@ -100,8 +100,8 @@ static void vfio_mdev_request(struct vfio_device *core_vdev, unsigned int count)
 
 static const struct vfio_device_ops vfio_mdev_dev_ops = {
        .name           = "vfio-mdev",
-       .open           = vfio_mdev_open,
-       .release        = vfio_mdev_release,
+       .open_device    = vfio_mdev_open_device,
+       .close_device   = vfio_mdev_close_device,
        .ioctl          = vfio_mdev_unlocked_ioctl,
        .read           = vfio_mdev_read,
        .write          = vfio_mdev_write,
@@ -120,12 +120,16 @@ static int vfio_mdev_probe(struct mdev_device *mdev)
 
        vfio_init_group_dev(vdev, &mdev->dev, &vfio_mdev_dev_ops);
        ret = vfio_register_group_dev(vdev);
-       if (ret) {
-               kfree(vdev);
-               return ret;
-       }
+       if (ret)
+               goto out_uninit;
+
        dev_set_drvdata(&mdev->dev, vdev);
        return 0;
+
+out_uninit:
+       vfio_uninit_group_dev(vdev);
+       kfree(vdev);
+       return ret;
 }
 
 static void vfio_mdev_remove(struct mdev_device *mdev)
@@ -133,6 +137,7 @@ static void vfio_mdev_remove(struct mdev_device *mdev)
        struct vfio_device *vdev = dev_get_drvdata(&mdev->dev);
 
        vfio_unregister_group_dev(vdev);
+       vfio_uninit_group_dev(vdev);
        kfree(vdev);
 }
 
index 5e2e1b9..860424c 100644 (file)
@@ -1,19 +1,29 @@
 # SPDX-License-Identifier: GPL-2.0-only
-config VFIO_PCI
-       tristate "VFIO support for PCI devices"
-       depends on VFIO && PCI && EVENTFD
-       depends on MMU
+if PCI && MMU
+config VFIO_PCI_CORE
+       tristate
        select VFIO_VIRQFD
        select IRQ_BYPASS_MANAGER
+
+config VFIO_PCI_MMAP
+       def_bool y if !S390
+
+config VFIO_PCI_INTX
+       def_bool y if !S390
+
+config VFIO_PCI
+       tristate "Generic VFIO support for any PCI device"
+       select VFIO_PCI_CORE
        help
-         Support for the PCI VFIO bus driver.  This is required to make
-         use of PCI drivers using the VFIO framework.
+         Support for the generic PCI VFIO bus driver which can connect any
+         PCI device to the VFIO framework.
 
          If you don't know what to do here, say N.
 
+if VFIO_PCI
 config VFIO_PCI_VGA
-       bool "VFIO PCI support for VGA devices"
-       depends on VFIO_PCI && X86 && VGA_ARB
+       bool "Generic VFIO PCI support for VGA devices"
+       depends on X86 && VGA_ARB
        help
          Support for VGA extension to VFIO PCI.  This exposes an additional
          region on VGA devices for accessing legacy VGA addresses used by
@@ -21,17 +31,9 @@ config VFIO_PCI_VGA
 
          If you don't know what to do here, say N.
 
-config VFIO_PCI_MMAP
-       depends on VFIO_PCI
-       def_bool y if !S390
-
-config VFIO_PCI_INTX
-       depends on VFIO_PCI
-       def_bool y if !S390
-
 config VFIO_PCI_IGD
-       bool "VFIO PCI extensions for Intel graphics (GVT-d)"
-       depends on VFIO_PCI && X86
+       bool "Generic VFIO PCI extensions for Intel graphics (GVT-d)"
+       depends on X86
        default y
        help
          Support for Intel IGD specific extensions to enable direct
@@ -40,3 +42,5 @@ config VFIO_PCI_IGD
          and LPC bridge config space.
 
          To enable Intel IGD assignment through vfio-pci, say Y.
+endif
+endif
index 3ff4209..349d68d 100644 (file)
@@ -1,7 +1,9 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
-vfio-pci-y := vfio_pci.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o
-vfio-pci-$(CONFIG_VFIO_PCI_IGD) += vfio_pci_igd.o
-vfio-pci-$(CONFIG_S390) += vfio_pci_zdev.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_S390) += vfio_pci_zdev.o
+obj-$(CONFIG_VFIO_PCI_CORE) += vfio-pci-core.o
 
+vfio-pci-y := vfio_pci.o
+vfio-pci-$(CONFIG_VFIO_PCI_IGD) += vfio_pci_igd.o
 obj-$(CONFIG_VFIO_PCI) += vfio-pci.o
index cf27df8..a5ce92b 100644 (file)
@@ -1,5 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
+ * Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved
+ *
  * Copyright (C) 2012 Red Hat, Inc.  All rights reserved.
  *     Author: Alex Williamson <alex.williamson@redhat.com>
  *
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/notifier.h>
-#include <linux/pci.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/uaccess.h>
-#include <linux/vfio.h>
-#include <linux/vgaarb.h>
-#include <linux/nospec.h>
-#include <linux/sched/mm.h>
 
-#include "vfio_pci_private.h"
+#include <linux/vfio_pci_core.h>
 
-#define DRIVER_VERSION  "0.2"
 #define DRIVER_AUTHOR   "Alex Williamson <alex.williamson@redhat.com>"
 #define DRIVER_DESC     "VFIO PCI - User Level meta-driver"
 
@@ -64,15 +60,6 @@ static bool disable_denylist;
 module_param(disable_denylist, bool, 0444);
 MODULE_PARM_DESC(disable_denylist, "Disable use of device denylist. Disabling the denylist allows binding to devices with known errata that may lead to exploitable stability or security issues when accessed by untrusted users.");
 
-static inline bool vfio_vga_disabled(void)
-{
-#ifdef CONFIG_VFIO_PCI_VGA
-       return disable_vga;
-#else
-       return true;
-#endif
-}
-
 static bool vfio_pci_dev_in_denylist(struct pci_dev *pdev)
 {
        switch (pdev->vendor) {
@@ -111,2340 +98,175 @@ static bool vfio_pci_is_denylisted(struct pci_dev *pdev)
        return true;
 }
 
-/*
- * Our VGA arbiter participation is limited since we don't know anything
- * about the device itself.  However, if the device is the only VGA device
- * downstream of a bridge and VFIO VGA support is disabled, then we can
- * safely return legacy VGA IO and memory as not decoded since the user
- * has no way to get to it and routing can be disabled externally at the
- * bridge.
- */
-static unsigned int vfio_pci_set_decode(struct pci_dev *pdev, bool single_vga)
-{
-       struct pci_dev *tmp = NULL;
-       unsigned char max_busnr;
-       unsigned int decodes;
-
-       if (single_vga || !vfio_vga_disabled() || pci_is_root_bus(pdev->bus))
-               return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM |
-                      VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM;
-
-       max_busnr = pci_bus_max_busnr(pdev->bus);
-       decodes = VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
-
-       while ((tmp = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, tmp)) != NULL) {
-               if (tmp == pdev ||
-                   pci_domain_nr(tmp->bus) != pci_domain_nr(pdev->bus) ||
-                   pci_is_root_bus(tmp->bus))
-                       continue;
-
-               if (tmp->bus->number >= pdev->bus->number &&
-                   tmp->bus->number <= max_busnr) {
-                       pci_dev_put(tmp);
-                       decodes |= VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM;
-                       break;
-               }
-       }
-
-       return decodes;
-}
-
-static inline bool vfio_pci_is_vga(struct pci_dev *pdev)
-{
-       return (pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA;
-}
-
-static void vfio_pci_probe_mmaps(struct vfio_pci_device *vdev)
-{
-       struct resource *res;
-       int i;
-       struct vfio_pci_dummy_resource *dummy_res;
-
-       for (i = 0; i < PCI_STD_NUM_BARS; i++) {
-               int bar = i + PCI_STD_RESOURCES;
-
-               res = &vdev->pdev->resource[bar];
-
-               if (!IS_ENABLED(CONFIG_VFIO_PCI_MMAP))
-                       goto no_mmap;
-
-               if (!(res->flags & IORESOURCE_MEM))
-                       goto no_mmap;
-
-               /*
-                * The PCI core shouldn't set up a resource with a
-                * type but zero size. But there may be bugs that
-                * cause us to do that.
-                */
-               if (!resource_size(res))
-                       goto no_mmap;
-
-               if (resource_size(res) >= PAGE_SIZE) {
-                       vdev->bar_mmap_supported[bar] = true;
-                       continue;
-               }
-
-               if (!(res->start & ~PAGE_MASK)) {
-                       /*
-                        * Add a dummy resource to reserve the remainder
-                        * of the exclusive page in case that hot-add
-                        * device's bar is assigned into it.
-                        */
-                       dummy_res = kzalloc(sizeof(*dummy_res), GFP_KERNEL);
-                       if (dummy_res == NULL)
-                               goto no_mmap;
-
-                       dummy_res->resource.name = "vfio sub-page reserved";
-                       dummy_res->resource.start = res->end + 1;
-                       dummy_res->resource.end = res->start + PAGE_SIZE - 1;
-                       dummy_res->resource.flags = res->flags;
-                       if (request_resource(res->parent,
-                                               &dummy_res->resource)) {
-                               kfree(dummy_res);
-                               goto no_mmap;
-                       }
-                       dummy_res->index = bar;
-                       list_add(&dummy_res->res_next,
-                                       &vdev->dummy_resources_list);
-                       vdev->bar_mmap_supported[bar] = true;
-                       continue;
-               }
-               /*
-                * Here we don't handle the case when the BAR is not page
-                * aligned because we can't expect the BAR will be
-                * assigned into the same location in a page in guest
-                * when we passthrough the BAR. And it's hard to access
-                * this BAR in userspace because we have no way to get
-                * the BAR's location in a page.
-                */
-no_mmap:
-               vdev->bar_mmap_supported[bar] = false;
-       }
-}
-
-static void vfio_pci_try_bus_reset(struct vfio_pci_device *vdev);
-static void vfio_pci_disable(struct vfio_pci_device *vdev);
-static int vfio_pci_try_zap_and_vma_lock_cb(struct pci_dev *pdev, void *data);
-
-/*
- * INTx masking requires the ability to disable INTx signaling via PCI_COMMAND
- * _and_ the ability detect when the device is asserting INTx via PCI_STATUS.
- * If a device implements the former but not the latter we would typically
- * expect broken_intx_masking be set and require an exclusive interrupt.
- * However since we do have control of the device's ability to assert INTx,
- * we can instead pretend that the device does not implement INTx, virtualizing
- * the pin register to report zero and maintaining DisINTx set on the host.
- */
-static bool vfio_pci_nointx(struct pci_dev *pdev)
-{
-       switch (pdev->vendor) {
-       case PCI_VENDOR_ID_INTEL:
-               switch (pdev->device) {
-               /* All i40e (XL710/X710/XXV710) 10/20/25/40GbE NICs */
-               case 0x1572:
-               case 0x1574:
-               case 0x1580 ... 0x1581:
-               case 0x1583 ... 0x158b:
-               case 0x37d0 ... 0x37d2:
-               /* X550 */
-               case 0x1563:
-                       return true;
-               default:
-                       return false;
-               }
-       }
-
-       return false;
-}
-
-static void vfio_pci_probe_power_state(struct vfio_pci_device *vdev)
-{
-       struct pci_dev *pdev = vdev->pdev;
-       u16 pmcsr;
-
-       if (!pdev->pm_cap)
-               return;
-
-       pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &pmcsr);
-
-       vdev->needs_pm_restore = !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET);
-}
-
-/*
- * pci_set_power_state() wrapper handling devices which perform a soft reset on
- * D3->D0 transition.  Save state prior to D0/1/2->D3, stash it on the vdev,
- * restore when returned to D0.  Saved separately from pci_saved_state for use
- * by PM capability emulation and separately from pci_dev internal saved state
- * to avoid it being overwritten and consumed around other resets.
- */
-int vfio_pci_set_power_state(struct vfio_pci_device *vdev, pci_power_t state)
-{
-       struct pci_dev *pdev = vdev->pdev;
-       bool needs_restore = false, needs_save = false;
-       int ret;
-
-       if (vdev->needs_pm_restore) {
-               if (pdev->current_state < PCI_D3hot && state >= PCI_D3hot) {
-                       pci_save_state(pdev);
-                       needs_save = true;
-               }
-
-               if (pdev->current_state >= PCI_D3hot && state <= PCI_D0)
-                       needs_restore = true;
-       }
-
-       ret = pci_set_power_state(pdev, state);
-
-       if (!ret) {
-               /* D3 might be unsupported via quirk, skip unless in D3 */
-               if (needs_save && pdev->current_state >= PCI_D3hot) {
-                       vdev->pm_save = pci_store_saved_state(pdev);
-               } else if (needs_restore) {
-                       pci_load_and_free_saved_state(pdev, &vdev->pm_save);
-                       pci_restore_state(pdev);
-               }
-       }
-
-       return ret;
-}
-
-static int vfio_pci_enable(struct vfio_pci_device *vdev)
+static int vfio_pci_open_device(struct vfio_device *core_vdev)
 {
+       struct vfio_pci_core_device *vdev =
+               container_of(core_vdev, struct vfio_pci_core_device, vdev);
        struct pci_dev *pdev = vdev->pdev;
        int ret;
-       u16 cmd;
-       u8 msix_pos;
 
-       vfio_pci_set_power_state(vdev, PCI_D0);
-
-       /* Don't allow our initial saved state to include busmaster */
-       pci_clear_master(pdev);
-
-       ret = pci_enable_device(pdev);
+       ret = vfio_pci_core_enable(vdev);
        if (ret)
                return ret;
 
-       /* If reset fails because of the device lock, fail this path entirely */
-       ret = pci_try_reset_function(pdev);
-       if (ret == -EAGAIN) {
-               pci_disable_device(pdev);
-               return ret;
-       }
-
-       vdev->reset_works = !ret;
-       pci_save_state(pdev);
-       vdev->pci_saved_state = pci_store_saved_state(pdev);
-       if (!vdev->pci_saved_state)
-               pci_dbg(pdev, "%s: Couldn't store saved state\n", __func__);
-
-       if (likely(!nointxmask)) {
-               if (vfio_pci_nointx(pdev)) {
-                       pci_info(pdev, "Masking broken INTx support\n");
-                       vdev->nointx = true;
-                       pci_intx(pdev, 0);
-               } else
-                       vdev->pci_2_3 = pci_intx_mask_supported(pdev);
-       }
-
-       pci_read_config_word(pdev, PCI_COMMAND, &cmd);
-       if (vdev->pci_2_3 && (cmd & PCI_COMMAND_INTX_DISABLE)) {
-               cmd &= ~PCI_COMMAND_INTX_DISABLE;
-               pci_write_config_word(pdev, PCI_COMMAND, cmd);
-       }
-
-       ret = vfio_config_init(vdev);
-       if (ret) {
-               kfree(vdev->pci_saved_state);
-               vdev->pci_saved_state = NULL;
-               pci_disable_device(pdev);
-               return ret;
-       }
-
-       msix_pos = pdev->msix_cap;
-       if (msix_pos) {
-               u16 flags;
-               u32 table;
-
-               pci_read_config_word(pdev, msix_pos + PCI_MSIX_FLAGS, &flags);
-               pci_read_config_dword(pdev, msix_pos + PCI_MSIX_TABLE, &table);
-
-               vdev->msix_bar = table & PCI_MSIX_TABLE_BIR;
-               vdev->msix_offset = table & PCI_MSIX_TABLE_OFFSET;
-               vdev->msix_size = ((flags & PCI_MSIX_FLAGS_QSIZE) + 1) * 16;
-       } else
-               vdev->msix_bar = 0xFF;
-
-       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;
+                       vfio_pci_core_disable(vdev);
+                       return ret;
                }
        }
 
-       vfio_pci_probe_mmaps(vdev);
+       vfio_pci_core_finish_enable(vdev);
 
        return 0;
-
-disable_exit:
-       vfio_pci_disable(vdev);
-       return ret;
-}
-
-static void vfio_pci_disable(struct vfio_pci_device *vdev)
-{
-       struct pci_dev *pdev = vdev->pdev;
-       struct vfio_pci_dummy_resource *dummy_res, *tmp;
-       struct vfio_pci_ioeventfd *ioeventfd, *ioeventfd_tmp;
-       int i, bar;
-
-       /* Stop the device from further DMA */
-       pci_clear_master(pdev);
-
-       vfio_pci_set_irqs_ioctl(vdev, VFIO_IRQ_SET_DATA_NONE |
-                               VFIO_IRQ_SET_ACTION_TRIGGER,
-                               vdev->irq_type, 0, 0, NULL);
-
-       /* Device closed, don't need mutex here */
-       list_for_each_entry_safe(ioeventfd, ioeventfd_tmp,
-                                &vdev->ioeventfds_list, next) {
-               vfio_virqfd_disable(&ioeventfd->virqfd);
-               list_del(&ioeventfd->next);
-               kfree(ioeventfd);
-       }
-       vdev->ioeventfds_nr = 0;
-
-       vdev->virq_disabled = false;
-
-       for (i = 0; i < vdev->num_regions; i++)
-               vdev->region[i].ops->release(vdev, &vdev->region[i]);
-
-       vdev->num_regions = 0;
-       kfree(vdev->region);
-       vdev->region = NULL; /* don't krealloc a freed pointer */
-
-       vfio_config_free(vdev);
-
-       for (i = 0; i < PCI_STD_NUM_BARS; i++) {
-               bar = i + PCI_STD_RESOURCES;
-               if (!vdev->barmap[bar])
-                       continue;
-               pci_iounmap(pdev, vdev->barmap[bar]);
-               pci_release_selected_regions(pdev, 1 << bar);
-               vdev->barmap[bar] = NULL;
-       }
-
-       list_for_each_entry_safe(dummy_res, tmp,
-                                &vdev->dummy_resources_list, res_next) {
-               list_del(&dummy_res->res_next);
-               release_resource(&dummy_res->resource);
-               kfree(dummy_res);
-       }
-
-       vdev->needs_reset = true;
-
-       /*
-        * If we have saved state, restore it.  If we can reset the device,
-        * even better.  Resetting with current state seems better than
-        * nothing, but saving and restoring current state without reset
-        * is just busy work.
-        */
-       if (pci_load_and_free_saved_state(pdev, &vdev->pci_saved_state)) {
-               pci_info(pdev, "%s: Couldn't reload saved state\n", __func__);
-
-               if (!vdev->reset_works)
-                       goto out;
-
-               pci_save_state(pdev);
-       }
-
-       /*
-        * Disable INTx and MSI, presumably to avoid spurious interrupts
-        * during reset.  Stolen from pci_reset_function()
-        */
-       pci_write_config_word(pdev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
-
-       /*
-        * Try to get the locks ourselves to prevent a deadlock. The
-        * success of this is dependent on being able to lock the device,
-        * which is not always possible.
-        * We can not use the "try" reset interface here, which will
-        * overwrite the previously restored configuration information.
-        */
-       if (vdev->reset_works && pci_dev_trylock(pdev)) {
-               if (!__pci_reset_function_locked(pdev))
-                       vdev->needs_reset = false;
-               pci_dev_unlock(pdev);
-       }
-
-       pci_restore_state(pdev);
-out:
-       pci_disable_device(pdev);
-
-       vfio_pci_try_bus_reset(vdev);
-
-       if (!disable_idle_d3)
-               vfio_pci_set_power_state(vdev, PCI_D3hot);
 }
 
-static struct pci_driver vfio_pci_driver;
-
-static struct vfio_pci_device *get_pf_vdev(struct vfio_pci_device *vdev)
-{
-       struct pci_dev *physfn = pci_physfn(vdev->pdev);
-       struct vfio_device *pf_dev;
-
-       if (!vdev->pdev->is_virtfn)
-               return NULL;
-
-       pf_dev = vfio_device_get_from_dev(&physfn->dev);
-       if (!pf_dev)
-               return NULL;
-
-       if (pci_dev_driver(physfn) != &vfio_pci_driver) {
-               vfio_device_put(pf_dev);
-               return NULL;
-       }
-
-       return container_of(pf_dev, struct vfio_pci_device, vdev);
-}
-
-static void vfio_pci_vf_token_user_add(struct vfio_pci_device *vdev, int val)
-{
-       struct vfio_pci_device *pf_vdev = get_pf_vdev(vdev);
-
-       if (!pf_vdev)
-               return;
-
-       mutex_lock(&pf_vdev->vf_token->lock);
-       pf_vdev->vf_token->users += val;
-       WARN_ON(pf_vdev->vf_token->users < 0);
-       mutex_unlock(&pf_vdev->vf_token->lock);
-
-       vfio_device_put(&pf_vdev->vdev);
-}
+static const struct vfio_device_ops vfio_pci_ops = {
+       .name           = "vfio-pci",
+       .open_device    = vfio_pci_open_device,
+       .close_device   = vfio_pci_core_close_device,
+       .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 void vfio_pci_release(struct vfio_device *core_vdev)
+static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
-       struct vfio_pci_device *vdev =
-               container_of(core_vdev, struct vfio_pci_device, vdev);
-
-       mutex_lock(&vdev->reflck->lock);
-
-       if (!(--vdev->refcnt)) {
-               vfio_pci_vf_token_user_add(vdev, -1);
-               vfio_spapr_pci_eeh_release(vdev->pdev);
-               vfio_pci_disable(vdev);
-
-               mutex_lock(&vdev->igate);
-               if (vdev->err_trigger) {
-                       eventfd_ctx_put(vdev->err_trigger);
-                       vdev->err_trigger = NULL;
-               }
-               if (vdev->req_trigger) {
-                       eventfd_ctx_put(vdev->req_trigger);
-                       vdev->req_trigger = NULL;
-               }
-               mutex_unlock(&vdev->igate);
-       }
-
-       mutex_unlock(&vdev->reflck->lock);
-}
+       struct vfio_pci_core_device *vdev;
+       int ret;
 
-static int vfio_pci_open(struct vfio_device *core_vdev)
-{
-       struct vfio_pci_device *vdev =
-               container_of(core_vdev, struct vfio_pci_device, vdev);
-       int ret = 0;
+       if (vfio_pci_is_denylisted(pdev))
+               return -EINVAL;
 
-       mutex_lock(&vdev->reflck->lock);
+       vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+       if (!vdev)
+               return -ENOMEM;
+       vfio_pci_core_init_device(vdev, pdev, &vfio_pci_ops);
 
-       if (!vdev->refcnt) {
-               ret = vfio_pci_enable(vdev);
-               if (ret)
-                       goto error;
+       ret = vfio_pci_core_register_device(vdev);
+       if (ret)
+               goto out_free;
+       dev_set_drvdata(&pdev->dev, vdev);
+       return 0;
 
-               vfio_spapr_pci_eeh_open(vdev->pdev);
-               vfio_pci_vf_token_user_add(vdev, 1);
-       }
-       vdev->refcnt++;
-error:
-       mutex_unlock(&vdev->reflck->lock);
+out_free:
+       vfio_pci_core_uninit_device(vdev);
+       kfree(vdev);
        return ret;
 }
 
-static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type)
+static void vfio_pci_remove(struct pci_dev *pdev)
 {
-       if (irq_type == VFIO_PCI_INTX_IRQ_INDEX) {
-               u8 pin;
-
-               if (!IS_ENABLED(CONFIG_VFIO_PCI_INTX) ||
-                   vdev->nointx || vdev->pdev->is_virtfn)
-                       return 0;
-
-               pci_read_config_byte(vdev->pdev, PCI_INTERRUPT_PIN, &pin);
-
-               return pin ? 1 : 0;
-       } else if (irq_type == VFIO_PCI_MSI_IRQ_INDEX) {
-               u8 pos;
-               u16 flags;
-
-               pos = vdev->pdev->msi_cap;
-               if (pos) {
-                       pci_read_config_word(vdev->pdev,
-                                            pos + PCI_MSI_FLAGS, &flags);
-                       return 1 << ((flags & PCI_MSI_FLAGS_QMASK) >> 1);
-               }
-       } else if (irq_type == VFIO_PCI_MSIX_IRQ_INDEX) {
-               u8 pos;
-               u16 flags;
-
-               pos = vdev->pdev->msix_cap;
-               if (pos) {
-                       pci_read_config_word(vdev->pdev,
-                                            pos + PCI_MSIX_FLAGS, &flags);
-
-                       return (flags & PCI_MSIX_FLAGS_QSIZE) + 1;
-               }
-       } else if (irq_type == VFIO_PCI_ERR_IRQ_INDEX) {
-               if (pci_is_pcie(vdev->pdev))
-                       return 1;
-       } else if (irq_type == VFIO_PCI_REQ_IRQ_INDEX) {
-               return 1;
-       }
-
-       return 0;
-}
+       struct vfio_pci_core_device *vdev = dev_get_drvdata(&pdev->dev);
 
-static int vfio_pci_count_devs(struct pci_dev *pdev, void *data)
-{
-       (*(int *)data)++;
-       return 0;
+       vfio_pci_core_unregister_device(vdev);
+       vfio_pci_core_uninit_device(vdev);
+       kfree(vdev);
 }
 
-struct vfio_pci_fill_info {
-       int max;
-       int cur;
-       struct vfio_pci_dependent_device *devices;
-};
-
-static int vfio_pci_fill_devs(struct pci_dev *pdev, void *data)
+static int vfio_pci_sriov_configure(struct pci_dev *pdev, int nr_virtfn)
 {
-       struct vfio_pci_fill_info *fill = data;
-       struct iommu_group *iommu_group;
-
-       if (fill->cur == fill->max)
-               return -EAGAIN; /* Something changed, try again */
-
-       iommu_group = iommu_group_get(&pdev->dev);
-       if (!iommu_group)
-               return -EPERM; /* Cannot reset non-isolated devices */
+       if (!enable_sriov)
+               return -ENOENT;
 
-       fill->devices[fill->cur].group_id = iommu_group_id(iommu_group);
-       fill->devices[fill->cur].segment = pci_domain_nr(pdev->bus);
-       fill->devices[fill->cur].bus = pdev->bus->number;
-       fill->devices[fill->cur].devfn = pdev->devfn;
-       fill->cur++;
-       iommu_group_put(iommu_group);
-       return 0;
+       return vfio_pci_core_sriov_configure(pdev, nr_virtfn);
 }
 
-struct vfio_pci_group_entry {
-       struct vfio_group *group;
-       int id;
+static const struct pci_device_id vfio_pci_table[] = {
+       { PCI_DRIVER_OVERRIDE_DEVICE_VFIO(PCI_ANY_ID, PCI_ANY_ID) }, /* match all by default */
+       {}
 };
 
-struct vfio_pci_group_info {
-       int count;
-       struct vfio_pci_group_entry *groups;
+MODULE_DEVICE_TABLE(pci, vfio_pci_table);
+
+static struct pci_driver vfio_pci_driver = {
+       .name                   = "vfio-pci",
+       .id_table               = vfio_pci_table,
+       .probe                  = vfio_pci_probe,
+       .remove                 = vfio_pci_remove,
+       .sriov_configure        = vfio_pci_sriov_configure,
+       .err_handler            = &vfio_pci_core_err_handlers,
 };
 
-static int vfio_pci_validate_devs(struct pci_dev *pdev, void *data)
+static void __init vfio_pci_fill_ids(void)
 {
-       struct vfio_pci_group_info *info = data;
-       struct iommu_group *group;
-       int id, i;
+       char *p, *id;
+       int rc;
 
-       group = iommu_group_get(&pdev->dev);
-       if (!group)
-               return -EPERM;
+       /* no ids passed actually */
+       if (ids[0] == '\0')
+               return;
 
-       id = iommu_group_id(group);
+       /* add ids specified in the module parameter */
+       p = ids;
+       while ((id = strsep(&p, ","))) {
+               unsigned int vendor, device, subvendor = PCI_ANY_ID,
+                       subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
+               int fields;
 
-       for (i = 0; i < info->count; i++)
-               if (info->groups[i].id == id)
-                       break;
+               if (!strlen(id))
+                       continue;
 
-       iommu_group_put(group);
+               fields = sscanf(id, "%x:%x:%x:%x:%x:%x",
+                               &vendor, &device, &subvendor, &subdevice,
+                               &class, &class_mask);
 
-       return (i == info->count) ? -EINVAL : 0;
-}
+               if (fields < 2) {
+                       pr_warn("invalid id string \"%s\"\n", id);
+                       continue;
+               }
 
-static bool vfio_pci_dev_below_slot(struct pci_dev *pdev, struct pci_slot *slot)
-{
-       for (; pdev; pdev = pdev->bus->self)
-               if (pdev->bus == slot->bus)
-                       return (pdev->slot == slot);
-       return false;
+               rc = pci_add_dynid(&vfio_pci_driver, vendor, device,
+                                  subvendor, subdevice, class, class_mask, 0);
+               if (rc)
+                       pr_warn("failed to add dynamic id [%04x:%04x[%04x:%04x]] class %#08x/%08x (%d)\n",
+                               vendor, device, subvendor, subdevice,
+                               class, class_mask, rc);
+               else
+                       pr_info("add [%04x:%04x[%04x:%04x]] class %#08x/%08x\n",
+                               vendor, device, subvendor, subdevice,
+                               class, class_mask);
+       }
 }
 
-struct vfio_pci_walk_info {
-       int (*fn)(struct pci_dev *, void *data);
-       void *data;
-       struct pci_dev *pdev;
-       bool slot;
+static int __init vfio_pci_init(void)
+{
        int ret;
-};
+       bool is_disable_vga = true;
 
-static int vfio_pci_walk_wrapper(struct pci_dev *pdev, void *data)
-{
-       struct vfio_pci_walk_info *walk = data;
+#ifdef CONFIG_VFIO_PCI_VGA
+       is_disable_vga = disable_vga;
+#endif
 
-       if (!walk->slot || vfio_pci_dev_below_slot(pdev, walk->pdev->slot))
-               walk->ret = walk->fn(pdev, walk->data);
+       vfio_pci_core_set_params(nointxmask, is_disable_vga, disable_idle_d3);
 
-       return walk->ret;
-}
+       /* Register and scan for devices */
+       ret = pci_register_driver(&vfio_pci_driver);
+       if (ret)
+               return ret;
 
-static int vfio_pci_for_each_slot_or_bus(struct pci_dev *pdev,
-                                        int (*fn)(struct pci_dev *,
-                                                  void *data), void *data,
-                                        bool slot)
-{
-       struct vfio_pci_walk_info walk = {
-               .fn = fn, .data = data, .pdev = pdev, .slot = slot, .ret = 0,
-       };
+       vfio_pci_fill_ids();
 
-       pci_walk_bus(pdev->bus, vfio_pci_walk_wrapper, &walk);
+       if (disable_denylist)
+               pr_warn("device denylist disabled.\n");
 
-       return walk.ret;
+       return 0;
 }
+module_init(vfio_pci_init);
 
-static int msix_mmappable_cap(struct vfio_pci_device *vdev,
-                             struct vfio_info_cap *caps)
+static void __exit vfio_pci_cleanup(void)
 {
-       struct vfio_info_cap_header header = {
-               .id = VFIO_REGION_INFO_CAP_MSIX_MAPPABLE,
-               .version = 1
-       };
-
-       return vfio_info_add_capability(caps, &header, sizeof(header));
+       pci_unregister_driver(&vfio_pci_driver);
 }
-
-int vfio_pci_register_dev_region(struct vfio_pci_device *vdev,
-                                unsigned int type, unsigned int subtype,
-                                const struct vfio_pci_regops *ops,
-                                size_t size, u32 flags, void *data)
-{
-       struct vfio_pci_region *region;
-
-       region = krealloc(vdev->region,
-                         (vdev->num_regions + 1) * sizeof(*region),
-                         GFP_KERNEL);
-       if (!region)
-               return -ENOMEM;
-
-       vdev->region = region;
-       vdev->region[vdev->num_regions].type = type;
-       vdev->region[vdev->num_regions].subtype = subtype;
-       vdev->region[vdev->num_regions].ops = ops;
-       vdev->region[vdev->num_regions].size = size;
-       vdev->region[vdev->num_regions].flags = flags;
-       vdev->region[vdev->num_regions].data = data;
-
-       vdev->num_regions++;
-
-       return 0;
-}
-
-struct vfio_devices {
-       struct vfio_pci_device **devices;
-       int cur_index;
-       int max_index;
-};
-
-static long vfio_pci_ioctl(struct vfio_device *core_vdev,
-                          unsigned int cmd, unsigned long arg)
-{
-       struct vfio_pci_device *vdev =
-               container_of(core_vdev, struct vfio_pci_device, vdev);
-       unsigned long minsz;
-
-       if (cmd == VFIO_DEVICE_GET_INFO) {
-               struct vfio_device_info info;
-               struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
-               unsigned long capsz;
-               int ret;
-
-               minsz = offsetofend(struct vfio_device_info, num_irqs);
-
-               /* For backward compatibility, cannot require this */
-               capsz = offsetofend(struct vfio_iommu_type1_info, cap_offset);
-
-               if (copy_from_user(&info, (void __user *)arg, minsz))
-                       return -EFAULT;
-
-               if (info.argsz < minsz)
-                       return -EINVAL;
-
-               if (info.argsz >= capsz) {
-                       minsz = capsz;
-                       info.cap_offset = 0;
-               }
-
-               info.flags = VFIO_DEVICE_FLAGS_PCI;
-
-               if (vdev->reset_works)
-                       info.flags |= VFIO_DEVICE_FLAGS_RESET;
-
-               info.num_regions = VFIO_PCI_NUM_REGIONS + vdev->num_regions;
-               info.num_irqs = VFIO_PCI_NUM_IRQS;
-
-               ret = vfio_pci_info_zdev_add_caps(vdev, &caps);
-               if (ret && ret != -ENODEV) {
-                       pci_warn(vdev->pdev, "Failed to setup zPCI info capabilities\n");
-                       return ret;
-               }
-
-               if (caps.size) {
-                       info.flags |= VFIO_DEVICE_FLAGS_CAPS;
-                       if (info.argsz < sizeof(info) + caps.size) {
-                               info.argsz = sizeof(info) + caps.size;
-                       } else {
-                               vfio_info_cap_shift(&caps, sizeof(info));
-                               if (copy_to_user((void __user *)arg +
-                                                 sizeof(info), caps.buf,
-                                                 caps.size)) {
-                                       kfree(caps.buf);
-                                       return -EFAULT;
-                               }
-                               info.cap_offset = sizeof(info);
-                       }
-
-                       kfree(caps.buf);
-               }
-
-               return copy_to_user((void __user *)arg, &info, minsz) ?
-                       -EFAULT : 0;
-
-       } else if (cmd == VFIO_DEVICE_GET_REGION_INFO) {
-               struct pci_dev *pdev = vdev->pdev;
-               struct vfio_region_info info;
-               struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
-               int i, ret;
-
-               minsz = offsetofend(struct vfio_region_info, offset);
-
-               if (copy_from_user(&info, (void __user *)arg, minsz))
-                       return -EFAULT;
-
-               if (info.argsz < minsz)
-                       return -EINVAL;
-
-               switch (info.index) {
-               case VFIO_PCI_CONFIG_REGION_INDEX:
-                       info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
-                       info.size = pdev->cfg_size;
-                       info.flags = VFIO_REGION_INFO_FLAG_READ |
-                                    VFIO_REGION_INFO_FLAG_WRITE;
-                       break;
-               case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
-                       info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
-                       info.size = pci_resource_len(pdev, info.index);
-                       if (!info.size) {
-                               info.flags = 0;
-                               break;
-                       }
-
-                       info.flags = VFIO_REGION_INFO_FLAG_READ |
-                                    VFIO_REGION_INFO_FLAG_WRITE;
-                       if (vdev->bar_mmap_supported[info.index]) {
-                               info.flags |= VFIO_REGION_INFO_FLAG_MMAP;
-                               if (info.index == vdev->msix_bar) {
-                                       ret = msix_mmappable_cap(vdev, &caps);
-                                       if (ret)
-                                               return ret;
-                               }
-                       }
-
-                       break;
-               case VFIO_PCI_ROM_REGION_INDEX:
-               {
-                       void __iomem *io;
-                       size_t size;
-                       u16 cmd;
-
-                       info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
-                       info.flags = 0;
-
-                       /* Report the BAR size, not the ROM size */
-                       info.size = pci_resource_len(pdev, info.index);
-                       if (!info.size) {
-                               /* Shadow ROMs appear as PCI option ROMs */
-                               if (pdev->resource[PCI_ROM_RESOURCE].flags &
-                                                       IORESOURCE_ROM_SHADOW)
-                                       info.size = 0x20000;
-                               else
-                                       break;
-                       }
-
-                       /*
-                        * Is it really there?  Enable memory decode for
-                        * implicit access in pci_map_rom().
-                        */
-                       cmd = vfio_pci_memory_lock_and_enable(vdev);
-                       io = pci_map_rom(pdev, &size);
-                       if (io) {
-                               info.flags = VFIO_REGION_INFO_FLAG_READ;
-                               pci_unmap_rom(pdev, io);
-                       } else {
-                               info.size = 0;
-                       }
-                       vfio_pci_memory_unlock_and_restore(vdev, cmd);
-
-                       break;
-               }
-               case VFIO_PCI_VGA_REGION_INDEX:
-                       if (!vdev->has_vga)
-                               return -EINVAL;
-
-                       info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
-                       info.size = 0xc0000;
-                       info.flags = VFIO_REGION_INFO_FLAG_READ |
-                                    VFIO_REGION_INFO_FLAG_WRITE;
-
-                       break;
-               default:
-               {
-                       struct vfio_region_info_cap_type cap_type = {
-                                       .header.id = VFIO_REGION_INFO_CAP_TYPE,
-                                       .header.version = 1 };
-
-                       if (info.index >=
-                           VFIO_PCI_NUM_REGIONS + vdev->num_regions)
-                               return -EINVAL;
-                       info.index = array_index_nospec(info.index,
-                                                       VFIO_PCI_NUM_REGIONS +
-                                                       vdev->num_regions);
-
-                       i = info.index - VFIO_PCI_NUM_REGIONS;
-
-                       info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
-                       info.size = vdev->region[i].size;
-                       info.flags = vdev->region[i].flags;
-
-                       cap_type.type = vdev->region[i].type;
-                       cap_type.subtype = vdev->region[i].subtype;
-
-                       ret = vfio_info_add_capability(&caps, &cap_type.header,
-                                                      sizeof(cap_type));
-                       if (ret)
-                               return ret;
-
-                       if (vdev->region[i].ops->add_capability) {
-                               ret = vdev->region[i].ops->add_capability(vdev,
-                                               &vdev->region[i], &caps);
-                               if (ret)
-                                       return ret;
-                       }
-               }
-               }
-
-               if (caps.size) {
-                       info.flags |= VFIO_REGION_INFO_FLAG_CAPS;
-                       if (info.argsz < sizeof(info) + caps.size) {
-                               info.argsz = sizeof(info) + caps.size;
-                               info.cap_offset = 0;
-                       } else {
-                               vfio_info_cap_shift(&caps, sizeof(info));
-                               if (copy_to_user((void __user *)arg +
-                                                 sizeof(info), caps.buf,
-                                                 caps.size)) {
-                                       kfree(caps.buf);
-                                       return -EFAULT;
-                               }
-                               info.cap_offset = sizeof(info);
-                       }
-
-                       kfree(caps.buf);
-               }
-
-               return copy_to_user((void __user *)arg, &info, minsz) ?
-                       -EFAULT : 0;
-
-       } else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) {
-               struct vfio_irq_info info;
-
-               minsz = offsetofend(struct vfio_irq_info, count);
-
-               if (copy_from_user(&info, (void __user *)arg, minsz))
-                       return -EFAULT;
-
-               if (info.argsz < minsz || info.index >= VFIO_PCI_NUM_IRQS)
-                       return -EINVAL;
-
-               switch (info.index) {
-               case VFIO_PCI_INTX_IRQ_INDEX ... VFIO_PCI_MSIX_IRQ_INDEX:
-               case VFIO_PCI_REQ_IRQ_INDEX:
-                       break;
-               case VFIO_PCI_ERR_IRQ_INDEX:
-                       if (pci_is_pcie(vdev->pdev))
-                               break;
-                       fallthrough;
-               default:
-                       return -EINVAL;
-               }
-
-               info.flags = VFIO_IRQ_INFO_EVENTFD;
-
-               info.count = vfio_pci_get_irq_count(vdev, info.index);
-
-               if (info.index == VFIO_PCI_INTX_IRQ_INDEX)
-                       info.flags |= (VFIO_IRQ_INFO_MASKABLE |
-                                      VFIO_IRQ_INFO_AUTOMASKED);
-               else
-                       info.flags |= VFIO_IRQ_INFO_NORESIZE;
-
-               return copy_to_user((void __user *)arg, &info, minsz) ?
-                       -EFAULT : 0;
-
-       } else if (cmd == VFIO_DEVICE_SET_IRQS) {
-               struct vfio_irq_set hdr;
-               u8 *data = NULL;
-               int max, ret = 0;
-               size_t data_size = 0;
-
-               minsz = offsetofend(struct vfio_irq_set, count);
-
-               if (copy_from_user(&hdr, (void __user *)arg, minsz))
-                       return -EFAULT;
-
-               max = vfio_pci_get_irq_count(vdev, hdr.index);
-
-               ret = vfio_set_irqs_validate_and_prepare(&hdr, max,
-                                                VFIO_PCI_NUM_IRQS, &data_size);
-               if (ret)
-                       return ret;
-
-               if (data_size) {
-                       data = memdup_user((void __user *)(arg + minsz),
-                                           data_size);
-                       if (IS_ERR(data))
-                               return PTR_ERR(data);
-               }
-
-               mutex_lock(&vdev->igate);
-
-               ret = vfio_pci_set_irqs_ioctl(vdev, hdr.flags, hdr.index,
-                                             hdr.start, hdr.count, data);
-
-               mutex_unlock(&vdev->igate);
-               kfree(data);
-
-               return ret;
-
-       } else if (cmd == VFIO_DEVICE_RESET) {
-               int ret;
-
-               if (!vdev->reset_works)
-                       return -EINVAL;
-
-               vfio_pci_zap_and_down_write_memory_lock(vdev);
-               ret = pci_try_reset_function(vdev->pdev);
-               up_write(&vdev->memory_lock);
-
-               return ret;
-
-       } else if (cmd == VFIO_DEVICE_GET_PCI_HOT_RESET_INFO) {
-               struct vfio_pci_hot_reset_info hdr;
-               struct vfio_pci_fill_info fill = { 0 };
-               struct vfio_pci_dependent_device *devices = NULL;
-               bool slot = false;
-               int ret = 0;
-
-               minsz = offsetofend(struct vfio_pci_hot_reset_info, count);
-
-               if (copy_from_user(&hdr, (void __user *)arg, minsz))
-                       return -EFAULT;
-
-               if (hdr.argsz < minsz)
-                       return -EINVAL;
-
-               hdr.flags = 0;
-
-               /* Can we do a slot or bus reset or neither? */
-               if (!pci_probe_reset_slot(vdev->pdev->slot))
-                       slot = true;
-               else if (pci_probe_reset_bus(vdev->pdev->bus))
-                       return -ENODEV;
-
-               /* How many devices are affected? */
-               ret = vfio_pci_for_each_slot_or_bus(vdev->pdev,
-                                                   vfio_pci_count_devs,
-                                                   &fill.max, slot);
-               if (ret)
-                       return ret;
-
-               WARN_ON(!fill.max); /* Should always be at least one */
-
-               /*
-                * If there's enough space, fill it now, otherwise return
-                * -ENOSPC and the number of devices affected.
-                */
-               if (hdr.argsz < sizeof(hdr) + (fill.max * sizeof(*devices))) {
-                       ret = -ENOSPC;
-                       hdr.count = fill.max;
-                       goto reset_info_exit;
-               }
-
-               devices = kcalloc(fill.max, sizeof(*devices), GFP_KERNEL);
-               if (!devices)
-                       return -ENOMEM;
-
-               fill.devices = devices;
-
-               ret = vfio_pci_for_each_slot_or_bus(vdev->pdev,
-                                                   vfio_pci_fill_devs,
-                                                   &fill, slot);
-
-               /*
-                * If a device was removed between counting and filling,
-                * we may come up short of fill.max.  If a device was
-                * added, we'll have a return of -EAGAIN above.
-                */
-               if (!ret)
-                       hdr.count = fill.cur;
-
-reset_info_exit:
-               if (copy_to_user((void __user *)arg, &hdr, minsz))
-                       ret = -EFAULT;
-
-               if (!ret) {
-                       if (copy_to_user((void __user *)(arg + minsz), devices,
-                                        hdr.count * sizeof(*devices)))
-                               ret = -EFAULT;
-               }
-
-               kfree(devices);
-               return ret;
-
-       } else if (cmd == VFIO_DEVICE_PCI_HOT_RESET) {
-               struct vfio_pci_hot_reset hdr;
-               int32_t *group_fds;
-               struct vfio_pci_group_entry *groups;
-               struct vfio_pci_group_info info;
-               struct vfio_devices devs = { .cur_index = 0 };
-               bool slot = false;
-               int i, group_idx, mem_idx = 0, count = 0, ret = 0;
-
-               minsz = offsetofend(struct vfio_pci_hot_reset, count);
-
-               if (copy_from_user(&hdr, (void __user *)arg, minsz))
-                       return -EFAULT;
-
-               if (hdr.argsz < minsz || hdr.flags)
-                       return -EINVAL;
-
-               /* Can we do a slot or bus reset or neither? */
-               if (!pci_probe_reset_slot(vdev->pdev->slot))
-                       slot = true;
-               else if (pci_probe_reset_bus(vdev->pdev->bus))
-                       return -ENODEV;
-
-               /*
-                * We can't let userspace give us an arbitrarily large
-                * buffer to copy, so verify how many we think there
-                * could be.  Note groups can have multiple devices so
-                * one group per device is the max.
-                */
-               ret = vfio_pci_for_each_slot_or_bus(vdev->pdev,
-                                                   vfio_pci_count_devs,
-                                                   &count, slot);
-               if (ret)
-                       return ret;
-
-               /* Somewhere between 1 and count is OK */
-               if (!hdr.count || hdr.count > count)
-                       return -EINVAL;
-
-               group_fds = kcalloc(hdr.count, sizeof(*group_fds), GFP_KERNEL);
-               groups = kcalloc(hdr.count, sizeof(*groups), GFP_KERNEL);
-               if (!group_fds || !groups) {
-                       kfree(group_fds);
-                       kfree(groups);
-                       return -ENOMEM;
-               }
-
-               if (copy_from_user(group_fds, (void __user *)(arg + minsz),
-                                  hdr.count * sizeof(*group_fds))) {
-                       kfree(group_fds);
-                       kfree(groups);
-                       return -EFAULT;
-               }
-
-               /*
-                * For each group_fd, get the group through the vfio external
-                * user interface and store the group and iommu ID.  This
-                * ensures the group is held across the reset.
-                */
-               for (group_idx = 0; group_idx < hdr.count; group_idx++) {
-                       struct vfio_group *group;
-                       struct fd f = fdget(group_fds[group_idx]);
-                       if (!f.file) {
-                               ret = -EBADF;
-                               break;
-                       }
-
-                       group = vfio_group_get_external_user(f.file);
-                       fdput(f);
-                       if (IS_ERR(group)) {
-                               ret = PTR_ERR(group);
-                               break;
-                       }
-
-                       groups[group_idx].group = group;
-                       groups[group_idx].id =
-                                       vfio_external_user_iommu_id(group);
-               }
-
-               kfree(group_fds);
-
-               /* release reference to groups on error */
-               if (ret)
-                       goto hot_reset_release;
-
-               info.count = hdr.count;
-               info.groups = groups;
-
-               /*
-                * Test whether all the affected devices are contained
-                * by the set of groups provided by the user.
-                */
-               ret = vfio_pci_for_each_slot_or_bus(vdev->pdev,
-                                                   vfio_pci_validate_devs,
-                                                   &info, slot);
-               if (ret)
-                       goto hot_reset_release;
-
-               devs.max_index = count;
-               devs.devices = kcalloc(count, sizeof(struct vfio_device *),
-                                      GFP_KERNEL);
-               if (!devs.devices) {
-                       ret = -ENOMEM;
-                       goto hot_reset_release;
-               }
-
-               /*
-                * We need to get memory_lock for each device, but devices
-                * can share mmap_lock, therefore we need to zap and hold
-                * the vma_lock for each device, and only then get each
-                * memory_lock.
-                */
-               ret = vfio_pci_for_each_slot_or_bus(vdev->pdev,
-                                           vfio_pci_try_zap_and_vma_lock_cb,
-                                           &devs, slot);
-               if (ret)
-                       goto hot_reset_release;
-
-               for (; mem_idx < devs.cur_index; mem_idx++) {
-                       struct vfio_pci_device *tmp = devs.devices[mem_idx];
-
-                       ret = down_write_trylock(&tmp->memory_lock);
-                       if (!ret) {
-                               ret = -EBUSY;
-                               goto hot_reset_release;
-                       }
-                       mutex_unlock(&tmp->vma_lock);
-               }
-
-               /* User has access, do the reset */
-               ret = pci_reset_bus(vdev->pdev);
-
-hot_reset_release:
-               for (i = 0; i < devs.cur_index; i++) {
-                       struct vfio_pci_device *tmp = devs.devices[i];
-
-                       if (i < mem_idx)
-                               up_write(&tmp->memory_lock);
-                       else
-                               mutex_unlock(&tmp->vma_lock);
-                       vfio_device_put(&tmp->vdev);
-               }
-               kfree(devs.devices);
-
-               for (group_idx--; group_idx >= 0; group_idx--)
-                       vfio_group_put_external_user(groups[group_idx].group);
-
-               kfree(groups);
-               return ret;
-       } else if (cmd == VFIO_DEVICE_IOEVENTFD) {
-               struct vfio_device_ioeventfd ioeventfd;
-               int count;
-
-               minsz = offsetofend(struct vfio_device_ioeventfd, fd);
-
-               if (copy_from_user(&ioeventfd, (void __user *)arg, minsz))
-                       return -EFAULT;
-
-               if (ioeventfd.argsz < minsz)
-                       return -EINVAL;
-
-               if (ioeventfd.flags & ~VFIO_DEVICE_IOEVENTFD_SIZE_MASK)
-                       return -EINVAL;
-
-               count = ioeventfd.flags & VFIO_DEVICE_IOEVENTFD_SIZE_MASK;
-
-               if (hweight8(count) != 1 || ioeventfd.fd < -1)
-                       return -EINVAL;
-
-               return vfio_pci_ioeventfd(vdev, ioeventfd.offset,
-                                         ioeventfd.data, count, ioeventfd.fd);
-       } else if (cmd == VFIO_DEVICE_FEATURE) {
-               struct vfio_device_feature feature;
-               uuid_t uuid;
-
-               minsz = offsetofend(struct vfio_device_feature, flags);
-
-               if (copy_from_user(&feature, (void __user *)arg, minsz))
-                       return -EFAULT;
-
-               if (feature.argsz < minsz)
-                       return -EINVAL;
-
-               /* Check unknown flags */
-               if (feature.flags & ~(VFIO_DEVICE_FEATURE_MASK |
-                                     VFIO_DEVICE_FEATURE_SET |
-                                     VFIO_DEVICE_FEATURE_GET |
-                                     VFIO_DEVICE_FEATURE_PROBE))
-                       return -EINVAL;
-
-               /* GET & SET are mutually exclusive except with PROBE */
-               if (!(feature.flags & VFIO_DEVICE_FEATURE_PROBE) &&
-                   (feature.flags & VFIO_DEVICE_FEATURE_SET) &&
-                   (feature.flags & VFIO_DEVICE_FEATURE_GET))
-                       return -EINVAL;
-
-               switch (feature.flags & VFIO_DEVICE_FEATURE_MASK) {
-               case VFIO_DEVICE_FEATURE_PCI_VF_TOKEN:
-                       if (!vdev->vf_token)
-                               return -ENOTTY;
-
-                       /*
-                        * We do not support GET of the VF Token UUID as this
-                        * could expose the token of the previous device user.
-                        */
-                       if (feature.flags & VFIO_DEVICE_FEATURE_GET)
-                               return -EINVAL;
-
-                       if (feature.flags & VFIO_DEVICE_FEATURE_PROBE)
-                               return 0;
-
-                       /* Don't SET unless told to do so */
-                       if (!(feature.flags & VFIO_DEVICE_FEATURE_SET))
-                               return -EINVAL;
-
-                       if (feature.argsz < minsz + sizeof(uuid))
-                               return -EINVAL;
-
-                       if (copy_from_user(&uuid, (void __user *)(arg + minsz),
-                                          sizeof(uuid)))
-                               return -EFAULT;
-
-                       mutex_lock(&vdev->vf_token->lock);
-                       uuid_copy(&vdev->vf_token->uuid, &uuid);
-                       mutex_unlock(&vdev->vf_token->lock);
-
-                       return 0;
-               default:
-                       return -ENOTTY;
-               }
-       }
-
-       return -ENOTTY;
-}
-
-static ssize_t vfio_pci_rw(struct vfio_pci_device *vdev, char __user *buf,
-                          size_t count, loff_t *ppos, bool iswrite)
-{
-       unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
-
-       if (index >= VFIO_PCI_NUM_REGIONS + vdev->num_regions)
-               return -EINVAL;
-
-       switch (index) {
-       case VFIO_PCI_CONFIG_REGION_INDEX:
-               return vfio_pci_config_rw(vdev, buf, count, ppos, iswrite);
-
-       case VFIO_PCI_ROM_REGION_INDEX:
-               if (iswrite)
-                       return -EINVAL;
-               return vfio_pci_bar_rw(vdev, buf, count, ppos, false);
-
-       case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
-               return vfio_pci_bar_rw(vdev, buf, count, ppos, iswrite);
-
-       case VFIO_PCI_VGA_REGION_INDEX:
-               return vfio_pci_vga_rw(vdev, buf, count, ppos, iswrite);
-       default:
-               index -= VFIO_PCI_NUM_REGIONS;
-               return vdev->region[index].ops->rw(vdev, buf,
-                                                  count, ppos, iswrite);
-       }
-
-       return -EINVAL;
-}
-
-static ssize_t vfio_pci_read(struct vfio_device *core_vdev, char __user *buf,
-                            size_t count, loff_t *ppos)
-{
-       struct vfio_pci_device *vdev =
-               container_of(core_vdev, struct vfio_pci_device, vdev);
-
-       if (!count)
-               return 0;
-
-       return vfio_pci_rw(vdev, buf, count, ppos, false);
-}
-
-static ssize_t vfio_pci_write(struct vfio_device *core_vdev, const char __user *buf,
-                             size_t count, loff_t *ppos)
-{
-       struct vfio_pci_device *vdev =
-               container_of(core_vdev, struct vfio_pci_device, vdev);
-
-       if (!count)
-               return 0;
-
-       return vfio_pci_rw(vdev, (char __user *)buf, count, ppos, true);
-}
-
-/* Return 1 on zap and vma_lock acquired, 0 on contention (only with @try) */
-static int vfio_pci_zap_and_vma_lock(struct vfio_pci_device *vdev, bool try)
-{
-       struct vfio_pci_mmap_vma *mmap_vma, *tmp;
-
-       /*
-        * Lock ordering:
-        * vma_lock is nested under mmap_lock for vm_ops callback paths.
-        * The memory_lock semaphore is used by both code paths calling
-        * into this function to zap vmas and the vm_ops.fault callback
-        * to protect the memory enable state of the device.
-        *
-        * When zapping vmas we need to maintain the mmap_lock => vma_lock
-        * ordering, which requires using vma_lock to walk vma_list to
-        * acquire an mm, then dropping vma_lock to get the mmap_lock and
-        * reacquiring vma_lock.  This logic is derived from similar
-        * requirements in uverbs_user_mmap_disassociate().
-        *
-        * mmap_lock must always be the top-level lock when it is taken.
-        * Therefore we can only hold the memory_lock write lock when
-        * vma_list is empty, as we'd need to take mmap_lock to clear
-        * entries.  vma_list can only be guaranteed empty when holding
-        * vma_lock, thus memory_lock is nested under vma_lock.
-        *
-        * This enables the vm_ops.fault callback to acquire vma_lock,
-        * followed by memory_lock read lock, while already holding
-        * mmap_lock without risk of deadlock.
-        */
-       while (1) {
-               struct mm_struct *mm = NULL;
-
-               if (try) {
-                       if (!mutex_trylock(&vdev->vma_lock))
-                               return 0;
-               } else {
-                       mutex_lock(&vdev->vma_lock);
-               }
-               while (!list_empty(&vdev->vma_list)) {
-                       mmap_vma = list_first_entry(&vdev->vma_list,
-                                                   struct vfio_pci_mmap_vma,
-                                                   vma_next);
-                       mm = mmap_vma->vma->vm_mm;
-                       if (mmget_not_zero(mm))
-                               break;
-
-                       list_del(&mmap_vma->vma_next);
-                       kfree(mmap_vma);
-                       mm = NULL;
-               }
-               if (!mm)
-                       return 1;
-               mutex_unlock(&vdev->vma_lock);
-
-               if (try) {
-                       if (!mmap_read_trylock(mm)) {
-                               mmput(mm);
-                               return 0;
-                       }
-               } else {
-                       mmap_read_lock(mm);
-               }
-               if (try) {
-                       if (!mutex_trylock(&vdev->vma_lock)) {
-                               mmap_read_unlock(mm);
-                               mmput(mm);
-                               return 0;
-                       }
-               } else {
-                       mutex_lock(&vdev->vma_lock);
-               }
-               list_for_each_entry_safe(mmap_vma, tmp,
-                                        &vdev->vma_list, vma_next) {
-                       struct vm_area_struct *vma = mmap_vma->vma;
-
-                       if (vma->vm_mm != mm)
-                               continue;
-
-                       list_del(&mmap_vma->vma_next);
-                       kfree(mmap_vma);
-
-                       zap_vma_ptes(vma, vma->vm_start,
-                                    vma->vm_end - vma->vm_start);
-               }
-               mutex_unlock(&vdev->vma_lock);
-               mmap_read_unlock(mm);
-               mmput(mm);
-       }
-}
-
-void vfio_pci_zap_and_down_write_memory_lock(struct vfio_pci_device *vdev)
-{
-       vfio_pci_zap_and_vma_lock(vdev, false);
-       down_write(&vdev->memory_lock);
-       mutex_unlock(&vdev->vma_lock);
-}
-
-u16 vfio_pci_memory_lock_and_enable(struct vfio_pci_device *vdev)
-{
-       u16 cmd;
-
-       down_write(&vdev->memory_lock);
-       pci_read_config_word(vdev->pdev, PCI_COMMAND, &cmd);
-       if (!(cmd & PCI_COMMAND_MEMORY))
-               pci_write_config_word(vdev->pdev, PCI_COMMAND,
-                                     cmd | PCI_COMMAND_MEMORY);
-
-       return cmd;
-}
-
-void vfio_pci_memory_unlock_and_restore(struct vfio_pci_device *vdev, u16 cmd)
-{
-       pci_write_config_word(vdev->pdev, PCI_COMMAND, cmd);
-       up_write(&vdev->memory_lock);
-}
-
-/* Caller holds vma_lock */
-static int __vfio_pci_add_vma(struct vfio_pci_device *vdev,
-                             struct vm_area_struct *vma)
-{
-       struct vfio_pci_mmap_vma *mmap_vma;
-
-       mmap_vma = kmalloc(sizeof(*mmap_vma), GFP_KERNEL);
-       if (!mmap_vma)
-               return -ENOMEM;
-
-       mmap_vma->vma = vma;
-       list_add(&mmap_vma->vma_next, &vdev->vma_list);
-
-       return 0;
-}
-
-/*
- * Zap mmaps on open so that we can fault them in on access and therefore
- * our vma_list only tracks mappings accessed since last zap.
- */
-static void vfio_pci_mmap_open(struct vm_area_struct *vma)
-{
-       zap_vma_ptes(vma, vma->vm_start, vma->vm_end - vma->vm_start);
-}
-
-static void vfio_pci_mmap_close(struct vm_area_struct *vma)
-{
-       struct vfio_pci_device *vdev = vma->vm_private_data;
-       struct vfio_pci_mmap_vma *mmap_vma;
-
-       mutex_lock(&vdev->vma_lock);
-       list_for_each_entry(mmap_vma, &vdev->vma_list, vma_next) {
-               if (mmap_vma->vma == vma) {
-                       list_del(&mmap_vma->vma_next);
-                       kfree(mmap_vma);
-                       break;
-               }
-       }
-       mutex_unlock(&vdev->vma_lock);
-}
-
-static vm_fault_t vfio_pci_mmap_fault(struct vm_fault *vmf)
-{
-       struct vm_area_struct *vma = vmf->vma;
-       struct vfio_pci_device *vdev = vma->vm_private_data;
-       struct vfio_pci_mmap_vma *mmap_vma;
-       vm_fault_t ret = VM_FAULT_NOPAGE;
-
-       mutex_lock(&vdev->vma_lock);
-       down_read(&vdev->memory_lock);
-
-       if (!__vfio_pci_memory_enabled(vdev)) {
-               ret = VM_FAULT_SIGBUS;
-               goto up_out;
-       }
-
-       /*
-        * We populate the whole vma on fault, so we need to test whether
-        * the vma has already been mapped, such as for concurrent faults
-        * to the same vma.  io_remap_pfn_range() will trigger a BUG_ON if
-        * we ask it to fill the same range again.
-        */
-       list_for_each_entry(mmap_vma, &vdev->vma_list, vma_next) {
-               if (mmap_vma->vma == vma)
-                       goto up_out;
-       }
-
-       if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
-                              vma->vm_end - vma->vm_start,
-                              vma->vm_page_prot)) {
-               ret = VM_FAULT_SIGBUS;
-               zap_vma_ptes(vma, vma->vm_start, vma->vm_end - vma->vm_start);
-               goto up_out;
-       }
-
-       if (__vfio_pci_add_vma(vdev, vma)) {
-               ret = VM_FAULT_OOM;
-               zap_vma_ptes(vma, vma->vm_start, vma->vm_end - vma->vm_start);
-       }
-
-up_out:
-       up_read(&vdev->memory_lock);
-       mutex_unlock(&vdev->vma_lock);
-       return ret;
-}
-
-static const struct vm_operations_struct vfio_pci_mmap_ops = {
-       .open = vfio_pci_mmap_open,
-       .close = vfio_pci_mmap_close,
-       .fault = vfio_pci_mmap_fault,
-};
-
-static int vfio_pci_mmap(struct vfio_device *core_vdev, struct vm_area_struct *vma)
-{
-       struct vfio_pci_device *vdev =
-               container_of(core_vdev, struct vfio_pci_device, vdev);
-       struct pci_dev *pdev = vdev->pdev;
-       unsigned int index;
-       u64 phys_len, req_len, pgoff, req_start;
-       int ret;
-
-       index = vma->vm_pgoff >> (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT);
-
-       if (index >= VFIO_PCI_NUM_REGIONS + vdev->num_regions)
-               return -EINVAL;
-       if (vma->vm_end < vma->vm_start)
-               return -EINVAL;
-       if ((vma->vm_flags & VM_SHARED) == 0)
-               return -EINVAL;
-       if (index >= VFIO_PCI_NUM_REGIONS) {
-               int regnum = index - VFIO_PCI_NUM_REGIONS;
-               struct vfio_pci_region *region = vdev->region + regnum;
-
-               if (region->ops && region->ops->mmap &&
-                   (region->flags & VFIO_REGION_INFO_FLAG_MMAP))
-                       return region->ops->mmap(vdev, region, vma);
-               return -EINVAL;
-       }
-       if (index >= VFIO_PCI_ROM_REGION_INDEX)
-               return -EINVAL;
-       if (!vdev->bar_mmap_supported[index])
-               return -EINVAL;
-
-       phys_len = PAGE_ALIGN(pci_resource_len(pdev, index));
-       req_len = vma->vm_end - vma->vm_start;
-       pgoff = vma->vm_pgoff &
-               ((1U << (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
-       req_start = pgoff << PAGE_SHIFT;
-
-       if (req_start + req_len > phys_len)
-               return -EINVAL;
-
-       /*
-        * Even though we don't make use of the barmap for the mmap,
-        * we need to request the region and the barmap tracks that.
-        */
-       if (!vdev->barmap[index]) {
-               ret = pci_request_selected_regions(pdev,
-                                                  1 << index, "vfio-pci");
-               if (ret)
-                       return ret;
-
-               vdev->barmap[index] = pci_iomap(pdev, index, 0);
-               if (!vdev->barmap[index]) {
-                       pci_release_selected_regions(pdev, 1 << index);
-                       return -ENOMEM;
-               }
-       }
-
-       vma->vm_private_data = vdev;
-       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-       vma->vm_pgoff = (pci_resource_start(pdev, index) >> PAGE_SHIFT) + pgoff;
-
-       /*
-        * See remap_pfn_range(), called from vfio_pci_fault() but we can't
-        * change vm_flags within the fault handler.  Set them now.
-        */
-       vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
-       vma->vm_ops = &vfio_pci_mmap_ops;
-
-       return 0;
-}
-
-static void vfio_pci_request(struct vfio_device *core_vdev, unsigned int count)
-{
-       struct vfio_pci_device *vdev =
-               container_of(core_vdev, struct vfio_pci_device, vdev);
-       struct pci_dev *pdev = vdev->pdev;
-
-       mutex_lock(&vdev->igate);
-
-       if (vdev->req_trigger) {
-               if (!(count % 10))
-                       pci_notice_ratelimited(pdev,
-                               "Relaying device request to user (#%u)\n",
-                               count);
-               eventfd_signal(vdev->req_trigger, 1);
-       } else if (count == 0) {
-               pci_warn(pdev,
-                       "No device request channel registered, blocked until released by user\n");
-       }
-
-       mutex_unlock(&vdev->igate);
-}
-
-static int vfio_pci_validate_vf_token(struct vfio_pci_device *vdev,
-                                     bool vf_token, uuid_t *uuid)
-{
-       /*
-        * There's always some degree of trust or collaboration between SR-IOV
-        * PF and VFs, even if just that the PF hosts the SR-IOV capability and
-        * can disrupt VFs with a reset, but often the PF has more explicit
-        * access to deny service to the VF or access data passed through the
-        * VF.  We therefore require an opt-in via a shared VF token (UUID) to
-        * represent this trust.  This both prevents that a VF driver might
-        * assume the PF driver is a trusted, in-kernel driver, and also that
-        * a PF driver might be replaced with a rogue driver, unknown to in-use
-        * VF drivers.
-        *
-        * Therefore when presented with a VF, if the PF is a vfio device and
-        * it is bound to the vfio-pci driver, the user needs to provide a VF
-        * token to access the device, in the form of appending a vf_token to
-        * the device name, for example:
-        *
-        * "0000:04:10.0 vf_token=bd8d9d2b-5a5f-4f5a-a211-f591514ba1f3"
-        *
-        * When presented with a PF which has VFs in use, the user must also
-        * provide the current VF token to prove collaboration with existing
-        * VF users.  If VFs are not in use, the VF token provided for the PF
-        * device will act to set the VF token.
-        *
-        * If the VF token is provided but unused, an error is generated.
-        */
-       if (!vdev->pdev->is_virtfn && !vdev->vf_token && !vf_token)
-               return 0; /* No VF token provided or required */
-
-       if (vdev->pdev->is_virtfn) {
-               struct vfio_pci_device *pf_vdev = get_pf_vdev(vdev);
-               bool match;
-
-               if (!pf_vdev) {
-                       if (!vf_token)
-                               return 0; /* PF is not vfio-pci, no VF token */
-
-                       pci_info_ratelimited(vdev->pdev,
-                               "VF token incorrectly provided, PF not bound to vfio-pci\n");
-                       return -EINVAL;
-               }
-
-               if (!vf_token) {
-                       vfio_device_put(&pf_vdev->vdev);
-                       pci_info_ratelimited(vdev->pdev,
-                               "VF token required to access device\n");
-                       return -EACCES;
-               }
-
-               mutex_lock(&pf_vdev->vf_token->lock);
-               match = uuid_equal(uuid, &pf_vdev->vf_token->uuid);
-               mutex_unlock(&pf_vdev->vf_token->lock);
-
-               vfio_device_put(&pf_vdev->vdev);
-
-               if (!match) {
-                       pci_info_ratelimited(vdev->pdev,
-                               "Incorrect VF token provided for device\n");
-                       return -EACCES;
-               }
-       } else if (vdev->vf_token) {
-               mutex_lock(&vdev->vf_token->lock);
-               if (vdev->vf_token->users) {
-                       if (!vf_token) {
-                               mutex_unlock(&vdev->vf_token->lock);
-                               pci_info_ratelimited(vdev->pdev,
-                                       "VF token required to access device\n");
-                               return -EACCES;
-                       }
-
-                       if (!uuid_equal(uuid, &vdev->vf_token->uuid)) {
-                               mutex_unlock(&vdev->vf_token->lock);
-                               pci_info_ratelimited(vdev->pdev,
-                                       "Incorrect VF token provided for device\n");
-                               return -EACCES;
-                       }
-               } else if (vf_token) {
-                       uuid_copy(&vdev->vf_token->uuid, uuid);
-               }
-
-               mutex_unlock(&vdev->vf_token->lock);
-       } else if (vf_token) {
-               pci_info_ratelimited(vdev->pdev,
-                       "VF token incorrectly provided, not a PF or VF\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-#define VF_TOKEN_ARG "vf_token="
-
-static int vfio_pci_match(struct vfio_device *core_vdev, char *buf)
-{
-       struct vfio_pci_device *vdev =
-               container_of(core_vdev, struct vfio_pci_device, vdev);
-       bool vf_token = false;
-       uuid_t uuid;
-       int ret;
-
-       if (strncmp(pci_name(vdev->pdev), buf, strlen(pci_name(vdev->pdev))))
-               return 0; /* No match */
-
-       if (strlen(buf) > strlen(pci_name(vdev->pdev))) {
-               buf += strlen(pci_name(vdev->pdev));
-
-               if (*buf != ' ')
-                       return 0; /* No match: non-whitespace after name */
-
-               while (*buf) {
-                       if (*buf == ' ') {
-                               buf++;
-                               continue;
-                       }
-
-                       if (!vf_token && !strncmp(buf, VF_TOKEN_ARG,
-                                                 strlen(VF_TOKEN_ARG))) {
-                               buf += strlen(VF_TOKEN_ARG);
-
-                               if (strlen(buf) < UUID_STRING_LEN)
-                                       return -EINVAL;
-
-                               ret = uuid_parse(buf, &uuid);
-                               if (ret)
-                                       return ret;
-
-                               vf_token = true;
-                               buf += UUID_STRING_LEN;
-                       } else {
-                               /* Unknown/duplicate option */
-                               return -EINVAL;
-                       }
-               }
-       }
-
-       ret = vfio_pci_validate_vf_token(vdev, vf_token, &uuid);
-       if (ret)
-               return ret;
-
-       return 1; /* Match */
-}
-
-static const struct vfio_device_ops vfio_pci_ops = {
-       .name           = "vfio-pci",
-       .open           = vfio_pci_open,
-       .release        = vfio_pci_release,
-       .ioctl          = vfio_pci_ioctl,
-       .read           = vfio_pci_read,
-       .write          = vfio_pci_write,
-       .mmap           = vfio_pci_mmap,
-       .request        = vfio_pci_request,
-       .match          = vfio_pci_match,
-};
-
-static int vfio_pci_reflck_attach(struct vfio_pci_device *vdev);
-static void vfio_pci_reflck_put(struct vfio_pci_reflck *reflck);
-
-static int vfio_pci_bus_notifier(struct notifier_block *nb,
-                                unsigned long action, void *data)
-{
-       struct vfio_pci_device *vdev = container_of(nb,
-                                                   struct vfio_pci_device, nb);
-       struct device *dev = data;
-       struct pci_dev *pdev = to_pci_dev(dev);
-       struct pci_dev *physfn = pci_physfn(pdev);
-
-       if (action == BUS_NOTIFY_ADD_DEVICE &&
-           pdev->is_virtfn && physfn == vdev->pdev) {
-               pci_info(vdev->pdev, "Captured SR-IOV VF %s driver_override\n",
-                        pci_name(pdev));
-               pdev->driver_override = kasprintf(GFP_KERNEL, "%s",
-                                                 vfio_pci_ops.name);
-       } else if (action == BUS_NOTIFY_BOUND_DRIVER &&
-                  pdev->is_virtfn && physfn == vdev->pdev) {
-               struct pci_driver *drv = pci_dev_driver(pdev);
-
-               if (drv && drv != &vfio_pci_driver)
-                       pci_warn(vdev->pdev,
-                                "VF %s bound to driver %s while PF bound to vfio-pci\n",
-                                pci_name(pdev), drv->name);
-       }
-
-       return 0;
-}
-
-static int vfio_pci_vf_init(struct vfio_pci_device *vdev)
-{
-       struct pci_dev *pdev = vdev->pdev;
-       int ret;
-
-       if (!pdev->is_physfn)
-               return 0;
-
-       vdev->vf_token = kzalloc(sizeof(*vdev->vf_token), GFP_KERNEL);
-       if (!vdev->vf_token)
-               return -ENOMEM;
-
-       mutex_init(&vdev->vf_token->lock);
-       uuid_gen(&vdev->vf_token->uuid);
-
-       vdev->nb.notifier_call = vfio_pci_bus_notifier;
-       ret = bus_register_notifier(&pci_bus_type, &vdev->nb);
-       if (ret) {
-               kfree(vdev->vf_token);
-               return ret;
-       }
-       return 0;
-}
-
-static void vfio_pci_vf_uninit(struct vfio_pci_device *vdev)
-{
-       if (!vdev->vf_token)
-               return;
-
-       bus_unregister_notifier(&pci_bus_type, &vdev->nb);
-       WARN_ON(vdev->vf_token->users);
-       mutex_destroy(&vdev->vf_token->lock);
-       kfree(vdev->vf_token);
-}
-
-static int vfio_pci_vga_init(struct vfio_pci_device *vdev)
-{
-       struct pci_dev *pdev = vdev->pdev;
-       int ret;
-
-       if (!vfio_pci_is_vga(pdev))
-               return 0;
-
-       ret = vga_client_register(pdev, vfio_pci_set_decode);
-       if (ret)
-               return ret;
-       vga_set_legacy_decoding(pdev, vfio_pci_set_decode(pdev, false));
-       return 0;
-}
-
-static void vfio_pci_vga_uninit(struct vfio_pci_device *vdev)
-{
-       struct pci_dev *pdev = vdev->pdev;
-
-       if (!vfio_pci_is_vga(pdev))
-               return;
-       vga_client_unregister(pdev);
-       vga_set_legacy_decoding(pdev, VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM |
-                                             VGA_RSRC_LEGACY_IO |
-                                             VGA_RSRC_LEGACY_MEM);
-}
-
-static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
-       struct vfio_pci_device *vdev;
-       struct iommu_group *group;
-       int ret;
-
-       if (vfio_pci_is_denylisted(pdev))
-               return -EINVAL;
-
-       if (pdev->hdr_type != PCI_HEADER_TYPE_NORMAL)
-               return -EINVAL;
-
-       /*
-        * Prevent binding to PFs with VFs enabled, the VFs might be in use
-        * by the host or other users.  We cannot capture the VFs if they
-        * already exist, nor can we track VF users.  Disabling SR-IOV here
-        * would initiate removing the VFs, which would unbind the driver,
-        * which is prone to blocking if that VF is also in use by vfio-pci.
-        * Just reject these PFs and let the user sort it out.
-        */
-       if (pci_num_vf(pdev)) {
-               pci_warn(pdev, "Cannot bind to PF with SR-IOV enabled\n");
-               return -EBUSY;
-       }
-
-       group = vfio_iommu_group_get(&pdev->dev);
-       if (!group)
-               return -EINVAL;
-
-       vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
-       if (!vdev) {
-               ret = -ENOMEM;
-               goto out_group_put;
-       }
-
-       vfio_init_group_dev(&vdev->vdev, &pdev->dev, &vfio_pci_ops);
-       vdev->pdev = pdev;
-       vdev->irq_type = VFIO_PCI_NUM_IRQS;
-       mutex_init(&vdev->igate);
-       spin_lock_init(&vdev->irqlock);
-       mutex_init(&vdev->ioeventfds_lock);
-       INIT_LIST_HEAD(&vdev->dummy_resources_list);
-       INIT_LIST_HEAD(&vdev->ioeventfds_list);
-       mutex_init(&vdev->vma_lock);
-       INIT_LIST_HEAD(&vdev->vma_list);
-       init_rwsem(&vdev->memory_lock);
-
-       ret = vfio_pci_reflck_attach(vdev);
-       if (ret)
-               goto out_free;
-       ret = vfio_pci_vf_init(vdev);
-       if (ret)
-               goto out_reflck;
-       ret = vfio_pci_vga_init(vdev);
-       if (ret)
-               goto out_vf;
-
-       vfio_pci_probe_power_state(vdev);
-
-       if (!disable_idle_d3) {
-               /*
-                * pci-core sets the device power state to an unknown value at
-                * bootup and after being removed from a driver.  The only
-                * transition it allows from this unknown state is to D0, which
-                * typically happens when a driver calls pci_enable_device().
-                * We're not ready to enable the device yet, but we do want to
-                * be able to get to D3.  Therefore first do a D0 transition
-                * before going to D3.
-                */
-               vfio_pci_set_power_state(vdev, PCI_D0);
-               vfio_pci_set_power_state(vdev, PCI_D3hot);
-       }
-
-       ret = vfio_register_group_dev(&vdev->vdev);
-       if (ret)
-               goto out_power;
-       dev_set_drvdata(&pdev->dev, vdev);
-       return 0;
-
-out_power:
-       if (!disable_idle_d3)
-               vfio_pci_set_power_state(vdev, PCI_D0);
-out_vf:
-       vfio_pci_vf_uninit(vdev);
-out_reflck:
-       vfio_pci_reflck_put(vdev->reflck);
-out_free:
-       kfree(vdev->pm_save);
-       kfree(vdev);
-out_group_put:
-       vfio_iommu_group_put(group, &pdev->dev);
-       return ret;
-}
-
-static void vfio_pci_remove(struct pci_dev *pdev)
-{
-       struct vfio_pci_device *vdev = dev_get_drvdata(&pdev->dev);
-
-       pci_disable_sriov(pdev);
-
-       vfio_unregister_group_dev(&vdev->vdev);
-
-       vfio_pci_vf_uninit(vdev);
-       vfio_pci_reflck_put(vdev->reflck);
-       vfio_pci_vga_uninit(vdev);
-
-       vfio_iommu_group_put(pdev->dev.iommu_group, &pdev->dev);
-
-       if (!disable_idle_d3)
-               vfio_pci_set_power_state(vdev, PCI_D0);
-
-       mutex_destroy(&vdev->ioeventfds_lock);
-       kfree(vdev->region);
-       kfree(vdev->pm_save);
-       kfree(vdev);
-}
-
-static pci_ers_result_t vfio_pci_aer_err_detected(struct pci_dev *pdev,
-                                                 pci_channel_state_t state)
-{
-       struct vfio_pci_device *vdev;
-       struct vfio_device *device;
-
-       device = vfio_device_get_from_dev(&pdev->dev);
-       if (device == NULL)
-               return PCI_ERS_RESULT_DISCONNECT;
-
-       vdev = container_of(device, struct vfio_pci_device, vdev);
-
-       mutex_lock(&vdev->igate);
-
-       if (vdev->err_trigger)
-               eventfd_signal(vdev->err_trigger, 1);
-
-       mutex_unlock(&vdev->igate);
-
-       vfio_device_put(device);
-
-       return PCI_ERS_RESULT_CAN_RECOVER;
-}
-
-static int vfio_pci_sriov_configure(struct pci_dev *pdev, int nr_virtfn)
-{
-       struct vfio_device *device;
-       int ret = 0;
-
-       might_sleep();
-
-       if (!enable_sriov)
-               return -ENOENT;
-
-       device = vfio_device_get_from_dev(&pdev->dev);
-       if (!device)
-               return -ENODEV;
-
-       if (nr_virtfn == 0)
-               pci_disable_sriov(pdev);
-       else
-               ret = pci_enable_sriov(pdev, nr_virtfn);
-
-       vfio_device_put(device);
-
-       return ret < 0 ? ret : nr_virtfn;
-}
-
-static const struct pci_error_handlers vfio_err_handlers = {
-       .error_detected = vfio_pci_aer_err_detected,
-};
-
-static struct pci_driver vfio_pci_driver = {
-       .name                   = "vfio-pci",
-       .id_table               = NULL, /* only dynamic ids */
-       .probe                  = vfio_pci_probe,
-       .remove                 = vfio_pci_remove,
-       .sriov_configure        = vfio_pci_sriov_configure,
-       .err_handler            = &vfio_err_handlers,
-};
-
-static DEFINE_MUTEX(reflck_lock);
-
-static struct vfio_pci_reflck *vfio_pci_reflck_alloc(void)
-{
-       struct vfio_pci_reflck *reflck;
-
-       reflck = kzalloc(sizeof(*reflck), GFP_KERNEL);
-       if (!reflck)
-               return ERR_PTR(-ENOMEM);
-
-       kref_init(&reflck->kref);
-       mutex_init(&reflck->lock);
-
-       return reflck;
-}
-
-static void vfio_pci_reflck_get(struct vfio_pci_reflck *reflck)
-{
-       kref_get(&reflck->kref);
-}
-
-static int vfio_pci_reflck_find(struct pci_dev *pdev, void *data)
-{
-       struct vfio_pci_reflck **preflck = data;
-       struct vfio_device *device;
-       struct vfio_pci_device *vdev;
-
-       device = vfio_device_get_from_dev(&pdev->dev);
-       if (!device)
-               return 0;
-
-       if (pci_dev_driver(pdev) != &vfio_pci_driver) {
-               vfio_device_put(device);
-               return 0;
-       }
-
-       vdev = container_of(device, struct vfio_pci_device, vdev);
-
-       if (vdev->reflck) {
-               vfio_pci_reflck_get(vdev->reflck);
-               *preflck = vdev->reflck;
-               vfio_device_put(device);
-               return 1;
-       }
-
-       vfio_device_put(device);
-       return 0;
-}
-
-static int vfio_pci_reflck_attach(struct vfio_pci_device *vdev)
-{
-       bool slot = !pci_probe_reset_slot(vdev->pdev->slot);
-
-       mutex_lock(&reflck_lock);
-
-       if (pci_is_root_bus(vdev->pdev->bus) ||
-           vfio_pci_for_each_slot_or_bus(vdev->pdev, vfio_pci_reflck_find,
-                                         &vdev->reflck, slot) <= 0)
-               vdev->reflck = vfio_pci_reflck_alloc();
-
-       mutex_unlock(&reflck_lock);
-
-       return PTR_ERR_OR_ZERO(vdev->reflck);
-}
-
-static void vfio_pci_reflck_release(struct kref *kref)
-{
-       struct vfio_pci_reflck *reflck = container_of(kref,
-                                                     struct vfio_pci_reflck,
-                                                     kref);
-
-       kfree(reflck);
-       mutex_unlock(&reflck_lock);
-}
-
-static void vfio_pci_reflck_put(struct vfio_pci_reflck *reflck)
-{
-       kref_put_mutex(&reflck->kref, vfio_pci_reflck_release, &reflck_lock);
-}
-
-static int vfio_pci_get_unused_devs(struct pci_dev *pdev, void *data)
-{
-       struct vfio_devices *devs = data;
-       struct vfio_device *device;
-       struct vfio_pci_device *vdev;
-
-       if (devs->cur_index == devs->max_index)
-               return -ENOSPC;
-
-       device = vfio_device_get_from_dev(&pdev->dev);
-       if (!device)
-               return -EINVAL;
-
-       if (pci_dev_driver(pdev) != &vfio_pci_driver) {
-               vfio_device_put(device);
-               return -EBUSY;
-       }
-
-       vdev = container_of(device, struct vfio_pci_device, vdev);
-
-       /* Fault if the device is not unused */
-       if (vdev->refcnt) {
-               vfio_device_put(device);
-               return -EBUSY;
-       }
-
-       devs->devices[devs->cur_index++] = vdev;
-       return 0;
-}
-
-static int vfio_pci_try_zap_and_vma_lock_cb(struct pci_dev *pdev, void *data)
-{
-       struct vfio_devices *devs = data;
-       struct vfio_device *device;
-       struct vfio_pci_device *vdev;
-
-       if (devs->cur_index == devs->max_index)
-               return -ENOSPC;
-
-       device = vfio_device_get_from_dev(&pdev->dev);
-       if (!device)
-               return -EINVAL;
-
-       if (pci_dev_driver(pdev) != &vfio_pci_driver) {
-               vfio_device_put(device);
-               return -EBUSY;
-       }
-
-       vdev = container_of(device, struct vfio_pci_device, vdev);
-
-       /*
-        * Locking multiple devices is prone to deadlock, runaway and
-        * unwind if we hit contention.
-        */
-       if (!vfio_pci_zap_and_vma_lock(vdev, true)) {
-               vfio_device_put(device);
-               return -EBUSY;
-       }
-
-       devs->devices[devs->cur_index++] = vdev;
-       return 0;
-}
-
-/*
- * If a bus or slot reset is available for the provided device and:
- *  - All of the devices affected by that bus or slot reset are unused
- *    (!refcnt)
- *  - At least one of the affected devices is marked dirty via
- *    needs_reset (such as by lack of FLR support)
- * Then attempt to perform that bus or slot reset.  Callers are required
- * to hold vdev->reflck->lock, protecting the bus/slot reset group from
- * concurrent opens.  A vfio_device reference is acquired for each device
- * to prevent unbinds during the reset operation.
- *
- * NB: vfio-core considers a group to be viable even if some devices are
- * bound to drivers like pci-stub or pcieport.  Here we require all devices
- * to be bound to vfio_pci since that's the only way we can be sure they
- * stay put.
- */
-static void vfio_pci_try_bus_reset(struct vfio_pci_device *vdev)
-{
-       struct vfio_devices devs = { .cur_index = 0 };
-       int i = 0, ret = -EINVAL;
-       bool slot = false;
-       struct vfio_pci_device *tmp;
-
-       if (!pci_probe_reset_slot(vdev->pdev->slot))
-               slot = true;
-       else if (pci_probe_reset_bus(vdev->pdev->bus))
-               return;
-
-       if (vfio_pci_for_each_slot_or_bus(vdev->pdev, vfio_pci_count_devs,
-                                         &i, slot) || !i)
-               return;
-
-       devs.max_index = i;
-       devs.devices = kcalloc(i, sizeof(struct vfio_device *), GFP_KERNEL);
-       if (!devs.devices)
-               return;
-
-       if (vfio_pci_for_each_slot_or_bus(vdev->pdev,
-                                         vfio_pci_get_unused_devs,
-                                         &devs, slot))
-               goto put_devs;
-
-       /* Does at least one need a reset? */
-       for (i = 0; i < devs.cur_index; i++) {
-               tmp = devs.devices[i];
-               if (tmp->needs_reset) {
-                       ret = pci_reset_bus(vdev->pdev);
-                       break;
-               }
-       }
-
-put_devs:
-       for (i = 0; i < devs.cur_index; i++) {
-               tmp = devs.devices[i];
-
-               /*
-                * If reset was successful, affected devices no longer need
-                * a reset and we should return all the collateral devices
-                * to low power.  If not successful, we either didn't reset
-                * the bus or timed out waiting for it, so let's not touch
-                * the power state.
-                */
-               if (!ret) {
-                       tmp->needs_reset = false;
-
-                       if (tmp != vdev && !disable_idle_d3)
-                               vfio_pci_set_power_state(tmp, PCI_D3hot);
-               }
-
-               vfio_device_put(&tmp->vdev);
-       }
-
-       kfree(devs.devices);
-}
-
-static void __exit vfio_pci_cleanup(void)
-{
-       pci_unregister_driver(&vfio_pci_driver);
-       vfio_pci_uninit_perm_bits();
-}
-
-static void __init vfio_pci_fill_ids(void)
-{
-       char *p, *id;
-       int rc;
-
-       /* no ids passed actually */
-       if (ids[0] == '\0')
-               return;
-
-       /* add ids specified in the module parameter */
-       p = ids;
-       while ((id = strsep(&p, ","))) {
-               unsigned int vendor, device, subvendor = PCI_ANY_ID,
-                       subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
-               int fields;
-
-               if (!strlen(id))
-                       continue;
-
-               fields = sscanf(id, "%x:%x:%x:%x:%x:%x",
-                               &vendor, &device, &subvendor, &subdevice,
-                               &class, &class_mask);
-
-               if (fields < 2) {
-                       pr_warn("invalid id string \"%s\"\n", id);
-                       continue;
-               }
-
-               rc = pci_add_dynid(&vfio_pci_driver, vendor, device,
-                                  subvendor, subdevice, class, class_mask, 0);
-               if (rc)
-                       pr_warn("failed to add dynamic id [%04x:%04x[%04x:%04x]] class %#08x/%08x (%d)\n",
-                               vendor, device, subvendor, subdevice,
-                               class, class_mask, rc);
-               else
-                       pr_info("add [%04x:%04x[%04x:%04x]] class %#08x/%08x\n",
-                               vendor, device, subvendor, subdevice,
-                               class, class_mask);
-       }
-}
-
-static int __init vfio_pci_init(void)
-{
-       int ret;
-
-       /* Allocate shared config space permission data used by all devices */
-       ret = vfio_pci_init_perm_bits();
-       if (ret)
-               return ret;
-
-       /* Register and scan for devices */
-       ret = pci_register_driver(&vfio_pci_driver);
-       if (ret)
-               goto out_driver;
-
-       vfio_pci_fill_ids();
-
-       if (disable_denylist)
-               pr_warn("device denylist disabled.\n");
-
-       return 0;
-
-out_driver:
-       vfio_pci_uninit_perm_bits();
-       return ret;
-}
-
-module_init(vfio_pci_init);
 module_exit(vfio_pci_cleanup);
 
-MODULE_VERSION(DRIVER_VERSION);
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
index 70e28ef..6e58b4b 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/vfio.h>
 #include <linux/slab.h>
 
-#include "vfio_pci_private.h"
+#include <linux/vfio_pci_core.h>
 
 /* Fake capability ID for standard config space */
 #define PCI_CAP_ID_BASIC       0
@@ -108,9 +108,9 @@ static const u16 pci_ext_cap_length[PCI_EXT_CAP_ID_MAX + 1] = {
 struct perm_bits {
        u8      *virt;          /* read/write virtual data, not hw */
        u8      *write;         /* writeable bits */
-       int     (*readfn)(struct vfio_pci_device *vdev, int pos, int count,
+       int     (*readfn)(struct vfio_pci_core_device *vdev, int pos, int count,
                          struct perm_bits *perm, int offset, __le32 *val);
-       int     (*writefn)(struct vfio_pci_device *vdev, int pos, int count,
+       int     (*writefn)(struct vfio_pci_core_device *vdev, int pos, int count,
                           struct perm_bits *perm, int offset, __le32 val);
 };
 
@@ -171,7 +171,7 @@ static int vfio_user_config_write(struct pci_dev *pdev, int offset,
        return ret;
 }
 
-static int vfio_default_config_read(struct vfio_pci_device *vdev, int pos,
+static int vfio_default_config_read(struct vfio_pci_core_device *vdev, int pos,
                                    int count, struct perm_bits *perm,
                                    int offset, __le32 *val)
 {
@@ -197,7 +197,7 @@ static int vfio_default_config_read(struct vfio_pci_device *vdev, int pos,
        return count;
 }
 
-static int vfio_default_config_write(struct vfio_pci_device *vdev, int pos,
+static int vfio_default_config_write(struct vfio_pci_core_device *vdev, int pos,
                                     int count, struct perm_bits *perm,
                                     int offset, __le32 val)
 {
@@ -244,7 +244,7 @@ static int vfio_default_config_write(struct vfio_pci_device *vdev, int pos,
 }
 
 /* Allow direct read from hardware, except for capability next pointer */
-static int vfio_direct_config_read(struct vfio_pci_device *vdev, int pos,
+static int vfio_direct_config_read(struct vfio_pci_core_device *vdev, int pos,
                                   int count, struct perm_bits *perm,
                                   int offset, __le32 *val)
 {
@@ -269,7 +269,7 @@ static int vfio_direct_config_read(struct vfio_pci_device *vdev, int pos,
 }
 
 /* Raw access skips any kind of virtualization */
-static int vfio_raw_config_write(struct vfio_pci_device *vdev, int pos,
+static int vfio_raw_config_write(struct vfio_pci_core_device *vdev, int pos,
                                 int count, struct perm_bits *perm,
                                 int offset, __le32 val)
 {
@@ -282,7 +282,7 @@ static int vfio_raw_config_write(struct vfio_pci_device *vdev, int pos,
        return count;
 }
 
-static int vfio_raw_config_read(struct vfio_pci_device *vdev, int pos,
+static int vfio_raw_config_read(struct vfio_pci_core_device *vdev, int pos,
                                int count, struct perm_bits *perm,
                                int offset, __le32 *val)
 {
@@ -296,7 +296,7 @@ static int vfio_raw_config_read(struct vfio_pci_device *vdev, int pos,
 }
 
 /* Virt access uses only virtualization */
-static int vfio_virt_config_write(struct vfio_pci_device *vdev, int pos,
+static int vfio_virt_config_write(struct vfio_pci_core_device *vdev, int pos,
                                  int count, struct perm_bits *perm,
                                  int offset, __le32 val)
 {
@@ -304,7 +304,7 @@ static int vfio_virt_config_write(struct vfio_pci_device *vdev, int pos,
        return count;
 }
 
-static int vfio_virt_config_read(struct vfio_pci_device *vdev, int pos,
+static int vfio_virt_config_read(struct vfio_pci_core_device *vdev, int pos,
                                 int count, struct perm_bits *perm,
                                 int offset, __le32 *val)
 {
@@ -396,7 +396,7 @@ static inline void p_setd(struct perm_bits *p, int off, u32 virt, u32 write)
 }
 
 /* Caller should hold memory_lock semaphore */
-bool __vfio_pci_memory_enabled(struct vfio_pci_device *vdev)
+bool __vfio_pci_memory_enabled(struct vfio_pci_core_device *vdev)
 {
        struct pci_dev *pdev = vdev->pdev;
        u16 cmd = le16_to_cpu(*(__le16 *)&vdev->vconfig[PCI_COMMAND]);
@@ -413,7 +413,7 @@ bool __vfio_pci_memory_enabled(struct vfio_pci_device *vdev)
  * Restore the *real* BARs after we detect a FLR or backdoor reset.
  * (backdoor = some device specific technique that we didn't catch)
  */
-static void vfio_bar_restore(struct vfio_pci_device *vdev)
+static void vfio_bar_restore(struct vfio_pci_core_device *vdev)
 {
        struct pci_dev *pdev = vdev->pdev;
        u32 *rbar = vdev->rbar;
@@ -460,7 +460,7 @@ static __le32 vfio_generate_bar_flags(struct pci_dev *pdev, int bar)
  * Pretend we're hardware and tweak the values of the *virtual* PCI BARs
  * to reflect the hardware capabilities.  This implements BAR sizing.
  */
-static void vfio_bar_fixup(struct vfio_pci_device *vdev)
+static void vfio_bar_fixup(struct vfio_pci_core_device *vdev)
 {
        struct pci_dev *pdev = vdev->pdev;
        int i;
@@ -514,7 +514,7 @@ static void vfio_bar_fixup(struct vfio_pci_device *vdev)
        vdev->bardirty = false;
 }
 
-static int vfio_basic_config_read(struct vfio_pci_device *vdev, int pos,
+static int vfio_basic_config_read(struct vfio_pci_core_device *vdev, int pos,
                                  int count, struct perm_bits *perm,
                                  int offset, __le32 *val)
 {
@@ -536,7 +536,7 @@ static int vfio_basic_config_read(struct vfio_pci_device *vdev, int pos,
 }
 
 /* Test whether BARs match the value we think they should contain */
-static bool vfio_need_bar_restore(struct vfio_pci_device *vdev)
+static bool vfio_need_bar_restore(struct vfio_pci_core_device *vdev)
 {
        int i = 0, pos = PCI_BASE_ADDRESS_0, ret;
        u32 bar;
@@ -552,7 +552,7 @@ static bool vfio_need_bar_restore(struct vfio_pci_device *vdev)
        return false;
 }
 
-static int vfio_basic_config_write(struct vfio_pci_device *vdev, int pos,
+static int vfio_basic_config_write(struct vfio_pci_core_device *vdev, int pos,
                                   int count, struct perm_bits *perm,
                                   int offset, __le32 val)
 {
@@ -692,7 +692,7 @@ static int __init init_pci_cap_basic_perm(struct perm_bits *perm)
        return 0;
 }
 
-static int vfio_pm_config_write(struct vfio_pci_device *vdev, int pos,
+static int vfio_pm_config_write(struct vfio_pci_core_device *vdev, int pos,
                                int count, struct perm_bits *perm,
                                int offset, __le32 val)
 {
@@ -747,7 +747,7 @@ static int __init init_pci_cap_pm_perm(struct perm_bits *perm)
        return 0;
 }
 
-static int vfio_vpd_config_write(struct vfio_pci_device *vdev, int pos,
+static int vfio_vpd_config_write(struct vfio_pci_core_device *vdev, int pos,
                                 int count, struct perm_bits *perm,
                                 int offset, __le32 val)
 {
@@ -829,7 +829,7 @@ static int __init init_pci_cap_pcix_perm(struct perm_bits *perm)
        return 0;
 }
 
-static int vfio_exp_config_write(struct vfio_pci_device *vdev, int pos,
+static int vfio_exp_config_write(struct vfio_pci_core_device *vdev, int pos,
                                 int count, struct perm_bits *perm,
                                 int offset, __le32 val)
 {
@@ -913,7 +913,7 @@ static int __init init_pci_cap_exp_perm(struct perm_bits *perm)
        return 0;
 }
 
-static int vfio_af_config_write(struct vfio_pci_device *vdev, int pos,
+static int vfio_af_config_write(struct vfio_pci_core_device *vdev, int pos,
                                int count, struct perm_bits *perm,
                                int offset, __le32 val)
 {
@@ -1072,7 +1072,7 @@ int __init vfio_pci_init_perm_bits(void)
        return ret;
 }
 
-static int vfio_find_cap_start(struct vfio_pci_device *vdev, int pos)
+static int vfio_find_cap_start(struct vfio_pci_core_device *vdev, int pos)
 {
        u8 cap;
        int base = (pos >= PCI_CFG_SPACE_SIZE) ? PCI_CFG_SPACE_SIZE :
@@ -1089,7 +1089,7 @@ static int vfio_find_cap_start(struct vfio_pci_device *vdev, int pos)
        return pos;
 }
 
-static int vfio_msi_config_read(struct vfio_pci_device *vdev, int pos,
+static int vfio_msi_config_read(struct vfio_pci_core_device *vdev, int pos,
                                int count, struct perm_bits *perm,
                                int offset, __le32 *val)
 {
@@ -1109,7 +1109,7 @@ static int vfio_msi_config_read(struct vfio_pci_device *vdev, int pos,
        return vfio_default_config_read(vdev, pos, count, perm, offset, val);
 }
 
-static int vfio_msi_config_write(struct vfio_pci_device *vdev, int pos,
+static int vfio_msi_config_write(struct vfio_pci_core_device *vdev, int pos,
                                 int count, struct perm_bits *perm,
                                 int offset, __le32 val)
 {
@@ -1189,7 +1189,7 @@ static int init_pci_cap_msi_perm(struct perm_bits *perm, int len, u16 flags)
 }
 
 /* Determine MSI CAP field length; initialize msi_perms on 1st call per vdev */
-static int vfio_msi_cap_len(struct vfio_pci_device *vdev, u8 pos)
+static int vfio_msi_cap_len(struct vfio_pci_core_device *vdev, u8 pos)
 {
        struct pci_dev *pdev = vdev->pdev;
        int len, ret;
@@ -1222,7 +1222,7 @@ static int vfio_msi_cap_len(struct vfio_pci_device *vdev, u8 pos)
 }
 
 /* Determine extended capability length for VC (2 & 9) and MFVC */
-static int vfio_vc_cap_len(struct vfio_pci_device *vdev, u16 pos)
+static int vfio_vc_cap_len(struct vfio_pci_core_device *vdev, u16 pos)
 {
        struct pci_dev *pdev = vdev->pdev;
        u32 tmp;
@@ -1263,7 +1263,7 @@ static int vfio_vc_cap_len(struct vfio_pci_device *vdev, u16 pos)
        return len;
 }
 
-static int vfio_cap_len(struct vfio_pci_device *vdev, u8 cap, u8 pos)
+static int vfio_cap_len(struct vfio_pci_core_device *vdev, u8 cap, u8 pos)
 {
        struct pci_dev *pdev = vdev->pdev;
        u32 dword;
@@ -1338,7 +1338,7 @@ static int vfio_cap_len(struct vfio_pci_device *vdev, u8 cap, u8 pos)
        return 0;
 }
 
-static int vfio_ext_cap_len(struct vfio_pci_device *vdev, u16 ecap, u16 epos)
+static int vfio_ext_cap_len(struct vfio_pci_core_device *vdev, u16 ecap, u16 epos)
 {
        struct pci_dev *pdev = vdev->pdev;
        u8 byte;
@@ -1412,7 +1412,7 @@ static int vfio_ext_cap_len(struct vfio_pci_device *vdev, u16 ecap, u16 epos)
        return 0;
 }
 
-static int vfio_fill_vconfig_bytes(struct vfio_pci_device *vdev,
+static int vfio_fill_vconfig_bytes(struct vfio_pci_core_device *vdev,
                                   int offset, int size)
 {
        struct pci_dev *pdev = vdev->pdev;
@@ -1459,7 +1459,7 @@ static int vfio_fill_vconfig_bytes(struct vfio_pci_device *vdev,
        return ret;
 }
 
-static int vfio_cap_init(struct vfio_pci_device *vdev)
+static int vfio_cap_init(struct vfio_pci_core_device *vdev)
 {
        struct pci_dev *pdev = vdev->pdev;
        u8 *map = vdev->pci_config_map;
@@ -1549,7 +1549,7 @@ static int vfio_cap_init(struct vfio_pci_device *vdev)
        return 0;
 }
 
-static int vfio_ecap_init(struct vfio_pci_device *vdev)
+static int vfio_ecap_init(struct vfio_pci_core_device *vdev)
 {
        struct pci_dev *pdev = vdev->pdev;
        u8 *map = vdev->pci_config_map;
@@ -1669,7 +1669,7 @@ static const struct pci_device_id known_bogus_vf_intx_pin[] = {
  * for each area requiring emulated bits, but the array of pointers
  * would be comparable in size (at least for standard config space).
  */
-int vfio_config_init(struct vfio_pci_device *vdev)
+int vfio_config_init(struct vfio_pci_core_device *vdev)
 {
        struct pci_dev *pdev = vdev->pdev;
        u8 *map, *vconfig;
@@ -1773,7 +1773,7 @@ out:
        return pcibios_err_to_errno(ret);
 }
 
-void vfio_config_free(struct vfio_pci_device *vdev)
+void vfio_config_free(struct vfio_pci_core_device *vdev)
 {
        kfree(vdev->vconfig);
        vdev->vconfig = NULL;
@@ -1790,7 +1790,7 @@ void vfio_config_free(struct vfio_pci_device *vdev)
  * Find the remaining number of bytes in a dword that match the given
  * position.  Stop at either the end of the capability or the dword boundary.
  */
-static size_t vfio_pci_cap_remaining_dword(struct vfio_pci_device *vdev,
+static size_t vfio_pci_cap_remaining_dword(struct vfio_pci_core_device *vdev,
                                           loff_t pos)
 {
        u8 cap = vdev->pci_config_map[pos];
@@ -1802,7 +1802,7 @@ static size_t vfio_pci_cap_remaining_dword(struct vfio_pci_device *vdev,
        return i;
 }
 
-static ssize_t vfio_config_do_rw(struct vfio_pci_device *vdev, char __user *buf,
+static ssize_t vfio_config_do_rw(struct vfio_pci_core_device *vdev, char __user *buf,
                                 size_t count, loff_t *ppos, bool iswrite)
 {
        struct pci_dev *pdev = vdev->pdev;
@@ -1885,7 +1885,7 @@ static ssize_t vfio_config_do_rw(struct vfio_pci_device *vdev, char __user *buf,
        return ret;
 }
 
-ssize_t vfio_pci_config_rw(struct vfio_pci_device *vdev, char __user *buf,
+ssize_t vfio_pci_config_rw(struct vfio_pci_core_device *vdev, char __user *buf,
                           size_t count, loff_t *ppos, bool iswrite)
 {
        size_t done = 0;
diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
new file mode 100644 (file)
index 0000000..68198e0
--- /dev/null
@@ -0,0 +1,2157 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2012 Red Hat, Inc.  All rights reserved.
+ *     Author: Alex Williamson <alex.williamson@redhat.com>
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc.  All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/device.h>
+#include <linux/eventfd.h>
+#include <linux/file.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/vgaarb.h>
+#include <linux/nospec.h>
+#include <linux/sched/mm.h>
+
+#include <linux/vfio_pci_core.h>
+
+#define DRIVER_AUTHOR   "Alex Williamson <alex.williamson@redhat.com>"
+#define DRIVER_DESC "core driver for VFIO based PCI devices"
+
+static bool nointxmask;
+static bool disable_vga;
+static bool disable_idle_d3;
+
+static inline bool vfio_vga_disabled(void)
+{
+#ifdef CONFIG_VFIO_PCI_VGA
+       return disable_vga;
+#else
+       return true;
+#endif
+}
+
+/*
+ * Our VGA arbiter participation is limited since we don't know anything
+ * about the device itself.  However, if the device is the only VGA device
+ * downstream of a bridge and VFIO VGA support is disabled, then we can
+ * safely return legacy VGA IO and memory as not decoded since the user
+ * has no way to get to it and routing can be disabled externally at the
+ * bridge.
+ */
+static unsigned int vfio_pci_set_decode(struct pci_dev *pdev, bool single_vga)
+{
+       struct pci_dev *tmp = NULL;
+       unsigned char max_busnr;
+       unsigned int decodes;
+
+       if (single_vga || !vfio_vga_disabled() || pci_is_root_bus(pdev->bus))
+               return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM |
+                      VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM;
+
+       max_busnr = pci_bus_max_busnr(pdev->bus);
+       decodes = VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
+
+       while ((tmp = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, tmp)) != NULL) {
+               if (tmp == pdev ||
+                   pci_domain_nr(tmp->bus) != pci_domain_nr(pdev->bus) ||
+                   pci_is_root_bus(tmp->bus))
+                       continue;
+
+               if (tmp->bus->number >= pdev->bus->number &&
+                   tmp->bus->number <= max_busnr) {
+                       pci_dev_put(tmp);
+                       decodes |= VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM;
+                       break;
+               }
+       }
+
+       return decodes;
+}
+
+static void vfio_pci_probe_mmaps(struct vfio_pci_core_device *vdev)
+{
+       struct resource *res;
+       int i;
+       struct vfio_pci_dummy_resource *dummy_res;
+
+       for (i = 0; i < PCI_STD_NUM_BARS; i++) {
+               int bar = i + PCI_STD_RESOURCES;
+
+               res = &vdev->pdev->resource[bar];
+
+               if (!IS_ENABLED(CONFIG_VFIO_PCI_MMAP))
+                       goto no_mmap;
+
+               if (!(res->flags & IORESOURCE_MEM))
+                       goto no_mmap;
+
+               /*
+                * The PCI core shouldn't set up a resource with a
+                * type but zero size. But there may be bugs that
+                * cause us to do that.
+                */
+               if (!resource_size(res))
+                       goto no_mmap;
+
+               if (resource_size(res) >= PAGE_SIZE) {
+                       vdev->bar_mmap_supported[bar] = true;
+                       continue;
+               }
+
+               if (!(res->start & ~PAGE_MASK)) {
+                       /*
+                        * Add a dummy resource to reserve the remainder
+                        * of the exclusive page in case that hot-add
+                        * device's bar is assigned into it.
+                        */
+                       dummy_res = kzalloc(sizeof(*dummy_res), GFP_KERNEL);
+                       if (dummy_res == NULL)
+                               goto no_mmap;
+
+                       dummy_res->resource.name = "vfio sub-page reserved";
+                       dummy_res->resource.start = res->end + 1;
+                       dummy_res->resource.end = res->start + PAGE_SIZE - 1;
+                       dummy_res->resource.flags = res->flags;
+                       if (request_resource(res->parent,
+                                               &dummy_res->resource)) {
+                               kfree(dummy_res);
+                               goto no_mmap;
+                       }
+                       dummy_res->index = bar;
+                       list_add(&dummy_res->res_next,
+                                       &vdev->dummy_resources_list);
+                       vdev->bar_mmap_supported[bar] = true;
+                       continue;
+               }
+               /*
+                * Here we don't handle the case when the BAR is not page
+                * aligned because we can't expect the BAR will be
+                * assigned into the same location in a page in guest
+                * when we passthrough the BAR. And it's hard to access
+                * this BAR in userspace because we have no way to get
+                * the BAR's location in a page.
+                */
+no_mmap:
+               vdev->bar_mmap_supported[bar] = false;
+       }
+}
+
+struct vfio_pci_group_info;
+static bool vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set);
+static int vfio_pci_dev_set_hot_reset(struct vfio_device_set *dev_set,
+                                     struct vfio_pci_group_info *groups);
+
+/*
+ * INTx masking requires the ability to disable INTx signaling via PCI_COMMAND
+ * _and_ the ability detect when the device is asserting INTx via PCI_STATUS.
+ * If a device implements the former but not the latter we would typically
+ * expect broken_intx_masking be set and require an exclusive interrupt.
+ * However since we do have control of the device's ability to assert INTx,
+ * we can instead pretend that the device does not implement INTx, virtualizing
+ * the pin register to report zero and maintaining DisINTx set on the host.
+ */
+static bool vfio_pci_nointx(struct pci_dev *pdev)
+{
+       switch (pdev->vendor) {
+       case PCI_VENDOR_ID_INTEL:
+               switch (pdev->device) {
+               /* All i40e (XL710/X710/XXV710) 10/20/25/40GbE NICs */
+               case 0x1572:
+               case 0x1574:
+               case 0x1580 ... 0x1581:
+               case 0x1583 ... 0x158b:
+               case 0x37d0 ... 0x37d2:
+               /* X550 */
+               case 0x1563:
+                       return true;
+               default:
+                       return false;
+               }
+       }
+
+       return false;
+}
+
+static void vfio_pci_probe_power_state(struct vfio_pci_core_device *vdev)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       u16 pmcsr;
+
+       if (!pdev->pm_cap)
+               return;
+
+       pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &pmcsr);
+
+       vdev->needs_pm_restore = !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET);
+}
+
+/*
+ * pci_set_power_state() wrapper handling devices which perform a soft reset on
+ * D3->D0 transition.  Save state prior to D0/1/2->D3, stash it on the vdev,
+ * restore when returned to D0.  Saved separately from pci_saved_state for use
+ * by PM capability emulation and separately from pci_dev internal saved state
+ * to avoid it being overwritten and consumed around other resets.
+ */
+int vfio_pci_set_power_state(struct vfio_pci_core_device *vdev, pci_power_t state)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       bool needs_restore = false, needs_save = false;
+       int ret;
+
+       if (vdev->needs_pm_restore) {
+               if (pdev->current_state < PCI_D3hot && state >= PCI_D3hot) {
+                       pci_save_state(pdev);
+                       needs_save = true;
+               }
+
+               if (pdev->current_state >= PCI_D3hot && state <= PCI_D0)
+                       needs_restore = true;
+       }
+
+       ret = pci_set_power_state(pdev, state);
+
+       if (!ret) {
+               /* D3 might be unsupported via quirk, skip unless in D3 */
+               if (needs_save && pdev->current_state >= PCI_D3hot) {
+                       vdev->pm_save = pci_store_saved_state(pdev);
+               } else if (needs_restore) {
+                       pci_load_and_free_saved_state(pdev, &vdev->pm_save);
+                       pci_restore_state(pdev);
+               }
+       }
+
+       return ret;
+}
+
+int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       int ret;
+       u16 cmd;
+       u8 msix_pos;
+
+       vfio_pci_set_power_state(vdev, PCI_D0);
+
+       /* Don't allow our initial saved state to include busmaster */
+       pci_clear_master(pdev);
+
+       ret = pci_enable_device(pdev);
+       if (ret)
+               return ret;
+
+       /* If reset fails because of the device lock, fail this path entirely */
+       ret = pci_try_reset_function(pdev);
+       if (ret == -EAGAIN) {
+               pci_disable_device(pdev);
+               return ret;
+       }
+
+       vdev->reset_works = !ret;
+       pci_save_state(pdev);
+       vdev->pci_saved_state = pci_store_saved_state(pdev);
+       if (!vdev->pci_saved_state)
+               pci_dbg(pdev, "%s: Couldn't store saved state\n", __func__);
+
+       if (likely(!nointxmask)) {
+               if (vfio_pci_nointx(pdev)) {
+                       pci_info(pdev, "Masking broken INTx support\n");
+                       vdev->nointx = true;
+                       pci_intx(pdev, 0);
+               } else
+                       vdev->pci_2_3 = pci_intx_mask_supported(pdev);
+       }
+
+       pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+       if (vdev->pci_2_3 && (cmd & PCI_COMMAND_INTX_DISABLE)) {
+               cmd &= ~PCI_COMMAND_INTX_DISABLE;
+               pci_write_config_word(pdev, PCI_COMMAND, cmd);
+       }
+
+       ret = vfio_config_init(vdev);
+       if (ret) {
+               kfree(vdev->pci_saved_state);
+               vdev->pci_saved_state = NULL;
+               pci_disable_device(pdev);
+               return ret;
+       }
+
+       msix_pos = pdev->msix_cap;
+       if (msix_pos) {
+               u16 flags;
+               u32 table;
+
+               pci_read_config_word(pdev, msix_pos + PCI_MSIX_FLAGS, &flags);
+               pci_read_config_dword(pdev, msix_pos + PCI_MSIX_TABLE, &table);
+
+               vdev->msix_bar = table & PCI_MSIX_TABLE_BIR;
+               vdev->msix_offset = table & PCI_MSIX_TABLE_OFFSET;
+               vdev->msix_size = ((flags & PCI_MSIX_FLAGS_QSIZE) + 1) * 16;
+       } else
+               vdev->msix_bar = 0xFF;
+
+       if (!vfio_vga_disabled() && vfio_pci_is_vga(pdev))
+               vdev->has_vga = true;
+
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_enable);
+
+void vfio_pci_core_disable(struct vfio_pci_core_device *vdev)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       struct vfio_pci_dummy_resource *dummy_res, *tmp;
+       struct vfio_pci_ioeventfd *ioeventfd, *ioeventfd_tmp;
+       int i, bar;
+
+       /* For needs_reset */
+       lockdep_assert_held(&vdev->vdev.dev_set->lock);
+
+       /* Stop the device from further DMA */
+       pci_clear_master(pdev);
+
+       vfio_pci_set_irqs_ioctl(vdev, VFIO_IRQ_SET_DATA_NONE |
+                               VFIO_IRQ_SET_ACTION_TRIGGER,
+                               vdev->irq_type, 0, 0, NULL);
+
+       /* Device closed, don't need mutex here */
+       list_for_each_entry_safe(ioeventfd, ioeventfd_tmp,
+                                &vdev->ioeventfds_list, next) {
+               vfio_virqfd_disable(&ioeventfd->virqfd);
+               list_del(&ioeventfd->next);
+               kfree(ioeventfd);
+       }
+       vdev->ioeventfds_nr = 0;
+
+       vdev->virq_disabled = false;
+
+       for (i = 0; i < vdev->num_regions; i++)
+               vdev->region[i].ops->release(vdev, &vdev->region[i]);
+
+       vdev->num_regions = 0;
+       kfree(vdev->region);
+       vdev->region = NULL; /* don't krealloc a freed pointer */
+
+       vfio_config_free(vdev);
+
+       for (i = 0; i < PCI_STD_NUM_BARS; i++) {
+               bar = i + PCI_STD_RESOURCES;
+               if (!vdev->barmap[bar])
+                       continue;
+               pci_iounmap(pdev, vdev->barmap[bar]);
+               pci_release_selected_regions(pdev, 1 << bar);
+               vdev->barmap[bar] = NULL;
+       }
+
+       list_for_each_entry_safe(dummy_res, tmp,
+                                &vdev->dummy_resources_list, res_next) {
+               list_del(&dummy_res->res_next);
+               release_resource(&dummy_res->resource);
+               kfree(dummy_res);
+       }
+
+       vdev->needs_reset = true;
+
+       /*
+        * If we have saved state, restore it.  If we can reset the device,
+        * even better.  Resetting with current state seems better than
+        * nothing, but saving and restoring current state without reset
+        * is just busy work.
+        */
+       if (pci_load_and_free_saved_state(pdev, &vdev->pci_saved_state)) {
+               pci_info(pdev, "%s: Couldn't reload saved state\n", __func__);
+
+               if (!vdev->reset_works)
+                       goto out;
+
+               pci_save_state(pdev);
+       }
+
+       /*
+        * Disable INTx and MSI, presumably to avoid spurious interrupts
+        * during reset.  Stolen from pci_reset_function()
+        */
+       pci_write_config_word(pdev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
+
+       /*
+        * Try to get the locks ourselves to prevent a deadlock. The
+        * success of this is dependent on being able to lock the device,
+        * which is not always possible.
+        * We can not use the "try" reset interface here, which will
+        * overwrite the previously restored configuration information.
+        */
+       if (vdev->reset_works && pci_dev_trylock(pdev)) {
+               if (!__pci_reset_function_locked(pdev))
+                       vdev->needs_reset = false;
+               pci_dev_unlock(pdev);
+       }
+
+       pci_restore_state(pdev);
+out:
+       pci_disable_device(pdev);
+
+       if (!vfio_pci_dev_set_try_reset(vdev->vdev.dev_set) && !disable_idle_d3)
+               vfio_pci_set_power_state(vdev, PCI_D3hot);
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_disable);
+
+static struct vfio_pci_core_device *get_pf_vdev(struct vfio_pci_core_device *vdev)
+{
+       struct pci_dev *physfn = pci_physfn(vdev->pdev);
+       struct vfio_device *pf_dev;
+
+       if (!vdev->pdev->is_virtfn)
+               return NULL;
+
+       pf_dev = vfio_device_get_from_dev(&physfn->dev);
+       if (!pf_dev)
+               return NULL;
+
+       if (pci_dev_driver(physfn) != pci_dev_driver(vdev->pdev)) {
+               vfio_device_put(pf_dev);
+               return NULL;
+       }
+
+       return container_of(pf_dev, struct vfio_pci_core_device, vdev);
+}
+
+static void vfio_pci_vf_token_user_add(struct vfio_pci_core_device *vdev, int val)
+{
+       struct vfio_pci_core_device *pf_vdev = get_pf_vdev(vdev);
+
+       if (!pf_vdev)
+               return;
+
+       mutex_lock(&pf_vdev->vf_token->lock);
+       pf_vdev->vf_token->users += val;
+       WARN_ON(pf_vdev->vf_token->users < 0);
+       mutex_unlock(&pf_vdev->vf_token->lock);
+
+       vfio_device_put(&pf_vdev->vdev);
+}
+
+void vfio_pci_core_close_device(struct vfio_device *core_vdev)
+{
+       struct vfio_pci_core_device *vdev =
+               container_of(core_vdev, struct vfio_pci_core_device, vdev);
+
+       vfio_pci_vf_token_user_add(vdev, -1);
+       vfio_spapr_pci_eeh_release(vdev->pdev);
+       vfio_pci_core_disable(vdev);
+
+       mutex_lock(&vdev->igate);
+       if (vdev->err_trigger) {
+               eventfd_ctx_put(vdev->err_trigger);
+               vdev->err_trigger = NULL;
+       }
+       if (vdev->req_trigger) {
+               eventfd_ctx_put(vdev->req_trigger);
+               vdev->req_trigger = NULL;
+       }
+       mutex_unlock(&vdev->igate);
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_close_device);
+
+void vfio_pci_core_finish_enable(struct vfio_pci_core_device *vdev)
+{
+       vfio_pci_probe_mmaps(vdev);
+       vfio_spapr_pci_eeh_open(vdev->pdev);
+       vfio_pci_vf_token_user_add(vdev, 1);
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_finish_enable);
+
+static int vfio_pci_get_irq_count(struct vfio_pci_core_device *vdev, int irq_type)
+{
+       if (irq_type == VFIO_PCI_INTX_IRQ_INDEX) {
+               u8 pin;
+
+               if (!IS_ENABLED(CONFIG_VFIO_PCI_INTX) ||
+                   vdev->nointx || vdev->pdev->is_virtfn)
+                       return 0;
+
+               pci_read_config_byte(vdev->pdev, PCI_INTERRUPT_PIN, &pin);
+
+               return pin ? 1 : 0;
+       } else if (irq_type == VFIO_PCI_MSI_IRQ_INDEX) {
+               u8 pos;
+               u16 flags;
+
+               pos = vdev->pdev->msi_cap;
+               if (pos) {
+                       pci_read_config_word(vdev->pdev,
+                                            pos + PCI_MSI_FLAGS, &flags);
+                       return 1 << ((flags & PCI_MSI_FLAGS_QMASK) >> 1);
+               }
+       } else if (irq_type == VFIO_PCI_MSIX_IRQ_INDEX) {
+               u8 pos;
+               u16 flags;
+
+               pos = vdev->pdev->msix_cap;
+               if (pos) {
+                       pci_read_config_word(vdev->pdev,
+                                            pos + PCI_MSIX_FLAGS, &flags);
+
+                       return (flags & PCI_MSIX_FLAGS_QSIZE) + 1;
+               }
+       } else if (irq_type == VFIO_PCI_ERR_IRQ_INDEX) {
+               if (pci_is_pcie(vdev->pdev))
+                       return 1;
+       } else if (irq_type == VFIO_PCI_REQ_IRQ_INDEX) {
+               return 1;
+       }
+
+       return 0;
+}
+
+static int vfio_pci_count_devs(struct pci_dev *pdev, void *data)
+{
+       (*(int *)data)++;
+       return 0;
+}
+
+struct vfio_pci_fill_info {
+       int max;
+       int cur;
+       struct vfio_pci_dependent_device *devices;
+};
+
+static int vfio_pci_fill_devs(struct pci_dev *pdev, void *data)
+{
+       struct vfio_pci_fill_info *fill = data;
+       struct iommu_group *iommu_group;
+
+       if (fill->cur == fill->max)
+               return -EAGAIN; /* Something changed, try again */
+
+       iommu_group = iommu_group_get(&pdev->dev);
+       if (!iommu_group)
+               return -EPERM; /* Cannot reset non-isolated devices */
+
+       fill->devices[fill->cur].group_id = iommu_group_id(iommu_group);
+       fill->devices[fill->cur].segment = pci_domain_nr(pdev->bus);
+       fill->devices[fill->cur].bus = pdev->bus->number;
+       fill->devices[fill->cur].devfn = pdev->devfn;
+       fill->cur++;
+       iommu_group_put(iommu_group);
+       return 0;
+}
+
+struct vfio_pci_group_info {
+       int count;
+       struct vfio_group **groups;
+};
+
+static bool vfio_pci_dev_below_slot(struct pci_dev *pdev, struct pci_slot *slot)
+{
+       for (; pdev; pdev = pdev->bus->self)
+               if (pdev->bus == slot->bus)
+                       return (pdev->slot == slot);
+       return false;
+}
+
+struct vfio_pci_walk_info {
+       int (*fn)(struct pci_dev *, void *data);
+       void *data;
+       struct pci_dev *pdev;
+       bool slot;
+       int ret;
+};
+
+static int vfio_pci_walk_wrapper(struct pci_dev *pdev, void *data)
+{
+       struct vfio_pci_walk_info *walk = data;
+
+       if (!walk->slot || vfio_pci_dev_below_slot(pdev, walk->pdev->slot))
+               walk->ret = walk->fn(pdev, walk->data);
+
+       return walk->ret;
+}
+
+static int vfio_pci_for_each_slot_or_bus(struct pci_dev *pdev,
+                                        int (*fn)(struct pci_dev *,
+                                                  void *data), void *data,
+                                        bool slot)
+{
+       struct vfio_pci_walk_info walk = {
+               .fn = fn, .data = data, .pdev = pdev, .slot = slot, .ret = 0,
+       };
+
+       pci_walk_bus(pdev->bus, vfio_pci_walk_wrapper, &walk);
+
+       return walk.ret;
+}
+
+static int msix_mmappable_cap(struct vfio_pci_core_device *vdev,
+                             struct vfio_info_cap *caps)
+{
+       struct vfio_info_cap_header header = {
+               .id = VFIO_REGION_INFO_CAP_MSIX_MAPPABLE,
+               .version = 1
+       };
+
+       return vfio_info_add_capability(caps, &header, sizeof(header));
+}
+
+int vfio_pci_register_dev_region(struct vfio_pci_core_device *vdev,
+                                unsigned int type, unsigned int subtype,
+                                const struct vfio_pci_regops *ops,
+                                size_t size, u32 flags, void *data)
+{
+       struct vfio_pci_region *region;
+
+       region = krealloc(vdev->region,
+                         (vdev->num_regions + 1) * sizeof(*region),
+                         GFP_KERNEL);
+       if (!region)
+               return -ENOMEM;
+
+       vdev->region = region;
+       vdev->region[vdev->num_regions].type = type;
+       vdev->region[vdev->num_regions].subtype = subtype;
+       vdev->region[vdev->num_regions].ops = ops;
+       vdev->region[vdev->num_regions].size = size;
+       vdev->region[vdev->num_regions].flags = flags;
+       vdev->region[vdev->num_regions].data = data;
+
+       vdev->num_regions++;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(vfio_pci_register_dev_region);
+
+long vfio_pci_core_ioctl(struct vfio_device *core_vdev, unsigned int cmd,
+               unsigned long arg)
+{
+       struct vfio_pci_core_device *vdev =
+               container_of(core_vdev, struct vfio_pci_core_device, vdev);
+       unsigned long minsz;
+
+       if (cmd == VFIO_DEVICE_GET_INFO) {
+               struct vfio_device_info info;
+               struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
+               unsigned long capsz;
+               int ret;
+
+               minsz = offsetofend(struct vfio_device_info, num_irqs);
+
+               /* For backward compatibility, cannot require this */
+               capsz = offsetofend(struct vfio_iommu_type1_info, cap_offset);
+
+               if (copy_from_user(&info, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (info.argsz < minsz)
+                       return -EINVAL;
+
+               if (info.argsz >= capsz) {
+                       minsz = capsz;
+                       info.cap_offset = 0;
+               }
+
+               info.flags = VFIO_DEVICE_FLAGS_PCI;
+
+               if (vdev->reset_works)
+                       info.flags |= VFIO_DEVICE_FLAGS_RESET;
+
+               info.num_regions = VFIO_PCI_NUM_REGIONS + vdev->num_regions;
+               info.num_irqs = VFIO_PCI_NUM_IRQS;
+
+               ret = vfio_pci_info_zdev_add_caps(vdev, &caps);
+               if (ret && ret != -ENODEV) {
+                       pci_warn(vdev->pdev, "Failed to setup zPCI info capabilities\n");
+                       return ret;
+               }
+
+               if (caps.size) {
+                       info.flags |= VFIO_DEVICE_FLAGS_CAPS;
+                       if (info.argsz < sizeof(info) + caps.size) {
+                               info.argsz = sizeof(info) + caps.size;
+                       } else {
+                               vfio_info_cap_shift(&caps, sizeof(info));
+                               if (copy_to_user((void __user *)arg +
+                                                 sizeof(info), caps.buf,
+                                                 caps.size)) {
+                                       kfree(caps.buf);
+                                       return -EFAULT;
+                               }
+                               info.cap_offset = sizeof(info);
+                       }
+
+                       kfree(caps.buf);
+               }
+
+               return copy_to_user((void __user *)arg, &info, minsz) ?
+                       -EFAULT : 0;
+
+       } else if (cmd == VFIO_DEVICE_GET_REGION_INFO) {
+               struct pci_dev *pdev = vdev->pdev;
+               struct vfio_region_info info;
+               struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
+               int i, ret;
+
+               minsz = offsetofend(struct vfio_region_info, offset);
+
+               if (copy_from_user(&info, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (info.argsz < minsz)
+                       return -EINVAL;
+
+               switch (info.index) {
+               case VFIO_PCI_CONFIG_REGION_INDEX:
+                       info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+                       info.size = pdev->cfg_size;
+                       info.flags = VFIO_REGION_INFO_FLAG_READ |
+                                    VFIO_REGION_INFO_FLAG_WRITE;
+                       break;
+               case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
+                       info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+                       info.size = pci_resource_len(pdev, info.index);
+                       if (!info.size) {
+                               info.flags = 0;
+                               break;
+                       }
+
+                       info.flags = VFIO_REGION_INFO_FLAG_READ |
+                                    VFIO_REGION_INFO_FLAG_WRITE;
+                       if (vdev->bar_mmap_supported[info.index]) {
+                               info.flags |= VFIO_REGION_INFO_FLAG_MMAP;
+                               if (info.index == vdev->msix_bar) {
+                                       ret = msix_mmappable_cap(vdev, &caps);
+                                       if (ret)
+                                               return ret;
+                               }
+                       }
+
+                       break;
+               case VFIO_PCI_ROM_REGION_INDEX:
+               {
+                       void __iomem *io;
+                       size_t size;
+                       u16 cmd;
+
+                       info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+                       info.flags = 0;
+
+                       /* Report the BAR size, not the ROM size */
+                       info.size = pci_resource_len(pdev, info.index);
+                       if (!info.size) {
+                               /* Shadow ROMs appear as PCI option ROMs */
+                               if (pdev->resource[PCI_ROM_RESOURCE].flags &
+                                                       IORESOURCE_ROM_SHADOW)
+                                       info.size = 0x20000;
+                               else
+                                       break;
+                       }
+
+                       /*
+                        * Is it really there?  Enable memory decode for
+                        * implicit access in pci_map_rom().
+                        */
+                       cmd = vfio_pci_memory_lock_and_enable(vdev);
+                       io = pci_map_rom(pdev, &size);
+                       if (io) {
+                               info.flags = VFIO_REGION_INFO_FLAG_READ;
+                               pci_unmap_rom(pdev, io);
+                       } else {
+                               info.size = 0;
+                       }
+                       vfio_pci_memory_unlock_and_restore(vdev, cmd);
+
+                       break;
+               }
+               case VFIO_PCI_VGA_REGION_INDEX:
+                       if (!vdev->has_vga)
+                               return -EINVAL;
+
+                       info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+                       info.size = 0xc0000;
+                       info.flags = VFIO_REGION_INFO_FLAG_READ |
+                                    VFIO_REGION_INFO_FLAG_WRITE;
+
+                       break;
+               default:
+               {
+                       struct vfio_region_info_cap_type cap_type = {
+                                       .header.id = VFIO_REGION_INFO_CAP_TYPE,
+                                       .header.version = 1 };
+
+                       if (info.index >=
+                           VFIO_PCI_NUM_REGIONS + vdev->num_regions)
+                               return -EINVAL;
+                       info.index = array_index_nospec(info.index,
+                                                       VFIO_PCI_NUM_REGIONS +
+                                                       vdev->num_regions);
+
+                       i = info.index - VFIO_PCI_NUM_REGIONS;
+
+                       info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+                       info.size = vdev->region[i].size;
+                       info.flags = vdev->region[i].flags;
+
+                       cap_type.type = vdev->region[i].type;
+                       cap_type.subtype = vdev->region[i].subtype;
+
+                       ret = vfio_info_add_capability(&caps, &cap_type.header,
+                                                      sizeof(cap_type));
+                       if (ret)
+                               return ret;
+
+                       if (vdev->region[i].ops->add_capability) {
+                               ret = vdev->region[i].ops->add_capability(vdev,
+                                               &vdev->region[i], &caps);
+                               if (ret)
+                                       return ret;
+                       }
+               }
+               }
+
+               if (caps.size) {
+                       info.flags |= VFIO_REGION_INFO_FLAG_CAPS;
+                       if (info.argsz < sizeof(info) + caps.size) {
+                               info.argsz = sizeof(info) + caps.size;
+                               info.cap_offset = 0;
+                       } else {
+                               vfio_info_cap_shift(&caps, sizeof(info));
+                               if (copy_to_user((void __user *)arg +
+                                                 sizeof(info), caps.buf,
+                                                 caps.size)) {
+                                       kfree(caps.buf);
+                                       return -EFAULT;
+                               }
+                               info.cap_offset = sizeof(info);
+                       }
+
+                       kfree(caps.buf);
+               }
+
+               return copy_to_user((void __user *)arg, &info, minsz) ?
+                       -EFAULT : 0;
+
+       } else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) {
+               struct vfio_irq_info info;
+
+               minsz = offsetofend(struct vfio_irq_info, count);
+
+               if (copy_from_user(&info, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (info.argsz < minsz || info.index >= VFIO_PCI_NUM_IRQS)
+                       return -EINVAL;
+
+               switch (info.index) {
+               case VFIO_PCI_INTX_IRQ_INDEX ... VFIO_PCI_MSIX_IRQ_INDEX:
+               case VFIO_PCI_REQ_IRQ_INDEX:
+                       break;
+               case VFIO_PCI_ERR_IRQ_INDEX:
+                       if (pci_is_pcie(vdev->pdev))
+                               break;
+                       fallthrough;
+               default:
+                       return -EINVAL;
+               }
+
+               info.flags = VFIO_IRQ_INFO_EVENTFD;
+
+               info.count = vfio_pci_get_irq_count(vdev, info.index);
+
+               if (info.index == VFIO_PCI_INTX_IRQ_INDEX)
+                       info.flags |= (VFIO_IRQ_INFO_MASKABLE |
+                                      VFIO_IRQ_INFO_AUTOMASKED);
+               else
+                       info.flags |= VFIO_IRQ_INFO_NORESIZE;
+
+               return copy_to_user((void __user *)arg, &info, minsz) ?
+                       -EFAULT : 0;
+
+       } else if (cmd == VFIO_DEVICE_SET_IRQS) {
+               struct vfio_irq_set hdr;
+               u8 *data = NULL;
+               int max, ret = 0;
+               size_t data_size = 0;
+
+               minsz = offsetofend(struct vfio_irq_set, count);
+
+               if (copy_from_user(&hdr, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               max = vfio_pci_get_irq_count(vdev, hdr.index);
+
+               ret = vfio_set_irqs_validate_and_prepare(&hdr, max,
+                                                VFIO_PCI_NUM_IRQS, &data_size);
+               if (ret)
+                       return ret;
+
+               if (data_size) {
+                       data = memdup_user((void __user *)(arg + minsz),
+                                           data_size);
+                       if (IS_ERR(data))
+                               return PTR_ERR(data);
+               }
+
+               mutex_lock(&vdev->igate);
+
+               ret = vfio_pci_set_irqs_ioctl(vdev, hdr.flags, hdr.index,
+                                             hdr.start, hdr.count, data);
+
+               mutex_unlock(&vdev->igate);
+               kfree(data);
+
+               return ret;
+
+       } else if (cmd == VFIO_DEVICE_RESET) {
+               int ret;
+
+               if (!vdev->reset_works)
+                       return -EINVAL;
+
+               vfio_pci_zap_and_down_write_memory_lock(vdev);
+               ret = pci_try_reset_function(vdev->pdev);
+               up_write(&vdev->memory_lock);
+
+               return ret;
+
+       } else if (cmd == VFIO_DEVICE_GET_PCI_HOT_RESET_INFO) {
+               struct vfio_pci_hot_reset_info hdr;
+               struct vfio_pci_fill_info fill = { 0 };
+               struct vfio_pci_dependent_device *devices = NULL;
+               bool slot = false;
+               int ret = 0;
+
+               minsz = offsetofend(struct vfio_pci_hot_reset_info, count);
+
+               if (copy_from_user(&hdr, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (hdr.argsz < minsz)
+                       return -EINVAL;
+
+               hdr.flags = 0;
+
+               /* Can we do a slot or bus reset or neither? */
+               if (!pci_probe_reset_slot(vdev->pdev->slot))
+                       slot = true;
+               else if (pci_probe_reset_bus(vdev->pdev->bus))
+                       return -ENODEV;
+
+               /* How many devices are affected? */
+               ret = vfio_pci_for_each_slot_or_bus(vdev->pdev,
+                                                   vfio_pci_count_devs,
+                                                   &fill.max, slot);
+               if (ret)
+                       return ret;
+
+               WARN_ON(!fill.max); /* Should always be at least one */
+
+               /*
+                * If there's enough space, fill it now, otherwise return
+                * -ENOSPC and the number of devices affected.
+                */
+               if (hdr.argsz < sizeof(hdr) + (fill.max * sizeof(*devices))) {
+                       ret = -ENOSPC;
+                       hdr.count = fill.max;
+                       goto reset_info_exit;
+               }
+
+               devices = kcalloc(fill.max, sizeof(*devices), GFP_KERNEL);
+               if (!devices)
+                       return -ENOMEM;
+
+               fill.devices = devices;
+
+               ret = vfio_pci_for_each_slot_or_bus(vdev->pdev,
+                                                   vfio_pci_fill_devs,
+                                                   &fill, slot);
+
+               /*
+                * If a device was removed between counting and filling,
+                * we may come up short of fill.max.  If a device was
+                * added, we'll have a return of -EAGAIN above.
+                */
+               if (!ret)
+                       hdr.count = fill.cur;
+
+reset_info_exit:
+               if (copy_to_user((void __user *)arg, &hdr, minsz))
+                       ret = -EFAULT;
+
+               if (!ret) {
+                       if (copy_to_user((void __user *)(arg + minsz), devices,
+                                        hdr.count * sizeof(*devices)))
+                               ret = -EFAULT;
+               }
+
+               kfree(devices);
+               return ret;
+
+       } else if (cmd == VFIO_DEVICE_PCI_HOT_RESET) {
+               struct vfio_pci_hot_reset hdr;
+               int32_t *group_fds;
+               struct vfio_group **groups;
+               struct vfio_pci_group_info info;
+               bool slot = false;
+               int group_idx, count = 0, ret = 0;
+
+               minsz = offsetofend(struct vfio_pci_hot_reset, count);
+
+               if (copy_from_user(&hdr, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (hdr.argsz < minsz || hdr.flags)
+                       return -EINVAL;
+
+               /* Can we do a slot or bus reset or neither? */
+               if (!pci_probe_reset_slot(vdev->pdev->slot))
+                       slot = true;
+               else if (pci_probe_reset_bus(vdev->pdev->bus))
+                       return -ENODEV;
+
+               /*
+                * We can't let userspace give us an arbitrarily large
+                * buffer to copy, so verify how many we think there
+                * could be.  Note groups can have multiple devices so
+                * one group per device is the max.
+                */
+               ret = vfio_pci_for_each_slot_or_bus(vdev->pdev,
+                                                   vfio_pci_count_devs,
+                                                   &count, slot);
+               if (ret)
+                       return ret;
+
+               /* Somewhere between 1 and count is OK */
+               if (!hdr.count || hdr.count > count)
+                       return -EINVAL;
+
+               group_fds = kcalloc(hdr.count, sizeof(*group_fds), GFP_KERNEL);
+               groups = kcalloc(hdr.count, sizeof(*groups), GFP_KERNEL);
+               if (!group_fds || !groups) {
+                       kfree(group_fds);
+                       kfree(groups);
+                       return -ENOMEM;
+               }
+
+               if (copy_from_user(group_fds, (void __user *)(arg + minsz),
+                                  hdr.count * sizeof(*group_fds))) {
+                       kfree(group_fds);
+                       kfree(groups);
+                       return -EFAULT;
+               }
+
+               /*
+                * For each group_fd, get the group through the vfio external
+                * user interface and store the group and iommu ID.  This
+                * ensures the group is held across the reset.
+                */
+               for (group_idx = 0; group_idx < hdr.count; group_idx++) {
+                       struct vfio_group *group;
+                       struct fd f = fdget(group_fds[group_idx]);
+                       if (!f.file) {
+                               ret = -EBADF;
+                               break;
+                       }
+
+                       group = vfio_group_get_external_user(f.file);
+                       fdput(f);
+                       if (IS_ERR(group)) {
+                               ret = PTR_ERR(group);
+                               break;
+                       }
+
+                       groups[group_idx] = group;
+               }
+
+               kfree(group_fds);
+
+               /* release reference to groups on error */
+               if (ret)
+                       goto hot_reset_release;
+
+               info.count = hdr.count;
+               info.groups = groups;
+
+               ret = vfio_pci_dev_set_hot_reset(vdev->vdev.dev_set, &info);
+
+hot_reset_release:
+               for (group_idx--; group_idx >= 0; group_idx--)
+                       vfio_group_put_external_user(groups[group_idx]);
+
+               kfree(groups);
+               return ret;
+       } else if (cmd == VFIO_DEVICE_IOEVENTFD) {
+               struct vfio_device_ioeventfd ioeventfd;
+               int count;
+
+               minsz = offsetofend(struct vfio_device_ioeventfd, fd);
+
+               if (copy_from_user(&ioeventfd, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (ioeventfd.argsz < minsz)
+                       return -EINVAL;
+
+               if (ioeventfd.flags & ~VFIO_DEVICE_IOEVENTFD_SIZE_MASK)
+                       return -EINVAL;
+
+               count = ioeventfd.flags & VFIO_DEVICE_IOEVENTFD_SIZE_MASK;
+
+               if (hweight8(count) != 1 || ioeventfd.fd < -1)
+                       return -EINVAL;
+
+               return vfio_pci_ioeventfd(vdev, ioeventfd.offset,
+                                         ioeventfd.data, count, ioeventfd.fd);
+       } else if (cmd == VFIO_DEVICE_FEATURE) {
+               struct vfio_device_feature feature;
+               uuid_t uuid;
+
+               minsz = offsetofend(struct vfio_device_feature, flags);
+
+               if (copy_from_user(&feature, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (feature.argsz < minsz)
+                       return -EINVAL;
+
+               /* Check unknown flags */
+               if (feature.flags & ~(VFIO_DEVICE_FEATURE_MASK |
+                                     VFIO_DEVICE_FEATURE_SET |
+                                     VFIO_DEVICE_FEATURE_GET |
+                                     VFIO_DEVICE_FEATURE_PROBE))
+                       return -EINVAL;
+
+               /* GET & SET are mutually exclusive except with PROBE */
+               if (!(feature.flags & VFIO_DEVICE_FEATURE_PROBE) &&
+                   (feature.flags & VFIO_DEVICE_FEATURE_SET) &&
+                   (feature.flags & VFIO_DEVICE_FEATURE_GET))
+                       return -EINVAL;
+
+               switch (feature.flags & VFIO_DEVICE_FEATURE_MASK) {
+               case VFIO_DEVICE_FEATURE_PCI_VF_TOKEN:
+                       if (!vdev->vf_token)
+                               return -ENOTTY;
+
+                       /*
+                        * We do not support GET of the VF Token UUID as this
+                        * could expose the token of the previous device user.
+                        */
+                       if (feature.flags & VFIO_DEVICE_FEATURE_GET)
+                               return -EINVAL;
+
+                       if (feature.flags & VFIO_DEVICE_FEATURE_PROBE)
+                               return 0;
+
+                       /* Don't SET unless told to do so */
+                       if (!(feature.flags & VFIO_DEVICE_FEATURE_SET))
+                               return -EINVAL;
+
+                       if (feature.argsz < minsz + sizeof(uuid))
+                               return -EINVAL;
+
+                       if (copy_from_user(&uuid, (void __user *)(arg + minsz),
+                                          sizeof(uuid)))
+                               return -EFAULT;
+
+                       mutex_lock(&vdev->vf_token->lock);
+                       uuid_copy(&vdev->vf_token->uuid, &uuid);
+                       mutex_unlock(&vdev->vf_token->lock);
+
+                       return 0;
+               default:
+                       return -ENOTTY;
+               }
+       }
+
+       return -ENOTTY;
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_ioctl);
+
+static ssize_t vfio_pci_rw(struct vfio_pci_core_device *vdev, char __user *buf,
+                          size_t count, loff_t *ppos, bool iswrite)
+{
+       unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
+
+       if (index >= VFIO_PCI_NUM_REGIONS + vdev->num_regions)
+               return -EINVAL;
+
+       switch (index) {
+       case VFIO_PCI_CONFIG_REGION_INDEX:
+               return vfio_pci_config_rw(vdev, buf, count, ppos, iswrite);
+
+       case VFIO_PCI_ROM_REGION_INDEX:
+               if (iswrite)
+                       return -EINVAL;
+               return vfio_pci_bar_rw(vdev, buf, count, ppos, false);
+
+       case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
+               return vfio_pci_bar_rw(vdev, buf, count, ppos, iswrite);
+
+       case VFIO_PCI_VGA_REGION_INDEX:
+               return vfio_pci_vga_rw(vdev, buf, count, ppos, iswrite);
+       default:
+               index -= VFIO_PCI_NUM_REGIONS;
+               return vdev->region[index].ops->rw(vdev, buf,
+                                                  count, ppos, iswrite);
+       }
+
+       return -EINVAL;
+}
+
+ssize_t vfio_pci_core_read(struct vfio_device *core_vdev, char __user *buf,
+               size_t count, loff_t *ppos)
+{
+       struct vfio_pci_core_device *vdev =
+               container_of(core_vdev, struct vfio_pci_core_device, vdev);
+
+       if (!count)
+               return 0;
+
+       return vfio_pci_rw(vdev, buf, count, ppos, false);
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_read);
+
+ssize_t vfio_pci_core_write(struct vfio_device *core_vdev, const char __user *buf,
+               size_t count, loff_t *ppos)
+{
+       struct vfio_pci_core_device *vdev =
+               container_of(core_vdev, struct vfio_pci_core_device, vdev);
+
+       if (!count)
+               return 0;
+
+       return vfio_pci_rw(vdev, (char __user *)buf, count, ppos, true);
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_write);
+
+/* Return 1 on zap and vma_lock acquired, 0 on contention (only with @try) */
+static int vfio_pci_zap_and_vma_lock(struct vfio_pci_core_device *vdev, bool try)
+{
+       struct vfio_pci_mmap_vma *mmap_vma, *tmp;
+
+       /*
+        * Lock ordering:
+        * vma_lock is nested under mmap_lock for vm_ops callback paths.
+        * The memory_lock semaphore is used by both code paths calling
+        * into this function to zap vmas and the vm_ops.fault callback
+        * to protect the memory enable state of the device.
+        *
+        * When zapping vmas we need to maintain the mmap_lock => vma_lock
+        * ordering, which requires using vma_lock to walk vma_list to
+        * acquire an mm, then dropping vma_lock to get the mmap_lock and
+        * reacquiring vma_lock.  This logic is derived from similar
+        * requirements in uverbs_user_mmap_disassociate().
+        *
+        * mmap_lock must always be the top-level lock when it is taken.
+        * Therefore we can only hold the memory_lock write lock when
+        * vma_list is empty, as we'd need to take mmap_lock to clear
+        * entries.  vma_list can only be guaranteed empty when holding
+        * vma_lock, thus memory_lock is nested under vma_lock.
+        *
+        * This enables the vm_ops.fault callback to acquire vma_lock,
+        * followed by memory_lock read lock, while already holding
+        * mmap_lock without risk of deadlock.
+        */
+       while (1) {
+               struct mm_struct *mm = NULL;
+
+               if (try) {
+                       if (!mutex_trylock(&vdev->vma_lock))
+                               return 0;
+               } else {
+                       mutex_lock(&vdev->vma_lock);
+               }
+               while (!list_empty(&vdev->vma_list)) {
+                       mmap_vma = list_first_entry(&vdev->vma_list,
+                                                   struct vfio_pci_mmap_vma,
+                                                   vma_next);
+                       mm = mmap_vma->vma->vm_mm;
+                       if (mmget_not_zero(mm))
+                               break;
+
+                       list_del(&mmap_vma->vma_next);
+                       kfree(mmap_vma);
+                       mm = NULL;
+               }
+               if (!mm)
+                       return 1;
+               mutex_unlock(&vdev->vma_lock);
+
+               if (try) {
+                       if (!mmap_read_trylock(mm)) {
+                               mmput(mm);
+                               return 0;
+                       }
+               } else {
+                       mmap_read_lock(mm);
+               }
+               if (try) {
+                       if (!mutex_trylock(&vdev->vma_lock)) {
+                               mmap_read_unlock(mm);
+                               mmput(mm);
+                               return 0;
+                       }
+               } else {
+                       mutex_lock(&vdev->vma_lock);
+               }
+               list_for_each_entry_safe(mmap_vma, tmp,
+                                        &vdev->vma_list, vma_next) {
+                       struct vm_area_struct *vma = mmap_vma->vma;
+
+                       if (vma->vm_mm != mm)
+                               continue;
+
+                       list_del(&mmap_vma->vma_next);
+                       kfree(mmap_vma);
+
+                       zap_vma_ptes(vma, vma->vm_start,
+                                    vma->vm_end - vma->vm_start);
+               }
+               mutex_unlock(&vdev->vma_lock);
+               mmap_read_unlock(mm);
+               mmput(mm);
+       }
+}
+
+void vfio_pci_zap_and_down_write_memory_lock(struct vfio_pci_core_device *vdev)
+{
+       vfio_pci_zap_and_vma_lock(vdev, false);
+       down_write(&vdev->memory_lock);
+       mutex_unlock(&vdev->vma_lock);
+}
+
+u16 vfio_pci_memory_lock_and_enable(struct vfio_pci_core_device *vdev)
+{
+       u16 cmd;
+
+       down_write(&vdev->memory_lock);
+       pci_read_config_word(vdev->pdev, PCI_COMMAND, &cmd);
+       if (!(cmd & PCI_COMMAND_MEMORY))
+               pci_write_config_word(vdev->pdev, PCI_COMMAND,
+                                     cmd | PCI_COMMAND_MEMORY);
+
+       return cmd;
+}
+
+void vfio_pci_memory_unlock_and_restore(struct vfio_pci_core_device *vdev, u16 cmd)
+{
+       pci_write_config_word(vdev->pdev, PCI_COMMAND, cmd);
+       up_write(&vdev->memory_lock);
+}
+
+/* Caller holds vma_lock */
+static int __vfio_pci_add_vma(struct vfio_pci_core_device *vdev,
+                             struct vm_area_struct *vma)
+{
+       struct vfio_pci_mmap_vma *mmap_vma;
+
+       mmap_vma = kmalloc(sizeof(*mmap_vma), GFP_KERNEL);
+       if (!mmap_vma)
+               return -ENOMEM;
+
+       mmap_vma->vma = vma;
+       list_add(&mmap_vma->vma_next, &vdev->vma_list);
+
+       return 0;
+}
+
+/*
+ * Zap mmaps on open so that we can fault them in on access and therefore
+ * our vma_list only tracks mappings accessed since last zap.
+ */
+static void vfio_pci_mmap_open(struct vm_area_struct *vma)
+{
+       zap_vma_ptes(vma, vma->vm_start, vma->vm_end - vma->vm_start);
+}
+
+static void vfio_pci_mmap_close(struct vm_area_struct *vma)
+{
+       struct vfio_pci_core_device *vdev = vma->vm_private_data;
+       struct vfio_pci_mmap_vma *mmap_vma;
+
+       mutex_lock(&vdev->vma_lock);
+       list_for_each_entry(mmap_vma, &vdev->vma_list, vma_next) {
+               if (mmap_vma->vma == vma) {
+                       list_del(&mmap_vma->vma_next);
+                       kfree(mmap_vma);
+                       break;
+               }
+       }
+       mutex_unlock(&vdev->vma_lock);
+}
+
+static vm_fault_t vfio_pci_mmap_fault(struct vm_fault *vmf)
+{
+       struct vm_area_struct *vma = vmf->vma;
+       struct vfio_pci_core_device *vdev = vma->vm_private_data;
+       struct vfio_pci_mmap_vma *mmap_vma;
+       vm_fault_t ret = VM_FAULT_NOPAGE;
+
+       mutex_lock(&vdev->vma_lock);
+       down_read(&vdev->memory_lock);
+
+       if (!__vfio_pci_memory_enabled(vdev)) {
+               ret = VM_FAULT_SIGBUS;
+               goto up_out;
+       }
+
+       /*
+        * We populate the whole vma on fault, so we need to test whether
+        * the vma has already been mapped, such as for concurrent faults
+        * to the same vma.  io_remap_pfn_range() will trigger a BUG_ON if
+        * we ask it to fill the same range again.
+        */
+       list_for_each_entry(mmap_vma, &vdev->vma_list, vma_next) {
+               if (mmap_vma->vma == vma)
+                       goto up_out;
+       }
+
+       if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+                              vma->vm_end - vma->vm_start,
+                              vma->vm_page_prot)) {
+               ret = VM_FAULT_SIGBUS;
+               zap_vma_ptes(vma, vma->vm_start, vma->vm_end - vma->vm_start);
+               goto up_out;
+       }
+
+       if (__vfio_pci_add_vma(vdev, vma)) {
+               ret = VM_FAULT_OOM;
+               zap_vma_ptes(vma, vma->vm_start, vma->vm_end - vma->vm_start);
+       }
+
+up_out:
+       up_read(&vdev->memory_lock);
+       mutex_unlock(&vdev->vma_lock);
+       return ret;
+}
+
+static const struct vm_operations_struct vfio_pci_mmap_ops = {
+       .open = vfio_pci_mmap_open,
+       .close = vfio_pci_mmap_close,
+       .fault = vfio_pci_mmap_fault,
+};
+
+int vfio_pci_core_mmap(struct vfio_device *core_vdev, struct vm_area_struct *vma)
+{
+       struct vfio_pci_core_device *vdev =
+               container_of(core_vdev, struct vfio_pci_core_device, vdev);
+       struct pci_dev *pdev = vdev->pdev;
+       unsigned int index;
+       u64 phys_len, req_len, pgoff, req_start;
+       int ret;
+
+       index = vma->vm_pgoff >> (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT);
+
+       if (index >= VFIO_PCI_NUM_REGIONS + vdev->num_regions)
+               return -EINVAL;
+       if (vma->vm_end < vma->vm_start)
+               return -EINVAL;
+       if ((vma->vm_flags & VM_SHARED) == 0)
+               return -EINVAL;
+       if (index >= VFIO_PCI_NUM_REGIONS) {
+               int regnum = index - VFIO_PCI_NUM_REGIONS;
+               struct vfio_pci_region *region = vdev->region + regnum;
+
+               if (region->ops && region->ops->mmap &&
+                   (region->flags & VFIO_REGION_INFO_FLAG_MMAP))
+                       return region->ops->mmap(vdev, region, vma);
+               return -EINVAL;
+       }
+       if (index >= VFIO_PCI_ROM_REGION_INDEX)
+               return -EINVAL;
+       if (!vdev->bar_mmap_supported[index])
+               return -EINVAL;
+
+       phys_len = PAGE_ALIGN(pci_resource_len(pdev, index));
+       req_len = vma->vm_end - vma->vm_start;
+       pgoff = vma->vm_pgoff &
+               ((1U << (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
+       req_start = pgoff << PAGE_SHIFT;
+
+       if (req_start + req_len > phys_len)
+               return -EINVAL;
+
+       /*
+        * Even though we don't make use of the barmap for the mmap,
+        * we need to request the region and the barmap tracks that.
+        */
+       if (!vdev->barmap[index]) {
+               ret = pci_request_selected_regions(pdev,
+                                                  1 << index, "vfio-pci");
+               if (ret)
+                       return ret;
+
+               vdev->barmap[index] = pci_iomap(pdev, index, 0);
+               if (!vdev->barmap[index]) {
+                       pci_release_selected_regions(pdev, 1 << index);
+                       return -ENOMEM;
+               }
+       }
+
+       vma->vm_private_data = vdev;
+       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+       vma->vm_pgoff = (pci_resource_start(pdev, index) >> PAGE_SHIFT) + pgoff;
+
+       /*
+        * See remap_pfn_range(), called from vfio_pci_fault() but we can't
+        * change vm_flags within the fault handler.  Set them now.
+        */
+       vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
+       vma->vm_ops = &vfio_pci_mmap_ops;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_mmap);
+
+void vfio_pci_core_request(struct vfio_device *core_vdev, unsigned int count)
+{
+       struct vfio_pci_core_device *vdev =
+               container_of(core_vdev, struct vfio_pci_core_device, vdev);
+       struct pci_dev *pdev = vdev->pdev;
+
+       mutex_lock(&vdev->igate);
+
+       if (vdev->req_trigger) {
+               if (!(count % 10))
+                       pci_notice_ratelimited(pdev,
+                               "Relaying device request to user (#%u)\n",
+                               count);
+               eventfd_signal(vdev->req_trigger, 1);
+       } else if (count == 0) {
+               pci_warn(pdev,
+                       "No device request channel registered, blocked until released by user\n");
+       }
+
+       mutex_unlock(&vdev->igate);
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_request);
+
+static int vfio_pci_validate_vf_token(struct vfio_pci_core_device *vdev,
+                                     bool vf_token, uuid_t *uuid)
+{
+       /*
+        * There's always some degree of trust or collaboration between SR-IOV
+        * PF and VFs, even if just that the PF hosts the SR-IOV capability and
+        * can disrupt VFs with a reset, but often the PF has more explicit
+        * access to deny service to the VF or access data passed through the
+        * VF.  We therefore require an opt-in via a shared VF token (UUID) to
+        * represent this trust.  This both prevents that a VF driver might
+        * assume the PF driver is a trusted, in-kernel driver, and also that
+        * a PF driver might be replaced with a rogue driver, unknown to in-use
+        * VF drivers.
+        *
+        * Therefore when presented with a VF, if the PF is a vfio device and
+        * it is bound to the vfio-pci driver, the user needs to provide a VF
+        * token to access the device, in the form of appending a vf_token to
+        * the device name, for example:
+        *
+        * "0000:04:10.0 vf_token=bd8d9d2b-5a5f-4f5a-a211-f591514ba1f3"
+        *
+        * When presented with a PF which has VFs in use, the user must also
+        * provide the current VF token to prove collaboration with existing
+        * VF users.  If VFs are not in use, the VF token provided for the PF
+        * device will act to set the VF token.
+        *
+        * If the VF token is provided but unused, an error is generated.
+        */
+       if (!vdev->pdev->is_virtfn && !vdev->vf_token && !vf_token)
+               return 0; /* No VF token provided or required */
+
+       if (vdev->pdev->is_virtfn) {
+               struct vfio_pci_core_device *pf_vdev = get_pf_vdev(vdev);
+               bool match;
+
+               if (!pf_vdev) {
+                       if (!vf_token)
+                               return 0; /* PF is not vfio-pci, no VF token */
+
+                       pci_info_ratelimited(vdev->pdev,
+                               "VF token incorrectly provided, PF not bound to vfio-pci\n");
+                       return -EINVAL;
+               }
+
+               if (!vf_token) {
+                       vfio_device_put(&pf_vdev->vdev);
+                       pci_info_ratelimited(vdev->pdev,
+                               "VF token required to access device\n");
+                       return -EACCES;
+               }
+
+               mutex_lock(&pf_vdev->vf_token->lock);
+               match = uuid_equal(uuid, &pf_vdev->vf_token->uuid);
+               mutex_unlock(&pf_vdev->vf_token->lock);
+
+               vfio_device_put(&pf_vdev->vdev);
+
+               if (!match) {
+                       pci_info_ratelimited(vdev->pdev,
+                               "Incorrect VF token provided for device\n");
+                       return -EACCES;
+               }
+       } else if (vdev->vf_token) {
+               mutex_lock(&vdev->vf_token->lock);
+               if (vdev->vf_token->users) {
+                       if (!vf_token) {
+                               mutex_unlock(&vdev->vf_token->lock);
+                               pci_info_ratelimited(vdev->pdev,
+                                       "VF token required to access device\n");
+                               return -EACCES;
+                       }
+
+                       if (!uuid_equal(uuid, &vdev->vf_token->uuid)) {
+                               mutex_unlock(&vdev->vf_token->lock);
+                               pci_info_ratelimited(vdev->pdev,
+                                       "Incorrect VF token provided for device\n");
+                               return -EACCES;
+                       }
+               } else if (vf_token) {
+                       uuid_copy(&vdev->vf_token->uuid, uuid);
+               }
+
+               mutex_unlock(&vdev->vf_token->lock);
+       } else if (vf_token) {
+               pci_info_ratelimited(vdev->pdev,
+                       "VF token incorrectly provided, not a PF or VF\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+#define VF_TOKEN_ARG "vf_token="
+
+int vfio_pci_core_match(struct vfio_device *core_vdev, char *buf)
+{
+       struct vfio_pci_core_device *vdev =
+               container_of(core_vdev, struct vfio_pci_core_device, vdev);
+       bool vf_token = false;
+       uuid_t uuid;
+       int ret;
+
+       if (strncmp(pci_name(vdev->pdev), buf, strlen(pci_name(vdev->pdev))))
+               return 0; /* No match */
+
+       if (strlen(buf) > strlen(pci_name(vdev->pdev))) {
+               buf += strlen(pci_name(vdev->pdev));
+
+               if (*buf != ' ')
+                       return 0; /* No match: non-whitespace after name */
+
+               while (*buf) {
+                       if (*buf == ' ') {
+                               buf++;
+                               continue;
+                       }
+
+                       if (!vf_token && !strncmp(buf, VF_TOKEN_ARG,
+                                                 strlen(VF_TOKEN_ARG))) {
+                               buf += strlen(VF_TOKEN_ARG);
+
+                               if (strlen(buf) < UUID_STRING_LEN)
+                                       return -EINVAL;
+
+                               ret = uuid_parse(buf, &uuid);
+                               if (ret)
+                                       return ret;
+
+                               vf_token = true;
+                               buf += UUID_STRING_LEN;
+                       } else {
+                               /* Unknown/duplicate option */
+                               return -EINVAL;
+                       }
+               }
+       }
+
+       ret = vfio_pci_validate_vf_token(vdev, vf_token, &uuid);
+       if (ret)
+               return ret;
+
+       return 1; /* Match */
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_match);
+
+static int vfio_pci_bus_notifier(struct notifier_block *nb,
+                                unsigned long action, void *data)
+{
+       struct vfio_pci_core_device *vdev = container_of(nb,
+                                                   struct vfio_pci_core_device, nb);
+       struct device *dev = data;
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct pci_dev *physfn = pci_physfn(pdev);
+
+       if (action == BUS_NOTIFY_ADD_DEVICE &&
+           pdev->is_virtfn && physfn == vdev->pdev) {
+               pci_info(vdev->pdev, "Captured SR-IOV VF %s driver_override\n",
+                        pci_name(pdev));
+               pdev->driver_override = kasprintf(GFP_KERNEL, "%s",
+                                                 vdev->vdev.ops->name);
+       } else if (action == BUS_NOTIFY_BOUND_DRIVER &&
+                  pdev->is_virtfn && physfn == vdev->pdev) {
+               struct pci_driver *drv = pci_dev_driver(pdev);
+
+               if (drv && drv != pci_dev_driver(vdev->pdev))
+                       pci_warn(vdev->pdev,
+                                "VF %s bound to driver %s while PF bound to driver %s\n",
+                                pci_name(pdev), drv->name,
+                                pci_dev_driver(vdev->pdev)->name);
+       }
+
+       return 0;
+}
+
+static int vfio_pci_vf_init(struct vfio_pci_core_device *vdev)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       int ret;
+
+       if (!pdev->is_physfn)
+               return 0;
+
+       vdev->vf_token = kzalloc(sizeof(*vdev->vf_token), GFP_KERNEL);
+       if (!vdev->vf_token)
+               return -ENOMEM;
+
+       mutex_init(&vdev->vf_token->lock);
+       uuid_gen(&vdev->vf_token->uuid);
+
+       vdev->nb.notifier_call = vfio_pci_bus_notifier;
+       ret = bus_register_notifier(&pci_bus_type, &vdev->nb);
+       if (ret) {
+               kfree(vdev->vf_token);
+               return ret;
+       }
+       return 0;
+}
+
+static void vfio_pci_vf_uninit(struct vfio_pci_core_device *vdev)
+{
+       if (!vdev->vf_token)
+               return;
+
+       bus_unregister_notifier(&pci_bus_type, &vdev->nb);
+       WARN_ON(vdev->vf_token->users);
+       mutex_destroy(&vdev->vf_token->lock);
+       kfree(vdev->vf_token);
+}
+
+static int vfio_pci_vga_init(struct vfio_pci_core_device *vdev)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       int ret;
+
+       if (!vfio_pci_is_vga(pdev))
+               return 0;
+
+       ret = vga_client_register(pdev, vfio_pci_set_decode);
+       if (ret)
+               return ret;
+       vga_set_legacy_decoding(pdev, vfio_pci_set_decode(pdev, false));
+       return 0;
+}
+
+static void vfio_pci_vga_uninit(struct vfio_pci_core_device *vdev)
+{
+       struct pci_dev *pdev = vdev->pdev;
+
+       if (!vfio_pci_is_vga(pdev))
+               return;
+       vga_client_unregister(pdev);
+       vga_set_legacy_decoding(pdev, VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM |
+                                             VGA_RSRC_LEGACY_IO |
+                                             VGA_RSRC_LEGACY_MEM);
+}
+
+void vfio_pci_core_init_device(struct vfio_pci_core_device *vdev,
+                              struct pci_dev *pdev,
+                              const struct vfio_device_ops *vfio_pci_ops)
+{
+       vfio_init_group_dev(&vdev->vdev, &pdev->dev, vfio_pci_ops);
+       vdev->pdev = pdev;
+       vdev->irq_type = VFIO_PCI_NUM_IRQS;
+       mutex_init(&vdev->igate);
+       spin_lock_init(&vdev->irqlock);
+       mutex_init(&vdev->ioeventfds_lock);
+       INIT_LIST_HEAD(&vdev->dummy_resources_list);
+       INIT_LIST_HEAD(&vdev->ioeventfds_list);
+       mutex_init(&vdev->vma_lock);
+       INIT_LIST_HEAD(&vdev->vma_list);
+       init_rwsem(&vdev->memory_lock);
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_init_device);
+
+void vfio_pci_core_uninit_device(struct vfio_pci_core_device *vdev)
+{
+       mutex_destroy(&vdev->igate);
+       mutex_destroy(&vdev->ioeventfds_lock);
+       mutex_destroy(&vdev->vma_lock);
+       vfio_uninit_group_dev(&vdev->vdev);
+       kfree(vdev->region);
+       kfree(vdev->pm_save);
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_uninit_device);
+
+int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev)
+{
+       struct pci_dev *pdev = vdev->pdev;
+       struct iommu_group *group;
+       int ret;
+
+       if (pdev->hdr_type != PCI_HEADER_TYPE_NORMAL)
+               return -EINVAL;
+
+       /*
+        * Prevent binding to PFs with VFs enabled, the VFs might be in use
+        * by the host or other users.  We cannot capture the VFs if they
+        * already exist, nor can we track VF users.  Disabling SR-IOV here
+        * would initiate removing the VFs, which would unbind the driver,
+        * which is prone to blocking if that VF is also in use by vfio-pci.
+        * Just reject these PFs and let the user sort it out.
+        */
+       if (pci_num_vf(pdev)) {
+               pci_warn(pdev, "Cannot bind to PF with SR-IOV enabled\n");
+               return -EBUSY;
+       }
+
+       group = vfio_iommu_group_get(&pdev->dev);
+       if (!group)
+               return -EINVAL;
+
+       if (pci_is_root_bus(pdev->bus)) {
+               ret = vfio_assign_device_set(&vdev->vdev, vdev);
+       } else if (!pci_probe_reset_slot(pdev->slot)) {
+               ret = vfio_assign_device_set(&vdev->vdev, pdev->slot);
+       } else {
+               /*
+                * If there is no slot reset support for this device, the whole
+                * bus needs to be grouped together to support bus-wide resets.
+                */
+               ret = vfio_assign_device_set(&vdev->vdev, pdev->bus);
+       }
+
+       if (ret)
+               goto out_group_put;
+       ret = vfio_pci_vf_init(vdev);
+       if (ret)
+               goto out_group_put;
+       ret = vfio_pci_vga_init(vdev);
+       if (ret)
+               goto out_vf;
+
+       vfio_pci_probe_power_state(vdev);
+
+       if (!disable_idle_d3) {
+               /*
+                * pci-core sets the device power state to an unknown value at
+                * bootup and after being removed from a driver.  The only
+                * transition it allows from this unknown state is to D0, which
+                * typically happens when a driver calls pci_enable_device().
+                * We're not ready to enable the device yet, but we do want to
+                * be able to get to D3.  Therefore first do a D0 transition
+                * before going to D3.
+                */
+               vfio_pci_set_power_state(vdev, PCI_D0);
+               vfio_pci_set_power_state(vdev, PCI_D3hot);
+       }
+
+       ret = vfio_register_group_dev(&vdev->vdev);
+       if (ret)
+               goto out_power;
+       return 0;
+
+out_power:
+       if (!disable_idle_d3)
+               vfio_pci_set_power_state(vdev, PCI_D0);
+out_vf:
+       vfio_pci_vf_uninit(vdev);
+out_group_put:
+       vfio_iommu_group_put(group, &pdev->dev);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_register_device);
+
+void vfio_pci_core_unregister_device(struct vfio_pci_core_device *vdev)
+{
+       struct pci_dev *pdev = vdev->pdev;
+
+       pci_disable_sriov(pdev);
+
+       vfio_unregister_group_dev(&vdev->vdev);
+
+       vfio_pci_vf_uninit(vdev);
+       vfio_pci_vga_uninit(vdev);
+
+       vfio_iommu_group_put(pdev->dev.iommu_group, &pdev->dev);
+
+       if (!disable_idle_d3)
+               vfio_pci_set_power_state(vdev, PCI_D0);
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_unregister_device);
+
+static pci_ers_result_t vfio_pci_aer_err_detected(struct pci_dev *pdev,
+                                                 pci_channel_state_t state)
+{
+       struct vfio_pci_core_device *vdev;
+       struct vfio_device *device;
+
+       device = vfio_device_get_from_dev(&pdev->dev);
+       if (device == NULL)
+               return PCI_ERS_RESULT_DISCONNECT;
+
+       vdev = container_of(device, struct vfio_pci_core_device, vdev);
+
+       mutex_lock(&vdev->igate);
+
+       if (vdev->err_trigger)
+               eventfd_signal(vdev->err_trigger, 1);
+
+       mutex_unlock(&vdev->igate);
+
+       vfio_device_put(device);
+
+       return PCI_ERS_RESULT_CAN_RECOVER;
+}
+
+int vfio_pci_core_sriov_configure(struct pci_dev *pdev, int nr_virtfn)
+{
+       struct vfio_device *device;
+       int ret = 0;
+
+       device = vfio_device_get_from_dev(&pdev->dev);
+       if (!device)
+               return -ENODEV;
+
+       if (nr_virtfn == 0)
+               pci_disable_sriov(pdev);
+       else
+               ret = pci_enable_sriov(pdev, nr_virtfn);
+
+       vfio_device_put(device);
+
+       return ret < 0 ? ret : nr_virtfn;
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_sriov_configure);
+
+const struct pci_error_handlers vfio_pci_core_err_handlers = {
+       .error_detected = vfio_pci_aer_err_detected,
+};
+EXPORT_SYMBOL_GPL(vfio_pci_core_err_handlers);
+
+static bool vfio_dev_in_groups(struct vfio_pci_core_device *vdev,
+                              struct vfio_pci_group_info *groups)
+{
+       unsigned int i;
+
+       for (i = 0; i < groups->count; i++)
+               if (groups->groups[i] == vdev->vdev.group)
+                       return true;
+       return false;
+}
+
+static int vfio_pci_is_device_in_set(struct pci_dev *pdev, void *data)
+{
+       struct vfio_device_set *dev_set = data;
+       struct vfio_device *cur;
+
+       list_for_each_entry(cur, &dev_set->device_list, dev_set_list)
+               if (cur->dev == &pdev->dev)
+                       return 0;
+       return -EBUSY;
+}
+
+/*
+ * vfio-core considers a group to be viable and will create a vfio_device even
+ * if some devices are bound to drivers like pci-stub or pcieport. Here we
+ * require all PCI devices to be inside our dev_set since that ensures they stay
+ * put and that every driver controlling the device can co-ordinate with the
+ * device reset.
+ *
+ * Returns the pci_dev to pass to pci_reset_bus() if every PCI device to be
+ * reset is inside the dev_set, and pci_reset_bus() can succeed. NULL otherwise.
+ */
+static struct pci_dev *
+vfio_pci_dev_set_resettable(struct vfio_device_set *dev_set)
+{
+       struct pci_dev *pdev;
+
+       lockdep_assert_held(&dev_set->lock);
+
+       /*
+        * By definition all PCI devices in the dev_set share the same PCI
+        * reset, so any pci_dev will have the same outcomes for
+        * pci_probe_reset_*() and pci_reset_bus().
+        */
+       pdev = list_first_entry(&dev_set->device_list,
+                               struct vfio_pci_core_device,
+                               vdev.dev_set_list)->pdev;
+
+       /* pci_reset_bus() is supported */
+       if (pci_probe_reset_slot(pdev->slot) && pci_probe_reset_bus(pdev->bus))
+               return NULL;
+
+       if (vfio_pci_for_each_slot_or_bus(pdev, vfio_pci_is_device_in_set,
+                                         dev_set,
+                                         !pci_probe_reset_slot(pdev->slot)))
+               return NULL;
+       return pdev;
+}
+
+/*
+ * We need to get memory_lock for each device, but devices can share mmap_lock,
+ * therefore we need to zap and hold the vma_lock for each device, and only then
+ * get each memory_lock.
+ */
+static int vfio_pci_dev_set_hot_reset(struct vfio_device_set *dev_set,
+                                     struct vfio_pci_group_info *groups)
+{
+       struct vfio_pci_core_device *cur_mem;
+       struct vfio_pci_core_device *cur_vma;
+       struct vfio_pci_core_device *cur;
+       struct pci_dev *pdev;
+       bool is_mem = true;
+       int ret;
+
+       mutex_lock(&dev_set->lock);
+       cur_mem = list_first_entry(&dev_set->device_list,
+                                  struct vfio_pci_core_device,
+                                  vdev.dev_set_list);
+
+       pdev = vfio_pci_dev_set_resettable(dev_set);
+       if (!pdev) {
+               ret = -EINVAL;
+               goto err_unlock;
+       }
+
+       list_for_each_entry(cur_vma, &dev_set->device_list, vdev.dev_set_list) {
+               /*
+                * Test whether all the affected devices are contained by the
+                * set of groups provided by the user.
+                */
+               if (!vfio_dev_in_groups(cur_vma, groups)) {
+                       ret = -EINVAL;
+                       goto err_undo;
+               }
+
+               /*
+                * Locking multiple devices is prone to deadlock, runaway and
+                * unwind if we hit contention.
+                */
+               if (!vfio_pci_zap_and_vma_lock(cur_vma, true)) {
+                       ret = -EBUSY;
+                       goto err_undo;
+               }
+       }
+       cur_vma = NULL;
+
+       list_for_each_entry(cur_mem, &dev_set->device_list, vdev.dev_set_list) {
+               if (!down_write_trylock(&cur_mem->memory_lock)) {
+                       ret = -EBUSY;
+                       goto err_undo;
+               }
+               mutex_unlock(&cur_mem->vma_lock);
+       }
+       cur_mem = NULL;
+
+       ret = pci_reset_bus(pdev);
+
+err_undo:
+       list_for_each_entry(cur, &dev_set->device_list, vdev.dev_set_list) {
+               if (cur == cur_mem)
+                       is_mem = false;
+               if (cur == cur_vma)
+                       break;
+               if (is_mem)
+                       up_write(&cur->memory_lock);
+               else
+                       mutex_unlock(&cur->vma_lock);
+       }
+err_unlock:
+       mutex_unlock(&dev_set->lock);
+       return ret;
+}
+
+static bool vfio_pci_dev_set_needs_reset(struct vfio_device_set *dev_set)
+{
+       struct vfio_pci_core_device *cur;
+       bool needs_reset = false;
+
+       list_for_each_entry(cur, &dev_set->device_list, vdev.dev_set_list) {
+               /* No VFIO device in the set can have an open device FD */
+               if (cur->vdev.open_count)
+                       return false;
+               needs_reset |= cur->needs_reset;
+       }
+       return needs_reset;
+}
+
+/*
+ * If a bus or slot reset is available for the provided dev_set and:
+ *  - All of the devices affected by that bus or slot reset are unused
+ *  - At least one of the affected devices is marked dirty via
+ *    needs_reset (such as by lack of FLR support)
+ * Then attempt to perform that bus or slot reset.
+ * Returns true if the dev_set was reset.
+ */
+static bool vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set)
+{
+       struct vfio_pci_core_device *cur;
+       struct pci_dev *pdev;
+       int ret;
+
+       if (!vfio_pci_dev_set_needs_reset(dev_set))
+               return false;
+
+       pdev = vfio_pci_dev_set_resettable(dev_set);
+       if (!pdev)
+               return false;
+
+       ret = pci_reset_bus(pdev);
+       if (ret)
+               return false;
+
+       list_for_each_entry(cur, &dev_set->device_list, vdev.dev_set_list) {
+               cur->needs_reset = false;
+               if (!disable_idle_d3)
+                       vfio_pci_set_power_state(cur, PCI_D3hot);
+       }
+       return true;
+}
+
+void vfio_pci_core_set_params(bool is_nointxmask, bool is_disable_vga,
+                             bool is_disable_idle_d3)
+{
+       nointxmask = is_nointxmask;
+       disable_vga = is_disable_vga;
+       disable_idle_d3 = is_disable_idle_d3;
+}
+EXPORT_SYMBOL_GPL(vfio_pci_core_set_params);
+
+static void vfio_pci_core_cleanup(void)
+{
+       vfio_pci_uninit_perm_bits();
+}
+
+static int __init vfio_pci_core_init(void)
+{
+       /* Allocate shared config space permission data used by all devices */
+       return vfio_pci_init_perm_bits();
+}
+
+module_init(vfio_pci_core_init);
+module_exit(vfio_pci_core_cleanup);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
index 228df56..7ca4109 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/uaccess.h>
 #include <linux/vfio.h>
 
-#include "vfio_pci_private.h"
+#include <linux/vfio_pci_core.h>
 
 #define OPREGION_SIGNATURE     "IntelGraphicsMem"
 #define OPREGION_SIZE          (8 * 1024)
@@ -25,8 +25,9 @@
 #define OPREGION_RVDS          0x3c2
 #define OPREGION_VERSION       0x16
 
-static size_t vfio_pci_igd_rw(struct vfio_pci_device *vdev, char __user *buf,
-                             size_t count, loff_t *ppos, bool iswrite)
+static ssize_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;
@@ -45,7 +46,7 @@ static size_t vfio_pci_igd_rw(struct vfio_pci_device *vdev, char __user *buf,
        return count;
 }
 
-static void vfio_pci_igd_release(struct vfio_pci_device *vdev,
+static void vfio_pci_igd_release(struct vfio_pci_core_device *vdev,
                                 struct vfio_pci_region *region)
 {
        memunmap(region->data);
@@ -56,7 +57,7 @@ static const struct vfio_pci_regops vfio_pci_igd_regops = {
        .release        = vfio_pci_igd_release,
 };
 
-static int vfio_pci_igd_opregion_init(struct vfio_pci_device *vdev)
+static int vfio_pci_igd_opregion_init(struct vfio_pci_core_device *vdev)
 {
        __le32 *dwordp = (__le32 *)(vdev->vconfig + OPREGION_PCI_ADDR);
        u32 addr, size;
@@ -160,9 +161,9 @@ static int vfio_pci_igd_opregion_init(struct vfio_pci_device *vdev)
        return ret;
 }
 
-static size_t vfio_pci_igd_cfg_rw(struct vfio_pci_device *vdev,
-                                 char __user *buf, size_t count, loff_t *ppos,
-                                 bool iswrite)
+static ssize_t vfio_pci_igd_cfg_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;
        struct pci_dev *pdev = vdev->region[i].data;
@@ -253,7 +254,7 @@ static size_t vfio_pci_igd_cfg_rw(struct vfio_pci_device *vdev,
        return count;
 }
 
-static void vfio_pci_igd_cfg_release(struct vfio_pci_device *vdev,
+static void vfio_pci_igd_cfg_release(struct vfio_pci_core_device *vdev,
                                     struct vfio_pci_region *region)
 {
        struct pci_dev *pdev = region->data;
@@ -266,7 +267,7 @@ static const struct vfio_pci_regops vfio_pci_igd_cfg_regops = {
        .release        = vfio_pci_igd_cfg_release,
 };
 
-static int vfio_pci_igd_cfg_init(struct vfio_pci_device *vdev)
+static int vfio_pci_igd_cfg_init(struct vfio_pci_core_device *vdev)
 {
        struct pci_dev *host_bridge, *lpc_bridge;
        int ret;
@@ -314,7 +315,7 @@ static int vfio_pci_igd_cfg_init(struct vfio_pci_device *vdev)
        return 0;
 }
 
-int vfio_pci_igd_init(struct vfio_pci_device *vdev)
+int vfio_pci_igd_init(struct vfio_pci_core_device *vdev)
 {
        int ret;
 
index 869dce5..6069a11 100644 (file)
 #include <linux/wait.h>
 #include <linux/slab.h>
 
-#include "vfio_pci_private.h"
+#include <linux/vfio_pci_core.h>
 
 /*
  * INTx
  */
 static void vfio_send_intx_eventfd(void *opaque, void *unused)
 {
-       struct vfio_pci_device *vdev = opaque;
+       struct vfio_pci_core_device *vdev = opaque;
 
        if (likely(is_intx(vdev) && !vdev->virq_disabled))
                eventfd_signal(vdev->ctx[0].trigger, 1);
 }
 
-void vfio_pci_intx_mask(struct vfio_pci_device *vdev)
+void vfio_pci_intx_mask(struct vfio_pci_core_device *vdev)
 {
        struct pci_dev *pdev = vdev->pdev;
        unsigned long flags;
@@ -73,7 +73,7 @@ void vfio_pci_intx_mask(struct vfio_pci_device *vdev)
  */
 static int vfio_pci_intx_unmask_handler(void *opaque, void *unused)
 {
-       struct vfio_pci_device *vdev = opaque;
+       struct vfio_pci_core_device *vdev = opaque;
        struct pci_dev *pdev = vdev->pdev;
        unsigned long flags;
        int ret = 0;
@@ -107,7 +107,7 @@ static int vfio_pci_intx_unmask_handler(void *opaque, void *unused)
        return ret;
 }
 
-void vfio_pci_intx_unmask(struct vfio_pci_device *vdev)
+void vfio_pci_intx_unmask(struct vfio_pci_core_device *vdev)
 {
        if (vfio_pci_intx_unmask_handler(vdev, NULL) > 0)
                vfio_send_intx_eventfd(vdev, NULL);
@@ -115,7 +115,7 @@ void vfio_pci_intx_unmask(struct vfio_pci_device *vdev)
 
 static irqreturn_t vfio_intx_handler(int irq, void *dev_id)
 {
-       struct vfio_pci_device *vdev = dev_id;
+       struct vfio_pci_core_device *vdev = dev_id;
        unsigned long flags;
        int ret = IRQ_NONE;
 
@@ -139,7 +139,7 @@ static irqreturn_t vfio_intx_handler(int irq, void *dev_id)
        return ret;
 }
 
-static int vfio_intx_enable(struct vfio_pci_device *vdev)
+static int vfio_intx_enable(struct vfio_pci_core_device *vdev)
 {
        if (!is_irq_none(vdev))
                return -EINVAL;
@@ -168,7 +168,7 @@ static int vfio_intx_enable(struct vfio_pci_device *vdev)
        return 0;
 }
 
-static int vfio_intx_set_signal(struct vfio_pci_device *vdev, int fd)
+static int vfio_intx_set_signal(struct vfio_pci_core_device *vdev, int fd)
 {
        struct pci_dev *pdev = vdev->pdev;
        unsigned long irqflags = IRQF_SHARED;
@@ -223,7 +223,7 @@ static int vfio_intx_set_signal(struct vfio_pci_device *vdev, int fd)
        return 0;
 }
 
-static void vfio_intx_disable(struct vfio_pci_device *vdev)
+static void vfio_intx_disable(struct vfio_pci_core_device *vdev)
 {
        vfio_virqfd_disable(&vdev->ctx[0].unmask);
        vfio_virqfd_disable(&vdev->ctx[0].mask);
@@ -244,7 +244,7 @@ static irqreturn_t vfio_msihandler(int irq, void *arg)
        return IRQ_HANDLED;
 }
 
-static int vfio_msi_enable(struct vfio_pci_device *vdev, int nvec, bool msix)
+static int vfio_msi_enable(struct vfio_pci_core_device *vdev, int nvec, bool msix)
 {
        struct pci_dev *pdev = vdev->pdev;
        unsigned int flag = msix ? PCI_IRQ_MSIX : PCI_IRQ_MSI;
@@ -285,7 +285,7 @@ static int vfio_msi_enable(struct vfio_pci_device *vdev, int nvec, bool msix)
        return 0;
 }
 
-static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev,
+static int vfio_msi_set_vector_signal(struct vfio_pci_core_device *vdev,
                                      int vector, int fd, bool msix)
 {
        struct pci_dev *pdev = vdev->pdev;
@@ -364,7 +364,7 @@ static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev,
        return 0;
 }
 
-static int vfio_msi_set_block(struct vfio_pci_device *vdev, unsigned start,
+static int vfio_msi_set_block(struct vfio_pci_core_device *vdev, unsigned start,
                              unsigned count, int32_t *fds, bool msix)
 {
        int i, j, ret = 0;
@@ -385,7 +385,7 @@ static int vfio_msi_set_block(struct vfio_pci_device *vdev, unsigned start,
        return ret;
 }
 
-static void vfio_msi_disable(struct vfio_pci_device *vdev, bool msix)
+static void vfio_msi_disable(struct vfio_pci_core_device *vdev, bool msix)
 {
        struct pci_dev *pdev = vdev->pdev;
        int i;
@@ -417,7 +417,7 @@ static void vfio_msi_disable(struct vfio_pci_device *vdev, bool msix)
 /*
  * IOCTL support
  */
-static int vfio_pci_set_intx_unmask(struct vfio_pci_device *vdev,
+static int vfio_pci_set_intx_unmask(struct vfio_pci_core_device *vdev,
                                    unsigned index, unsigned start,
                                    unsigned count, uint32_t flags, void *data)
 {
@@ -444,7 +444,7 @@ static int vfio_pci_set_intx_unmask(struct vfio_pci_device *vdev,
        return 0;
 }
 
-static int vfio_pci_set_intx_mask(struct vfio_pci_device *vdev,
+static int vfio_pci_set_intx_mask(struct vfio_pci_core_device *vdev,
                                  unsigned index, unsigned start,
                                  unsigned count, uint32_t flags, void *data)
 {
@@ -464,7 +464,7 @@ static int vfio_pci_set_intx_mask(struct vfio_pci_device *vdev,
        return 0;
 }
 
-static int vfio_pci_set_intx_trigger(struct vfio_pci_device *vdev,
+static int vfio_pci_set_intx_trigger(struct vfio_pci_core_device *vdev,
                                     unsigned index, unsigned start,
                                     unsigned count, uint32_t flags, void *data)
 {
@@ -507,7 +507,7 @@ static int vfio_pci_set_intx_trigger(struct vfio_pci_device *vdev,
        return 0;
 }
 
-static int vfio_pci_set_msi_trigger(struct vfio_pci_device *vdev,
+static int vfio_pci_set_msi_trigger(struct vfio_pci_core_device *vdev,
                                    unsigned index, unsigned start,
                                    unsigned count, uint32_t flags, void *data)
 {
@@ -613,7 +613,7 @@ static int vfio_pci_set_ctx_trigger_single(struct eventfd_ctx **ctx,
        return -EINVAL;
 }
 
-static int vfio_pci_set_err_trigger(struct vfio_pci_device *vdev,
+static int vfio_pci_set_err_trigger(struct vfio_pci_core_device *vdev,
                                    unsigned index, unsigned start,
                                    unsigned count, uint32_t flags, void *data)
 {
@@ -624,7 +624,7 @@ static int vfio_pci_set_err_trigger(struct vfio_pci_device *vdev,
                                               count, flags, data);
 }
 
-static int vfio_pci_set_req_trigger(struct vfio_pci_device *vdev,
+static int vfio_pci_set_req_trigger(struct vfio_pci_core_device *vdev,
                                    unsigned index, unsigned start,
                                    unsigned count, uint32_t flags, void *data)
 {
@@ -635,11 +635,11 @@ static int vfio_pci_set_req_trigger(struct vfio_pci_device *vdev,
                                               count, flags, data);
 }
 
-int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags,
+int vfio_pci_set_irqs_ioctl(struct vfio_pci_core_device *vdev, uint32_t flags,
                            unsigned index, unsigned start, unsigned count,
                            void *data)
 {
-       int (*func)(struct vfio_pci_device *vdev, unsigned index,
+       int (*func)(struct vfio_pci_core_device *vdev, unsigned index,
                    unsigned start, unsigned count, uint32_t flags,
                    void *data) = NULL;
 
diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h
deleted file mode 100644 (file)
index 5a36272..0000000
+++ /dev/null
@@ -1,215 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) 2012 Red Hat, Inc.  All rights reserved.
- *     Author: Alex Williamson <alex.williamson@redhat.com>
- *
- * Derived from original vfio:
- * Copyright 2010 Cisco Systems, Inc.  All rights reserved.
- * Author: Tom Lyon, pugs@cisco.com
- */
-
-#include <linux/mutex.h>
-#include <linux/pci.h>
-#include <linux/irqbypass.h>
-#include <linux/types.h>
-#include <linux/uuid.h>
-#include <linux/notifier.h>
-
-#ifndef VFIO_PCI_PRIVATE_H
-#define VFIO_PCI_PRIVATE_H
-
-#define VFIO_PCI_OFFSET_SHIFT   40
-
-#define VFIO_PCI_OFFSET_TO_INDEX(off)  (off >> VFIO_PCI_OFFSET_SHIFT)
-#define VFIO_PCI_INDEX_TO_OFFSET(index)        ((u64)(index) << VFIO_PCI_OFFSET_SHIFT)
-#define VFIO_PCI_OFFSET_MASK   (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1)
-
-/* Special capability IDs predefined access */
-#define PCI_CAP_ID_INVALID             0xFF    /* default raw access */
-#define PCI_CAP_ID_INVALID_VIRT                0xFE    /* default virt access */
-
-/* Cap maximum number of ioeventfds per device (arbitrary) */
-#define VFIO_PCI_IOEVENTFD_MAX         1000
-
-struct vfio_pci_ioeventfd {
-       struct list_head        next;
-       struct vfio_pci_device  *vdev;
-       struct virqfd           *virqfd;
-       void __iomem            *addr;
-       uint64_t                data;
-       loff_t                  pos;
-       int                     bar;
-       int                     count;
-       bool                    test_mem;
-};
-
-struct vfio_pci_irq_ctx {
-       struct eventfd_ctx      *trigger;
-       struct virqfd           *unmask;
-       struct virqfd           *mask;
-       char                    *name;
-       bool                    masked;
-       struct irq_bypass_producer      producer;
-};
-
-struct vfio_pci_device;
-struct vfio_pci_region;
-
-struct vfio_pci_regops {
-       size_t  (*rw)(struct vfio_pci_device *vdev, char __user *buf,
-                     size_t count, loff_t *ppos, bool iswrite);
-       void    (*release)(struct vfio_pci_device *vdev,
-                          struct vfio_pci_region *region);
-       int     (*mmap)(struct vfio_pci_device *vdev,
-                       struct vfio_pci_region *region,
-                       struct vm_area_struct *vma);
-       int     (*add_capability)(struct vfio_pci_device *vdev,
-                                 struct vfio_pci_region *region,
-                                 struct vfio_info_cap *caps);
-};
-
-struct vfio_pci_region {
-       u32                             type;
-       u32                             subtype;
-       const struct vfio_pci_regops    *ops;
-       void                            *data;
-       size_t                          size;
-       u32                             flags;
-};
-
-struct vfio_pci_dummy_resource {
-       struct resource         resource;
-       int                     index;
-       struct list_head        res_next;
-};
-
-struct vfio_pci_reflck {
-       struct kref             kref;
-       struct mutex            lock;
-};
-
-struct vfio_pci_vf_token {
-       struct mutex            lock;
-       uuid_t                  uuid;
-       int                     users;
-};
-
-struct vfio_pci_mmap_vma {
-       struct vm_area_struct   *vma;
-       struct list_head        vma_next;
-};
-
-struct vfio_pci_device {
-       struct vfio_device      vdev;
-       struct pci_dev          *pdev;
-       void __iomem            *barmap[PCI_STD_NUM_BARS];
-       bool                    bar_mmap_supported[PCI_STD_NUM_BARS];
-       u8                      *pci_config_map;
-       u8                      *vconfig;
-       struct perm_bits        *msi_perm;
-       spinlock_t              irqlock;
-       struct mutex            igate;
-       struct vfio_pci_irq_ctx *ctx;
-       int                     num_ctx;
-       int                     irq_type;
-       int                     num_regions;
-       struct vfio_pci_region  *region;
-       u8                      msi_qmax;
-       u8                      msix_bar;
-       u16                     msix_size;
-       u32                     msix_offset;
-       u32                     rbar[7];
-       bool                    pci_2_3;
-       bool                    virq_disabled;
-       bool                    reset_works;
-       bool                    extended_caps;
-       bool                    bardirty;
-       bool                    has_vga;
-       bool                    needs_reset;
-       bool                    nointx;
-       bool                    needs_pm_restore;
-       struct pci_saved_state  *pci_saved_state;
-       struct pci_saved_state  *pm_save;
-       struct vfio_pci_reflck  *reflck;
-       int                     refcnt;
-       int                     ioeventfds_nr;
-       struct eventfd_ctx      *err_trigger;
-       struct eventfd_ctx      *req_trigger;
-       struct list_head        dummy_resources_list;
-       struct mutex            ioeventfds_lock;
-       struct list_head        ioeventfds_list;
-       struct vfio_pci_vf_token        *vf_token;
-       struct notifier_block   nb;
-       struct mutex            vma_lock;
-       struct list_head        vma_list;
-       struct rw_semaphore     memory_lock;
-};
-
-#define is_intx(vdev) (vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX)
-#define is_msi(vdev) (vdev->irq_type == VFIO_PCI_MSI_IRQ_INDEX)
-#define is_msix(vdev) (vdev->irq_type == VFIO_PCI_MSIX_IRQ_INDEX)
-#define is_irq_none(vdev) (!(is_intx(vdev) || is_msi(vdev) || is_msix(vdev)))
-#define irq_is(vdev, type) (vdev->irq_type == type)
-
-extern void vfio_pci_intx_mask(struct vfio_pci_device *vdev);
-extern void vfio_pci_intx_unmask(struct vfio_pci_device *vdev);
-
-extern int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev,
-                                  uint32_t flags, unsigned index,
-                                  unsigned start, unsigned count, void *data);
-
-extern ssize_t vfio_pci_config_rw(struct vfio_pci_device *vdev,
-                                 char __user *buf, size_t count,
-                                 loff_t *ppos, bool iswrite);
-
-extern ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf,
-                              size_t count, loff_t *ppos, bool iswrite);
-
-extern ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf,
-                              size_t count, loff_t *ppos, bool iswrite);
-
-extern long vfio_pci_ioeventfd(struct vfio_pci_device *vdev, loff_t offset,
-                              uint64_t data, int count, int fd);
-
-extern int vfio_pci_init_perm_bits(void);
-extern void vfio_pci_uninit_perm_bits(void);
-
-extern int vfio_config_init(struct vfio_pci_device *vdev);
-extern void vfio_config_free(struct vfio_pci_device *vdev);
-
-extern int vfio_pci_register_dev_region(struct vfio_pci_device *vdev,
-                                       unsigned int type, unsigned int subtype,
-                                       const struct vfio_pci_regops *ops,
-                                       size_t size, u32 flags, void *data);
-
-extern int vfio_pci_set_power_state(struct vfio_pci_device *vdev,
-                                   pci_power_t state);
-
-extern bool __vfio_pci_memory_enabled(struct vfio_pci_device *vdev);
-extern void vfio_pci_zap_and_down_write_memory_lock(struct vfio_pci_device
-                                                   *vdev);
-extern u16 vfio_pci_memory_lock_and_enable(struct vfio_pci_device *vdev);
-extern void vfio_pci_memory_unlock_and_restore(struct vfio_pci_device *vdev,
-                                              u16 cmd);
-
-#ifdef CONFIG_VFIO_PCI_IGD
-extern int vfio_pci_igd_init(struct vfio_pci_device *vdev);
-#else
-static inline int vfio_pci_igd_init(struct vfio_pci_device *vdev)
-{
-       return -ENODEV;
-}
-#endif
-
-#ifdef CONFIG_S390
-extern int vfio_pci_info_zdev_add_caps(struct vfio_pci_device *vdev,
-                                      struct vfio_info_cap *caps);
-#else
-static inline int vfio_pci_info_zdev_add_caps(struct vfio_pci_device *vdev,
-                                             struct vfio_info_cap *caps)
-{
-       return -ENODEV;
-}
-#endif
-
-#endif /* VFIO_PCI_PRIVATE_H */
index a0b5fc8..57d3b2c 100644 (file)
@@ -17,7 +17,7 @@
 #include <linux/vfio.h>
 #include <linux/vgaarb.h>
 
-#include "vfio_pci_private.h"
+#include <linux/vfio_pci_core.h>
 
 #ifdef __LITTLE_ENDIAN
 #define vfio_ioread64  ioread64
@@ -38,7 +38,7 @@
 #define vfio_iowrite8  iowrite8
 
 #define VFIO_IOWRITE(size) \
-static int vfio_pci_iowrite##size(struct vfio_pci_device *vdev,                \
+static int vfio_pci_iowrite##size(struct vfio_pci_core_device *vdev,           \
                        bool test_mem, u##size val, void __iomem *io)   \
 {                                                                      \
        if (test_mem) {                                                 \
@@ -65,7 +65,7 @@ VFIO_IOWRITE(64)
 #endif
 
 #define VFIO_IOREAD(size) \
-static int vfio_pci_ioread##size(struct vfio_pci_device *vdev,         \
+static int vfio_pci_ioread##size(struct vfio_pci_core_device *vdev,            \
                        bool test_mem, u##size *val, void __iomem *io)  \
 {                                                                      \
        if (test_mem) {                                                 \
@@ -94,7 +94,7 @@ VFIO_IOREAD(32)
  * reads with -1.  This is intended for handling MSI-X vector tables and
  * leftover space for ROM BARs.
  */
-static ssize_t do_io_rw(struct vfio_pci_device *vdev, bool test_mem,
+static ssize_t do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem,
                        void __iomem *io, char __user *buf,
                        loff_t off, size_t count, size_t x_start,
                        size_t x_end, bool iswrite)
@@ -200,7 +200,7 @@ static ssize_t do_io_rw(struct vfio_pci_device *vdev, bool test_mem,
        return done;
 }
 
-static int vfio_pci_setup_barmap(struct vfio_pci_device *vdev, int bar)
+static int vfio_pci_setup_barmap(struct vfio_pci_core_device *vdev, int bar)
 {
        struct pci_dev *pdev = vdev->pdev;
        int ret;
@@ -224,7 +224,7 @@ static int vfio_pci_setup_barmap(struct vfio_pci_device *vdev, int bar)
        return 0;
 }
 
-ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf,
+ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
                        size_t count, loff_t *ppos, bool iswrite)
 {
        struct pci_dev *pdev = vdev->pdev;
@@ -288,7 +288,7 @@ out:
        return done;
 }
 
-ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf,
+ssize_t vfio_pci_vga_rw(struct vfio_pci_core_device *vdev, char __user *buf,
                               size_t count, loff_t *ppos, bool iswrite)
 {
        int ret;
@@ -384,7 +384,7 @@ static void vfio_pci_ioeventfd_do_write(struct vfio_pci_ioeventfd *ioeventfd,
 static int vfio_pci_ioeventfd_handler(void *opaque, void *unused)
 {
        struct vfio_pci_ioeventfd *ioeventfd = opaque;
-       struct vfio_pci_device *vdev = ioeventfd->vdev;
+       struct vfio_pci_core_device *vdev = ioeventfd->vdev;
 
        if (ioeventfd->test_mem) {
                if (!down_read_trylock(&vdev->memory_lock))
@@ -410,7 +410,7 @@ static void vfio_pci_ioeventfd_thread(void *opaque, void *unused)
        vfio_pci_ioeventfd_do_write(ioeventfd, ioeventfd->test_mem);
 }
 
-long vfio_pci_ioeventfd(struct vfio_pci_device *vdev, loff_t offset,
+long vfio_pci_ioeventfd(struct vfio_pci_core_device *vdev, loff_t offset,
                        uint64_t data, int count, int fd)
 {
        struct pci_dev *pdev = vdev->pdev;
index 7b011b6..ea4c0d2 100644 (file)
@@ -1,15 +1,10 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * VFIO ZPCI devices support
  *
  * Copyright (C) IBM Corp. 2020.  All rights reserved.
  *     Author(s): Pierre Morel <pmorel@linux.ibm.com>
  *                 Matthew Rosato <mjrosato@linux.ibm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
  */
 #include <linux/io.h>
 #include <linux/pci.h>
@@ -19,7 +14,7 @@
 #include <asm/pci_clp.h>
 #include <asm/pci_io.h>
 
-#include "vfio_pci_private.h"
+#include <linux/vfio_pci_core.h>
 
 /*
  * Add the Base PCI Function information to the device info region.
@@ -114,7 +109,7 @@ static int zpci_pfip_cap(struct zpci_dev *zdev, struct vfio_info_cap *caps)
 /*
  * Add all supported capabilities to the VFIO_DEVICE_GET_INFO capability chain.
  */
-int vfio_pci_info_zdev_add_caps(struct vfio_pci_device *vdev,
+int vfio_pci_info_zdev_add_caps(struct vfio_pci_core_device *vdev,
                                struct vfio_info_cap *caps)
 {
        struct zpci_dev *zdev = to_zpci(vdev->pdev);
index ab34110..331a592 100644 (file)
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config VFIO_PLATFORM
        tristate "VFIO support for platform devices"
-       depends on VFIO && EVENTFD && (ARM || ARM64 || COMPILE_TEST)
+       depends on ARM || ARM64 || COMPILE_TEST
        select VFIO_VIRQFD
        help
          Support for platform devices with VFIO. This is required to make
@@ -10,9 +10,10 @@ config VFIO_PLATFORM
 
          If you don't know what to do here, say N.
 
+if VFIO_PLATFORM
 config VFIO_AMBA
        tristate "VFIO support for AMBA devices"
-       depends on VFIO_PLATFORM && (ARM_AMBA || COMPILE_TEST)
+       depends on ARM_AMBA || COMPILE_TEST
        help
          Support for ARM AMBA devices with VFIO. This is required to make
          use of ARM AMBA devices present on the system using the VFIO
@@ -21,3 +22,4 @@ config VFIO_AMBA
          If you don't know what to do here, say N.
 
 source "drivers/vfio/platform/reset/Kconfig"
+endif
index 1edbe9e..12f5f3d 100644 (file)
@@ -1,7 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 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
 
@@ -9,7 +8,6 @@ config VFIO_PLATFORM_CALXEDAXGMAC_RESET
 
 config VFIO_PLATFORM_AMDXGBE_RESET
        tristate "VFIO support for AMD XGBE reset"
-       depends on VFIO_PLATFORM
        help
          Enables the VFIO platform driver to handle reset for AMD XGBE
 
@@ -17,7 +15,7 @@ config VFIO_PLATFORM_AMDXGBE_RESET
 
 config VFIO_PLATFORM_BCMFLEXRM_RESET
        tristate "VFIO support for Broadcom FlexRM reset"
-       depends on VFIO_PLATFORM && (ARCH_BCM_IPROC || COMPILE_TEST)
+       depends on ARCH_BCM_IPROC || COMPILE_TEST
        default ARCH_BCM_IPROC
        help
          Enables the VFIO platform driver to handle reset for Broadcom FlexRM
index 96064ef..1131ebe 100644 (file)
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (C) 2017 Broadcom
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
  */
 
 /*
index 703164d..6af7ce7 100644 (file)
@@ -218,65 +218,52 @@ static int vfio_platform_call_reset(struct vfio_platform_device *vdev,
        return -EINVAL;
 }
 
-static void vfio_platform_release(struct vfio_device *core_vdev)
+static void vfio_platform_close_device(struct vfio_device *core_vdev)
 {
        struct vfio_platform_device *vdev =
                container_of(core_vdev, struct vfio_platform_device, vdev);
+       const char *extra_dbg = NULL;
+       int ret;
 
-       mutex_lock(&driver_lock);
-
-       if (!(--vdev->refcnt)) {
-               const char *extra_dbg = NULL;
-               int ret;
-
-               ret = vfio_platform_call_reset(vdev, &extra_dbg);
-               if (ret && vdev->reset_required) {
-                       dev_warn(vdev->device, "reset driver is required and reset call failed in release (%d) %s\n",
-                                ret, extra_dbg ? extra_dbg : "");
-                       WARN_ON(1);
-               }
-               pm_runtime_put(vdev->device);
-               vfio_platform_regions_cleanup(vdev);
-               vfio_platform_irq_cleanup(vdev);
+       ret = vfio_platform_call_reset(vdev, &extra_dbg);
+       if (WARN_ON(ret && vdev->reset_required)) {
+               dev_warn(
+                       vdev->device,
+                       "reset driver is required and reset call failed in release (%d) %s\n",
+                       ret, extra_dbg ? extra_dbg : "");
        }
-
-       mutex_unlock(&driver_lock);
+       pm_runtime_put(vdev->device);
+       vfio_platform_regions_cleanup(vdev);
+       vfio_platform_irq_cleanup(vdev);
 }
 
-static int vfio_platform_open(struct vfio_device *core_vdev)
+static int vfio_platform_open_device(struct vfio_device *core_vdev)
 {
        struct vfio_platform_device *vdev =
                container_of(core_vdev, struct vfio_platform_device, vdev);
+       const char *extra_dbg = NULL;
        int ret;
 
-       mutex_lock(&driver_lock);
-
-       if (!vdev->refcnt) {
-               const char *extra_dbg = NULL;
-
-               ret = vfio_platform_regions_init(vdev);
-               if (ret)
-                       goto err_reg;
+       ret = vfio_platform_regions_init(vdev);
+       if (ret)
+               return ret;
 
-               ret = vfio_platform_irq_init(vdev);
-               if (ret)
-                       goto err_irq;
+       ret = vfio_platform_irq_init(vdev);
+       if (ret)
+               goto err_irq;
 
-               ret = pm_runtime_get_sync(vdev->device);
-               if (ret < 0)
-                       goto err_rst;
+       ret = pm_runtime_get_sync(vdev->device);
+       if (ret < 0)
+               goto err_rst;
 
-               ret = vfio_platform_call_reset(vdev, &extra_dbg);
-               if (ret && vdev->reset_required) {
-                       dev_warn(vdev->device, "reset driver is required and reset call failed in open (%d) %s\n",
-                                ret, extra_dbg ? extra_dbg : "");
-                       goto err_rst;
-               }
+       ret = vfio_platform_call_reset(vdev, &extra_dbg);
+       if (ret && vdev->reset_required) {
+               dev_warn(
+                       vdev->device,
+                       "reset driver is required and reset call failed in open (%d) %s\n",
+                       ret, extra_dbg ? extra_dbg : "");
+               goto err_rst;
        }
-
-       vdev->refcnt++;
-
-       mutex_unlock(&driver_lock);
        return 0;
 
 err_rst:
@@ -284,8 +271,6 @@ err_rst:
        vfio_platform_irq_cleanup(vdev);
 err_irq:
        vfio_platform_regions_cleanup(vdev);
-err_reg:
-       mutex_unlock(&driver_lock);
        return ret;
 }
 
@@ -616,8 +601,8 @@ static int vfio_platform_mmap(struct vfio_device *core_vdev, struct vm_area_stru
 
 static const struct vfio_device_ops vfio_platform_ops = {
        .name           = "vfio-platform",
-       .open           = vfio_platform_open,
-       .release        = vfio_platform_release,
+       .open_device    = vfio_platform_open_device,
+       .close_device   = vfio_platform_close_device,
        .ioctl          = vfio_platform_ioctl,
        .read           = vfio_platform_read,
        .write          = vfio_platform_write,
@@ -667,7 +652,7 @@ int vfio_platform_probe_common(struct vfio_platform_device *vdev,
                ret = vfio_platform_of_probe(vdev, dev);
 
        if (ret)
-               return ret;
+               goto out_uninit;
 
        vdev->device = dev;
 
@@ -675,7 +660,7 @@ int vfio_platform_probe_common(struct vfio_platform_device *vdev,
        if (ret && vdev->reset_required) {
                dev_err(dev, "No reset function found for device %s\n",
                        vdev->name);
-               return ret;
+               goto out_uninit;
        }
 
        group = vfio_iommu_group_get(dev);
@@ -698,6 +683,8 @@ put_iommu:
        vfio_iommu_group_put(group, dev);
 put_reset:
        vfio_platform_put_reset(vdev);
+out_uninit:
+       vfio_uninit_group_dev(&vdev->vdev);
        return ret;
 }
 EXPORT_SYMBOL_GPL(vfio_platform_probe_common);
@@ -708,6 +695,7 @@ void vfio_platform_remove_common(struct vfio_platform_device *vdev)
 
        pm_runtime_disable(vdev->device);
        vfio_platform_put_reset(vdev);
+       vfio_uninit_group_dev(&vdev->vdev);
        vfio_iommu_group_put(vdev->vdev.dev->iommu_group, vdev->vdev.dev);
 }
 EXPORT_SYMBOL_GPL(vfio_platform_remove_common);
index dfb834c..520d2a8 100644 (file)
@@ -48,7 +48,6 @@ struct vfio_platform_device {
        u32                             num_regions;
        struct vfio_platform_irq        *irqs;
        u32                             num_irqs;
-       int                             refcnt;
        struct mutex                    igate;
        const char                      *compat;
        const char                      *acpihid;
index 02cc51c..3c034fe 100644 (file)
@@ -96,6 +96,79 @@ module_param_named(enable_unsafe_noiommu_mode,
 MODULE_PARM_DESC(enable_unsafe_noiommu_mode, "Enable UNSAFE, no-IOMMU mode.  This mode provides no device isolation, no DMA translation, no host kernel protection, cannot be used for device assignment to virtual machines, requires RAWIO permissions, and will taint the kernel.  If you do not know what this is for, step away. (default: false)");
 #endif
 
+static DEFINE_XARRAY(vfio_device_set_xa);
+
+int vfio_assign_device_set(struct vfio_device *device, void *set_id)
+{
+       unsigned long idx = (unsigned long)set_id;
+       struct vfio_device_set *new_dev_set;
+       struct vfio_device_set *dev_set;
+
+       if (WARN_ON(!set_id))
+               return -EINVAL;
+
+       /*
+        * Atomically acquire a singleton object in the xarray for this set_id
+        */
+       xa_lock(&vfio_device_set_xa);
+       dev_set = xa_load(&vfio_device_set_xa, idx);
+       if (dev_set)
+               goto found_get_ref;
+       xa_unlock(&vfio_device_set_xa);
+
+       new_dev_set = kzalloc(sizeof(*new_dev_set), GFP_KERNEL);
+       if (!new_dev_set)
+               return -ENOMEM;
+       mutex_init(&new_dev_set->lock);
+       INIT_LIST_HEAD(&new_dev_set->device_list);
+       new_dev_set->set_id = set_id;
+
+       xa_lock(&vfio_device_set_xa);
+       dev_set = __xa_cmpxchg(&vfio_device_set_xa, idx, NULL, new_dev_set,
+                              GFP_KERNEL);
+       if (!dev_set) {
+               dev_set = new_dev_set;
+               goto found_get_ref;
+       }
+
+       kfree(new_dev_set);
+       if (xa_is_err(dev_set)) {
+               xa_unlock(&vfio_device_set_xa);
+               return xa_err(dev_set);
+       }
+
+found_get_ref:
+       dev_set->device_count++;
+       xa_unlock(&vfio_device_set_xa);
+       mutex_lock(&dev_set->lock);
+       device->dev_set = dev_set;
+       list_add_tail(&device->dev_set_list, &dev_set->device_list);
+       mutex_unlock(&dev_set->lock);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(vfio_assign_device_set);
+
+static void vfio_release_device_set(struct vfio_device *device)
+{
+       struct vfio_device_set *dev_set = device->dev_set;
+
+       if (!dev_set)
+               return;
+
+       mutex_lock(&dev_set->lock);
+       list_del(&device->dev_set_list);
+       mutex_unlock(&dev_set->lock);
+
+       xa_lock(&vfio_device_set_xa);
+       if (!--dev_set->device_count) {
+               __xa_erase(&vfio_device_set_xa,
+                          (unsigned long)dev_set->set_id);
+               mutex_destroy(&dev_set->lock);
+               kfree(dev_set);
+       }
+       xa_unlock(&vfio_device_set_xa);
+}
+
 /*
  * vfio_iommu_group_{get,put} are only intended for VFIO bus driver probe
  * and remove functions, any use cases other than acquiring the first
@@ -749,12 +822,25 @@ void vfio_init_group_dev(struct vfio_device *device, struct device *dev,
 }
 EXPORT_SYMBOL_GPL(vfio_init_group_dev);
 
+void vfio_uninit_group_dev(struct vfio_device *device)
+{
+       vfio_release_device_set(device);
+}
+EXPORT_SYMBOL_GPL(vfio_uninit_group_dev);
+
 int vfio_register_group_dev(struct vfio_device *device)
 {
        struct vfio_device *existing_device;
        struct iommu_group *iommu_group;
        struct vfio_group *group;
 
+       /*
+        * If the driver doesn't specify a set then the device is added to a
+        * singleton set just for itself.
+        */
+       if (!device->dev_set)
+               vfio_assign_device_set(device, device);
+
        iommu_group = iommu_group_get(device->dev);
        if (!iommu_group)
                return -EINVAL;
@@ -1356,7 +1442,8 @@ static int vfio_group_get_device_fd(struct vfio_group *group, char *buf)
 {
        struct vfio_device *device;
        struct file *filep;
-       int ret;
+       int fdno;
+       int ret = 0;
 
        if (0 == atomic_read(&group->container_users) ||
            !group->container->iommu_driver || !vfio_group_viable(group))
@@ -1370,38 +1457,32 @@ static int vfio_group_get_device_fd(struct vfio_group *group, char *buf)
                return PTR_ERR(device);
 
        if (!try_module_get(device->dev->driver->owner)) {
-               vfio_device_put(device);
-               return -ENODEV;
+               ret = -ENODEV;
+               goto err_device_put;
        }
 
-       ret = device->ops->open(device);
-       if (ret) {
-               module_put(device->dev->driver->owner);
-               vfio_device_put(device);
-               return ret;
+       mutex_lock(&device->dev_set->lock);
+       device->open_count++;
+       if (device->open_count == 1 && device->ops->open_device) {
+               ret = device->ops->open_device(device);
+               if (ret)
+                       goto err_undo_count;
        }
+       mutex_unlock(&device->dev_set->lock);
 
        /*
         * We can't use anon_inode_getfd() because we need to modify
         * the f_mode flags directly to allow more than just ioctls
         */
-       ret = get_unused_fd_flags(O_CLOEXEC);
-       if (ret < 0) {
-               device->ops->release(device);
-               module_put(device->dev->driver->owner);
-               vfio_device_put(device);
-               return ret;
-       }
+       fdno = ret = get_unused_fd_flags(O_CLOEXEC);
+       if (ret < 0)
+               goto err_close_device;
 
        filep = anon_inode_getfile("[vfio-device]", &vfio_device_fops,
                                   device, O_RDWR);
        if (IS_ERR(filep)) {
-               put_unused_fd(ret);
                ret = PTR_ERR(filep);
-               device->ops->release(device);
-               module_put(device->dev->driver->owner);
-               vfio_device_put(device);
-               return ret;
+               goto err_fd;
        }
 
        /*
@@ -1413,12 +1494,25 @@ static int vfio_group_get_device_fd(struct vfio_group *group, char *buf)
 
        atomic_inc(&group->container_users);
 
-       fd_install(ret, filep);
+       fd_install(fdno, filep);
 
        if (group->noiommu)
                dev_warn(device->dev, "vfio-noiommu device opened by user "
                         "(%s:%d)\n", current->comm, task_pid_nr(current));
-
+       return fdno;
+
+err_fd:
+       put_unused_fd(fdno);
+err_close_device:
+       mutex_lock(&device->dev_set->lock);
+       if (device->open_count == 1 && device->ops->close_device)
+               device->ops->close_device(device);
+err_undo_count:
+       device->open_count--;
+       mutex_unlock(&device->dev_set->lock);
+       module_put(device->dev->driver->owner);
+err_device_put:
+       vfio_device_put(device);
        return ret;
 }
 
@@ -1556,7 +1650,10 @@ static int vfio_device_fops_release(struct inode *inode, struct file *filep)
 {
        struct vfio_device *device = filep->private_data;
 
-       device->ops->release(device);
+       mutex_lock(&device->dev_set->lock);
+       if (!--device->open_count && device->ops->close_device)
+               device->ops->close_device(device);
+       mutex_unlock(&device->dev_set->lock);
 
        module_put(device->dev->driver->owner);
 
@@ -2359,6 +2456,7 @@ static void __exit vfio_cleanup(void)
        class_destroy(vfio.class);
        vfio.class = NULL;
        misc_deregister(&vfio_dev);
+       xa_destroy(&vfio_device_set_xa);
 }
 
 module_init(vfio_init);
index 0b4f7c1..0e92176 100644 (file)
@@ -612,17 +612,17 @@ static int vfio_wait(struct vfio_iommu *iommu)
 static int vfio_find_dma_valid(struct vfio_iommu *iommu, dma_addr_t start,
                               size_t size, struct vfio_dma **dma_p)
 {
-       int ret;
+       int ret = 0;
 
        do {
                *dma_p = vfio_find_dma(iommu, start, size);
                if (!*dma_p)
-                       ret = -EINVAL;
+                       return -EINVAL;
                else if (!(*dma_p)->vaddr_invalid)
-                       ret = 0;
+                       return ret;
                else
                        ret = vfio_wait(iommu);
-       } while (ret > 0);
+       } while (ret == WAITED);
 
        return ret;
 }
index 3a38598..68427e8 100644 (file)
@@ -72,11 +72,6 @@ struct device *mtype_get_parent_dev(struct mdev_type *mtype);
  *                     @mdev: mdev_device device structure which is being
  *                            destroyed
  *                     Returns integer: success (0) or error (< 0)
- * @open:              Open mediated device.
- *                     @mdev: mediated device.
- *                     Returns integer: success (0) or error (< 0)
- * @release:           release mediated device
- *                     @mdev: mediated device.
  * @read:              Read emulation callback
  *                     @mdev: mediated device structure
  *                     @buf: read buffer
@@ -111,8 +106,8 @@ struct mdev_parent_ops {
 
        int     (*create)(struct mdev_device *mdev);
        int     (*remove)(struct mdev_device *mdev);
-       int     (*open)(struct mdev_device *mdev);
-       void    (*release)(struct mdev_device *mdev);
+       int     (*open_device)(struct mdev_device *mdev);
+       void    (*close_device)(struct mdev_device *mdev);
        ssize_t (*read)(struct mdev_device *mdev, char __user *buf,
                        size_t count, loff_t *ppos);
        ssize_t (*write)(struct mdev_device *mdev, const char __user *buf,
index 8e291cf..ae2e75d 100644 (file)
@@ -16,6 +16,10 @@ typedef unsigned long kernel_ulong_t;
 
 #define PCI_ANY_ID (~0)
 
+enum {
+       PCI_ID_F_VFIO_DRIVER_OVERRIDE = 1,
+};
+
 /**
  * struct pci_device_id - PCI device ID structure
  * @vendor:            Vendor ID to match (or PCI_ANY_ID)
@@ -34,12 +38,14 @@ typedef unsigned long kernel_ulong_t;
  *                     Best practice is to use driver_data as an index
  *                     into a static list of equivalent device types,
  *                     instead of using it as a pointer.
+ * @override_only:     Match only when dev->driver_override is this driver.
  */
 struct pci_device_id {
        __u32 vendor, device;           /* Vendor and device ID or PCI_ANY_ID*/
        __u32 subvendor, subdevice;     /* Subsystem ID's or PCI_ANY_ID */
        __u32 class, class_mask;        /* (class,subclass,prog-if) triplet */
        kernel_ulong_t driver_data;     /* Data private to the driver */
+       __u32 override_only;
 };
 
 
index 9474306..34d7d94 100644 (file)
@@ -901,6 +901,35 @@ struct pci_driver {
        .vendor = (vend), .device = (dev), \
        .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
 
+/**
+ * PCI_DEVICE_DRIVER_OVERRIDE - macro used to describe a PCI device with
+ *                              override_only flags.
+ * @vend: the 16 bit PCI Vendor ID
+ * @dev: the 16 bit PCI Device ID
+ * @driver_override: the 32 bit PCI Device override_only
+ *
+ * This macro is used to create a struct pci_device_id that matches only a
+ * driver_override device. The subvendor and subdevice fields will be set to
+ * PCI_ANY_ID.
+ */
+#define PCI_DEVICE_DRIVER_OVERRIDE(vend, dev, driver_override) \
+       .vendor = (vend), .device = (dev), .subvendor = PCI_ANY_ID, \
+       .subdevice = PCI_ANY_ID, .override_only = (driver_override)
+
+/**
+ * PCI_DRIVER_OVERRIDE_DEVICE_VFIO - macro used to describe a VFIO
+ *                                   "driver_override" PCI device.
+ * @vend: the 16 bit PCI Vendor ID
+ * @dev: the 16 bit PCI Device ID
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific device. The subvendor and subdevice fields will be set to
+ * PCI_ANY_ID and the driver_override will be set to
+ * PCI_ID_F_VFIO_DRIVER_OVERRIDE.
+ */
+#define PCI_DRIVER_OVERRIDE_DEVICE_VFIO(vend, dev) \
+       PCI_DEVICE_DRIVER_OVERRIDE(vend, dev, PCI_ID_F_VFIO_DRIVER_OVERRIDE)
+
 /**
  * PCI_DEVICE_SUB - macro used to describe a specific PCI device with subsystem
  * @vend: the 16 bit PCI Vendor ID
index a2c5b30..b53a955 100644 (file)
 #include <linux/poll.h>
 #include <uapi/linux/vfio.h>
 
+/*
+ * VFIO devices can be placed in a set, this allows all devices to share this
+ * structure and the VFIO core will provide a lock that is held around
+ * open_device()/close_device() for all devices in the set.
+ */
+struct vfio_device_set {
+       void *set_id;
+       struct mutex lock;
+       struct list_head device_list;
+       unsigned int device_count;
+};
+
 struct vfio_device {
        struct device *dev;
        const struct vfio_device_ops *ops;
        struct vfio_group *group;
+       struct vfio_device_set *dev_set;
+       struct list_head dev_set_list;
 
        /* Members below here are private, not for driver use */
        refcount_t refcount;
+       unsigned int open_count;
        struct completion comp;
        struct list_head group_next;
 };
@@ -29,8 +44,8 @@ struct vfio_device {
 /**
  * struct vfio_device_ops - VFIO bus driver device callbacks
  *
- * @open: Called when userspace creates new file descriptor for device
- * @release: Called when userspace releases file descriptor for device
+ * @open_device: Called when the first file descriptor is opened for this device
+ * @close_device: Opposite of open_device
  * @read: Perform read(2) on device file descriptor
  * @write: Perform write(2) on device file descriptor
  * @ioctl: Perform ioctl(2) on device file descriptor, supporting VFIO_DEVICE_*
@@ -43,8 +58,8 @@ struct vfio_device {
  */
 struct vfio_device_ops {
        char    *name;
-       int     (*open)(struct vfio_device *vdev);
-       void    (*release)(struct vfio_device *vdev);
+       int     (*open_device)(struct vfio_device *vdev);
+       void    (*close_device)(struct vfio_device *vdev);
        ssize_t (*read)(struct vfio_device *vdev, char __user *buf,
                        size_t count, loff_t *ppos);
        ssize_t (*write)(struct vfio_device *vdev, const char __user *buf,
@@ -61,11 +76,14 @@ extern void vfio_iommu_group_put(struct iommu_group *group, struct device *dev);
 
 void vfio_init_group_dev(struct vfio_device *device, struct device *dev,
                         const struct vfio_device_ops *ops);
+void vfio_uninit_group_dev(struct vfio_device *device);
 int vfio_register_group_dev(struct vfio_device *device);
 void vfio_unregister_group_dev(struct vfio_device *device);
 extern struct vfio_device *vfio_device_get_from_dev(struct device *dev);
 extern void vfio_device_put(struct vfio_device *device);
 
+int vfio_assign_device_set(struct vfio_device *device, void *set_id);
+
 /* events for the backend driver notify callback */
 enum vfio_iommu_notify_type {
        VFIO_IOMMU_CONTAINER_CLOSE = 0,
diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h
new file mode 100644 (file)
index 0000000..ef9a44b
--- /dev/null
@@ -0,0 +1,239 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2012 Red Hat, Inc.  All rights reserved.
+ *     Author: Alex Williamson <alex.williamson@redhat.com>
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc.  All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/vfio.h>
+#include <linux/irqbypass.h>
+#include <linux/types.h>
+#include <linux/uuid.h>
+#include <linux/notifier.h>
+
+#ifndef VFIO_PCI_CORE_H
+#define VFIO_PCI_CORE_H
+
+#define VFIO_PCI_OFFSET_SHIFT   40
+
+#define VFIO_PCI_OFFSET_TO_INDEX(off)  (off >> VFIO_PCI_OFFSET_SHIFT)
+#define VFIO_PCI_INDEX_TO_OFFSET(index)        ((u64)(index) << VFIO_PCI_OFFSET_SHIFT)
+#define VFIO_PCI_OFFSET_MASK   (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1)
+
+/* Special capability IDs predefined access */
+#define PCI_CAP_ID_INVALID             0xFF    /* default raw access */
+#define PCI_CAP_ID_INVALID_VIRT                0xFE    /* default virt access */
+
+/* Cap maximum number of ioeventfds per device (arbitrary) */
+#define VFIO_PCI_IOEVENTFD_MAX         1000
+
+struct vfio_pci_ioeventfd {
+       struct list_head        next;
+       struct vfio_pci_core_device     *vdev;
+       struct virqfd           *virqfd;
+       void __iomem            *addr;
+       uint64_t                data;
+       loff_t                  pos;
+       int                     bar;
+       int                     count;
+       bool                    test_mem;
+};
+
+struct vfio_pci_irq_ctx {
+       struct eventfd_ctx      *trigger;
+       struct virqfd           *unmask;
+       struct virqfd           *mask;
+       char                    *name;
+       bool                    masked;
+       struct irq_bypass_producer      producer;
+};
+
+struct vfio_pci_core_device;
+struct vfio_pci_region;
+
+struct vfio_pci_regops {
+       ssize_t (*rw)(struct vfio_pci_core_device *vdev, char __user *buf,
+                     size_t count, loff_t *ppos, bool iswrite);
+       void    (*release)(struct vfio_pci_core_device *vdev,
+                          struct vfio_pci_region *region);
+       int     (*mmap)(struct vfio_pci_core_device *vdev,
+                       struct vfio_pci_region *region,
+                       struct vm_area_struct *vma);
+       int     (*add_capability)(struct vfio_pci_core_device *vdev,
+                                 struct vfio_pci_region *region,
+                                 struct vfio_info_cap *caps);
+};
+
+struct vfio_pci_region {
+       u32                             type;
+       u32                             subtype;
+       const struct vfio_pci_regops    *ops;
+       void                            *data;
+       size_t                          size;
+       u32                             flags;
+};
+
+struct vfio_pci_dummy_resource {
+       struct resource         resource;
+       int                     index;
+       struct list_head        res_next;
+};
+
+struct vfio_pci_vf_token {
+       struct mutex            lock;
+       uuid_t                  uuid;
+       int                     users;
+};
+
+struct vfio_pci_mmap_vma {
+       struct vm_area_struct   *vma;
+       struct list_head        vma_next;
+};
+
+struct vfio_pci_core_device {
+       struct vfio_device      vdev;
+       struct pci_dev          *pdev;
+       void __iomem            *barmap[PCI_STD_NUM_BARS];
+       bool                    bar_mmap_supported[PCI_STD_NUM_BARS];
+       u8                      *pci_config_map;
+       u8                      *vconfig;
+       struct perm_bits        *msi_perm;
+       spinlock_t              irqlock;
+       struct mutex            igate;
+       struct vfio_pci_irq_ctx *ctx;
+       int                     num_ctx;
+       int                     irq_type;
+       int                     num_regions;
+       struct vfio_pci_region  *region;
+       u8                      msi_qmax;
+       u8                      msix_bar;
+       u16                     msix_size;
+       u32                     msix_offset;
+       u32                     rbar[7];
+       bool                    pci_2_3;
+       bool                    virq_disabled;
+       bool                    reset_works;
+       bool                    extended_caps;
+       bool                    bardirty;
+       bool                    has_vga;
+       bool                    needs_reset;
+       bool                    nointx;
+       bool                    needs_pm_restore;
+       struct pci_saved_state  *pci_saved_state;
+       struct pci_saved_state  *pm_save;
+       int                     ioeventfds_nr;
+       struct eventfd_ctx      *err_trigger;
+       struct eventfd_ctx      *req_trigger;
+       struct list_head        dummy_resources_list;
+       struct mutex            ioeventfds_lock;
+       struct list_head        ioeventfds_list;
+       struct vfio_pci_vf_token        *vf_token;
+       struct notifier_block   nb;
+       struct mutex            vma_lock;
+       struct list_head        vma_list;
+       struct rw_semaphore     memory_lock;
+};
+
+#define is_intx(vdev) (vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX)
+#define is_msi(vdev) (vdev->irq_type == VFIO_PCI_MSI_IRQ_INDEX)
+#define is_msix(vdev) (vdev->irq_type == VFIO_PCI_MSIX_IRQ_INDEX)
+#define is_irq_none(vdev) (!(is_intx(vdev) || is_msi(vdev) || is_msix(vdev)))
+#define irq_is(vdev, type) (vdev->irq_type == type)
+
+extern void vfio_pci_intx_mask(struct vfio_pci_core_device *vdev);
+extern void vfio_pci_intx_unmask(struct vfio_pci_core_device *vdev);
+
+extern int vfio_pci_set_irqs_ioctl(struct vfio_pci_core_device *vdev,
+                                  uint32_t flags, unsigned index,
+                                  unsigned start, unsigned count, void *data);
+
+extern ssize_t vfio_pci_config_rw(struct vfio_pci_core_device *vdev,
+                                 char __user *buf, size_t count,
+                                 loff_t *ppos, bool iswrite);
+
+extern ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf,
+                              size_t count, loff_t *ppos, bool iswrite);
+
+extern ssize_t vfio_pci_vga_rw(struct vfio_pci_core_device *vdev, char __user *buf,
+                              size_t count, loff_t *ppos, bool iswrite);
+
+extern long vfio_pci_ioeventfd(struct vfio_pci_core_device *vdev, loff_t offset,
+                              uint64_t data, int count, int fd);
+
+extern int vfio_pci_init_perm_bits(void);
+extern void vfio_pci_uninit_perm_bits(void);
+
+extern int vfio_config_init(struct vfio_pci_core_device *vdev);
+extern void vfio_config_free(struct vfio_pci_core_device *vdev);
+
+extern int vfio_pci_register_dev_region(struct vfio_pci_core_device *vdev,
+                                       unsigned int type, unsigned int subtype,
+                                       const struct vfio_pci_regops *ops,
+                                       size_t size, u32 flags, void *data);
+
+extern int vfio_pci_set_power_state(struct vfio_pci_core_device *vdev,
+                                   pci_power_t state);
+
+extern bool __vfio_pci_memory_enabled(struct vfio_pci_core_device *vdev);
+extern void vfio_pci_zap_and_down_write_memory_lock(struct vfio_pci_core_device
+                                                   *vdev);
+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);
+#else
+static inline int vfio_pci_info_zdev_add_caps(struct vfio_pci_core_device *vdev,
+                                             struct vfio_info_cap *caps)
+{
+       return -ENODEV;
+}
+#endif
+
+/* Will be exported for vfio pci drivers usage */
+void vfio_pci_core_set_params(bool nointxmask, bool is_disable_vga,
+                             bool is_disable_idle_d3);
+void vfio_pci_core_close_device(struct vfio_device *core_vdev);
+void vfio_pci_core_init_device(struct vfio_pci_core_device *vdev,
+                              struct pci_dev *pdev,
+                              const struct vfio_device_ops *vfio_pci_ops);
+int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev);
+void vfio_pci_core_uninit_device(struct vfio_pci_core_device *vdev);
+void vfio_pci_core_unregister_device(struct vfio_pci_core_device *vdev);
+int vfio_pci_core_sriov_configure(struct pci_dev *pdev, int nr_virtfn);
+extern const struct pci_error_handlers vfio_pci_core_err_handlers;
+long vfio_pci_core_ioctl(struct vfio_device *core_vdev, unsigned int cmd,
+               unsigned long arg);
+ssize_t vfio_pci_core_read(struct vfio_device *core_vdev, char __user *buf,
+               size_t count, loff_t *ppos);
+ssize_t vfio_pci_core_write(struct vfio_device *core_vdev, const char __user *buf,
+               size_t count, loff_t *ppos);
+int vfio_pci_core_mmap(struct vfio_device *core_vdev, struct vm_area_struct *vma);
+void vfio_pci_core_request(struct vfio_device *core_vdev, unsigned int count);
+int vfio_pci_core_match(struct vfio_device *core_vdev, char *buf);
+int vfio_pci_core_enable(struct vfio_pci_core_device *vdev);
+void vfio_pci_core_disable(struct vfio_pci_core_device *vdev);
+void vfio_pci_core_finish_enable(struct vfio_pci_core_device *vdev);
+
+static inline bool vfio_pci_is_vga(struct pci_dev *pdev)
+{
+       return (pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA;
+}
+
+#endif /* VFIO_PCI_CORE_H */
index 6c0f229..c313ab4 100644 (file)
@@ -129,7 +129,7 @@ static dev_t                mbochs_devt;
 static struct class    *mbochs_class;
 static struct cdev     mbochs_cdev;
 static struct device   mbochs_dev;
-static int             mbochs_used_mbytes;
+static atomic_t mbochs_avail_mbytes;
 static const struct vfio_device_ops mbochs_dev_ops;
 
 struct vfio_region_info_ext {
@@ -507,18 +507,22 @@ static int mbochs_reset(struct mdev_state *mdev_state)
 
 static int mbochs_probe(struct mdev_device *mdev)
 {
+       int avail_mbytes = atomic_read(&mbochs_avail_mbytes);
        const struct mbochs_type *type =
                &mbochs_types[mdev_get_type_group_id(mdev)];
        struct device *dev = mdev_dev(mdev);
        struct mdev_state *mdev_state;
        int ret = -ENOMEM;
 
-       if (type->mbytes + mbochs_used_mbytes > max_mbytes)
-               return -ENOMEM;
+       do {
+               if (avail_mbytes < type->mbytes)
+                       return -ENOSPC;
+       } while (!atomic_try_cmpxchg(&mbochs_avail_mbytes, &avail_mbytes,
+                                    avail_mbytes - type->mbytes));
 
        mdev_state = kzalloc(sizeof(struct mdev_state), GFP_KERNEL);
        if (mdev_state == NULL)
-               return -ENOMEM;
+               goto err_avail;
        vfio_init_group_dev(&mdev_state->vdev, &mdev->dev, &mbochs_dev_ops);
 
        mdev_state->vconfig = kzalloc(MBOCHS_CONFIG_SPACE_SIZE, GFP_KERNEL);
@@ -549,17 +553,18 @@ static int mbochs_probe(struct mdev_device *mdev)
        mbochs_create_config_space(mdev_state);
        mbochs_reset(mdev_state);
 
-       mbochs_used_mbytes += type->mbytes;
-
        ret = vfio_register_group_dev(&mdev_state->vdev);
        if (ret)
                goto err_mem;
        dev_set_drvdata(&mdev->dev, mdev_state);
        return 0;
-
 err_mem:
+       vfio_uninit_group_dev(&mdev_state->vdev);
+       kfree(mdev_state->pages);
        kfree(mdev_state->vconfig);
        kfree(mdev_state);
+err_avail:
+       atomic_add(type->mbytes, &mbochs_avail_mbytes);
        return ret;
 }
 
@@ -567,8 +572,9 @@ static void mbochs_remove(struct mdev_device *mdev)
 {
        struct mdev_state *mdev_state = dev_get_drvdata(&mdev->dev);
 
-       mbochs_used_mbytes -= mdev_state->type->mbytes;
        vfio_unregister_group_dev(&mdev_state->vdev);
+       vfio_uninit_group_dev(&mdev_state->vdev);
+       atomic_add(mdev_state->type->mbytes, &mbochs_avail_mbytes);
        kfree(mdev_state->pages);
        kfree(mdev_state->vconfig);
        kfree(mdev_state);
@@ -1272,15 +1278,7 @@ static long mbochs_ioctl(struct vfio_device *vdev, unsigned int cmd,
        return -ENOTTY;
 }
 
-static int mbochs_open(struct vfio_device *vdev)
-{
-       if (!try_module_get(THIS_MODULE))
-               return -ENODEV;
-
-       return 0;
-}
-
-static void mbochs_close(struct vfio_device *vdev)
+static void mbochs_close_device(struct vfio_device *vdev)
 {
        struct mdev_state *mdev_state =
                container_of(vdev, struct mdev_state, vdev);
@@ -1300,7 +1298,6 @@ static void mbochs_close(struct vfio_device *vdev)
        mbochs_put_pages(mdev_state);
 
        mutex_unlock(&mdev_state->ops_lock);
-       module_put(THIS_MODULE);
 }
 
 static ssize_t
@@ -1355,7 +1352,7 @@ static ssize_t available_instances_show(struct mdev_type *mtype,
 {
        const struct mbochs_type *type =
                &mbochs_types[mtype_get_type_group_id(mtype)];
-       int count = (max_mbytes - mbochs_used_mbytes) / type->mbytes;
+       int count = atomic_read(&mbochs_avail_mbytes) / type->mbytes;
 
        return sprintf(buf, "%d\n", count);
 }
@@ -1399,8 +1396,7 @@ static struct attribute_group *mdev_type_groups[] = {
 };
 
 static const struct vfio_device_ops mbochs_dev_ops = {
-       .open = mbochs_open,
-       .release = mbochs_close,
+       .close_device = mbochs_close_device,
        .read = mbochs_read,
        .write = mbochs_write,
        .ioctl = mbochs_ioctl,
@@ -1437,6 +1433,8 @@ static int __init mbochs_dev_init(void)
 {
        int ret = 0;
 
+       atomic_set(&mbochs_avail_mbytes, max_mbytes);
+
        ret = alloc_chrdev_region(&mbochs_devt, 0, MINORMASK + 1, MBOCHS_NAME);
        if (ret < 0) {
                pr_err("Error: failed to register mbochs_dev, err: %d\n", ret);
index 393c9df..8d1a80a 100644 (file)
@@ -235,17 +235,16 @@ static int mdpy_probe(struct mdev_device *mdev)
 
        mdev_state->vconfig = kzalloc(MDPY_CONFIG_SPACE_SIZE, GFP_KERNEL);
        if (mdev_state->vconfig == NULL) {
-               kfree(mdev_state);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto err_state;
        }
 
        fbsize = roundup_pow_of_two(type->width * type->height * type->bytepp);
 
        mdev_state->memblk = vmalloc_user(fbsize);
        if (!mdev_state->memblk) {
-               kfree(mdev_state->vconfig);
-               kfree(mdev_state);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto err_vconfig;
        }
        dev_info(dev, "%s: %s (%dx%d)\n", __func__, type->name, type->width,
                 type->height);
@@ -260,13 +259,18 @@ static int mdpy_probe(struct mdev_device *mdev)
        mdpy_count++;
 
        ret = vfio_register_group_dev(&mdev_state->vdev);
-       if (ret) {
-               kfree(mdev_state->vconfig);
-               kfree(mdev_state);
-               return ret;
-       }
+       if (ret)
+               goto err_mem;
        dev_set_drvdata(&mdev->dev, mdev_state);
        return 0;
+err_mem:
+       vfree(mdev_state->memblk);
+err_vconfig:
+       kfree(mdev_state->vconfig);
+err_state:
+       vfio_uninit_group_dev(&mdev_state->vdev);
+       kfree(mdev_state);
+       return ret;
 }
 
 static void mdpy_remove(struct mdev_device *mdev)
@@ -278,6 +282,7 @@ static void mdpy_remove(struct mdev_device *mdev)
        vfio_unregister_group_dev(&mdev_state->vdev);
        vfree(mdev_state->memblk);
        kfree(mdev_state->vconfig);
+       vfio_uninit_group_dev(&mdev_state->vdev);
        kfree(mdev_state);
 
        mdpy_count--;
@@ -609,19 +614,6 @@ static long mdpy_ioctl(struct vfio_device *vdev, unsigned int cmd,
        return -ENOTTY;
 }
 
-static int mdpy_open(struct vfio_device *vdev)
-{
-       if (!try_module_get(THIS_MODULE))
-               return -ENODEV;
-
-       return 0;
-}
-
-static void mdpy_close(struct vfio_device *vdev)
-{
-       module_put(THIS_MODULE);
-}
-
 static ssize_t
 resolution_show(struct device *dev, struct device_attribute *attr,
                char *buf)
@@ -716,8 +708,6 @@ static struct attribute_group *mdev_type_groups[] = {
 };
 
 static const struct vfio_device_ops mdpy_dev_ops = {
-       .open = mdpy_open,
-       .release = mdpy_close,
        .read = mdpy_read,
        .write = mdpy_write,
        .ioctl = mdpy_ioctl,
index 8b26fec..5983cdb 100644 (file)
@@ -718,8 +718,8 @@ static int mtty_probe(struct mdev_device *mdev)
 
        mdev_state = kzalloc(sizeof(struct mdev_state), GFP_KERNEL);
        if (mdev_state == NULL) {
-               atomic_add(nr_ports, &mdev_avail_ports);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto err_nr_ports;
        }
 
        vfio_init_group_dev(&mdev_state->vdev, &mdev->dev, &mtty_dev_ops);
@@ -732,9 +732,8 @@ static int mtty_probe(struct mdev_device *mdev)
        mdev_state->vconfig = kzalloc(MTTY_CONFIG_SPACE_SIZE, GFP_KERNEL);
 
        if (mdev_state->vconfig == NULL) {
-               kfree(mdev_state);
-               atomic_add(nr_ports, &mdev_avail_ports);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto err_state;
        }
 
        mutex_init(&mdev_state->ops_lock);
@@ -743,14 +742,19 @@ static int mtty_probe(struct mdev_device *mdev)
        mtty_create_config_space(mdev_state);
 
        ret = vfio_register_group_dev(&mdev_state->vdev);
-       if (ret) {
-               kfree(mdev_state);
-               atomic_add(nr_ports, &mdev_avail_ports);
-               return ret;
-       }
-
+       if (ret)
+               goto err_vconfig;
        dev_set_drvdata(&mdev->dev, mdev_state);
        return 0;
+
+err_vconfig:
+       kfree(mdev_state->vconfig);
+err_state:
+       vfio_uninit_group_dev(&mdev_state->vdev);
+       kfree(mdev_state);
+err_nr_ports:
+       atomic_add(nr_ports, &mdev_avail_ports);
+       return ret;
 }
 
 static void mtty_remove(struct mdev_device *mdev)
@@ -761,6 +765,7 @@ static void mtty_remove(struct mdev_device *mdev)
        vfio_unregister_group_dev(&mdev_state->vdev);
 
        kfree(mdev_state->vconfig);
+       vfio_uninit_group_dev(&mdev_state->vdev);
        kfree(mdev_state);
        atomic_add(nr_ports, &mdev_avail_ports);
 }
@@ -1202,17 +1207,6 @@ static long mtty_ioctl(struct vfio_device *vdev, unsigned int cmd,
        return -ENOTTY;
 }
 
-static int mtty_open(struct vfio_device *vdev)
-{
-       pr_info("%s\n", __func__);
-       return 0;
-}
-
-static void mtty_close(struct vfio_device *mdev)
-{
-       pr_info("%s\n", __func__);
-}
-
 static ssize_t
 sample_mtty_dev_show(struct device *dev, struct device_attribute *attr,
                     char *buf)
@@ -1320,8 +1314,6 @@ static struct attribute_group *mdev_type_groups[] = {
 
 static const struct vfio_device_ops mtty_dev_ops = {
        .name = "vfio-mtty",
-       .open = mtty_open,
-       .release = mtty_close,
        .read = mtty_read,
        .write = mtty_write,
        .ioctl = mtty_ioctl,
index 9bb6c7e..cc36256 100644 (file)
@@ -42,6 +42,7 @@ int main(void)
        DEVID_FIELD(pci_device_id, subdevice);
        DEVID_FIELD(pci_device_id, class);
        DEVID_FIELD(pci_device_id, class_mask);
+       DEVID_FIELD(pci_device_id, override_only);
 
        DEVID(ccw_device_id);
        DEVID_FIELD(ccw_device_id, match_flags);
index 7c97fa8..49aba86 100644 (file)
@@ -426,7 +426,7 @@ static int do_ieee1394_entry(const char *filename,
        return 1;
 }
 
-/* Looks like: pci:vNdNsvNsdNbcNscNiN. */
+/* Looks like: pci:vNdNsvNsdNbcNscNiN or <prefix>_pci:vNdNsvNsdNbcNscNiN. */
 static int do_pci_entry(const char *filename,
                        void *symval, char *alias)
 {
@@ -440,8 +440,21 @@ static int do_pci_entry(const char *filename,
        DEF_FIELD(symval, pci_device_id, subdevice);
        DEF_FIELD(symval, pci_device_id, class);
        DEF_FIELD(symval, pci_device_id, class_mask);
+       DEF_FIELD(symval, pci_device_id, override_only);
+
+       switch (override_only) {
+       case 0:
+               strcpy(alias, "pci:");
+               break;
+       case PCI_ID_F_VFIO_DRIVER_OVERRIDE:
+               strcpy(alias, "vfio_pci:");
+               break;
+       default:
+               warn("Unknown PCI driver_override alias %08X\n",
+                    override_only);
+               return 0;
+       }
 
-       strcpy(alias, "pci:");
        ADD(alias, "v", vendor != PCI_ANY_ID, vendor);
        ADD(alias, "d", device != PCI_ANY_ID, device);
        ADD(alias, "sv", subvendor != PCI_ANY_ID, subvendor);