static ssize_t recover_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct pci_dev *pdev = to_pci_dev(dev); struct zpci_dev *zdev = to_zpci(pdev); int ret; if (!device_remove_file_self(dev, attr)) return count; pci_lock_rescan_remove(); pci_stop_and_remove_bus_device(pdev); ret = zpci_disable_device(zdev); if (ret) goto error; ret = zpci_enable_device(zdev); if (ret) goto error; pci_rescan_bus(zdev->bus); pci_unlock_rescan_remove(); return count; error: pci_unlock_rescan_remove(); return ret; }
static void *eeh_rmv_device(void *data, void *userdata) { struct pci_driver *driver; struct eeh_dev *edev = (struct eeh_dev *)data; struct pci_dev *dev = eeh_dev_to_pci_dev(edev); int *removed = (int *)userdata; /* * Actually, we should remove the PCI bridges as well. * However, that's lots of complexity to do that, * particularly some of devices under the bridge might * support EEH. So we just care about PCI devices for * simplicity here. */ if (!dev || (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)) return NULL; /* * We rely on count-based pcibios_release_device() to * detach permanently offlined PEs. Unfortunately, that's * not reliable enough. We might have the permanently * offlined PEs attached, but we needn't take care of * them and their child devices. */ if (eeh_dev_removed(edev)) return NULL; driver = eeh_pcid_get(dev); if (driver) { eeh_pcid_put(dev); if (driver->err_handler) return NULL; } /* Remove it from PCI subsystem */ pr_debug("EEH: Removing %s without EEH sensitive driver\n", pci_name(dev)); edev->bus = dev->bus; edev->mode |= EEH_DEV_DISCONNECTED; (*removed)++; pci_lock_rescan_remove(); pci_stop_and_remove_bus_device(dev); pci_unlock_rescan_remove(); return NULL; }
void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev) { pci_lock_rescan_remove(); pci_stop_and_remove_bus_device(dev); pci_unlock_rescan_remove(); }
/** * eeh_reset_device - Perform actual reset of a pci slot * @pe: EEH PE * @bus: PCI bus corresponding to the isolcated slot * * This routine must be called to do reset on the indicated PE. * During the reset, udev might be invoked because those affected * PCI devices will be removed and then added. */ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus) { struct pci_bus *frozen_bus = eeh_pe_bus_get(pe); struct timeval tstamp; int cnt, rc, removed = 0; /* pcibios will clear the counter; save the value */ cnt = pe->freeze_count; tstamp = pe->tstamp; /* * We don't remove the corresponding PE instances because * we need the information afterwords. The attached EEH * devices are expected to be attached soon when calling * into pcibios_add_pci_devices(). */ eeh_pe_state_mark(pe, EEH_PE_KEEP); if (bus) { pci_lock_rescan_remove(); pcibios_remove_pci_devices(bus); pci_unlock_rescan_remove(); } else if (frozen_bus) { eeh_pe_dev_traverse(pe, eeh_rmv_device, &removed); } /* * Reset the pci controller. (Asserts RST#; resets config space). * Reconfigure bridges and devices. Don't try to bring the system * up if the reset failed for some reason. * * During the reset, it's very dangerous to have uncontrolled PCI * config accesses. So we prefer to block them. However, controlled * PCI config accesses initiated from EEH itself are allowed. */ eeh_pe_state_mark(pe, EEH_PE_RESET); rc = eeh_reset_pe(pe); if (rc) { eeh_pe_state_clear(pe, EEH_PE_RESET); return rc; } pci_lock_rescan_remove(); /* Restore PE */ eeh_ops->configure_bridge(pe); eeh_pe_restore_bars(pe); eeh_pe_state_clear(pe, EEH_PE_RESET); /* Clear frozen state */ rc = eeh_clear_pe_frozen_state(pe); if (rc) return rc; /* Give the system 5 seconds to finish running the user-space * hotplug shutdown scripts, e.g. ifdown for ethernet. Yes, * this is a hack, but if we don't do this, and try to bring * the device up before the scripts have taken it down, * potentially weird things happen. */ if (bus) { pr_info("EEH: Sleep 5s ahead of complete hotplug\n"); ssleep(5); /* * The EEH device is still connected with its parent * PE. We should disconnect it so the binding can be * rebuilt when adding PCI devices. */ eeh_pe_traverse(pe, eeh_pe_detach_dev, NULL); pcibios_add_pci_devices(bus); } else if (frozen_bus && removed) { pr_info("EEH: Sleep 5s ahead of partial hotplug\n"); ssleep(5); eeh_pe_traverse(pe, eeh_pe_detach_dev, NULL); pcibios_add_pci_devices(frozen_bus); } eeh_pe_state_clear(pe, EEH_PE_KEEP); pe->tstamp = tstamp; pe->freeze_count = cnt; pci_unlock_rescan_remove(); return 0; }
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; int ret; if (zdev) pdev = pci_get_slot(zdev->bus, ZPCI_DEVFN); pr_info("%s: Event 0x%x reconfigured PCI function 0x%x\n", pdev ? pci_name(pdev) : "n/a", ccdf->pec, ccdf->fid); zpci_err("avail CCDF:\n"); zpci_err_hex(ccdf, sizeof(*ccdf)); switch (ccdf->pec) { case 0x0301: /* Reserved|Standby -> Configured */ if (!zdev) { ret = clp_add_pci_device(ccdf->fid, ccdf->fh, 0); if (ret) break; zdev = get_zdev_by_fid(ccdf->fid); } if (!zdev || zdev->state != ZPCI_FN_STATE_STANDBY) break; zdev->state = ZPCI_FN_STATE_CONFIGURED; zdev->fh = ccdf->fh; ret = zpci_enable_device(zdev); if (ret) break; pci_lock_rescan_remove(); pci_rescan_bus(zdev->bus); pci_unlock_rescan_remove(); break; case 0x0302: /* Reserved -> Standby */ if (!zdev) clp_add_pci_device(ccdf->fid, ccdf->fh, 0); break; case 0x0303: /* Deconfiguration requested */ if (pdev) pci_stop_and_remove_bus_device_locked(pdev); ret = zpci_disable_device(zdev); if (ret) break; ret = sclp_pci_deconfigure(zdev->fid); zpci_dbg(3, "deconf fid:%x, rc:%d\n", zdev->fid, ret); if (!ret) zdev->state = ZPCI_FN_STATE_STANDBY; break; case 0x0304: /* Configured -> Standby */ if (pdev) { /* Give the driver a hint that the function is * already unusable. */ pdev->error_state = pci_channel_io_perm_failure; pci_stop_and_remove_bus_device_locked(pdev); } zdev->fh = ccdf->fh; zpci_disable_device(zdev); zdev->state = ZPCI_FN_STATE_STANDBY; break; case 0x0306: /* 0x308 or 0x302 for multiple devices */ clp_rescan_pci_devices(); break; case 0x0308: /* Standby -> Reserved */ if (!zdev) break; pci_stop_root_bus(zdev->bus); pci_remove_root_bus(zdev->bus); break; default: break; } if (pdev) pci_dev_put(pdev); }