Blob Blame History Raw
From: Sebastian Ott <sebott@linux.vnet.ibm.com>
Subject: s390/pci: fix handling of PEC 306
Patch-mainline: v4.13-rc1
Git-commit: 01553d9a2ba7c658bf9e9e5c65466508c1bd6db5
References: bnc#1066983, LTC#157731

Description:  PCI: fix hotplug related issues
Symptom:      After serveral cycles of hot-unplug/hotplug the affected
              function stops working or the kernel oopses.
Problem:      Hotplug notifications happen after the fact, meaning it's
              not possible to issue instructions to the function for
              cleanup purposes. Thus we never release the associated
              resources and at some point fail to allocate new ones.
Solution:     During clean up treat some errors as success (e.g. a
              function that's gone can't create an interrupt).
Reproduction: vmcp att pcif XXX to \* ;vmcp det pcif XXX #in a loop

Upstream-Description:

              s390/pci: fix handling of PEC 306

              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>


Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Acked-by: Hannes Reinecke <hare@suse.com>
---
 arch/s390/include/asm/pci.h |    2 +-
 arch/s390/pci/pci.c         |   29 +++++++++++++++++++----------
 arch/s390/pci/pci_clp.c     |   25 ++++---------------------
 3 files changed, 24 insertions(+), 32 deletions(-)

--- a/arch/s390/include/asm/pci.h
+++ b/arch/s390/include/asm/pci.h
@@ -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);
--- a/arch/s390/pci/pci.c
+++ b/arch/s390/pci/pci.c
@@ -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)
--- a/arch/s390/pci/pci_clp.c
+++ b/arch/s390/pci/pci_clp.c
@@ -335,33 +335,14 @@ 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;
 
 	if (!entry->vendor_id)
 		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;