PCI / VFIO: Add 'override_only' support for VFIO PCI sub system
authorMax Gurtovoy <mgurtovoy@nvidia.com>
Thu, 26 Aug 2021 10:39:09 +0000 (13:39 +0300)
committerAlex Williamson <alex.williamson@redhat.com>
Thu, 26 Aug 2021 16:36:51 +0000 (10:36 -0600)
Expose an 'override_only' helper macro (i.e.
PCI_DRIVER_OVERRIDE_DEVICE_VFIO) for VFIO PCI sub system and add the
required code to prefix its matching entries with "vfio_" in
modules.alias file.

It allows VFIO device drivers to include match entries in the
modules.alias file produced by kbuild that are not used for normal
driver autoprobing and module autoloading. Drivers using these match
entries can be connected to the PCI device manually, by userspace, using
the existing driver_override sysfs.

For example the resulting modules.alias may have:

  alias pci:v000015B3d00001021sv*sd*bc*sc*i* mlx5_core
  alias vfio_pci:v000015B3d00001021sv*sd*bc*sc*i* mlx5_vfio_pci
  alias vfio_pci:v*d*sv*sd*bc*sc*i* vfio_pci

In this example mlx5_core and mlx5_vfio_pci match to the same PCI
device. The kernel will autoload and autobind to mlx5_core but the
kernel and udev mechanisms will ignore mlx5_vfio_pci.

When userspace wants to change a device to the VFIO subsystem it can
implement a generic algorithm:

   1) Identify the sysfs path to the device:
    /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0

   2) Get the modalias string from the kernel:
    $ cat /sys/bus/pci/devices/0000:01:00.0/modalias
    pci:v000015B3d00001021sv000015B3sd00000001bc02sc00i00

   3) Prefix it with vfio_:
    vfio_pci:v000015B3d00001021sv000015B3sd00000001bc02sc00i00

   4) Search modules.alias for the above string and select the entry that
      has the fewest *'s:
    alias vfio_pci:v000015B3d00001021sv*sd*bc*sc*i* mlx5_vfio_pci

   5) modprobe the matched module name:
    $ modprobe mlx5_vfio_pci

   6) cat the matched module name to driver_override:
    echo mlx5_vfio_pci > /sys/bus/pci/devices/0000:01:00.0/driver_override

   7) unbind device from original module
     echo 0000:01:00.0 > /sys/bus/pci/devices/0000:01:00.0/driver/unbind

   8) probe PCI drivers (or explicitly bind to mlx5_vfio_pci)
    echo 0000:01:00.0 > /sys/bus/pci/drivers_probe

The algorithm is independent of bus type. In future the other buses with
VFIO device drivers, like platform and ACPI, can use this algorithm as
well.

This patch is the infrastructure to provide the information in the
modules.alias to userspace. Convert the only VFIO pci_driver which results
in one new line in the modules.alias:

  alias vfio_pci:v*d*sv*sd*bc*sc*i* vfio_pci

Later series introduce additional HW specific VFIO PCI drivers, such as
mlx5_vfio_pci.

Signed-off-by: Max Gurtovoy <mgurtovoy@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com> # for pci.h
Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
Link: https://lore.kernel.org/r/20210826103912.128972-11-yishaih@nvidia.com
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
drivers/vfio/pci/vfio_pci.c
include/linux/mod_devicetable.h
include/linux/pci.h
scripts/mod/devicetable-offsets.c
scripts/mod/file2alias.c

index 163e560c449502aaf7f0394c62d86e909a215bdd..85fd638a59553b08b472b487935ff63be1945993 100644 (file)
@@ -178,9 +178,16 @@ static int vfio_pci_sriov_configure(struct pci_dev *pdev, int nr_virtfn)
        return vfio_pci_core_sriov_configure(pdev, nr_virtfn);
 }
 
+static const struct pci_device_id vfio_pci_table[] = {
+       { PCI_DRIVER_OVERRIDE_DEVICE_VFIO(PCI_ANY_ID, PCI_ANY_ID) }, /* match all by default */
+       {}
+};
+
+MODULE_DEVICE_TABLE(pci, vfio_pci_table);
+
 static struct pci_driver vfio_pci_driver = {
        .name                   = "vfio-pci",
-       .id_table               = NULL, /* only dynamic ids */
+       .id_table               = vfio_pci_table,
        .probe                  = vfio_pci_probe,
        .remove                 = vfio_pci_remove,
        .sriov_configure        = vfio_pci_sriov_configure,
index 2e3ba6d9ece05915b421f7c2e808bb11c9aec603..ae2e75d15b2199206b79b0a413c00196e5dabdfc 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)
index 0506b1a8c921dbd2a545d0cf10e79b5f28ff7517..527a1dfd1d0629a64073e66f9e8355ea51b6ede5 100644 (file)
@@ -916,6 +916,20 @@ struct pci_driver {
        .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 9bb6c7edccc4494e689b6f0dfc46f49662e7379e..cc3625617a0ef58becdca4e298f8c80d34d274e6 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 7c97fa8e36bcd3b95ed942ee443c55db1b3d2085..49aba862073e017557bc736637f13c62a80944e8 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);