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