⬢ DragonFlyBSD Kernel Audit
DF-3673 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/disk/mpt/mpt.h
+++ b/sys/dev/disk/mpt/mpt.h
@@ -296,9 +296,11 @@ struct req_entry {
 	uint16_t	ResponseCode;	/* TMF Response Code */
 	uint16_t	serno;		/* serial number */
 	union ccb      *ccb;		/* CAM request */
+	union ccb      *to_arg;		/* CCB the timeout callout was armed for */
 	void	       *req_vbuf;	/* Virtual Address of Entry */
 	void	       *sense_vbuf;	/* Virtual Address of sense data */
 	bus_addr_t	req_pbuf;	/* Physical Address of Entry */
 	bus_addr_t	sense_pbuf;	/* Physical Address of sense data */
+	struct mpt_softc *sc;		/* owning softc (pool-resident) */
 	bus_dmamap_t	dmap;		/* DMA map for data buffers */
 	struct req_entry *chain;	/* for SGE overallocations */
 	struct callout  callout;	/* Timeout for the request */
--- a/sys/dev/disk/mpt/mpt.c
+++ b/sys/dev/disk/mpt/mpt.c
@@ -2700,6 +2700,7 @@ mpt_get_request
 	for (val = 0; val < MPT_MAX_REQUESTS(mpt); val++) {
 		request_t *req = &mpt->request_pool[val];
 		req->state = REQ_STATE_ALLOCATED;
+		req->sc = mpt;
 		mpt_callout_init(mpt, &req->callout);
 		mpt_free_request(mpt, req);
 	}
--- a/sys/dev/disk/mpt/mpt_cam.c
+++ b/sys/dev/disk/mpt/mpt_cam.c
@@ -1229,15 +1229,27 @@ mpt_poll(struct cam_sim *sim)
 /*
  * Watchdog timeout routine for SCSI requests.
+ *
+ * The callout is armed with the request_t (pool-resident — never freed
+ * until detach) rather than the CCB.  callout_stop() does not interlock
+ * against an in-progress callback, so a callback blocked on the mpt lock
+ * must never touch a CCB that the completion path may have already handed
+ * back via xpt_done().  The softc comes from the request itself.
  */
 static void
 mpt_timeout(void *arg)
 {
-	union ccb	 *ccb;
 	struct mpt_softc *mpt;
 	request_t	 *req;
+	union ccb	 *ccb;
 
-	ccb = (union ccb *)arg;
-	mpt = ccb->ccb_h.ccb_mpt_ptr;
+	req = (request_t *)arg;
+	mpt = req->sc;
 
 	MPT_LOCK(mpt);
-	req = ccb->ccb_h.ccb_req_ptr;
+	/*
+	 * Re-validate under the lock: if the completion path beat us here,
+	 * the request is no longer queued and its CCB has been handed back
+	 * to CAM — do not touch the CCB in that case.
+	 */
+	if ((req->state & REQ_STATE_QUEUED) != REQ_STATE_QUEUED) {
+		MPT_UNLOCK(mpt);
+		return;
+	}
+	ccb = req->to_arg;
 	mpt_prt(mpt, "request %p:%u timed out for ccb %p (req->ccb %p)\n", req,
 	    req->serno, ccb, req->ccb);
 /* XXX: WHAT ARE WE TRYING TO DO HERE? */
-	if ((req->state & REQ_STATE_QUEUED) == REQ_STATE_QUEUED) {
-		TAILQ_REMOVE(&mpt->request_pending_list, req, links);
-		TAILQ_INSERT_TAIL(&mpt->request_timeout_list, req, links);
-		req->state |= REQ_STATE_TIMEDOUT;
-		mpt_wakeup_recovery_thread(mpt);
-	}
+	TAILQ_REMOVE(&mpt->request_pending_list, req, links);
+	TAILQ_INSERT_TAIL(&mpt->request_timeout_list, req, links);
+	req->state |= REQ_STATE_TIMEDOUT;
+	mpt_wakeup_recovery_thread(mpt);
 	MPT_UNLOCK(mpt);
 }
@@ -1638,7 +1650,9 @@ out:
 	}
 	ccb->ccb_h.status |= CAM_SIM_QUEUED;
 	if (ccb->ccb_h.timeout != CAM_TIME_INFINITY) {
-		mpt_req_timeout(req, (ccb->ccb_h.timeout * hz) / 1000,
+		req->to_arg = ccb;
+		mpt_req_timeout(req, (ccb->ccb_h.timeout * hz) / 1000,
 		    mpt_timeout, req);
 	}
@@ -2025,7 +2039,9 @@ out:
 	}
 	ccb->ccb_h.status |= CAM_SIM_QUEUED;
 	if (ccb->ccb_h.timeout != CAM_TIME_INFINITY) {
-		mpt_req_timeout(req, (ccb->ccb_h.timeout * hz) / 1000,
+		req->to_arg = ccb;
+		mpt_req_timeout(req, (ccb->ccb_h.timeout * hz) / 1000,
 		    mpt_timeout, req);
 	}
@@ -4846,7 +4862,10 @@ mpt_scsi_tgt_status(struct mpt_softc *mpt, union ccb *ccb, request_t *cmd_req,
 	}
 	if (ccb) {
 		ccb->ccb_h.status = CAM_SIM_QUEUED | CAM_REQ_INPROG;
-		mpt_req_timeout(req, 60 * hz, mpt_timeout, ccb);
+		ccb->ccb_h.ccb_req_ptr = req;
+		req->to_arg = ccb;
+		mpt_req_timeout(req, 60 * hz, mpt_timeout, req);
 	}
 	mpt_send_cmd(mpt, req);
 }