--- a/sys/dev/raid/tws/tws_user.c +++ b/sys/dev/raid/tws/tws_user.c @@ -97,10 +97,18 @@ tws_passthru(struct tws_softc *sc, void *buf) int error; u_int16_t lun4; if ( tws_get_state(sc) == TWS_RESET ) { return(EBUSY); } + + /* + * The data tag (tws.c:518-533) is built for TWS_MAX_IO_SIZE (128 kB) + * with 46 segments. Larger lengths make bus_dmamap_load() deliver + * EFBIG (command is still submitted with a truncated SGL) or return + * EINPROGRESS, which panics the kernel for this BUS_DMA_PROTECTED tag + * ("protected dmamap callback will be defered"). Reject up front. + */ + if ( ubuf->driver_pkt.buffer_length > TWS_MAX_IO_SIZE ) + return(EINVAL); do { req = tws_get_request(sc, TWS_PASSTHRU_REQ); if ( !req ) { sc->chan = 1; @@ -117,11 +125,19 @@ tws_passthru(struct tws_softc *sc, void *buf) req->length = ubuf->driver_pkt.buffer_length; TWS_TRACE_DEBUG(sc, "datal,rid", req->length, req->request_id); if ( req->length ) { req->data = kmalloc(req->length, M_TWS, M_WAITOK | M_ZERO); - error = copyin(ubuf->pdata, req->data, req->length); + if ( req->data == NULL ) { + req->state = TWS_REQ_STATE_FREE; + if ( sc->chan ) { sc->chan = 0; wakeup((void *)&sc->chan); } + return(ENOMEM); + } + error = copyin(ubuf->pdata, req->data, req->length); + if ( error ) { + kfree(req->data, M_TWS); + req->data = NULL; + req->state = TWS_REQ_STATE_FREE; + if ( sc->chan ) { sc->chan = 0; wakeup((void *)&sc->chan); } + return(error); /* do NOT submit the command with zeroed data */ + } }