DF-5377 / fix.diff
--- a/sys/bus/cam/scsi/scsi_target.c +++ b/sys/bus/cam/scsi/scsi_target.c @@ -539,6 +539,12 @@ * interested in them. We free them now. */ while ((ccb_h = TAILQ_FIRST(&softc->user_ccb_queue)) != NULL) { + struct targ_cmd_descr *map_descr; + + map_descr = (struct targ_cmd_descr *)ccb_h->targ_descr; + if (map_descr->mapinfo.num_bufs_used != 0) + cam_periph_unmapmem((union ccb *)ccb_h, + &map_descr->mapinfo); TAILQ_REMOVE(&softc->user_ccb_queue, ccb_h, periph_links.tqe); targfreeccb(softc, (union ccb *)ccb_h); } @@ -825,11 +831,24 @@ TAILQ_REMOVE(&softc->pending_ccb_queue, &done_ccb->ccb_h, periph_links.tqe); - /* If we're no longer enabled, throw away CCB */ + + /* + * If we're no longer enabled, hand the CCB back to the user + * instead of freeing it here: targdone() runs in the cambio SWI + * context, where neither cam_periph_unmapmem() (copyout) nor + * relpbuf() (the pbuf is locked by the mapping thread) may run. + * Delivering the CCB via user_ccb_queue makes the daemon's + * read()/close path release the mapping, as documented in + * scsi_targetio.h ("aborted CCBs will be returned to the user + * via read(2) or discarded if the user closes the device"). + */ if ((softc->state & TARG_STATE_LUN_ENABLED) == 0) { - targfreeccb(softc, done_ccb); + TAILQ_INSERT_TAIL(&softc->user_ccb_queue, &done_ccb->ccb_h, + periph_links.tqe); + notify_user(softc); return; } + /* abort_all_pending() waits for pending queue to be empty */ if (TAILQ_EMPTY(&softc->pending_ccb_queue)) wakeup(&softc->pending_ccb_queue); |