]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
s390/pci: refresh function handle in iomap
authorNiklas Schnelle <schnelle@linux.ibm.com>
Thu, 17 Mar 2022 15:35:00 +0000 (12:35 -0300)
committerPaolo Pisati <paolo.pisati@canonical.com>
Tue, 22 Mar 2022 16:57:50 +0000 (17:57 +0100)
BugLink: https://bugs.launchpad.net/bugs/1959532
The function handle of a PCI function is updated when disabling or
enabling it as well as when the function's availability changes or it
enters the error state.

Until now this only occurred either while there is no struct pci_dev
associated with the function yet or the function became unavailable.
This meant that leaving a stale function handle in the iomap either
didn't happen because there was no iomap yet or it lead to errors on PCI
access but so would the correct disabled function handle.

In the future a CLP Set PCI Function Disable/Enable cycle during PCI
device recovery may be done while the device is bound to a driver.  In
this case we must update the iomap associated with the now-stale
function handle to ensure that the resulting zPCI instruction references
an accurate function handle.

Since the function handle is accessed by the PCI accessor helpers
without locking use READ_ONCE()/WRITE_ONCE() to mark this access and
prevent compiler optimizations that would move the load/store.

With that infrastructure in place let's also properly update the
function handle in the existing cases. This makes sure that in the
future debugging of a zPCI function access through the handle will
show an up to date handle reducing the chance of confusion. Also it
makes sure we have one single place where a zPCI function handle is
updated after initialization.

Reviewed-by: Pierre Morel <pmorel@linux.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
(cherry picked from commit 4fe204977096e900cb91a3298b05c794ac24f540)
Signed-off-by: Patricia Domingues <patricia.domingues@canonical.com>
Acked-by: Tim Gardner <tim.gardner@canonical.com>
Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
arch/s390/include/asm/pci.h
arch/s390/pci/pci.c
arch/s390/pci/pci_event.c
arch/s390/pci/pci_insn.c

index 6b3c366af78eb5e9608cdd732d25155c9e05fb9a..35adc0cd0e6a15d7cb52c1809673d7925d6d5fde 100644 (file)
@@ -213,6 +213,7 @@ bool zpci_is_device_configured(struct zpci_dev *zdev);
 int zpci_register_ioat(struct zpci_dev *, u8, u64, u64, u64);
 int zpci_unregister_ioat(struct zpci_dev *, u8);
 void zpci_remove_reserved_devices(void);
+void zpci_update_fh(struct zpci_dev *zdev, u32 fh);
 
 /* CLP */
 int clp_setup_writeback_mio(void);
index b833155ce838111981b424418703c584c47c8771..65a8e3322f5f207e5dbbc80b080023e881bf8431 100644 (file)
@@ -481,6 +481,34 @@ static void zpci_free_iomap(struct zpci_dev *zdev, int entry)
        spin_unlock(&zpci_iomap_lock);
 }
 
+static void zpci_do_update_iomap_fh(struct zpci_dev *zdev, u32 fh)
+{
+       int bar, idx;
+
+       spin_lock(&zpci_iomap_lock);
+       for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
+               if (!zdev->bars[bar].size)
+                       continue;
+               idx = zdev->bars[bar].map_idx;
+               if (!zpci_iomap_start[idx].count)
+                       continue;
+               WRITE_ONCE(zpci_iomap_start[idx].fh, zdev->fh);
+       }
+       spin_unlock(&zpci_iomap_lock);
+}
+
+void zpci_update_fh(struct zpci_dev *zdev, u32 fh)
+{
+       if (!fh || zdev->fh == fh)
+               return;
+
+       zdev->fh = fh;
+       if (zpci_use_mio(zdev))
+               return;
+       if (zdev->has_resources && zdev_enabled(zdev))
+               zpci_do_update_iomap_fh(zdev, fh);
+}
+
 static struct resource *__alloc_res(struct zpci_dev *zdev, unsigned long start,
                                    unsigned long size, unsigned long flags)
 {
@@ -668,7 +696,7 @@ int zpci_enable_device(struct zpci_dev *zdev)
        if (clp_enable_fh(zdev, &fh, ZPCI_NR_DMA_SPACES))
                rc = -EIO;
        else
-               zdev->fh = fh;
+               zpci_update_fh(zdev, fh);
        return rc;
 }
 
@@ -679,14 +707,14 @@ int zpci_disable_device(struct zpci_dev *zdev)
 
        cc = clp_disable_fh(zdev, &fh);
        if (!cc) {
-               zdev->fh = fh;
+               zpci_update_fh(zdev, fh);
        } else if (cc == CLP_RC_SETPCIFN_ALRDY) {
                pr_info("Disabling PCI function %08x had no effect as it was already disabled\n",
                        zdev->fid);
                /* Function is already disabled - update handle */
                rc = clp_refresh_fh(zdev->fid, &fh);
                if (!rc) {
-                       zdev->fh = fh;
+                       zpci_update_fh(zdev, fh);
                        rc = -EINVAL;
                }
        } else {
@@ -776,7 +804,7 @@ int zpci_scan_configured_device(struct zpci_dev *zdev, u32 fh)
 {
        int rc;
 
-       zdev->fh = fh;
+       zpci_update_fh(zdev, fh);
        /* the PCI function will be scanned once function 0 appears */
        if (!zdev->zbus->bus)
                return 0;
index 6a5bfa9dc1f2e74e2a706df44c3e9418e41df9ad..8df8b3210c5be36e11a837795f67256ad97b9709 100644 (file)
@@ -78,7 +78,7 @@ void zpci_event_error(void *data)
 
 static void zpci_event_hard_deconfigured(struct zpci_dev *zdev, u32 fh)
 {
-       zdev->fh = fh;
+       zpci_update_fh(zdev, fh);
        /* Give the driver a hint that the function is
         * already unusable.
         */
@@ -121,7 +121,7 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
                if (!zdev)
                        zpci_create_device(ccdf->fid, ccdf->fh, ZPCI_FN_STATE_STANDBY);
                else
-                       zdev->fh = ccdf->fh;
+                       zpci_update_fh(zdev, ccdf->fh);
                break;
        case 0x0303: /* Deconfiguration requested */
                if (zdev) {
@@ -130,7 +130,7 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
                         */
                        if (zdev->state != ZPCI_FN_STATE_CONFIGURED)
                                break;
-                       zdev->fh = ccdf->fh;
+                       zpci_update_fh(zdev, ccdf->fh);
                        zpci_deconfigure_device(zdev);
                }
                break;
index 2e43996159f05881c218671afaa1663230899fed..28d863aaafea53159c4b039b3e1ac9334f2a84e1 100644 (file)
@@ -163,7 +163,7 @@ static inline int zpci_load_fh(u64 *data, const volatile void __iomem *addr,
                               unsigned long len)
 {
        struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(addr)];
-       u64 req = ZPCI_CREATE_REQ(entry->fh, entry->bar, len);
+       u64 req = ZPCI_CREATE_REQ(READ_ONCE(entry->fh), entry->bar, len);
 
        return __zpci_load(data, req, ZPCI_OFFSET(addr));
 }
@@ -244,7 +244,7 @@ static inline int zpci_store_fh(const volatile void __iomem *addr, u64 data,
                                unsigned long len)
 {
        struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(addr)];
-       u64 req = ZPCI_CREATE_REQ(entry->fh, entry->bar, len);
+       u64 req = ZPCI_CREATE_REQ(READ_ONCE(entry->fh), entry->bar, len);
 
        return __zpci_store(data, req, ZPCI_OFFSET(addr));
 }