Make detaching drivers from PCI devices more robust. While here, fix a
bug where a PCI device would be powered down if it failed to probe, but not when its driver was detached (e.g. via kldunload). - Add a new helper method resource_list_release_active() which forcefully releases any active resources of a specified type from a resource list. - Add a bus_child_detached method for the PCI bus driver which forces any active resources to be released (and whines to the console if it finds any) and then powers the device down. - Call pci_child_detached() if we fail to probe a device when a driver is kldloaded. This isn't perfect but can avoid leaking resources from a probe() routine in the kldload case. Reviewed by: imp, brooks MFC after: 1 month
This commit is contained in:
parent
11764d8411
commit
e35ce1f271
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=252315
@ -152,6 +152,7 @@ static device_method_t pci_methods[] = {
|
||||
DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource),
|
||||
DEVMETHOD(bus_activate_resource, pci_activate_resource),
|
||||
DEVMETHOD(bus_deactivate_resource, pci_deactivate_resource),
|
||||
DEVMETHOD(bus_child_detached, pci_child_detached),
|
||||
DEVMETHOD(bus_child_pnpinfo_str, pci_child_pnpinfo_str_method),
|
||||
DEVMETHOD(bus_child_location_str, pci_child_location_str_method),
|
||||
DEVMETHOD(bus_remap_intr, pci_remap_intr_method),
|
||||
@ -3489,7 +3490,7 @@ pci_driver_added(device_t dev, driver_t *driver)
|
||||
pci_printf(&dinfo->cfg, "reprobing on driver added\n");
|
||||
pci_cfg_restore(child, dinfo);
|
||||
if (device_probe_and_attach(child) != 0)
|
||||
pci_cfg_save(child, dinfo, 1);
|
||||
pci_child_detached(dev, child);
|
||||
}
|
||||
free(devlist, M_TEMP);
|
||||
}
|
||||
@ -3804,6 +3805,34 @@ pci_probe_nomatch(device_t dev, device_t child)
|
||||
pci_cfg_save(child, device_get_ivars(child), 1);
|
||||
}
|
||||
|
||||
void
|
||||
pci_child_detached(device_t dev, device_t child)
|
||||
{
|
||||
struct pci_devinfo *dinfo;
|
||||
struct resource_list *rl;
|
||||
|
||||
dinfo = device_get_ivars(child);
|
||||
rl = &dinfo->resources;
|
||||
|
||||
/*
|
||||
* Have to deallocate IRQs before releasing any MSI messages and
|
||||
* have to release MSI messages before deallocating any memory
|
||||
* BARs.
|
||||
*/
|
||||
if (resource_list_release_active(rl, dev, child, SYS_RES_IRQ) != 0)
|
||||
pci_printf(&dinfo->cfg, "Device leaked IRQ resources\n");
|
||||
if (dinfo->cfg.msi.msi_alloc != 0 || dinfo->cfg.msix.msix_alloc != 0) {
|
||||
pci_printf(&dinfo->cfg, "Device leaked MSI vectors\n");
|
||||
(void)pci_release_msi(child);
|
||||
}
|
||||
if (resource_list_release_active(rl, dev, child, SYS_RES_MEMORY) != 0)
|
||||
pci_printf(&dinfo->cfg, "Device leaked memory resources\n");
|
||||
if (resource_list_release_active(rl, dev, child, SYS_RES_IOPORT) != 0)
|
||||
pci_printf(&dinfo->cfg, "Device leaked I/O resources\n");
|
||||
|
||||
pci_cfg_save(child, dinfo, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the PCI device database, if loaded, and return a pointer to a
|
||||
* description of the device.
|
||||
|
@ -106,6 +106,7 @@ struct pci_devinfo *pci_read_device(device_t pcib, int d, int b, int s, int f,
|
||||
size_t size);
|
||||
void pci_print_verbose(struct pci_devinfo *dinfo);
|
||||
int pci_freecfg(struct pci_devinfo *dinfo);
|
||||
void pci_child_detached(device_t dev, device_t child);
|
||||
int pci_child_location_str_method(device_t cbdev, device_t child,
|
||||
char *buf, size_t buflen);
|
||||
int pci_child_pnpinfo_str_method(device_t cbdev, device_t child,
|
||||
|
@ -3315,6 +3315,48 @@ resource_list_release(struct resource_list *rl, device_t bus, device_t child,
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Release all active resources of a given type
|
||||
*
|
||||
* Release all active resources of a specified type. This is intended
|
||||
* to be used to cleanup resources leaked by a driver after detach or
|
||||
* a failed attach.
|
||||
*
|
||||
* @param rl the resource list which was allocated from
|
||||
* @param bus the parent device of @p child
|
||||
* @param child the device whose active resources are being released
|
||||
* @param type the type of resources to release
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval EBUSY at least one resource was active
|
||||
*/
|
||||
int
|
||||
resource_list_release_active(struct resource_list *rl, device_t bus,
|
||||
device_t child, int type)
|
||||
{
|
||||
struct resource_list_entry *rle;
|
||||
int error, retval;
|
||||
|
||||
retval = 0;
|
||||
STAILQ_FOREACH(rle, rl, link) {
|
||||
if (rle->type != type)
|
||||
continue;
|
||||
if (rle->res == NULL)
|
||||
continue;
|
||||
if ((rle->flags & (RLE_RESERVED | RLE_ALLOCATED)) ==
|
||||
RLE_RESERVED)
|
||||
continue;
|
||||
retval = EBUSY;
|
||||
error = resource_list_release(rl, bus, child, type,
|
||||
rman_get_rid(rle->res), rle->res);
|
||||
if (error != 0)
|
||||
device_printf(bus,
|
||||
"Failed to release active resource: %d\n", error);
|
||||
}
|
||||
return (retval);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Fully release a reserved resource
|
||||
*
|
||||
|
@ -273,6 +273,9 @@ struct resource *
|
||||
int resource_list_release(struct resource_list *rl,
|
||||
device_t bus, device_t child,
|
||||
int type, int rid, struct resource *res);
|
||||
int resource_list_release_active(struct resource_list *rl,
|
||||
device_t bus, device_t child,
|
||||
int type);
|
||||
struct resource *
|
||||
resource_list_reserve(struct resource_list *rl,
|
||||
device_t bus, device_t child,
|
||||
|
Loading…
Reference in New Issue
Block a user