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