⬢ DragonFlyBSD Kernel Audit
DF-3831 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/disk/trm/trm.c
+++ b/sys/dev/disk/trm/trm.c
@@ -163,6 +163,7 @@
 static void	trm_EnableMsgOutAbort2(PACB pACB, PSRB pSRB);
 static void	trm_EnableMsgOutAbort1(PACB pACB, PSRB pSRB);
 static void	trm_SendSRB(PACB pACB, PSRB pSRB);
+static void	trm_timeout(void *arg);
 static int	trm_probe(device_t tag);
 static int	trm_attach(device_t tag);
 static void	trm_reset(PACB pACB);
@@ -423,6 +424,19 @@
 	ccb = pSRB->pccb;
 	pACB = (PACB)ccb->ccb_h.ccb_trmacb_ptr;
 	TRM_DPRINTF("trm_ExecuteSRB..........\n");        
+	if (error != 0) {
+		/*
+		 * DF-3831: the dmamap load failed (e.g. EFBIG).  Fail the
+		 * CCB instead of orphaning it -- there is no timeout
+		 * handler to recover it later.
+		 */
+		pSRB->pNextSRB = pACB->pFreeSRB;
+		pACB->pFreeSRB = pSRB;
+		ccb->ccb_h.status = CAM_DEV_QFRZN | CAM_REQ_INVALID;
+		xpt_done(ccb);
+		crit_exit();
+		return;
+	}
 	if (nseg != 0) {
 		PSEG			psg;
 		bus_dma_segment_t	*end_seg;
@@ -467,11 +481,13 @@
 		return;
 	}
 	ccb->ccb_h.status |= CAM_SIM_QUEUED;
-#if 0
-	/* XXX Need a timeout handler */
-	callout_reset(&ccb->ccb_h.timeout_ch, (ccb->ccb_h.timeout * hz) / 1000,
-		      trmtimeout, srb);
-#endif
+	/*
+	 * DF-3831: arm the CCB timeout so a lost command does not wedge
+	 * the SIM queue forever.
+	 */
+	if (ccb->ccb_h.timeout != 0)
+		callout_reset(&ccb->ccb_h.timeout_ch,
+		    (ccb->ccb_h.timeout * hz) / 1000, trm_timeout, pSRB);
 	trm_SendSRB(pACB, pSRB);
 	crit_exit();
 	return;
@@ -527,6 +543,27 @@
 }
 
 
+/*
+ * DF-3831: CCB timeout -- the command was accepted by a target but never
+ * completed.  Reset the SCSI bus so the adapter state machine (and every
+ * queued CCB) is recovered by the existing trm_reset() machinery.
+ */
+static void
+trm_timeout(void *arg)
+{
+	PSRB	pSRB = arg;
+	PACB	pACB;
+	union ccb *ccb;
+
+	ccb = pSRB->pccb;
+	if (ccb == NULL)
+		return;
+	pACB = (PACB)ccb->ccb_h.ccb_trmacb_ptr;
+	kprintf("trm%d: command timeout, resetting SCSI bus\n",
+	    pACB->AdapterUnit);
+	trm_reset(pACB);
+}
+
 static void
 trm_action(struct cam_sim *psim, union ccb *pccb) 
 {
@@ -2488,6 +2525,7 @@
 	pccb  = pSRB->pccb;
 	if (pccb == NULL)
 		return;
+	callout_stop(&pccb->ccb_h.timeout_ch);
 	pcsio = &pccb->csio;
 	target_id  = pSRB->pccb->ccb_h.target_id;
 	target_lun = pSRB->pccb->ccb_h.target_lun;