s390/pci: update function handle after resume from hibernate

Function handles may change while the system was in hibernation
use list pci functions and update the function handles.

Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h
index 4b2bbc1..c290f13 100644
--- a/arch/s390/include/asm/pci.h
+++ b/arch/s390/include/asm/pci.h
@@ -6,6 +6,7 @@
 /* must be set before including pci_clp.h */
 #define PCI_BAR_COUNT	6
 
+#include <linux/pci.h>
 #include <asm-generic/pci.h>
 #include <asm-generic/pci-dma-compat.h>
 #include <asm/pci_clp.h>
@@ -137,6 +138,7 @@
 /* CLP */
 int clp_scan_pci_devices(void);
 int clp_rescan_pci_devices(void);
+int clp_rescan_pci_devices_simple(void);
 int clp_add_pci_device(u32, u32, int);
 int clp_enable_fh(struct zpci_dev *, u8);
 int clp_disable_fh(struct zpci_dev *);
@@ -145,9 +147,11 @@
 /* Error handling and recovery */
 void zpci_event_error(void *);
 void zpci_event_availability(void *);
+void zpci_rescan(void);
 #else /* CONFIG_PCI */
 static inline void zpci_event_error(void *e) {}
 static inline void zpci_event_availability(void *e) {}
+static inline void zpci_rescan(void) {}
 #endif /* CONFIG_PCI */
 
 #ifdef CONFIG_HOTPLUG_PCI_S390
diff --git a/arch/s390/kernel/suspend.c b/arch/s390/kernel/suspend.c
index eebab9f..737bff3 100644
--- a/arch/s390/kernel/suspend.c
+++ b/arch/s390/kernel/suspend.c
@@ -12,6 +12,7 @@
 #include <asm/ctl_reg.h>
 #include <asm/ipl.h>
 #include <asm/cio.h>
+#include <asm/pci.h>
 
 /*
  * References to section boundaries
@@ -219,4 +220,5 @@
 {
 	lgr_info_log();
 	channel_subsystem_reinit();
+	zpci_rescan();
 }
diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
index a7ed668..f17a834 100644
--- a/arch/s390/pci/pci.c
+++ b/arch/s390/pci/pci.c
@@ -46,7 +46,7 @@
 
 /* list of all detected zpci devices */
 static LIST_HEAD(zpci_list);
-static DEFINE_MUTEX(zpci_list_lock);
+static DEFINE_SPINLOCK(zpci_list_lock);
 
 static void zpci_enable_irq(struct irq_data *data);
 static void zpci_disable_irq(struct irq_data *data);
@@ -88,14 +88,14 @@
 {
 	struct zpci_dev *tmp, *zdev = NULL;
 
-	mutex_lock(&zpci_list_lock);
+	spin_lock(&zpci_list_lock);
 	list_for_each_entry(tmp, &zpci_list, entry) {
 		if (tmp->fid == fid) {
 			zdev = tmp;
 			break;
 		}
 	}
-	mutex_unlock(&zpci_list_lock);
+	spin_unlock(&zpci_list_lock);
 	return zdev;
 }
 
@@ -821,9 +821,9 @@
 	if (rc)
 		goto out_disable;
 
-	mutex_lock(&zpci_list_lock);
+	spin_lock(&zpci_list_lock);
 	list_add_tail(&zdev->entry, &zpci_list);
-	mutex_unlock(&zpci_list_lock);
+	spin_unlock(&zpci_list_lock);
 
 	zpci_init_slot(zdev);
 
@@ -939,3 +939,8 @@
 	return rc;
 }
 subsys_initcall_sync(pci_base_init);
+
+void zpci_rescan(void)
+{
+	clp_rescan_pci_devices_simple();
+}
diff --git a/arch/s390/pci/pci_clp.c b/arch/s390/pci/pci_clp.c
index 3eaf63a..475563c 100644
--- a/arch/s390/pci/pci_clp.c
+++ b/arch/s390/pci/pci_clp.c
@@ -319,6 +319,20 @@
 	}
 }
 
+static void __clp_update(struct clp_fh_list_entry *entry)
+{
+	struct zpci_dev *zdev;
+
+	if (!entry->vendor_id)
+		return;
+
+	zdev = get_zdev_by_fid(entry->fid);
+	if (!zdev)
+		return;
+
+	zdev->fh = entry->fh;
+}
+
 int clp_scan_pci_devices(void)
 {
 	struct clp_req_rsp_list_pci *rrb;
@@ -348,3 +362,18 @@
 	clp_free_block(rrb);
 	return rc;
 }
+
+int clp_rescan_pci_devices_simple(void)
+{
+	struct clp_req_rsp_list_pci *rrb;
+	int rc;
+
+	rrb = clp_alloc_block(GFP_NOWAIT);
+	if (!rrb)
+		return -ENOMEM;
+
+	rc = clp_list_pci(rrb, __clp_update);
+
+	clp_free_block(rrb);
+	return rc;
+}