--- a/sys/dev/disk/nata/atapi-cam.c +++ b/sys/dev/disk/nata/atapi-cam.c @@ -913,22 +913,40 @@ int devcount; switch (what) { - case MOD_UNLOAD: + case MOD_UNLOAD: { + int busy = 0; + if (devclass_get_devices(atapi_cam_devclass, &devlist, &devcount) != 0) return ENXIO; if (devlist != NULL) { - while (devlist != NULL && devcount > 0) { + while (devcount > 0) { device_t child = devlist[--devcount]; struct atapi_xpt_softc *scp = device_get_softc(child); - device_delete_child(device_get_parent(child),child); - if (scp != NULL) + if (device_delete_child(device_get_parent(child),child) != 0) { + busy = 1; + continue; + } + /* + * The softc is external (device_set_softc in identify), + * so we own it. Only free it when the SIM is gone: + * free_softc() leaves BUS_REGISTERED set (and the SIM + * live in the XPT) when the bus could not be + * deregistered - freeing the softc then would leave the + * still-reachable cam_sim with a dangling softc. + */ + if (scp != NULL && (scp->flags & BUS_REGISTERED) == 0) kfree(scp, M_ATACAM); + else + busy = 1; } kfree(devlist, M_TEMP); } + if (busy != 0) + return EBUSY; /* stay resident instead of UAF */ break; + } default: break;