DF-3673 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | --- 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); } |