1 /*- 2 * Copyright (c) 2010 by Panasas, Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice immediately at the beginning of the file, without modification, 10 * this list of conditions, and the following disclaimer. 11 * 2. The name of the author may not be used to endorse or promote products 12 * derived from this software without specific prior written permission. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 /* 27 * "Faulty" Device. Victimize random commands with a Selection Timeout. 28 */ 29 #include "vhba.h" 30 31 #define MAX_TGT VHBA_MAXTGT 32 #define MAX_LUN 4 33 34 #define DISK_SIZE 32 35 #define DISK_SHIFT 9 36 #define DISK_NBLKS ((DISK_SIZE << 20) >> DISK_SHIFT) 37 #define PSEUDO_SPT 64 38 #define PSEUDO_HDS 64 39 #define PSEUDO_SPC (PSEUDO_SPT * PSEUDO_HDS) 40 41 typedef struct { 42 vhba_softc_t * vhba; 43 uint8_t * disk; 44 size_t disk_size; 45 uint32_t ctr; 46 uint32_t dead; 47 struct task qt; 48 } faulty_t; 49 50 static void vhba_task(void *, int); 51 static void faulty_act(faulty_t *, struct ccb_scsiio *); 52 53 void 54 vhba_init(vhba_softc_t *vhba) 55 { 56 static faulty_t vhbastatic; 57 vhbastatic.vhba = vhba; 58 vhbastatic.disk_size = DISK_SIZE << 20; 59 vhbastatic.disk = malloc(vhbastatic.disk_size, M_DEVBUF, M_WAITOK|M_ZERO); 60 vhba->private = &vhbastatic; 61 vhbastatic.ctr = (arc4random() & 0xffff) + 1; 62 TASK_INIT(&vhbastatic.qt, 0, vhba_task, &vhbastatic); 63 } 64 65 66 void 67 vhba_fini(vhba_softc_t *vhba) 68 { 69 faulty_t *vhbas = vhba->private; 70 vhba->private = NULL; 71 free(vhbas->disk, M_DEVBUF); 72 } 73 74 void 75 vhba_kick(vhba_softc_t *vhba) 76 { 77 faulty_t *vhbas = vhba->private; 78 taskqueue_enqueue(taskqueue_swi, &vhbas->qt); 79 } 80 81 static void 82 vhba_task(void *arg, int pending) 83 { 84 faulty_t *vhbas = arg; 85 struct ccb_hdr *ccbh; 86 87 mtx_lock(&vhbas->vhba->lock); 88 while ((ccbh = TAILQ_FIRST(&vhbas->vhba->actv)) != NULL) { 89 TAILQ_REMOVE(&vhbas->vhba->actv, ccbh, sim_links.tqe); 90 faulty_act(vhbas, (struct ccb_scsiio *)ccbh); 91 if (--vhbas->ctr == 0) { 92 vhbas->dead = 1; 93 vhbas->ctr = (arc4random() & 0xff) + 1; 94 } 95 } 96 while ((ccbh = TAILQ_FIRST(&vhbas->vhba->done)) != NULL) { 97 TAILQ_REMOVE(&vhbas->vhba->done, ccbh, sim_links.tqe); 98 xpt_done((union ccb *)ccbh); 99 } 100 mtx_unlock(&vhbas->vhba->lock); 101 } 102 103 static void 104 faulty_act(faulty_t *vhbas, struct ccb_scsiio *csio) 105 { 106 char junk[128]; 107 cam_status camstatus; 108 uint8_t *cdb, *ptr, status; 109 uint32_t data_len; 110 uint64_t off; 111 112 data_len = 0; 113 status = SCSI_STATUS_OK; 114 115 memset(&csio->sense_data, 0, sizeof (csio->sense_data)); 116 cdb = csio->cdb_io.cdb_bytes; 117 118 if (csio->ccb_h.target_id >= MAX_TGT) { 119 vhba_set_status(&csio->ccb_h, CAM_SEL_TIMEOUT); 120 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 121 return; 122 } 123 if (vhbas->dead) { 124 vhbas->dead = 0; 125 vhba_set_status(&csio->ccb_h, CAM_SEL_TIMEOUT); 126 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 127 return; 128 } 129 if (csio->ccb_h.target_lun >= MAX_LUN && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) { 130 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0); 131 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 132 return; 133 } 134 135 switch (cdb[0]) { 136 case MODE_SENSE: 137 case MODE_SENSE_10: 138 { 139 unsigned int nbyte; 140 uint8_t page = cdb[2] & SMS_PAGE_CODE; 141 uint8_t pgctl = cdb[2] & SMS_PAGE_CTRL_MASK; 142 143 switch (page) { 144 case SMS_FORMAT_DEVICE_PAGE: 145 case SMS_GEOMETRY_PAGE: 146 case SMS_CACHE_PAGE: 147 case SMS_CONTROL_MODE_PAGE: 148 case SMS_ALL_PAGES_PAGE: 149 break; 150 default: 151 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); 152 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 153 return; 154 } 155 memset(junk, 0, sizeof (junk)); 156 if (cdb[1] & SMS_DBD) { 157 ptr = &junk[4]; 158 } else { 159 ptr = junk; 160 ptr[3] = 8; 161 ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; 162 ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; 163 ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; 164 ptr[7] = ((1 << DISK_SHIFT)) & 0xff; 165 166 ptr[8] = (DISK_NBLKS >> 24) & 0xff; 167 ptr[9] = (DISK_NBLKS >> 16) & 0xff; 168 ptr[10] = (DISK_NBLKS >> 8) & 0xff; 169 ptr[11] = DISK_NBLKS & 0xff; 170 ptr += 12; 171 } 172 173 if (page == SMS_ALL_PAGES_PAGE || page == SMS_FORMAT_DEVICE_PAGE) { 174 ptr[0] = SMS_FORMAT_DEVICE_PAGE; 175 ptr[1] = 24; 176 if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { 177 /* tracks per zone */ 178 /* ptr[2] = 0; */ 179 /* ptr[3] = 0; */ 180 /* alternate sectors per zone */ 181 /* ptr[4] = 0; */ 182 /* ptr[5] = 0; */ 183 /* alternate tracks per zone */ 184 /* ptr[6] = 0; */ 185 /* ptr[7] = 0; */ 186 /* alternate tracks per logical unit */ 187 /* ptr[8] = 0; */ 188 /* ptr[9] = 0; */ 189 /* sectors per track */ 190 ptr[10] = (PSEUDO_SPT >> 8) & 0xff; 191 ptr[11] = PSEUDO_SPT & 0xff; 192 /* data bytes per physical sector */ 193 ptr[12] = ((1 << DISK_SHIFT) >> 8) & 0xff; 194 ptr[13] = (1 << DISK_SHIFT) & 0xff; 195 /* interleave */ 196 /* ptr[14] = 0; */ 197 /* ptr[15] = 1; */ 198 /* track skew factor */ 199 /* ptr[16] = 0; */ 200 /* ptr[17] = 0; */ 201 /* cylinder skew factor */ 202 /* ptr[18] = 0; */ 203 /* ptr[19] = 0; */ 204 /* SSRC, HSEC, RMB, SURF */ 205 } 206 ptr += 26; 207 } 208 209 if (page == SMS_ALL_PAGES_PAGE || page == SMS_GEOMETRY_PAGE) { 210 ptr[0] = SMS_GEOMETRY_PAGE; 211 ptr[1] = 24; 212 if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { 213 uint32_t cyl = (DISK_NBLKS + ((PSEUDO_SPC - 1))) / PSEUDO_SPC; 214 /* number of cylinders */ 215 ptr[2] = (cyl >> 24) & 0xff; 216 ptr[3] = (cyl >> 16) & 0xff; 217 ptr[4] = cyl & 0xff; 218 /* number of heads */ 219 ptr[5] = PSEUDO_HDS; 220 /* starting cylinder- write precompensation */ 221 /* ptr[6] = 0; */ 222 /* ptr[7] = 0; */ 223 /* ptr[8] = 0; */ 224 /* starting cylinder- reduced write current */ 225 /* ptr[9] = 0; */ 226 /* ptr[10] = 0; */ 227 /* ptr[11] = 0; */ 228 /* drive step rate */ 229 /* ptr[12] = 0; */ 230 /* ptr[13] = 0; */ 231 /* landing zone cylinder */ 232 /* ptr[14] = 0; */ 233 /* ptr[15] = 0; */ 234 /* ptr[16] = 0; */ 235 /* RPL */ 236 /* ptr[17] = 0; */ 237 /* rotational offset */ 238 /* ptr[18] = 0; */ 239 /* medium rotation rate - 7200 RPM */ 240 ptr[20] = 0x1c; 241 ptr[21] = 0x20; 242 } 243 ptr += 26; 244 } 245 246 if (page == SMS_ALL_PAGES_PAGE || page == SMS_CACHE_PAGE) { 247 ptr[0] = SMS_CACHE_PAGE; 248 ptr[1] = 18; 249 ptr[2] = 1 << 2; 250 ptr += 20; 251 } 252 253 if (page == SMS_ALL_PAGES_PAGE || page == SMS_CONTROL_MODE_PAGE) { 254 ptr[0] = SMS_CONTROL_MODE_PAGE; 255 ptr[1] = 10; 256 if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { 257 ptr[3] = 1 << 4; /* unrestricted reordering allowed */ 258 ptr[8] = 0x75; /* 30000 ms */ 259 ptr[9] = 0x30; 260 } 261 ptr += 12; 262 } 263 nbyte = (char *)ptr - &junk[0]; 264 ptr[0] = nbyte - 4; 265 266 if (cdb[0] == MODE_SENSE) { 267 data_len = min(cdb[4], csio->dxfer_len); 268 } else { 269 uint16_t tw = (cdb[7] << 8) | cdb[8]; 270 data_len = min(tw, csio->dxfer_len); 271 } 272 data_len = min(data_len, nbyte); 273 if (data_len) { 274 memcpy(csio->data_ptr, junk, data_len); 275 } 276 csio->resid = csio->dxfer_len - data_len; 277 break; 278 } 279 case READ_6: 280 case READ_10: 281 case READ_12: 282 case READ_16: 283 case WRITE_6: 284 case WRITE_10: 285 case WRITE_12: 286 case WRITE_16: 287 if (vhba_rwparm(cdb, &off, &data_len, DISK_NBLKS, DISK_SHIFT)) { 288 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); 289 break; 290 } 291 if (data_len) { 292 if ((cdb[0] & 0xf) == 8) { 293 memcpy(csio->data_ptr, &vhbas->disk[off], data_len); 294 } else { 295 memcpy(&vhbas->disk[off], csio->data_ptr, data_len); 296 } 297 csio->resid = csio->dxfer_len - data_len; 298 } else { 299 csio->resid = csio->dxfer_len; 300 } 301 break; 302 303 case READ_CAPACITY: 304 if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) { 305 vhba_fill_sense(csio, SSD_KEY_UNIT_ATTENTION, 0x24, 0x0); 306 break; 307 } 308 if (cdb[8] & 0x1) { /* PMI */ 309 csio->data_ptr[0] = 0xff; 310 csio->data_ptr[1] = 0xff; 311 csio->data_ptr[2] = 0xff; 312 csio->data_ptr[3] = 0xff; 313 } else { 314 uint64_t last_blk = DISK_NBLKS - 1; 315 if (last_blk < 0xffffffffULL) { 316 csio->data_ptr[0] = (last_blk >> 24) & 0xff; 317 csio->data_ptr[1] = (last_blk >> 16) & 0xff; 318 csio->data_ptr[2] = (last_blk >> 8) & 0xff; 319 csio->data_ptr[3] = (last_blk) & 0xff; 320 } else { 321 csio->data_ptr[0] = 0xff; 322 csio->data_ptr[1] = 0xff; 323 csio->data_ptr[2] = 0xff; 324 csio->data_ptr[3] = 0xff; 325 } 326 } 327 csio->data_ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; 328 csio->data_ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; 329 csio->data_ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; 330 csio->data_ptr[7] = ((1 << DISK_SHIFT)) & 0xff; 331 break; 332 default: 333 vhba_default_cmd(csio, MAX_LUN, NULL); 334 break; 335 } 336 if (csio->scsi_status != SCSI_STATUS_OK) { 337 camstatus = CAM_SCSI_STATUS_ERROR; 338 if (csio->scsi_status == SCSI_STATUS_CHECK_COND) { 339 camstatus |= CAM_AUTOSNS_VALID; 340 } 341 } else { 342 csio->scsi_status = SCSI_STATUS_OK; 343 camstatus = CAM_REQ_CMP; 344 } 345 vhba_set_status(&csio->ccb_h, camstatus); 346 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 347 } 348 DEV_MODULE(vhba_faulty, vhba_modprobe, NULL); 349