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
vhba_init(vhba_softc_t * vhba)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
vhba_fini(vhba_softc_t * vhba)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
vhba_kick(vhba_softc_t * vhba)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
vhba_task(void * arg,int pending)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
vhbarptluns_act(vhbarptluns_t * vhbas,struct ccb_scsiio * csio)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