DF-4100 / 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 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | --- a/sys/dev/disk/buslogic/bt.c +++ b/sys/dev/disk/buslogic/bt.c @@ -887,12 +887,14 @@ KKASSERT(lockowned(&bt->lock)); if ((bccb = SLIST_FIRST(&bt->free_bt_ccbs)) != NULL) { SLIST_REMOVE_HEAD(&bt->free_bt_ccbs, links); + bccb->gen++; /* DF-4101 generation handshake */ bt->active_ccbs++; } else { btallocccbs(bt); bccb = SLIST_FIRST(&bt->free_bt_ccbs); if (bccb != NULL) { SLIST_REMOVE_HEAD(&bt->free_bt_ccbs, links); + bccb->gen++; /* DF-4101 generation handshake */ bt->active_ccbs++; } } @@ -1154,6 +1156,19 @@ u_int32_t secs_per_cylinder; ccg = &ccb->ccg; + /* + * DF-2429: block_size is device-fed (READ CAPACITY, da(4) + * passes it through unvalidated). 0 or > 1MiB makes the + * size_mb computation below divide by zero and panics the + * kernel; the XPT layer only filters block_size == 0 and + * volume_size == 0, not the > 1MiB case that zeroes the + * inner quotient. + */ + if (ccg->block_size == 0 || ccg->block_size > (1024L * 1024L)) { + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } size_mb = ccg->volume_size / ((1024L * 1024L) / ccg->block_size); @@ -1282,7 +1297,14 @@ bus_dmamap_sync(bt->buffer_dmat, bccb->dmamap, op); } else { - bccb->hccb.opcode = INITIATOR_CCB; + /* + * DF-4097: XPT_RESET_DEV already stored + * INITIATOR_BUS_DEV_RESET in btaction(); do not clobber a + * device reset into an ordinary opcode-0 CCB with + * cmd_len == 0 that conforming firmware must reject. + */ + if (bccb->hccb.opcode != INITIATOR_BUS_DEV_RESET) + bccb->hccb.opcode = INITIATOR_CCB; bccb->hccb.data_len = 0; bccb->hccb.data_addr = 0; } @@ -1303,6 +1325,7 @@ ccb->ccb_h.status |= CAM_SIM_QUEUED; LIST_INSERT_HEAD(&bt->pending_ccbs, &ccb->ccb_h, sim_links.le); + bccb->timer_gen = bccb->gen; /* DF-4101 */ callout_reset(&bccb->timer, (ccb->ccb_h.timeout * hz) / 1000, bttimeout, bccb); @@ -1443,9 +1466,20 @@ } } device_printf(bt->dev, "No longer in timeout\n"); + /* DF-4100: release the recovery BCCB for the next BDR */ + bt->recovery_bccb->flags = BCCB_FREE; return; } + /* DF-4101: callout_stop() does not interlock against a bttimeout() + * that has already fired and is blocked acquiring bt->lock. Use + * the generation handshake so a stale handler that runs after the + * CCB was completed and recycled cannot act on the new command. */ + if (bccb->gen != bccb->timer_gen) { + /* stale bttimeout on a recycled CCB: nothing to do */ + callout_stop(&bccb->timer); + return; + } callout_stop(&bccb->timer); switch (comp_code) { @@ -1514,17 +1548,29 @@ csio->ccb_h.status = CAM_SEQUENCE_FAIL; break; case BTSTAT_INVALID_ACTION_CODE: - panic("%s: Invalid Action code", bt_name(bt)); + /* + * DF-4098: btstat is a host-adapter-fed byte; + * never trust it with the kernel's life. + */ + device_printf(bt->dev, + "btdone - Invalid Action code from adapter\n"); + csio->ccb_h.status = CAM_REQ_CMP_ERR; break; case BTSTAT_INVALID_OPCODE: - panic("%s: Invalid CCB Opcode code", bt_name(bt)); + device_printf(bt->dev, + "btdone - Invalid CCB Opcode code from adapter\n"); + csio->ccb_h.status = CAM_REQ_CMP_ERR; break; case BTSTAT_LINKED_CCB_LUN_MISMATCH: /* We don't even support linked commands... */ - panic("%s: Linked CCB Lun Mismatch", bt_name(bt)); + device_printf(bt->dev, + "btdone - Linked CCB Lun Mismatch\n"); + csio->ccb_h.status = CAM_REQ_CMP_ERR; break; case BTSTAT_INVALID_CCB_OR_SG_PARAM: - panic("%s: Invalid CCB or SG list", bt_name(bt)); + device_printf(bt->dev, + "btdone - Invalid CCB or SG list\n"); + csio->ccb_h.status = CAM_REQ_CMP_ERR; break; case BTSTAT_AUTOSENSE_FAILED: csio->ccb_h.status = CAM_AUTOSENSE_FAIL; @@ -1605,6 +1651,20 @@ btfreeccb(bt, bccb); xpt_done(ccb); break; + default: + /* + * DF-4099: comp_code is a host-adapter-fed byte covering + * 256 values; anything unhandled must still complete (and + * free) the CCB, otherwise the command hangs forever with + * its timeout already stopped and the BCCB is lost. + */ + device_printf(bt->dev, + "btdone - unknown mailbox completion code 0x%x\n", + comp_code); + csio->ccb_h.status = CAM_REQ_CMP_ERR; + btfreeccb(bt, bccb); + xpt_done(ccb); + break; } } @@ -1647,8 +1707,10 @@ DELAY(100); } if (timeout == 0) { - panic("%s: btreset - Diagnostic Active failed to drop. " - "status = 0x%x\n", bt_name(bt), status); + /* DF-4098: hostile or wedged hardware must not panic */ + device_printf(bt->dev, + "btreset - Diagnostic Active failed to drop. " + "status = 0x%x\n", status); return (ETIMEDOUT); } @@ -2151,6 +2213,17 @@ lockmgr(&bt->lock, LK_RELEASE); return; } + if (bccb->gen != bccb->timer_gen) { + /* DF-4101: this bttimeout fired for a previous command that + * used this BCCB; the BCCB has since been recycled for a + * new, healthy command. Do not touch it. */ + xpt_print_path(ccb->ccb_h.path); + kprintf("CCB %p - stale timeout (gen %u != timer_gen %u) " + "ignored\n", (void *)bccb, bccb->gen, + bccb->timer_gen); + lockmgr(&bt->lock, LK_RELEASE); + return; + } /* * In order to simplify the recovery process, we ask the XPT @@ -2214,6 +2287,12 @@ bccb->flags |= BCCB_DEVICE_RESET; callout_reset(&bccb->timer, 2 * hz, bttimeout, bccb); + /* + * DF-4100: mark the recovery BCCB active so that its + * completion passes the btdone() BCCB_ACTIVE guard and + * actually runs the recovery branch. + */ + bt->recovery_bccb->flags = BCCB_ACTIVE; bt->recovery_bccb->hccb.opcode = INITIATOR_BUS_DEV_RESET; /* No Data Transfer */ --- a/sys/dev/disk/buslogic/btreg.h +++ b/sys/dev/disk/buslogic/btreg.h @@ -556,6 +556,8 @@ struct bt_hccb hccb; SLIST_ENTRY(bt_ccb) links; u_int32_t flags; + u_int32_t gen; /* DF-4101: reuse generation */ + u_int32_t timer_gen; /* DF-4101: gen timer fired for */ union ccb *ccb; bus_dmamap_t dmamap; struct callout timer; |