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