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