1208926Smjacob/*- 2208926Smjacob * Copyright (c) 2010 by Panasas, Inc. 3208926Smjacob * All rights reserved. 4208926Smjacob * 5208926Smjacob * Redistribution and use in source and binary forms, with or without 6208926Smjacob * modification, are permitted provided that the following conditions 7208926Smjacob * are met: 8208926Smjacob * 1. Redistributions of source code must retain the above copyright 9208926Smjacob * notice immediately at the beginning of the file, without modification, 10208926Smjacob * this list of conditions, and the following disclaimer. 11208926Smjacob * 2. The name of the author may not be used to endorse or promote products 12208926Smjacob * derived from this software without specific prior written permission. 13208926Smjacob * 14208926Smjacob * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15208926Smjacob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16208926Smjacob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17208926Smjacob * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 18208926Smjacob * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19208926Smjacob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20208926Smjacob * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21208926Smjacob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22208926Smjacob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23208926Smjacob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24208926Smjacob * SUCH DAMAGE. 25208926Smjacob */ 26208926Smjacob/* $FreeBSD$ */ 27208926Smjacob/* 28208926Smjacob * "Faulty" Device. Victimize random commands with a Selection Timeout. 29208926Smjacob */ 30208926Smjacob#include "vhba.h" 31208926Smjacob 32208926Smjacob#define MAX_TGT VHBA_MAXTGT 33208926Smjacob#define MAX_LUN 4 34208926Smjacob 35208926Smjacob#define DISK_SIZE 32 36208926Smjacob#define DISK_SHIFT 9 37208926Smjacob#define DISK_NBLKS ((DISK_SIZE << 20) >> DISK_SHIFT) 38208926Smjacob#define PSEUDO_SPT 64 39208926Smjacob#define PSEUDO_HDS 64 40208926Smjacob#define PSEUDO_SPC (PSEUDO_SPT * PSEUDO_HDS) 41208926Smjacob 42208926Smjacobtypedef struct { 43208926Smjacob vhba_softc_t * vhba; 44208926Smjacob uint8_t * disk; 45208926Smjacob size_t disk_size; 46208926Smjacob uint32_t ctr; 47208926Smjacob uint32_t dead; 48208926Smjacob struct task qt; 49208926Smjacob} faulty_t; 50208926Smjacob 51208926Smjacobstatic void vhba_task(void *, int); 52208926Smjacobstatic void faulty_act(faulty_t *, struct ccb_scsiio *); 53208926Smjacob 54208926Smjacobvoid 55208926Smjacobvhba_init(vhba_softc_t *vhba) 56208926Smjacob{ 57208926Smjacob static faulty_t vhbastatic; 58208926Smjacob vhbastatic.vhba = vhba; 59208926Smjacob vhbastatic.disk_size = DISK_SIZE << 20; 60208926Smjacob vhbastatic.disk = malloc(vhbastatic.disk_size, M_DEVBUF, M_WAITOK|M_ZERO); 61208926Smjacob vhba->private = &vhbastatic; 62208926Smjacob vhbastatic.ctr = (arc4random() & 0xffff) + 1; 63208926Smjacob TASK_INIT(&vhbastatic.qt, 0, vhba_task, &vhbastatic); 64208926Smjacob} 65208926Smjacob 66208926Smjacob 67208926Smjacobvoid 68208926Smjacobvhba_fini(vhba_softc_t *vhba) 69208926Smjacob{ 70208926Smjacob faulty_t *vhbas = vhba->private; 71208926Smjacob vhba->private = NULL; 72208926Smjacob free(vhbas->disk, M_DEVBUF); 73208926Smjacob} 74208926Smjacob 75208926Smjacobvoid 76208926Smjacobvhba_kick(vhba_softc_t *vhba) 77208926Smjacob{ 78208926Smjacob faulty_t *vhbas = vhba->private; 79208926Smjacob taskqueue_enqueue(taskqueue_swi, &vhbas->qt); 80208926Smjacob} 81208926Smjacob 82208926Smjacobstatic void 83208926Smjacobvhba_task(void *arg, int pending) 84208926Smjacob{ 85208926Smjacob faulty_t *vhbas = arg; 86208926Smjacob struct ccb_hdr *ccbh; 87208926Smjacob 88208926Smjacob mtx_lock(&vhbas->vhba->lock); 89208926Smjacob while ((ccbh = TAILQ_FIRST(&vhbas->vhba->actv)) != NULL) { 90208926Smjacob TAILQ_REMOVE(&vhbas->vhba->actv, ccbh, sim_links.tqe); 91208926Smjacob faulty_act(vhbas, (struct ccb_scsiio *)ccbh); 92208926Smjacob if (--vhbas->ctr == 0) { 93208926Smjacob vhbas->dead = 1; 94208926Smjacob vhbas->ctr = (arc4random() & 0xff) + 1; 95208926Smjacob } 96208926Smjacob } 97208926Smjacob while ((ccbh = TAILQ_FIRST(&vhbas->vhba->done)) != NULL) { 98208926Smjacob TAILQ_REMOVE(&vhbas->vhba->done, ccbh, sim_links.tqe); 99208926Smjacob xpt_done((union ccb *)ccbh); 100208926Smjacob } 101208926Smjacob mtx_unlock(&vhbas->vhba->lock); 102208926Smjacob} 103208926Smjacob 104208926Smjacobstatic void 105208926Smjacobfaulty_act(faulty_t *vhbas, struct ccb_scsiio *csio) 106208926Smjacob{ 107208926Smjacob char junk[128]; 108208926Smjacob cam_status camstatus; 109208926Smjacob uint8_t *cdb, *ptr, status; 110208926Smjacob uint32_t data_len; 111208926Smjacob uint64_t off; 112208926Smjacob 113208926Smjacob data_len = 0; 114208926Smjacob status = SCSI_STATUS_OK; 115208926Smjacob 116208926Smjacob memset(&csio->sense_data, 0, sizeof (csio->sense_data)); 117208926Smjacob cdb = csio->cdb_io.cdb_bytes; 118208926Smjacob 119208926Smjacob if (csio->ccb_h.target_id >= MAX_TGT) { 120208926Smjacob vhba_set_status(&csio->ccb_h, CAM_SEL_TIMEOUT); 121208926Smjacob TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 122208926Smjacob return; 123208926Smjacob } 124208926Smjacob if (vhbas->dead) { 125208926Smjacob vhbas->dead = 0; 126208926Smjacob vhba_set_status(&csio->ccb_h, CAM_SEL_TIMEOUT); 127208926Smjacob TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 128208926Smjacob return; 129208926Smjacob } 130208926Smjacob if (csio->ccb_h.target_lun >= MAX_LUN && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) { 131208926Smjacob vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0); 132208926Smjacob TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 133208926Smjacob return; 134208926Smjacob } 135208926Smjacob 136208926Smjacob switch (cdb[0]) { 137208926Smjacob case MODE_SENSE: 138208926Smjacob case MODE_SENSE_10: 139208926Smjacob { 140208926Smjacob unsigned int nbyte; 141208926Smjacob uint8_t page = cdb[2] & SMS_PAGE_CODE; 142208926Smjacob uint8_t pgctl = cdb[2] & SMS_PAGE_CTRL_MASK; 143208926Smjacob 144208926Smjacob switch (page) { 145208926Smjacob case SMS_FORMAT_DEVICE_PAGE: 146208926Smjacob case SMS_GEOMETRY_PAGE: 147208926Smjacob case SMS_CACHE_PAGE: 148208926Smjacob case SMS_CONTROL_MODE_PAGE: 149208926Smjacob case SMS_ALL_PAGES_PAGE: 150208926Smjacob break; 151208926Smjacob default: 152208926Smjacob vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); 153208926Smjacob TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 154208926Smjacob return; 155208926Smjacob } 156208926Smjacob memset(junk, 0, sizeof (junk)); 157208926Smjacob if (cdb[1] & SMS_DBD) { 158208926Smjacob ptr = &junk[4]; 159208926Smjacob } else { 160208926Smjacob ptr = junk; 161208926Smjacob ptr[3] = 8; 162208926Smjacob ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; 163208926Smjacob ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; 164208926Smjacob ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; 165208926Smjacob ptr[7] = ((1 << DISK_SHIFT)) & 0xff; 166208926Smjacob 167208926Smjacob ptr[8] = (DISK_NBLKS >> 24) & 0xff; 168208926Smjacob ptr[9] = (DISK_NBLKS >> 16) & 0xff; 169208926Smjacob ptr[10] = (DISK_NBLKS >> 8) & 0xff; 170208926Smjacob ptr[11] = DISK_NBLKS & 0xff; 171208926Smjacob ptr += 12; 172208926Smjacob } 173208926Smjacob 174208926Smjacob if (page == SMS_ALL_PAGES_PAGE || page == SMS_FORMAT_DEVICE_PAGE) { 175208926Smjacob ptr[0] = SMS_FORMAT_DEVICE_PAGE; 176208926Smjacob ptr[1] = 24; 177208926Smjacob if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { 178208926Smjacob /* tracks per zone */ 179208926Smjacob /* ptr[2] = 0; */ 180208926Smjacob /* ptr[3] = 0; */ 181208926Smjacob /* alternate sectors per zone */ 182208926Smjacob /* ptr[4] = 0; */ 183208926Smjacob /* ptr[5] = 0; */ 184208926Smjacob /* alternate tracks per zone */ 185208926Smjacob /* ptr[6] = 0; */ 186208926Smjacob /* ptr[7] = 0; */ 187208926Smjacob /* alternate tracks per logical unit */ 188208926Smjacob /* ptr[8] = 0; */ 189208926Smjacob /* ptr[9] = 0; */ 190208926Smjacob /* sectors per track */ 191208926Smjacob ptr[10] = (PSEUDO_SPT >> 8) & 0xff; 192208926Smjacob ptr[11] = PSEUDO_SPT & 0xff; 193208926Smjacob /* data bytes per physical sector */ 194208926Smjacob ptr[12] = ((1 << DISK_SHIFT) >> 8) & 0xff; 195208926Smjacob ptr[13] = (1 << DISK_SHIFT) & 0xff; 196208926Smjacob /* interleave */ 197208926Smjacob /* ptr[14] = 0; */ 198208926Smjacob /* ptr[15] = 1; */ 199208926Smjacob /* track skew factor */ 200208926Smjacob /* ptr[16] = 0; */ 201208926Smjacob /* ptr[17] = 0; */ 202208926Smjacob /* cylinder skew factor */ 203208926Smjacob /* ptr[18] = 0; */ 204208926Smjacob /* ptr[19] = 0; */ 205208926Smjacob /* SSRC, HSEC, RMB, SURF */ 206208926Smjacob } 207208926Smjacob ptr += 26; 208208926Smjacob } 209208926Smjacob 210208926Smjacob if (page == SMS_ALL_PAGES_PAGE || page == SMS_GEOMETRY_PAGE) { 211208926Smjacob ptr[0] = SMS_GEOMETRY_PAGE; 212208926Smjacob ptr[1] = 24; 213208926Smjacob if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { 214208926Smjacob uint32_t cyl = (DISK_NBLKS + ((PSEUDO_SPC - 1))) / PSEUDO_SPC; 215208926Smjacob /* number of cylinders */ 216208926Smjacob ptr[2] = (cyl >> 24) & 0xff; 217208926Smjacob ptr[3] = (cyl >> 16) & 0xff; 218208926Smjacob ptr[4] = cyl & 0xff; 219208926Smjacob /* number of heads */ 220208926Smjacob ptr[5] = PSEUDO_HDS; 221208926Smjacob /* starting cylinder- write precompensation */ 222208926Smjacob /* ptr[6] = 0; */ 223208926Smjacob /* ptr[7] = 0; */ 224208926Smjacob /* ptr[8] = 0; */ 225208926Smjacob /* starting cylinder- reduced write current */ 226208926Smjacob /* ptr[9] = 0; */ 227208926Smjacob /* ptr[10] = 0; */ 228208926Smjacob /* ptr[11] = 0; */ 229208926Smjacob /* drive step rate */ 230208926Smjacob /* ptr[12] = 0; */ 231208926Smjacob /* ptr[13] = 0; */ 232208926Smjacob /* landing zone cylinder */ 233208926Smjacob /* ptr[14] = 0; */ 234208926Smjacob /* ptr[15] = 0; */ 235208926Smjacob /* ptr[16] = 0; */ 236208926Smjacob /* RPL */ 237208926Smjacob /* ptr[17] = 0; */ 238208926Smjacob /* rotational offset */ 239208926Smjacob /* ptr[18] = 0; */ 240208926Smjacob /* medium rotation rate - 7200 RPM */ 241208926Smjacob ptr[20] = 0x1c; 242208926Smjacob ptr[21] = 0x20; 243208926Smjacob } 244208926Smjacob ptr += 26; 245208926Smjacob } 246208926Smjacob 247208926Smjacob if (page == SMS_ALL_PAGES_PAGE || page == SMS_CACHE_PAGE) { 248208926Smjacob ptr[0] = SMS_CACHE_PAGE; 249208926Smjacob ptr[1] = 18; 250208926Smjacob ptr[2] = 1 << 2; 251208926Smjacob ptr += 20; 252208926Smjacob } 253208926Smjacob 254208926Smjacob if (page == SMS_ALL_PAGES_PAGE || page == SMS_CONTROL_MODE_PAGE) { 255208926Smjacob ptr[0] = SMS_CONTROL_MODE_PAGE; 256208926Smjacob ptr[1] = 10; 257208926Smjacob if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { 258208926Smjacob ptr[3] = 1 << 4; /* unrestricted reordering allowed */ 259208926Smjacob ptr[8] = 0x75; /* 30000 ms */ 260208926Smjacob ptr[9] = 0x30; 261208926Smjacob } 262208926Smjacob ptr += 12; 263208926Smjacob } 264208926Smjacob nbyte = (char *)ptr - &junk[0]; 265208926Smjacob ptr[0] = nbyte - 4; 266208926Smjacob 267208926Smjacob if (cdb[0] == MODE_SENSE) { 268208926Smjacob data_len = min(cdb[4], csio->dxfer_len); 269208926Smjacob } else { 270208926Smjacob uint16_t tw = (cdb[7] << 8) | cdb[8]; 271208926Smjacob data_len = min(tw, csio->dxfer_len); 272208926Smjacob } 273208926Smjacob data_len = min(data_len, nbyte); 274208926Smjacob if (data_len) { 275208926Smjacob memcpy(csio->data_ptr, junk, data_len); 276208926Smjacob } 277208926Smjacob csio->resid = csio->dxfer_len - data_len; 278208926Smjacob break; 279208926Smjacob } 280208926Smjacob case READ_6: 281208926Smjacob case READ_10: 282208926Smjacob case READ_12: 283208926Smjacob case READ_16: 284208926Smjacob case WRITE_6: 285208926Smjacob case WRITE_10: 286208926Smjacob case WRITE_12: 287208926Smjacob case WRITE_16: 288208926Smjacob if (vhba_rwparm(cdb, &off, &data_len, DISK_NBLKS, DISK_SHIFT)) { 289208926Smjacob vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); 290208926Smjacob break; 291208926Smjacob } 292208926Smjacob if (data_len) { 293208926Smjacob if ((cdb[0] & 0xf) == 8) { 294208926Smjacob memcpy(csio->data_ptr, &vhbas->disk[off], data_len); 295208926Smjacob } else { 296208926Smjacob memcpy(&vhbas->disk[off], csio->data_ptr, data_len); 297208926Smjacob } 298208926Smjacob csio->resid = csio->dxfer_len - data_len; 299208926Smjacob } else { 300208926Smjacob csio->resid = csio->dxfer_len; 301208926Smjacob } 302208926Smjacob break; 303208926Smjacob 304208926Smjacob case READ_CAPACITY: 305208926Smjacob if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) { 306208926Smjacob vhba_fill_sense(csio, SSD_KEY_UNIT_ATTENTION, 0x24, 0x0); 307208926Smjacob break; 308208926Smjacob } 309208926Smjacob if (cdb[8] & 0x1) { /* PMI */ 310208926Smjacob csio->data_ptr[0] = 0xff; 311208926Smjacob csio->data_ptr[1] = 0xff; 312208926Smjacob csio->data_ptr[2] = 0xff; 313208926Smjacob csio->data_ptr[3] = 0xff; 314208926Smjacob } else { 315208926Smjacob uint64_t last_blk = DISK_NBLKS - 1; 316208926Smjacob if (last_blk < 0xffffffffULL) { 317208926Smjacob csio->data_ptr[0] = (last_blk >> 24) & 0xff; 318208926Smjacob csio->data_ptr[1] = (last_blk >> 16) & 0xff; 319208926Smjacob csio->data_ptr[2] = (last_blk >> 8) & 0xff; 320208926Smjacob csio->data_ptr[3] = (last_blk) & 0xff; 321208926Smjacob } else { 322208926Smjacob csio->data_ptr[0] = 0xff; 323208926Smjacob csio->data_ptr[1] = 0xff; 324208926Smjacob csio->data_ptr[2] = 0xff; 325208926Smjacob csio->data_ptr[3] = 0xff; 326208926Smjacob } 327208926Smjacob } 328208926Smjacob csio->data_ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; 329208926Smjacob csio->data_ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; 330208926Smjacob csio->data_ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; 331208926Smjacob csio->data_ptr[7] = ((1 << DISK_SHIFT)) & 0xff; 332208926Smjacob break; 333208926Smjacob default: 334208926Smjacob vhba_default_cmd(csio, MAX_LUN, NULL); 335208926Smjacob break; 336208926Smjacob } 337208926Smjacob if (csio->scsi_status != SCSI_STATUS_OK) { 338208926Smjacob camstatus = CAM_SCSI_STATUS_ERROR; 339208926Smjacob if (csio->scsi_status == SCSI_STATUS_CHECK_COND) { 340208926Smjacob camstatus |= CAM_AUTOSNS_VALID; 341208926Smjacob } 342208926Smjacob } else { 343208926Smjacob csio->scsi_status = SCSI_STATUS_OK; 344208926Smjacob camstatus = CAM_REQ_CMP; 345208926Smjacob } 346208926Smjacob vhba_set_status(&csio->ccb_h, camstatus); 347208926Smjacob TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 348208926Smjacob} 349208926SmjacobDEV_MODULE(vhba_faulty, vhba_modprobe, NULL); 350