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

              DMA tables are freed in zpci_dma_exit_device regardless of the return
              code of zpci_unregister_ioat. This could lead to a use after free. On
              the other hand during function hot-unplug, zpci_unregister_ioat will
              always fail since the function is already gone.

              So let zpci_unregister_ioat report success when the function is gone
              but don't cleanup the dma table when a function could still have it
              in access.

              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/pci/pci.c     |   19 ++++++++++++++-----
 arch/s390/pci/pci_dma.c |    4 +++-
 2 files changed, 17 insertions(+), 6 deletions(-)

--- a/arch/s390/pci/pci.c
+++ b/arch/s390/pci/pci.c
@@ -161,19 +161,28 @@ static int mod_pci(struct zpci_dev *zdev
 int zpci_register_ioat(struct zpci_dev *zdev, u8 dmaas,
 		       u64 base, u64 limit, u64 iota)
 {
-	struct mod_pci_args args = { base, limit, iota, 0 };
+	u64 req = ZPCI_CREATE_REQ(zdev->fh, dmaas, ZPCI_MOD_FC_REG_IOAT);
+	struct zpci_fib fib = {0};
+	u8 status;
 
 	WARN_ON_ONCE(iota & 0x3fff);
-	args.iota |= ZPCI_IOTA_RTTO_FLAG;
-	return mod_pci(zdev, ZPCI_MOD_FC_REG_IOAT, dmaas, &args);
+	fib.pba = base;
+	fib.pal = limit;
+	fib.iota = iota | ZPCI_IOTA_RTTO_FLAG;
+	return zpci_mod_fc(req, &fib, &status) ? -EIO : 0;
 }
 
 /* Modify PCI: Unregister I/O address translation parameters */
 int zpci_unregister_ioat(struct zpci_dev *zdev, u8 dmaas)
 {
-	struct mod_pci_args args = { 0, 0, 0, 0 };
+	u64 req = ZPCI_CREATE_REQ(zdev->fh, dmaas, ZPCI_MOD_FC_DEREG_IOAT);
+	struct zpci_fib fib = {0};
+	u8 cc, status;
 
-	return mod_pci(zdev, ZPCI_MOD_FC_DEREG_IOAT, dmaas, &args);
+	cc = zpci_mod_fc(req, &fib, &status);
+	if (cc == 3) /* Function already gone. */
+		cc = 0;
+	return cc ? -EIO : 0;
 }
 
 /* Modify PCI: Set PCI function measurement parameters */
--- a/arch/s390/pci/pci_dma.c
+++ b/arch/s390/pci/pci_dma.c
@@ -601,7 +601,9 @@ void zpci_dma_exit_device(struct zpci_de
 	 */
 	WARN_ON(zdev->s390_domain);
 
-	zpci_unregister_ioat(zdev, 0);
+	if (zpci_unregister_ioat(zdev, 0))
+		return;
+
 	dma_cleanup_tables(zdev->dma_table);
 	zdev->dma_table = NULL;
 	vfree(zdev->iommu_bitmap);