s390/pci: improve pci hotplug

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>
This commit is contained in:
Sebastian Ott 2017-05-09 12:27:30 +02:00 committed by Martin Schwidefsky
parent 783684f1f6
commit 623bd44d3f
3 changed files with 21 additions and 3 deletions

View File

@ -158,6 +158,7 @@ extern const struct attribute_group *zpci_attr_groups[];
----------------------------------------------------------------------------- */ ----------------------------------------------------------------------------- */
/* Base stuff */ /* Base stuff */
int zpci_create_device(struct zpci_dev *); int zpci_create_device(struct zpci_dev *);
void zpci_remove_device(struct zpci_dev *zdev);
int zpci_enable_device(struct zpci_dev *); int zpci_enable_device(struct zpci_dev *);
int zpci_disable_device(struct zpci_dev *); int zpci_disable_device(struct zpci_dev *);
void zpci_stop_device(struct zpci_dev *); void zpci_stop_device(struct zpci_dev *);

View File

@ -855,6 +855,15 @@ void zpci_stop_device(struct zpci_dev *zdev)
} }
EXPORT_SYMBOL_GPL(zpci_stop_device); 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, int zpci_report_error(struct pci_dev *pdev,
struct zpci_report_error_header *report) struct zpci_report_error_header *report)
{ {

View 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 zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);
struct pci_dev *pdev = NULL; struct pci_dev *pdev = NULL;
enum zpci_state state;
int ret; int ret;
if (zdev) 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); clp_add_pci_device(ccdf->fid, ccdf->fh, 0);
break; break;
case 0x0303: /* Deconfiguration requested */ case 0x0303: /* Deconfiguration requested */
if (!zdev)
break;
if (pdev) if (pdev)
pci_stop_and_remove_bus_device_locked(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; zdev->state = ZPCI_FN_STATE_STANDBY;
break; break;
case 0x0304: /* Configured -> Standby */ case 0x0304: /* Configured -> Standby|Reserved */
if (!zdev)
break;
if (pdev) { if (pdev) {
/* Give the driver a hint that the function is /* Give the driver a hint that the function is
* already unusable. */ * already unusable. */
@ -132,6 +137,10 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
zdev->fh = ccdf->fh; zdev->fh = ccdf->fh;
zpci_disable_device(zdev); zpci_disable_device(zdev);
zdev->state = ZPCI_FN_STATE_STANDBY; zdev->state = ZPCI_FN_STATE_STANDBY;
if (!clp_get_state(ccdf->fid, &state) &&
state == ZPCI_FN_STATE_RESERVED) {
zpci_remove_device(zdev);
}
break; break;
case 0x0306: /* 0x308 or 0x302 for multiple devices */ case 0x0306: /* 0x308 or 0x302 for multiple devices */
clp_rescan_pci_devices(); clp_rescan_pci_devices();
@ -139,8 +148,7 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
case 0x0308: /* Standby -> Reserved */ case 0x0308: /* Standby -> Reserved */
if (!zdev) if (!zdev)
break; break;
pci_stop_root_bus(zdev->bus); zpci_remove_device(zdev);
pci_remove_root_bus(zdev->bus);
break; break;
default: default:
break; break;