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