s390/pci: fix handling of PEC 306
authorSebastian Ott <sebott@linux.vnet.ibm.com>
Tue, 20 Jun 2017 13:56:05 +0000 (15:56 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 28 Jun 2017 05:32:13 +0000 (07:32 +0200)
In contrast to other hotplug events PEC 0x306 isn't about a single
but multiple devices. Also there's no information on what happened
to these devices. We correctly handled hotplug that way but failed
to handle hot-unplug. This patch addresses that and implements
hot-unplug of multiple devices via PEC 306.

Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Reviewed-by: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/pci.h
arch/s390/pci/pci.c
arch/s390/pci/pci_clp.c

index 280458c821049cc75ce603eec2746c089e055c0e..f36b4b7260577d574e2f8f70a0ca52609eee1ee0 100644 (file)
@@ -161,9 +161,9 @@ int zpci_create_device(struct zpci_dev *);
 void zpci_remove_device(struct zpci_dev *zdev);
 int zpci_enable_device(struct zpci_dev *);
 int zpci_disable_device(struct zpci_dev *);
-void zpci_stop_device(struct zpci_dev *);
 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);
 
 /* CLP */
 int clp_scan_pci_devices(void);
index f4928bc577735dfa6b3c086aeb5ef27b62b6d838..114b390d80f95bd2320a1b45bbcf49cc07dee3e5 100644 (file)
@@ -86,6 +86,25 @@ struct zpci_dev *get_zdev_by_fid(u32 fid)
        return zdev;
 }
 
+void zpci_remove_reserved_devices(void)
+{
+       struct zpci_dev *tmp, *zdev;
+       enum zpci_state state;
+       LIST_HEAD(remove);
+
+       spin_lock(&zpci_list_lock);
+       list_for_each_entry_safe(zdev, tmp, &zpci_list, entry) {
+               if (zdev->state == ZPCI_FN_STATE_STANDBY &&
+                   !clp_get_state(zdev->fid, &state) &&
+                   state == ZPCI_FN_STATE_RESERVED)
+                       list_move_tail(&zdev->entry, &remove);
+       }
+       spin_unlock(&zpci_list_lock);
+
+       list_for_each_entry_safe(zdev, tmp, &remove, entry)
+               zpci_remove_device(zdev);
+}
+
 static struct zpci_dev *get_zdev_by_bus(struct pci_bus *bus)
 {
        return (bus && bus->sysdata) ? (struct zpci_dev *) bus->sysdata : NULL;
@@ -845,16 +864,6 @@ out:
        return rc;
 }
 
-void zpci_stop_device(struct zpci_dev *zdev)
-{
-       zpci_dma_exit_device(zdev);
-       /*
-        * Note: SCLP disables fh via set-pci-fn so don't
-        * do that here.
-        */
-}
-EXPORT_SYMBOL_GPL(zpci_stop_device);
-
 void zpci_remove_device(struct zpci_dev *zdev)
 {
        if (!zdev->bus)
index 267cdd69e6da2c141c8e4ad57ef938ffb7e5928d..3a5cd84e5a3b365d2968e331adb125164f1ffaa6 100644 (file)
@@ -334,14 +334,6 @@ out:
 }
 
 static void __clp_add(struct clp_fh_list_entry *entry, void *data)
-{
-       if (!entry->vendor_id)
-               return;
-
-       clp_add_pci_device(entry->fid, entry->fh, entry->config_state);
-}
-
-static void __clp_rescan(struct clp_fh_list_entry *entry, void *data)
 {
        struct zpci_dev *zdev;
 
@@ -349,19 +341,8 @@ static void __clp_rescan(struct clp_fh_list_entry *entry, void *data)
                return;
 
        zdev = get_zdev_by_fid(entry->fid);
-       if (!zdev) {
+       if (!zdev)
                clp_add_pci_device(entry->fid, entry->fh, entry->config_state);
-               return;
-       }
-
-       if (!entry->config_state) {
-               /*
-                * The handle is already disabled, that means no iota/irq freeing via
-                * the firmware interfaces anymore. Need to free resources manually
-                * (DMA memory, debug, sysfs)...
-                */
-               zpci_stop_device(zdev);
-       }
 }
 
 static void __clp_update(struct clp_fh_list_entry *entry, void *data)
@@ -398,11 +379,13 @@ int clp_rescan_pci_devices(void)
        struct clp_req_rsp_list_pci *rrb;
        int rc;
 
+       zpci_remove_reserved_devices();
+
        rrb = clp_alloc_block(GFP_KERNEL);
        if (!rrb)
                return -ENOMEM;
 
-       rc = clp_list_pci(rrb, NULL, __clp_rescan);
+       rc = clp_list_pci(rrb, NULL, __clp_add);
 
        clp_free_block(rrb);
        return rc;