diff --git a/sys/bus/cam/scsi/scsi_target.c b/sys/bus/cam/scsi/scsi_target.c --- a/sys/bus/cam/scsi/scsi_target.c +++ b/sys/bus/cam/scsi/scsi_target.c @@ -213,8 +213,8 @@ int error; softc = (struct targ_softc *)dev->si_drv1; - if ((softc->periph == NULL) || - (softc->state & TARG_STATE_LUN_ENABLED) == 0) { + if (softc->periph == NULL) { + /* Never enabled: no periph to tear down; free softc here. */ devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(targ), dev->si_uminor); destroy_dev(dev); kfree(softc, M_TARG); @@ -223,21 +223,28 @@ /* * Acquire a hold on the periph so that it doesn't go away before - * we are ready at the end of the function. + * we are ready at the end of the function. The final release below + * drops the refcount to 0 with CAM_PERIPH_INVALID set and thus runs + * targdtor() synchronously; targdtor() drains the softc's queues and + * is the sole place softc is kfree()'d, so we must NOT free softc + * before cam_periph_release(). */ periph = softc->periph; cam_periph_acquire(periph); cam_periph_lock(periph); - error = targdisable(softc); + if ((softc->state & TARG_STATE_LUN_ENABLED) != 0) + error = targdisable(softc); + else + error = CAM_REQ_CMP; /* disabled via TARGIOCDISABLE */ if (error == CAM_REQ_CMP) { dev->si_drv1 = 0; if (softc->periph != NULL) { cam_periph_invalidate(softc->periph); softc->periph = NULL; } - destroy_dev(dev); /* eats the open ref */ devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(targ), dev->si_uminor); - kfree(softc, M_TARG); + destroy_dev(dev); /* eats the open ref */ + /* softc freed by targdtor() via cam_periph_release() below */ } else { release_dev(dev); } @@ -549,6 +556,7 @@ softc->periph = NULL; softc->path = NULL; + kfree(softc, M_TARG); periph->softc = NULL; }