xref: /freebsd/tools/tools/vhba/faulty/vhba_faulty.c (revision 2a63c3be)
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