]> Git Repo - linux.git/commitdiff
s390/pci: create links between PFs and VFs
authorNiklas Schnelle <[email protected]>
Tue, 28 Apr 2020 09:54:46 +0000 (11:54 +0200)
committerVasily Gorbik <[email protected]>
Wed, 20 May 2020 08:22:51 +0000 (10:22 +0200)
On s390 PCI Virtual Functions (VFs) are scanned by firmware and are made
available to Linux via the hot-plug interface. As such the common code
path of doing the scan directly using the parent Physical Function (PF)
is not used and fenced off with the no_vf_scan attribute.

Even if the partition created the VFs itself e.g. using the sriov_numvfs
attribute of a PF, the PF/VF links thus need to be established after the
fact. To do this when a VF is plugged we scan through all functions on
the same zbus and test whether they are the parent PF in which case we
establish the necessary links.

With these links established there is now no more need to fence off
pci_iov_remove_virtfn() for pdev->no_vf_scan as the common code now
works fine.

Signed-off-by: Niklas Schnelle <[email protected]>
Acked-by: Bjorn Helgaas <[email protected]>
Reviewed-by: Pierre Morel <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Vasily Gorbik <[email protected]>
arch/s390/include/asm/pci.h
arch/s390/include/asm/pci_clp.h
arch/s390/pci/pci_bus.c
arch/s390/pci/pci_clp.c
drivers/pci/iov.c

index c1558cf071b87068e3e4473c474502cc39049a4d..99b92c3e46b09696d1d491877f7ee75d1cb9e86e 100644 (file)
@@ -131,7 +131,8 @@ struct zpci_dev {
        u8              port;
        u8              rid_available   : 1;
        u8              has_hp_slot     : 1;
-       u8              reserved        : 6;
+       u8              is_physfn       : 1;
+       u8              reserved        : 5;
        unsigned int    devfn;          /* DEVFN part of the RID*/
 
        struct mutex lock;
index 896ee41e23e30a472ac51f100747145a09756c33..eb51272dd2ccda14d0db72989bce0c5e0be73047 100644 (file)
@@ -95,7 +95,8 @@ struct clp_rsp_query_pci {
        u16 vfn;                        /* virtual fn number */
        u16                     :  3;
        u16 rid_avail           :  1;
-       u16                     :  2;
+       u16 is_physfn           :  1;
+       u16 reserved1           :  1;
        u16 mio_addr_avail      :  1;
        u16 util_str_avail      :  1;   /* utility string available? */
        u16 pfgid               :  8;   /* pci function group id */
index ada571d1c63061f10045f8372a3edd93bfac4316..642a993846889935749280ad0b87d94c7b61841b 100644 (file)
@@ -126,6 +126,64 @@ static struct zpci_bus *zpci_bus_alloc(int pchid)
        return zbus;
 }
 
+#ifdef CONFIG_PCI_IOV
+static int zpci_bus_link_virtfn(struct pci_dev *pdev,
+               struct pci_dev *virtfn, int vfid)
+{
+       int rc;
+
+       virtfn->physfn = pci_dev_get(pdev);
+       rc = pci_iov_sysfs_link(pdev, virtfn, vfid);
+       if (rc) {
+               pci_dev_put(pdev);
+               virtfn->physfn = NULL;
+               return rc;
+       }
+       return 0;
+}
+
+static int zpci_bus_setup_virtfn(struct zpci_bus *zbus,
+               struct pci_dev *virtfn, int vfn)
+{
+       int i, cand_devfn;
+       struct zpci_dev *zdev;
+       struct pci_dev *pdev;
+       int vfid = vfn - 1; /* Linux' vfid's start at 0 vfn at 1*/
+       int rc = 0;
+
+       virtfn->is_virtfn = 1;
+       virtfn->multifunction = 0;
+       WARN_ON(vfid < 0);
+       /* If the parent PF for the given VF is also configured in the
+        * instance, it must be on the same zbus.
+        * We can then identify the parent PF by checking what
+        * devfn the VF would have if it belonged to that PF using the PF's
+        * stride and offset. Only if this candidate devfn matches the
+        * actual devfn will we link both functions.
+        */
+       for (i = 0; i < ZPCI_FUNCTIONS_PER_BUS; i++) {
+               zdev = zbus->function[i];
+               if (zdev && zdev->is_physfn) {
+                       pdev = pci_get_slot(zbus->bus, zdev->devfn);
+                       cand_devfn = pci_iov_virtfn_devfn(pdev, vfid);
+                       if (cand_devfn == virtfn->devfn) {
+                               rc = zpci_bus_link_virtfn(pdev, virtfn, vfid);
+                               break;
+                       }
+               }
+       }
+       return rc;
+}
+#else
+static inline int zpci_bus_setup_virtfn(struct zpci_bus *zbus,
+               struct pci_dev *virtfn, int vfn)
+{
+       virtfn->is_virtfn = 1;
+       virtfn->multifunction = 0;
+       return 0;
+}
+#endif
+
 static int zpci_bus_add_device(struct zpci_bus *zbus, struct zpci_dev *zdev)
 {
        struct pci_bus *bus;
@@ -156,10 +214,20 @@ static int zpci_bus_add_device(struct zpci_bus *zbus, struct zpci_dev *zdev)
        }
 
        pdev = pci_scan_single_device(bus, zdev->devfn);
-       if (pdev)
+       if (pdev) {
+               if (!zdev->is_physfn) {
+                       rc = zpci_bus_setup_virtfn(zbus, pdev, zdev->vfn);
+                       if (rc)
+                               goto failed_with_pdev;
+               }
                pci_bus_add_device(pdev);
-
+       }
        return 0;
+
+failed_with_pdev:
+       pci_stop_and_remove_bus_device(pdev);
+       pci_dev_put(pdev);
+       return rc;
 }
 
 static void zpci_bus_add_devices(struct zpci_bus *zbus)
index 9b318824a134dfcca47b66d9d81b3daa5455ba08..d7bd3c287cf7eb4342b9ff0b648f77987ae14f23 100644 (file)
@@ -159,6 +159,7 @@ static int clp_store_query_pci_fn(struct zpci_dev *zdev,
        zdev->uid = response->uid;
        zdev->fmb_length = sizeof(u32) * response->fmb_len;
        zdev->rid_available = response->rid_avail;
+       zdev->is_physfn = response->is_physfn;
        if (!s390_pci_no_rid && zdev->rid_available)
                zdev->devfn = response->rid & ZPCI_RID_MASK_DEVFN;
 
index ee6fbe688498972075dd59510301d31f0f9d3d0c..b37e08c4f9d1adcfa1a8b588390066d977b7d399 100644 (file)
@@ -571,9 +571,6 @@ static void sriov_del_vfs(struct pci_dev *dev)
        struct pci_sriov *iov = dev->sriov;
        int i;
 
-       if (dev->no_vf_scan)
-               return;
-
        for (i = 0; i < iov->num_VFs; i++)
                pci_iov_remove_virtfn(dev, i);
 }
This page took 0.057766 seconds and 4 git commands to generate.