⬢ DragonFlyBSD Kernel Audit
DF-4403 / fix.diff
← back to finding ↓ download raw
--- 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;