diff --git a/sys/bus/cam/scsi/scsi_targ_bh.c b/sys/bus/cam/scsi/scsi_targ_bh.c --- a/sys/bus/cam/scsi/scsi_targ_bh.c +++ b/sys/bus/cam/scsi/scsi_targ_bh.c @@ -85,6 +85,7 @@ struct ccb_queue pending_queue; struct ccb_queue work_queue; struct ccb_queue unknown_atio_queue; + u_int outstanding_ctios; struct devstat device_stats; targbh_state state; targbh_flags flags; @@ -416,8 +417,16 @@ case 1: /* FALLTHROUGH */ default: - /* XXX Wait for callback of targbhdislun() */ - sim_lock_sleep(softc, 0, "targbh", hz/2, periph->sim->lock); + /* + * Wait for all in-flight CTIOs to complete. targbhdone() + * decrements outstanding_ctios and wakes us when it hits 0. + * Replaces the fixed 500 ms timeout that had no wakeup + * source and could kfree(softc) while a CTIO completion + * was still outstanding -> use-after-free in targbhdone(). + */ + while (softc->outstanding_ctios > 0) + sim_lock_sleep(&softc->outstanding_ctios, 0, "targbh", + hz, periph->sim->lock); kfree(softc, M_SCSIBH); break; } @@ -492,6 +501,8 @@ start_ccb->ccb_h.ccb_type = TARGBH_CCB_WORKQ; start_ccb->ccb_h.ccb_atio = atio; + /* Track in-flight CTIO so targbhdtor() can wait for it. */ + softc->outstanding_ctios++; CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("Sending a CTIO\n")); xpt_action(start_ccb); @@ -675,6 +686,12 @@ } desc->data_resid -= desc->data_increment; xpt_release_ccb(done_ccb); + KKASSERT(softc->outstanding_ctios > 0); + if (--softc->outstanding_ctios == 0) { + /* Wake targbhdtor() teardown wait. */ + wakeup_one(&softc->outstanding_ctios); + } + if (softc->state != TARGBH_STATE_TEARDOWN) { /*