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" Multipath Device. Creates to devices to be set up as multipath, 28 * makes one or both of them non existent (or re existent) on demand. 29 */ 30 #include "vhba.h" 31 #include <sys/sysctl.h> 32 33 static int vhba_stop_lun; 34 static int vhba_start_lun = 0; 35 static int vhba_notify_stop = 1; 36 static int vhba_notify_start = 1; 37 static int vhba_inject_hwerr = 0; 38 SYSCTL_INT(_debug, OID_AUTO, vhba_stop_lun, CTLFLAG_RW, &vhba_stop_lun, 0, "stop lun bitmap"); 39 SYSCTL_INT(_debug, OID_AUTO, vhba_start_lun, CTLFLAG_RW, &vhba_start_lun, 0, "start lun bitmap"); 40 SYSCTL_INT(_debug, OID_AUTO, vhba_notify_stop, CTLFLAG_RW, &vhba_notify_stop, 1, "notify when luns go away"); 41 SYSCTL_INT(_debug, OID_AUTO, vhba_notify_start, CTLFLAG_RW, &vhba_notify_start, 1, "notify when luns arrive"); 42 SYSCTL_INT(_debug, OID_AUTO, vhba_inject_hwerr, CTLFLAG_RW, &vhba_inject_hwerr, 0, "inject hardware error on lost luns"); 43 44 #define MAX_TGT 1 45 #define MAX_LUN 2 46 #define VMP_TIME hz 47 48 #define DISK_SIZE 32 49 #define DISK_SHIFT 9 50 #define DISK_NBLKS ((DISK_SIZE << 20) >> DISK_SHIFT) 51 #define PSEUDO_SPT 64 52 #define PSEUDO_HDS 64 53 #define PSEUDO_SPC (PSEUDO_SPT * PSEUDO_HDS) 54 55 typedef struct { 56 vhba_softc_t * vhba; 57 uint8_t * disk; 58 size_t disk_size; 59 int luns[2]; 60 struct callout tick; 61 struct task qt; 62 TAILQ_HEAD(, ccb_hdr) inproc; 63 int nact, nact_high; 64 } mptest_t; 65 66 static timeout_t vhba_iodelay; 67 static timeout_t vhba_timer; 68 static void vhba_task(void *, int); 69 static void mptest_act(mptest_t *, struct ccb_scsiio *); 70 71 void 72 vhba_init(vhba_softc_t *vhba) 73 { 74 static mptest_t vhbastatic; 75 76 vhbastatic.vhba = vhba; 77 vhbastatic.disk_size = DISK_SIZE << 20; 78 vhbastatic.disk = malloc(vhbastatic.disk_size, M_DEVBUF, M_WAITOK|M_ZERO); 79 vhba->private = &vhbastatic; 80 callout_init_mtx(&vhbastatic.tick, &vhba->lock, 0); 81 callout_reset(&vhbastatic.tick, VMP_TIME, vhba_timer, vhba); 82 TAILQ_INIT(&vhbastatic.inproc); 83 TASK_INIT(&vhbastatic.qt, 0, vhba_task, &vhbastatic); 84 vhbastatic.luns[0] = 1; 85 vhbastatic.luns[1] = 1; 86 } 87 88 void 89 vhba_fini(vhba_softc_t *vhba) 90 { 91 mptest_t *vhbas = vhba->private; 92 callout_stop(&vhbas->tick); 93 vhba->private = NULL; 94 free(vhbas->disk, M_DEVBUF); 95 } 96 97 void 98 vhba_kick(vhba_softc_t *vhba) 99 { 100 mptest_t *vhbas = vhba->private; 101 taskqueue_enqueue(taskqueue_swi, &vhbas->qt); 102 } 103 104 static void 105 vhba_task(void *arg, int pending) 106 { 107 mptest_t *vhbas = arg; 108 struct ccb_hdr *ccbh; 109 int nadded = 0; 110 111 mtx_lock(&vhbas->vhba->lock); 112 while ((ccbh = TAILQ_FIRST(&vhbas->vhba->actv)) != NULL) { 113 TAILQ_REMOVE(&vhbas->vhba->actv, ccbh, sim_links.tqe); 114 mptest_act(vhbas, (struct ccb_scsiio *)ccbh); 115 nadded++; 116 ccbh->sim_priv.entries[0].ptr = vhbas; 117 callout_handle_init(&ccbh->timeout_ch); 118 } 119 if (nadded) { 120 vhba_kick(vhbas->vhba); 121 } else { 122 while ((ccbh = TAILQ_FIRST(&vhbas->vhba->done)) != NULL) { 123 TAILQ_REMOVE(&vhbas->vhba->done, ccbh, sim_links.tqe); 124 xpt_done((union ccb *)ccbh); 125 } 126 } 127 mtx_unlock(&vhbas->vhba->lock); 128 } 129 130 static void 131 mptest_act(mptest_t *vhbas, struct ccb_scsiio *csio) 132 { 133 char junk[128]; 134 cam_status camstatus; 135 uint8_t *cdb, *ptr, status; 136 uint32_t data_len, blkcmd; 137 uint64_t off; 138 139 blkcmd = data_len = 0; 140 status = SCSI_STATUS_OK; 141 142 memset(&csio->sense_data, 0, sizeof (csio->sense_data)); 143 cdb = csio->cdb_io.cdb_bytes; 144 145 if (csio->ccb_h.target_id >= MAX_TGT) { 146 vhba_set_status(&csio->ccb_h, CAM_SEL_TIMEOUT); 147 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 148 return; 149 } 150 if (vhba_inject_hwerr && csio->ccb_h.target_lun < MAX_LUN && vhbas->luns[csio->ccb_h.target_lun] == 0) { 151 vhba_fill_sense(csio, SSD_KEY_HARDWARE_ERROR, 0x44, 0x0); 152 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 153 return; 154 } 155 if ((csio->ccb_h.target_lun >= MAX_LUN || vhbas->luns[csio->ccb_h.target_lun] == 0) && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) { 156 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0); 157 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 158 return; 159 } 160 161 switch (cdb[0]) { 162 case MODE_SENSE: 163 case MODE_SENSE_10: 164 { 165 unsigned int nbyte; 166 uint8_t page = cdb[2] & SMS_PAGE_CODE; 167 uint8_t pgctl = cdb[2] & SMS_PAGE_CTRL_MASK; 168 169 switch (page) { 170 case SMS_FORMAT_DEVICE_PAGE: 171 case SMS_GEOMETRY_PAGE: 172 case SMS_CACHE_PAGE: 173 case SMS_CONTROL_MODE_PAGE: 174 case SMS_ALL_PAGES_PAGE: 175 break; 176 default: 177 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); 178 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 179 return; 180 } 181 memset(junk, 0, sizeof (junk)); 182 if (cdb[1] & SMS_DBD) { 183 ptr = &junk[4]; 184 } else { 185 ptr = junk; 186 ptr[3] = 8; 187 ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; 188 ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; 189 ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; 190 ptr[7] = ((1 << DISK_SHIFT)) & 0xff; 191 192 ptr[8] = (DISK_NBLKS >> 24) & 0xff; 193 ptr[9] = (DISK_NBLKS >> 16) & 0xff; 194 ptr[10] = (DISK_NBLKS >> 8) & 0xff; 195 ptr[11] = DISK_NBLKS & 0xff; 196 ptr += 12; 197 } 198 199 if (page == SMS_ALL_PAGES_PAGE || page == SMS_FORMAT_DEVICE_PAGE) { 200 ptr[0] = SMS_FORMAT_DEVICE_PAGE; 201 ptr[1] = 24; 202 if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { 203 /* tracks per zone */ 204 /* ptr[2] = 0; */ 205 /* ptr[3] = 0; */ 206 /* alternate sectors per zone */ 207 /* ptr[4] = 0; */ 208 /* ptr[5] = 0; */ 209 /* alternate tracks per zone */ 210 /* ptr[6] = 0; */ 211 /* ptr[7] = 0; */ 212 /* alternate tracks per logical unit */ 213 /* ptr[8] = 0; */ 214 /* ptr[9] = 0; */ 215 /* sectors per track */ 216 ptr[10] = (PSEUDO_SPT >> 8) & 0xff; 217 ptr[11] = PSEUDO_SPT & 0xff; 218 /* data bytes per physical sector */ 219 ptr[12] = ((1 << DISK_SHIFT) >> 8) & 0xff; 220 ptr[13] = (1 << DISK_SHIFT) & 0xff; 221 /* interleave */ 222 /* ptr[14] = 0; */ 223 /* ptr[15] = 1; */ 224 /* track skew factor */ 225 /* ptr[16] = 0; */ 226 /* ptr[17] = 0; */ 227 /* cylinder skew factor */ 228 /* ptr[18] = 0; */ 229 /* ptr[19] = 0; */ 230 /* SSRC, HSEC, RMB, SURF */ 231 } 232 ptr += 26; 233 } 234 235 if (page == SMS_ALL_PAGES_PAGE || page == SMS_GEOMETRY_PAGE) { 236 ptr[0] = SMS_GEOMETRY_PAGE; 237 ptr[1] = 24; 238 if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { 239 uint32_t cyl = (DISK_NBLKS + ((PSEUDO_SPC - 1))) / PSEUDO_SPC; 240 /* number of cylinders */ 241 ptr[2] = (cyl >> 24) & 0xff; 242 ptr[3] = (cyl >> 16) & 0xff; 243 ptr[4] = cyl & 0xff; 244 /* number of heads */ 245 ptr[5] = PSEUDO_HDS; 246 /* starting cylinder- write precompensation */ 247 /* ptr[6] = 0; */ 248 /* ptr[7] = 0; */ 249 /* ptr[8] = 0; */ 250 /* starting cylinder- reduced write current */ 251 /* ptr[9] = 0; */ 252 /* ptr[10] = 0; */ 253 /* ptr[11] = 0; */ 254 /* drive step rate */ 255 /* ptr[12] = 0; */ 256 /* ptr[13] = 0; */ 257 /* landing zone cylinder */ 258 /* ptr[14] = 0; */ 259 /* ptr[15] = 0; */ 260 /* ptr[16] = 0; */ 261 /* RPL */ 262 /* ptr[17] = 0; */ 263 /* rotational offset */ 264 /* ptr[18] = 0; */ 265 /* medium rotation rate - 7200 RPM */ 266 ptr[20] = 0x1c; 267 ptr[21] = 0x20; 268 } 269 ptr += 26; 270 } 271 272 if (page == SMS_ALL_PAGES_PAGE || page == SMS_CACHE_PAGE) { 273 ptr[0] = SMS_CACHE_PAGE; 274 ptr[1] = 18; 275 ptr[2] = 1 << 2; 276 ptr += 20; 277 } 278 279 if (page == SMS_ALL_PAGES_PAGE || page == SMS_CONTROL_MODE_PAGE) { 280 ptr[0] = SMS_CONTROL_MODE_PAGE; 281 ptr[1] = 10; 282 if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { 283 ptr[3] = 1 << 4; /* unrestricted reordering allowed */ 284 ptr[8] = 0x75; /* 30000 ms */ 285 ptr[9] = 0x30; 286 } 287 ptr += 12; 288 } 289 nbyte = (char *)ptr - &junk[0]; 290 ptr[0] = nbyte - 4; 291 292 if (cdb[0] == MODE_SENSE) { 293 data_len = min(cdb[4], csio->dxfer_len); 294 } else { 295 uint16_t tw = (cdb[7] << 8) | cdb[8]; 296 data_len = min(tw, csio->dxfer_len); 297 } 298 data_len = min(data_len, nbyte); 299 if (data_len) { 300 memcpy(csio->data_ptr, junk, data_len); 301 } 302 csio->resid = csio->dxfer_len - data_len; 303 break; 304 } 305 case READ_6: 306 case READ_10: 307 case READ_12: 308 case READ_16: 309 case WRITE_6: 310 case WRITE_10: 311 case WRITE_12: 312 case WRITE_16: 313 if (vhba_rwparm(cdb, &off, &data_len, DISK_NBLKS, DISK_SHIFT)) { 314 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); 315 break; 316 } 317 blkcmd++; 318 if (++vhbas->nact > vhbas->nact_high) { 319 vhbas->nact_high = vhbas->nact; 320 printf("%s: high block count now %d\n", __func__, vhbas->nact); 321 } 322 if (data_len) { 323 if ((cdb[0] & 0xf) == 8) { 324 memcpy(csio->data_ptr, &vhbas->disk[off], data_len); 325 } else { 326 memcpy(&vhbas->disk[off], csio->data_ptr, data_len); 327 } 328 csio->resid = csio->dxfer_len - data_len; 329 } else { 330 csio->resid = csio->dxfer_len; 331 } 332 break; 333 334 case READ_CAPACITY: 335 if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) { 336 vhba_fill_sense(csio, SSD_KEY_UNIT_ATTENTION, 0x24, 0x0); 337 break; 338 } 339 if (cdb[8] & 0x1) { /* PMI */ 340 csio->data_ptr[0] = 0xff; 341 csio->data_ptr[1] = 0xff; 342 csio->data_ptr[2] = 0xff; 343 csio->data_ptr[3] = 0xff; 344 } else { 345 uint64_t last_blk = DISK_NBLKS - 1; 346 if (last_blk < 0xffffffffULL) { 347 csio->data_ptr[0] = (last_blk >> 24) & 0xff; 348 csio->data_ptr[1] = (last_blk >> 16) & 0xff; 349 csio->data_ptr[2] = (last_blk >> 8) & 0xff; 350 csio->data_ptr[3] = (last_blk) & 0xff; 351 } else { 352 csio->data_ptr[0] = 0xff; 353 csio->data_ptr[1] = 0xff; 354 csio->data_ptr[2] = 0xff; 355 csio->data_ptr[3] = 0xff; 356 } 357 } 358 csio->data_ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; 359 csio->data_ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; 360 csio->data_ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; 361 csio->data_ptr[7] = ((1 << DISK_SHIFT)) & 0xff; 362 break; 363 default: 364 vhba_default_cmd(csio, MAX_LUN, NULL); 365 break; 366 } 367 if (csio->scsi_status != SCSI_STATUS_OK) { 368 camstatus = CAM_SCSI_STATUS_ERROR; 369 if (csio->scsi_status == SCSI_STATUS_CHECK_COND) { 370 camstatus |= CAM_AUTOSNS_VALID; 371 } 372 } else { 373 csio->scsi_status = SCSI_STATUS_OK; 374 camstatus = CAM_REQ_CMP; 375 } 376 vhba_set_status(&csio->ccb_h, camstatus); 377 if (blkcmd) { 378 int ticks; 379 struct timeval t; 380 381 TAILQ_INSERT_TAIL(&vhbas->inproc, &csio->ccb_h, sim_links.tqe); 382 t.tv_sec = 0; 383 t.tv_usec = (500 + arc4random()); 384 if (t.tv_usec > 10000) { 385 t.tv_usec = 10000; 386 } 387 ticks = tvtohz(&t); 388 csio->ccb_h.timeout_ch = timeout(vhba_iodelay, &csio->ccb_h, ticks); 389 } else { 390 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 391 } 392 } 393 394 static void 395 vhba_iodelay(void *arg) 396 { 397 struct ccb_hdr *ccbh = arg; 398 mptest_t *vhbas = ccbh->sim_priv.entries[0].ptr; 399 400 mtx_lock(&vhbas->vhba->lock); 401 TAILQ_REMOVE(&vhbas->inproc, ccbh, sim_links.tqe); 402 TAILQ_INSERT_TAIL(&vhbas->vhba->done, ccbh, sim_links.tqe); 403 vhbas->nact -= 1; 404 vhba_kick(vhbas->vhba); 405 mtx_unlock(&vhbas->vhba->lock); 406 } 407 408 static void 409 vhba_timer(void *arg) 410 { 411 int lun; 412 vhba_softc_t *vhba = arg; 413 mptest_t *vhbas = vhba->private; 414 if (vhba_stop_lun) { 415 lun = (vhba_stop_lun & 1)? 0 : 1; 416 if (lun == 0 || lun == 1) { 417 if (vhbas->luns[lun]) { 418 struct cam_path *tp; 419 if (vhba_notify_stop) { 420 if (xpt_create_path(&tp, xpt_periph, cam_sim_path(vhba->sim), 0, lun) != CAM_REQ_CMP) { 421 goto out; 422 } 423 vhbas->luns[lun] = 0; 424 xpt_async(AC_LOST_DEVICE, tp, NULL); 425 xpt_free_path(tp); 426 } else { 427 vhbas->luns[lun] = 0; 428 } 429 } 430 } 431 vhba_stop_lun &= ~(1 << lun); 432 } else if (vhba_start_lun) { 433 lun = (vhba_start_lun & 1)? 0 : 1; 434 if (lun == 0 || lun == 1) { 435 if (vhbas->luns[lun] == 0) { 436 if (vhba_notify_start) { 437 union ccb *ccb; 438 ccb = xpt_alloc_ccb_nowait(); 439 if (ccb == NULL) { 440 goto out; 441 } 442 if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, cam_sim_path(vhba->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { 443 xpt_free_ccb(ccb); 444 goto out; 445 } 446 vhbas->luns[lun] = 1; 447 xpt_rescan(ccb); 448 } else { 449 vhbas->luns[lun] = 1; 450 } 451 } 452 } 453 vhba_start_lun &= ~(1 << lun); 454 } 455 out: 456 callout_reset(&vhbas->tick, VMP_TIME, vhba_timer, vhba); 457 } 458 DEV_MODULE(vhba_mptest, vhba_modprobe, NULL); 459