s390/pci: improve pci hotplug
authorSebastian Ott <sebott@linux.vnet.ibm.com>
Tue, 9 May 2017 10:27:30 +0000 (12:27 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 28 Jun 2017 05:32:12 +0000 (07:32 +0200)
PCI hotplug events basically notify about the new state of a
function. Unfortunately some hypervisors implement hotplug
events in a way where it is not clear what the new state of
the function should be.

Use clp_get_state to find the current state of the function
and handle accordingly.

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_event.c

index 01c58d4..280458c 100644 (file)
@@ -158,6 +158,7 @@ extern const struct attribute_group *zpci_attr_groups[];
 ----------------------------------------------------------------------------- */
 /* Base stuff */
 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 *);
index 6a44a68..f4928bc 100644 (file)
@@ -855,6 +855,15 @@ void zpci_stop_device(struct zpci_dev *zdev)
 }
 EXPORT_SYMBOL_GPL(zpci_stop_device);
 
+void zpci_remove_device(struct zpci_dev *zdev)
+{
+       if (!zdev->bus)
+               return;
+
+       pci_stop_root_bus(zdev->bus);
+       pci_remove_root_bus(zdev->bus);
+}
+
 int zpci_report_error(struct pci_dev *pdev,
                      struct zpci_report_error_header *report)
 {
index c2b27ad..0bbc04a 100644 (file)
@@ -74,6 +74,7 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
 {
        struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);
        struct pci_dev *pdev = NULL;
+       enum zpci_state state;
        int ret;
 
        if (zdev)
@@ -108,6 +109,8 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
                        clp_add_pci_device(ccdf->fid, ccdf->fh, 0);
                break;
        case 0x0303: /* Deconfiguration requested */
+               if (!zdev)
+                       break;
                if (pdev)
                        pci_stop_and_remove_bus_device_locked(pdev);
 
@@ -121,7 +124,9 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
                        zdev->state = ZPCI_FN_STATE_STANDBY;
 
                break;
-       case 0x0304: /* Configured -> Standby */
+       case 0x0304: /* Configured -> Standby|Reserved */
+               if (!zdev)
+                       break;
                if (pdev) {
                        /* Give the driver a hint that the function is
                         * already unusable. */
@@ -132,6 +137,10 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
                zdev->fh = ccdf->fh;
                zpci_disable_device(zdev);
                zdev->state = ZPCI_FN_STATE_STANDBY;
+               if (!clp_get_state(ccdf->fid, &state) &&
+                   state == ZPCI_FN_STATE_RESERVED) {
+                       zpci_remove_device(zdev);
+               }
                break;
        case 0x0306: /* 0x308 or 0x302 for multiple devices */
                clp_rescan_pci_devices();
@@ -139,8 +148,7 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
        case 0x0308: /* Standby -> Reserved */
                if (!zdev)
                        break;
-               pci_stop_root_bus(zdev->bus);
-               pci_remove_root_bus(zdev->bus);
+               zpci_remove_device(zdev);
                break;
        default:
                break;