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