Blob Blame History Raw
From: Sebastian Ott <sebott@linux.vnet.ibm.com>
Subject: s390/pci: improve error handling during interrupt deregistration
Patch-mainline: v4.13-rc1
Git-commit: 4dfbd3efe3f0cf9ff1325b87491e1b1fe07afaf1
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: improve error handling during interrupt deregistration

              When we ask a function to stop creating interrupts this may fail
              due to the function being already gone (e.g. after hot-unplug).

              Consequently we don't free associated resources like summary bits
              and bit vectors used for irq processing. This could lead to
              situations where we ran out of these resources and fail to setup
              new interrupts.

              The fix is to just ignore the errors in cases where we can be
              sure no new interrupts are generated.

              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_insn.h |    2 +-
 arch/s390/pci/pci.c              |   29 +++++++++++++++++++----------
 arch/s390/pci/pci_insn.c         |   10 +++++-----
 3 files changed, 25 insertions(+), 16 deletions(-)

--- a/arch/s390/include/asm/pci_insn.h
+++ b/arch/s390/include/asm/pci_insn.h
@@ -76,7 +76,7 @@ struct zpci_fib {
 	u32 gd;
 } __packed __aligned(8);
 
-int zpci_mod_fc(u64 req, struct zpci_fib *fib);
+u8 zpci_mod_fc(u64 req, struct zpci_fib *fib, u8 *status);
 int zpci_refresh_trans(u64 fn, u64 addr, u64 range);
 int zpci_load(u64 *data, u64 req, u64 offset);
 int zpci_store(u64 data, u64 req, u64 offset);
--- a/arch/s390/pci/pci.c
+++ b/arch/s390/pci/pci.c
@@ -108,6 +108,7 @@ static int zpci_set_airq(struct zpci_dev
 {
 	u64 req = ZPCI_CREATE_REQ(zdev->fh, 0, ZPCI_MOD_FC_REG_INT);
 	struct zpci_fib fib = {0};
+	u8 status;
 
 	fib.isc = PCI_ISC;
 	fib.sum = 1;		/* enable summary notifications */
@@ -117,7 +118,22 @@ static int zpci_set_airq(struct zpci_dev
 	fib.aisb = (unsigned long) zpci_aisb_iv->vector + (zdev->aisb/64)*8;
 	fib.aisbo = zdev->aisb & 63;
 
-	return zpci_mod_fc(req, &fib);
+	return zpci_mod_fc(req, &fib, &status) ? -EIO : 0;
+}
+
+/* Modify PCI: Unregister adapter interruptions */
+static int zpci_clear_airq(struct zpci_dev *zdev)
+{
+	u64 req = ZPCI_CREATE_REQ(zdev->fh, 0, ZPCI_MOD_FC_DEREG_INT);
+	struct zpci_fib fib = {0};
+	u8 cc, status;
+
+	cc = zpci_mod_fc(req, &fib, &status);
+	if (cc == 3 || (cc == 1 && status == 24))
+		/* Function already gone or IRQs already deregistered. */
+		cc = 0;
+
+	return cc ? -EIO : 0;
 }
 
 struct mod_pci_args {
@@ -131,13 +147,14 @@ static int mod_pci(struct zpci_dev *zdev
 {
 	u64 req = ZPCI_CREATE_REQ(zdev->fh, dmaas, fn);
 	struct zpci_fib fib = {0};
+	u8 status;
 
 	fib.pba = args->base;
 	fib.pal = args->limit;
 	fib.iota = args->iota;
 	fib.fmb_addr = args->fmb_addr;
 
-	return zpci_mod_fc(req, &fib);
+	return zpci_mod_fc(req, &fib, &status) ? -EIO : 0;
 }
 
 /* Modify PCI: Register I/O address translation parameters */
@@ -159,14 +176,6 @@ int zpci_unregister_ioat(struct zpci_dev
 	return mod_pci(zdev, ZPCI_MOD_FC_DEREG_IOAT, dmaas, &args);
 }
 
-/* Modify PCI: Unregister adapter interruptions */
-static int zpci_clear_airq(struct zpci_dev *zdev)
-{
-	struct mod_pci_args args = { 0, 0, 0, 0 };
-
-	return mod_pci(zdev, ZPCI_MOD_FC_DEREG_INT, 0, &args);
-}
-
 /* Modify PCI: Set PCI function measurement parameters */
 int zpci_fmb_enable_device(struct zpci_dev *zdev)
 {
--- a/arch/s390/pci/pci_insn.c
+++ b/arch/s390/pci/pci_insn.c
@@ -40,20 +40,20 @@ static inline u8 __mpcifc(u64 req, struc
 	return cc;
 }
 
-int zpci_mod_fc(u64 req, struct zpci_fib *fib)
+u8 zpci_mod_fc(u64 req, struct zpci_fib *fib, u8 *status)
 {
-	u8 cc, status;
+	u8 cc;
 
 	do {
-		cc = __mpcifc(req, fib, &status);
+		cc = __mpcifc(req, fib, status);
 		if (cc == 2)
 			msleep(ZPCI_INSN_BUSY_DELAY);
 	} while (cc == 2);
 
 	if (cc)
-		zpci_err_insn(cc, status, req, 0);
+		zpci_err_insn(cc, *status, req, 0);
 
-	return (cc) ? -EIO : 0;
+	return cc;
 }
 
 /* Refresh PCI Translations */