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 * A VHBA device to test REPORT LUN functionality. 29208926Smjacob */ 30208926Smjacob#include "vhba.h" 31208926Smjacob 32208926Smjacob#define MAX_TGT 1 33208926Smjacob#define MAX_LUN 1024 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 struct task qt; 47208926Smjacob uint8_t rpbitmap[MAX_LUN >> 3]; 48208926Smjacob} vhbarptluns_t; 49208926Smjacob 50208926Smjacobstatic void vhba_task(void *, int); 51208926Smjacobstatic void vhbarptluns_act(vhbarptluns_t *, struct ccb_scsiio *); 52208926Smjacob 53208926Smjacobvoid 54208926Smjacobvhba_init(vhba_softc_t *vhba) 55208926Smjacob{ 56208926Smjacob static vhbarptluns_t vhbas; 57208926Smjacob struct timeval now; 58208926Smjacob int i; 59208926Smjacob 60208926Smjacob vhbas.vhba = vhba; 61208926Smjacob vhbas.disk_size = DISK_SIZE << 20; 62208926Smjacob vhbas.disk = malloc(vhbas.disk_size, M_DEVBUF, M_WAITOK|M_ZERO); 63208926Smjacob vhba->private = &vhbas; 64208926Smjacob printf("setting luns"); 65208926Smjacob getmicrotime(&now); 66208926Smjacob if (now.tv_usec & 0x1) { 67208926Smjacob vhbas.rpbitmap[0] |= 1; 68208926Smjacob } 69208926Smjacob for (i = 1; i < 8; i++) { 70208926Smjacob if (arc4random() & 1) { 71208926Smjacob printf(" %d", i); 72208926Smjacob vhbas.rpbitmap[0] |= (1 << i); 73208926Smjacob } 74208926Smjacob } 75208926Smjacob for (i = 8; i < MAX_LUN; i++) { 76208926Smjacob if ((arc4random() % i) == 0) { 77208926Smjacob vhbas.rpbitmap[i >> 3] |= (1 << (i & 0x7)); 78208926Smjacob printf(" %d", i); 79208926Smjacob } 80208926Smjacob } 81208926Smjacob printf("\n"); 82208926Smjacob TASK_INIT(&vhbas.qt, 0, vhba_task, &vhbas); 83208926Smjacob} 84208926Smjacob 85208926Smjacobvoid 86208926Smjacobvhba_fini(vhba_softc_t *vhba) 87208926Smjacob{ 88208926Smjacob vhbarptluns_t *vhbas = vhba->private; 89208926Smjacob vhba->private = NULL; 90208926Smjacob free(vhbas->disk, M_DEVBUF); 91208926Smjacob} 92208926Smjacob 93208926Smjacobvoid 94208926Smjacobvhba_kick(vhba_softc_t *vhba) 95208926Smjacob{ 96208926Smjacob vhbarptluns_t *vhbas = vhba->private; 97208926Smjacob taskqueue_enqueue(taskqueue_swi, &vhbas->qt); 98208926Smjacob} 99208926Smjacob 100208926Smjacobstatic void 101208926Smjacobvhba_task(void *arg, int pending) 102208926Smjacob{ 103208926Smjacob vhbarptluns_t *vhbas = arg; 104208926Smjacob struct ccb_hdr *ccbh; 105208926Smjacob 106208926Smjacob mtx_lock(&vhbas->vhba->lock); 107208926Smjacob while ((ccbh = TAILQ_FIRST(&vhbas->vhba->actv)) != NULL) { 108208926Smjacob TAILQ_REMOVE(&vhbas->vhba->actv, ccbh, sim_links.tqe); 109208926Smjacob vhbarptluns_act(vhbas, (struct ccb_scsiio *)ccbh); 110208926Smjacob } 111208926Smjacob while ((ccbh = TAILQ_FIRST(&vhbas->vhba->done)) != NULL) { 112208926Smjacob TAILQ_REMOVE(&vhbas->vhba->done, ccbh, sim_links.tqe); 113208926Smjacob xpt_done((union ccb *)ccbh); 114208926Smjacob } 115208926Smjacob mtx_unlock(&vhbas->vhba->lock); 116208926Smjacob} 117208926Smjacob 118208926Smjacobstatic void 119208926Smjacobvhbarptluns_act(vhbarptluns_t *vhbas, struct ccb_scsiio *csio) 120208926Smjacob{ 121208926Smjacob char junk[128]; 122208926Smjacob uint8_t *cdb, *ptr, status; 123208926Smjacob uint32_t data_len; 124208926Smjacob uint64_t off; 125208926Smjacob int i, attached_lun = 0; 126208926Smjacob 127208926Smjacob data_len = 0; 128208926Smjacob status = SCSI_STATUS_OK; 129208926Smjacob 130208926Smjacob memset(&csio->sense_data, 0, sizeof (csio->sense_data)); 131208926Smjacob cdb = csio->cdb_io.cdb_bytes; 132208926Smjacob 133208926Smjacob if (csio->ccb_h.target_id >= MAX_TGT) { 134208926Smjacob csio->ccb_h.status = CAM_SEL_TIMEOUT; 135208926Smjacob TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 136208926Smjacob return; 137208926Smjacob } 138208926Smjacob 139208926Smjacob if (csio->ccb_h.target_lun < MAX_LUN) { 140208926Smjacob i = csio->ccb_h.target_lun & 0x7; 141208926Smjacob if (vhbas->rpbitmap[csio->ccb_h.target_lun >> 3] & (1 << i)) { 142208926Smjacob attached_lun = 1; 143208926Smjacob } 144208926Smjacob } 145208926Smjacob if (attached_lun == 0 && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) { 146208926Smjacob vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0); 147208926Smjacob TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 148208926Smjacob return; 149208926Smjacob } 150208926Smjacob 151208926Smjacob switch (cdb[0]) { 152208926Smjacob case MODE_SENSE: 153208926Smjacob case MODE_SENSE_10: 154208926Smjacob { 155208926Smjacob unsigned int nbyte; 156208926Smjacob uint8_t page = cdb[2] & SMS_PAGE_CODE; 157208926Smjacob uint8_t pgctl = cdb[2] & SMS_PAGE_CTRL_MASK; 158208926Smjacob 159208926Smjacob switch (page) { 160208926Smjacob case SMS_FORMAT_DEVICE_PAGE: 161208926Smjacob case SMS_GEOMETRY_PAGE: 162208926Smjacob case SMS_CACHE_PAGE: 163208926Smjacob case SMS_CONTROL_MODE_PAGE: 164208926Smjacob case SMS_ALL_PAGES_PAGE: 165208926Smjacob break; 166208926Smjacob default: 167208926Smjacob vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); 168208926Smjacob TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 169208926Smjacob return; 170208926Smjacob } 171208926Smjacob memset(junk, 0, sizeof (junk)); 172208926Smjacob if (cdb[1] & SMS_DBD) { 173208926Smjacob ptr = &junk[4]; 174208926Smjacob } else { 175208926Smjacob ptr = junk; 176208926Smjacob ptr[3] = 8; 177208926Smjacob ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; 178208926Smjacob ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; 179208926Smjacob ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; 180208926Smjacob ptr[7] = ((1 << DISK_SHIFT)) & 0xff; 181208926Smjacob 182208926Smjacob ptr[8] = (DISK_NBLKS >> 24) & 0xff; 183208926Smjacob ptr[9] = (DISK_NBLKS >> 16) & 0xff; 184208926Smjacob ptr[10] = (DISK_NBLKS >> 8) & 0xff; 185208926Smjacob ptr[11] = DISK_NBLKS & 0xff; 186208926Smjacob ptr += 12; 187208926Smjacob } 188208926Smjacob 189208926Smjacob if (page == SMS_ALL_PAGES_PAGE || page == SMS_FORMAT_DEVICE_PAGE) { 190208926Smjacob ptr[0] = SMS_FORMAT_DEVICE_PAGE; 191208926Smjacob ptr[1] = 24; 192208926Smjacob if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { 193208926Smjacob /* tracks per zone */ 194208926Smjacob /* ptr[2] = 0; */ 195208926Smjacob /* ptr[3] = 0; */ 196208926Smjacob /* alternate sectors per zone */ 197208926Smjacob /* ptr[4] = 0; */ 198208926Smjacob /* ptr[5] = 0; */ 199208926Smjacob /* alternate tracks per zone */ 200208926Smjacob /* ptr[6] = 0; */ 201208926Smjacob /* ptr[7] = 0; */ 202208926Smjacob /* alternate tracks per logical unit */ 203208926Smjacob /* ptr[8] = 0; */ 204208926Smjacob /* ptr[9] = 0; */ 205208926Smjacob /* sectors per track */ 206208926Smjacob ptr[10] = (PSEUDO_SPT >> 8) & 0xff; 207208926Smjacob ptr[11] = PSEUDO_SPT & 0xff; 208208926Smjacob /* data bytes per physical sector */ 209208926Smjacob ptr[12] = ((1 << DISK_SHIFT) >> 8) & 0xff; 210208926Smjacob ptr[13] = (1 << DISK_SHIFT) & 0xff; 211208926Smjacob /* interleave */ 212208926Smjacob /* ptr[14] = 0; */ 213208926Smjacob /* ptr[15] = 1; */ 214208926Smjacob /* track skew factor */ 215208926Smjacob /* ptr[16] = 0; */ 216208926Smjacob /* ptr[17] = 0; */ 217208926Smjacob /* cylinder skew factor */ 218208926Smjacob /* ptr[18] = 0; */ 219208926Smjacob /* ptr[19] = 0; */ 220208926Smjacob /* SSRC, HSEC, RMB, SURF */ 221208926Smjacob } 222208926Smjacob ptr += 26; 223208926Smjacob } 224208926Smjacob 225208926Smjacob if (page == SMS_ALL_PAGES_PAGE || page == SMS_GEOMETRY_PAGE) { 226208926Smjacob ptr[0] = SMS_GEOMETRY_PAGE; 227208926Smjacob ptr[1] = 24; 228208926Smjacob if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { 229208926Smjacob uint32_t cyl = (DISK_NBLKS + ((PSEUDO_SPC - 1))) / PSEUDO_SPC; 230208926Smjacob /* number of cylinders */ 231208926Smjacob ptr[2] = (cyl >> 24) & 0xff; 232208926Smjacob ptr[3] = (cyl >> 16) & 0xff; 233208926Smjacob ptr[4] = cyl & 0xff; 234208926Smjacob /* number of heads */ 235208926Smjacob ptr[5] = PSEUDO_HDS; 236208926Smjacob /* starting cylinder- write precompensation */ 237208926Smjacob /* ptr[6] = 0; */ 238208926Smjacob /* ptr[7] = 0; */ 239208926Smjacob /* ptr[8] = 0; */ 240208926Smjacob /* starting cylinder- reduced write current */ 241208926Smjacob /* ptr[9] = 0; */ 242208926Smjacob /* ptr[10] = 0; */ 243208926Smjacob /* ptr[11] = 0; */ 244208926Smjacob /* drive step rate */ 245208926Smjacob /* ptr[12] = 0; */ 246208926Smjacob /* ptr[13] = 0; */ 247208926Smjacob /* landing zone cylinder */ 248208926Smjacob /* ptr[14] = 0; */ 249208926Smjacob /* ptr[15] = 0; */ 250208926Smjacob /* ptr[16] = 0; */ 251208926Smjacob /* RPL */ 252208926Smjacob /* ptr[17] = 0; */ 253208926Smjacob /* rotational offset */ 254208926Smjacob /* ptr[18] = 0; */ 255208926Smjacob /* medium rotation rate - 7200 RPM */ 256208926Smjacob ptr[20] = 0x1c; 257208926Smjacob ptr[21] = 0x20; 258208926Smjacob } 259208926Smjacob ptr += 26; 260208926Smjacob } 261208926Smjacob 262208926Smjacob if (page == SMS_ALL_PAGES_PAGE || page == SMS_CACHE_PAGE) { 263208926Smjacob ptr[0] = SMS_CACHE_PAGE; 264208926Smjacob ptr[1] = 18; 265208926Smjacob ptr[2] = 1 << 2; 266208926Smjacob ptr += 20; 267208926Smjacob } 268208926Smjacob 269208926Smjacob if (page == SMS_ALL_PAGES_PAGE || page == SMS_CONTROL_MODE_PAGE) { 270208926Smjacob ptr[0] = SMS_CONTROL_MODE_PAGE; 271208926Smjacob ptr[1] = 10; 272208926Smjacob if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { 273208926Smjacob ptr[3] = 1 << 4; /* unrestricted reordering allowed */ 274208926Smjacob ptr[8] = 0x75; /* 30000 ms */ 275208926Smjacob ptr[9] = 0x30; 276208926Smjacob } 277208926Smjacob ptr += 12; 278208926Smjacob } 279208926Smjacob nbyte = (char *)ptr - &junk[0]; 280208926Smjacob ptr[0] = nbyte - 4; 281208926Smjacob 282208926Smjacob if (cdb[0] == MODE_SENSE) { 283208926Smjacob data_len = min(cdb[4], csio->dxfer_len); 284208926Smjacob } else { 285208926Smjacob uint16_t tw = (cdb[7] << 8) | cdb[8]; 286208926Smjacob data_len = min(tw, csio->dxfer_len); 287208926Smjacob } 288208926Smjacob data_len = min(data_len, nbyte); 289208926Smjacob if (data_len) { 290208926Smjacob memcpy(csio->data_ptr, junk, data_len); 291208926Smjacob } 292208926Smjacob csio->resid = csio->dxfer_len - data_len; 293208926Smjacob break; 294208926Smjacob } 295208926Smjacob case READ_6: 296208926Smjacob case READ_10: 297208926Smjacob case READ_12: 298208926Smjacob case READ_16: 299208926Smjacob case WRITE_6: 300208926Smjacob case WRITE_10: 301208926Smjacob case WRITE_12: 302208926Smjacob case WRITE_16: 303208926Smjacob if (vhba_rwparm(cdb, &off, &data_len, DISK_NBLKS, DISK_SHIFT)) { 304208926Smjacob vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); 305208926Smjacob break; 306208926Smjacob } 307208926Smjacob if (data_len) { 308208926Smjacob if ((cdb[0] & 0xf) == 8) { 309208926Smjacob memcpy(csio->data_ptr, &vhbas->disk[off], data_len); 310208926Smjacob } else { 311208926Smjacob memcpy(&vhbas->disk[off], csio->data_ptr, data_len); 312208926Smjacob } 313208926Smjacob csio->resid = csio->dxfer_len - data_len; 314208926Smjacob } else { 315208926Smjacob csio->resid = csio->dxfer_len; 316208926Smjacob } 317208926Smjacob break; 318208926Smjacob break; 319208926Smjacob 320208926Smjacob case READ_CAPACITY: 321208926Smjacob if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) { 322208926Smjacob vhba_fill_sense(csio, SSD_KEY_UNIT_ATTENTION, 0x24, 0x0); 323208926Smjacob break; 324208926Smjacob } 325208926Smjacob if (cdb[8] & 0x1) { /* PMI */ 326208926Smjacob csio->data_ptr[0] = 0xff; 327208926Smjacob csio->data_ptr[1] = 0xff; 328208926Smjacob csio->data_ptr[2] = 0xff; 329208926Smjacob csio->data_ptr[3] = 0xff; 330208926Smjacob } else { 331208926Smjacob uint64_t last_blk = DISK_NBLKS - 1; 332208926Smjacob if (last_blk < 0xffffffffULL) { 333208926Smjacob csio->data_ptr[0] = (last_blk >> 24) & 0xff; 334208926Smjacob csio->data_ptr[1] = (last_blk >> 16) & 0xff; 335208926Smjacob csio->data_ptr[2] = (last_blk >> 8) & 0xff; 336208926Smjacob csio->data_ptr[3] = (last_blk) & 0xff; 337208926Smjacob } else { 338208926Smjacob csio->data_ptr[0] = 0xff; 339208926Smjacob csio->data_ptr[1] = 0xff; 340208926Smjacob csio->data_ptr[2] = 0xff; 341208926Smjacob csio->data_ptr[3] = 0xff; 342208926Smjacob } 343208926Smjacob } 344208926Smjacob csio->data_ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; 345208926Smjacob csio->data_ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; 346208926Smjacob csio->data_ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; 347208926Smjacob csio->data_ptr[7] = ((1 << DISK_SHIFT)) & 0xff; 348208926Smjacob break; 349208926Smjacob 350208926Smjacob default: 351208926Smjacob vhba_default_cmd(csio, MAX_LUN, vhbas->rpbitmap); 352208926Smjacob break; 353208926Smjacob } 354208926Smjacob csio->ccb_h.status &= ~CAM_STATUS_MASK; 355208926Smjacob if (csio->scsi_status != SCSI_STATUS_OK) { 356208926Smjacob csio->ccb_h.status |= CAM_SCSI_STATUS_ERROR; 357208926Smjacob if (csio->scsi_status == SCSI_STATUS_CHECK_COND) { 358208926Smjacob csio->ccb_h.status |= CAM_AUTOSNS_VALID; 359208926Smjacob } 360208926Smjacob } else { 361208926Smjacob csio->scsi_status = SCSI_STATUS_OK; 362208926Smjacob csio->ccb_h.status |= CAM_REQ_CMP; 363208926Smjacob } 364208926Smjacob TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 365208926Smjacob} 366208926SmjacobDEV_MODULE(vhba_rtpluns, vhba_modprobe, NULL); 367