--- a/sys/dev/raid/tws/tws_cam.c +++ b/sys/dev/raid/tws/tws_cam.c @@ -79,6 +79,8 @@ struct tws_command_header *hdr); static void tws_passthru_err_complete(struct tws_request *req, struct tws_command_header *hdr); +static void tws_free_reserved_req(struct tws_softc *sc, + struct tws_request *req); static void tws_timeout(void *arg); @@ -807,9 +809,32 @@ callout_reset(ccb_h->timeout_ch, (ccb_h->timeout * hz)/1000, tws_timeout, req); error = tws_map_request(sc, req); + if (error && error != TWS_REQ_ERR_INPROGRESS) { + /* mapping failed: the request was never submitted (it is on no + * queue), so no completion path can rescue it. Complete the ccb + * and return the slot, or both are leaked forever. */ + callout_stop(ccb_h->timeout_ch); + ccb_h->status = CAM_REQ_CMP_ERR; + ccb_h->status &= ~CAM_SIM_QUEUED; + xpt_done(ccb); + tws_release_request(req); + return(0); + } return(error); } +/* + * Reserved slots (0..TWS_RESERVED_REQS-1) are gated by state alone and are + * never on FREE_Q; release them by resetting the state. + */ +static void +tws_free_reserved_req(struct tws_softc *sc, struct tws_request *req) +{ + lockmgr(&sc->gen_lock, LK_EXCLUSIVE); + req->state = TWS_REQ_STATE_FREE; + lockmgr(&sc->gen_lock, LK_RELEASE); +} + int tws_send_scsi_cmd(struct tws_softc *sc, int cmd) @@ -840,8 +865,10 @@ req->length = TWS_SECTOR_SIZE; req->data = kmalloc(TWS_SECTOR_SIZE, M_TWS, M_NOWAIT | M_ZERO); - if ( req->data == NULL ) + if ( req->data == NULL ) { + tws_free_reserved_req(sc, req); /* or the AEN channel dies */ return(ENOMEM); + } req->flags = TWS_DIR_IN; callout_reset(&req->thandle, (TWS_IO_TIMEOUT * hz), tws_timeout, req); @@ -868,8 +895,10 @@ req->length = TWS_SECTOR_SIZE; req->data = kmalloc(TWS_SECTOR_SIZE, M_TWS, M_NOWAIT | M_ZERO); - if ( req->data == NULL ) + if ( req->data == NULL ) { + tws_free_reserved_req(sc, req); return(ENOMEM); + } param = (struct tws_getset_param *)req->data; req->cb = tws_getset_param_complete; @@ -917,8 +946,10 @@ req->length = TWS_SECTOR_SIZE; req->data = kmalloc(TWS_SECTOR_SIZE, M_TWS, M_NOWAIT | M_ZERO); - if ( req->data == NULL ) + if ( req->data == NULL ) { + tws_free_reserved_req(sc, req); return(FAILURE); + } param = (struct tws_getset_param *)req->data; req->cb = NULL;