xref: /freebsd/tools/tools/vhba/mptest/vhba_mptest.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" Multipath Device. Creates to devices to be set up as multipath,
29  * makes one or both of them non existent (or re existent) on demand.
30  */
31 #include "vhba.h"
32 #include <sys/sysctl.h>
33 
34 static int vhba_stop_lun;
35 static int vhba_start_lun = 0;
36 static int vhba_notify_stop = 1;
37 static int vhba_notify_start = 1;
38 static int vhba_inject_hwerr = 0;
39 SYSCTL_INT(_debug, OID_AUTO, vhba_stop_lun, CTLFLAG_RW, &vhba_stop_lun, 0, "stop lun bitmap");
40 SYSCTL_INT(_debug, OID_AUTO, vhba_start_lun, CTLFLAG_RW, &vhba_start_lun, 0, "start lun bitmap");
41 SYSCTL_INT(_debug, OID_AUTO, vhba_notify_stop, CTLFLAG_RW, &vhba_notify_stop, 1, "notify when luns go away");
42 SYSCTL_INT(_debug, OID_AUTO, vhba_notify_start, CTLFLAG_RW, &vhba_notify_start, 1, "notify when luns arrive");
43 SYSCTL_INT(_debug, OID_AUTO, vhba_inject_hwerr, CTLFLAG_RW, &vhba_inject_hwerr, 0, "inject hardware error on lost luns");
44 
45 #define	MAX_TGT		1
46 #define	MAX_LUN		2
47 #define	VMP_TIME	hz
48 
49 #define	DISK_SIZE	32
50 #define	DISK_SHIFT	9
51 #define	DISK_NBLKS	((DISK_SIZE << 20) >> DISK_SHIFT)
52 #define	PSEUDO_SPT	64
53 #define	PSEUDO_HDS	64
54 #define	PSEUDO_SPC	(PSEUDO_SPT * PSEUDO_HDS)
55 
56 typedef struct {
57 	vhba_softc_t *	vhba;
58 	uint8_t *	disk;
59 	size_t		disk_size;
60 	int		luns[2];
61 	struct callout	tick;
62 	struct task	qt;
63 	TAILQ_HEAD(, ccb_hdr)   inproc;
64 	int		nact, nact_high;
65 } mptest_t;
66 
67 static timeout_t vhba_iodelay;
68 static timeout_t vhba_timer;
69 static void vhba_task(void *, int);
70 static void mptest_act(mptest_t *, struct ccb_scsiio *);
71 
72 void
73 vhba_init(vhba_softc_t *vhba)
74 {
75 	static mptest_t vhbastatic;
76 
77 	vhbastatic.vhba = vhba;
78 	vhbastatic.disk_size = DISK_SIZE << 20;
79 	vhbastatic.disk = malloc(vhbastatic.disk_size, M_DEVBUF, M_WAITOK|M_ZERO);
80 	vhba->private = &vhbastatic;
81 	callout_init_mtx(&vhbastatic.tick, &vhba->lock, 0);
82 	callout_reset(&vhbastatic.tick, VMP_TIME, vhba_timer, vhba);
83 	TAILQ_INIT(&vhbastatic.inproc);
84 	TASK_INIT(&vhbastatic.qt, 0, vhba_task, &vhbastatic);
85 	vhbastatic.luns[0] = 1;
86 	vhbastatic.luns[1] = 1;
87 }
88 
89 void
90 vhba_fini(vhba_softc_t *vhba)
91 {
92 	mptest_t *vhbas = vhba->private;
93 	callout_stop(&vhbas->tick);
94 	vhba->private = NULL;
95 	free(vhbas->disk, M_DEVBUF);
96 }
97 
98 void
99 vhba_kick(vhba_softc_t *vhba)
100 {
101 	mptest_t *vhbas = vhba->private;
102 	taskqueue_enqueue(taskqueue_swi, &vhbas->qt);
103 }
104 
105 static void
106 vhba_task(void *arg, int pending)
107 {
108 	mptest_t *vhbas = arg;
109 	struct ccb_hdr *ccbh;
110 	int nadded = 0;
111 
112 	mtx_lock(&vhbas->vhba->lock);
113 	while ((ccbh = TAILQ_FIRST(&vhbas->vhba->actv)) != NULL) {
114 		TAILQ_REMOVE(&vhbas->vhba->actv, ccbh, sim_links.tqe);
115                 mptest_act(vhbas, (struct ccb_scsiio *)ccbh);
116 		nadded++;
117 		ccbh->sim_priv.entries[0].ptr = vhbas;
118 		callout_handle_init(&ccbh->timeout_ch);
119 	}
120 	if (nadded) {
121 		vhba_kick(vhbas->vhba);
122 	} else {
123 		while ((ccbh = TAILQ_FIRST(&vhbas->vhba->done)) != NULL) {
124 			TAILQ_REMOVE(&vhbas->vhba->done, ccbh, sim_links.tqe);
125 			xpt_done((union ccb *)ccbh);
126 		}
127 	}
128 	mtx_unlock(&vhbas->vhba->lock);
129 }
130 
131 static void
132 mptest_act(mptest_t *vhbas, struct ccb_scsiio *csio)
133 {
134 	char junk[128];
135 	cam_status camstatus;
136 	uint8_t *cdb, *ptr, status;
137 	uint32_t data_len, blkcmd;
138 	uint64_t off;
139 
140 	blkcmd = data_len = 0;
141 	status = SCSI_STATUS_OK;
142 
143 	memset(&csio->sense_data, 0, sizeof (csio->sense_data));
144 	cdb = csio->cdb_io.cdb_bytes;
145 
146 	if (csio->ccb_h.target_id >= MAX_TGT) {
147 		vhba_set_status(&csio->ccb_h, CAM_SEL_TIMEOUT);
148 		TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
149 		return;
150 	}
151 	if (vhba_inject_hwerr && csio->ccb_h.target_lun < MAX_LUN && vhbas->luns[csio->ccb_h.target_lun] == 0) {
152 		vhba_fill_sense(csio, SSD_KEY_HARDWARE_ERROR, 0x44, 0x0);
153 		TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
154 		return;
155 	}
156 	if ((csio->ccb_h.target_lun >= MAX_LUN || vhbas->luns[csio->ccb_h.target_lun] == 0) && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) {
157 		vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0);
158 		TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
159 		return;
160 	}
161 
162 	switch (cdb[0]) {
163 	case MODE_SENSE:
164 	case MODE_SENSE_10:
165 	{
166 		unsigned int nbyte;
167 		uint8_t page = cdb[2] & SMS_PAGE_CODE;
168 		uint8_t pgctl = cdb[2] & SMS_PAGE_CTRL_MASK;
169 
170 		switch (page) {
171 		case SMS_FORMAT_DEVICE_PAGE:
172 		case SMS_GEOMETRY_PAGE:
173 		case SMS_CACHE_PAGE:
174 		case SMS_CONTROL_MODE_PAGE:
175 		case SMS_ALL_PAGES_PAGE:
176 			break;
177 		default:
178 			vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0);
179 			TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
180 			return;
181 		}
182 		memset(junk, 0, sizeof (junk));
183 		if (cdb[1] & SMS_DBD) {
184 			ptr = &junk[4];
185 		} else {
186 			ptr = junk;
187 			ptr[3] = 8;
188 			ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff;
189 			ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff;
190 			ptr[6] = ((1 << DISK_SHIFT) >>  8) & 0xff;
191 			ptr[7] = ((1 << DISK_SHIFT)) & 0xff;
192 
193 			ptr[8] = (DISK_NBLKS >> 24) & 0xff;
194 			ptr[9] = (DISK_NBLKS >> 16) & 0xff;
195 			ptr[10] = (DISK_NBLKS >> 8) & 0xff;
196 			ptr[11] = DISK_NBLKS & 0xff;
197 			ptr += 12;
198 		}
199 
200 		if (page == SMS_ALL_PAGES_PAGE || page == SMS_FORMAT_DEVICE_PAGE) {
201 			ptr[0] = SMS_FORMAT_DEVICE_PAGE;
202 			ptr[1] = 24;
203 			if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) {
204 				/* tracks per zone */
205 				/* ptr[2] = 0; */
206 				/* ptr[3] = 0; */
207 				/* alternate sectors per zone */
208 				/* ptr[4] = 0; */
209 				/* ptr[5] = 0; */
210 				/* alternate tracks per zone */
211 				/* ptr[6] = 0; */
212 				/* ptr[7] = 0; */
213 				/* alternate tracks per logical unit */
214 				/* ptr[8] = 0; */
215 				/* ptr[9] = 0; */
216 				/* sectors per track */
217 				ptr[10] = (PSEUDO_SPT >> 8) & 0xff;
218 				ptr[11] = PSEUDO_SPT & 0xff;
219 				/* data bytes per physical sector */
220 				ptr[12] = ((1 << DISK_SHIFT) >> 8) & 0xff;
221 				ptr[13] = (1 << DISK_SHIFT) & 0xff;
222 				/* interleave */
223 				/* ptr[14] = 0; */
224 				/* ptr[15] = 1; */
225 				/* track skew factor */
226 				/* ptr[16] = 0; */
227 				/* ptr[17] = 0; */
228 				/* cylinder skew factor */
229 				/* ptr[18] = 0; */
230 				/* ptr[19] = 0; */
231 				/* SSRC, HSEC, RMB, SURF */
232 			}
233 			ptr += 26;
234 		}
235 
236 		if (page == SMS_ALL_PAGES_PAGE || page == SMS_GEOMETRY_PAGE) {
237 			ptr[0] = SMS_GEOMETRY_PAGE;
238 			ptr[1] = 24;
239 			if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) {
240 				uint32_t cyl = (DISK_NBLKS + ((PSEUDO_SPC - 1))) / PSEUDO_SPC;
241 				/* number of cylinders */
242 				ptr[2] = (cyl >> 24) & 0xff;
243 				ptr[3] = (cyl >> 16) & 0xff;
244 				ptr[4] = cyl & 0xff;
245 				/* number of heads */
246 				ptr[5] = PSEUDO_HDS;
247 				/* starting cylinder- write precompensation */
248 				/* ptr[6] = 0; */
249 				/* ptr[7] = 0; */
250 				/* ptr[8] = 0; */
251 				/* starting cylinder- reduced write current */
252 				/* ptr[9] = 0; */
253 				/* ptr[10] = 0; */
254 				/* ptr[11] = 0; */
255 				/* drive step rate */
256 				/* ptr[12] = 0; */
257 				/* ptr[13] = 0; */
258 				/* landing zone cylinder */
259 				/* ptr[14] = 0; */
260 				/* ptr[15] = 0; */
261 				/* ptr[16] = 0; */
262 				/* RPL */
263 				/* ptr[17] = 0; */
264 				/* rotational offset */
265 				/* ptr[18] = 0; */
266 				/* medium rotation rate -  7200 RPM */
267 				ptr[20] = 0x1c;
268 				ptr[21] = 0x20;
269 			}
270 			ptr += 26;
271 		}
272 
273 		if (page == SMS_ALL_PAGES_PAGE || page == SMS_CACHE_PAGE) {
274 			ptr[0] = SMS_CACHE_PAGE;
275 			ptr[1] = 18;
276 			ptr[2] = 1 << 2;
277 			ptr += 20;
278 		}
279 
280 		if (page == SMS_ALL_PAGES_PAGE || page == SMS_CONTROL_MODE_PAGE) {
281 			ptr[0] = SMS_CONTROL_MODE_PAGE;
282 			ptr[1] = 10;
283 			if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) {
284 				ptr[3] = 1 << 4; /* unrestricted reordering allowed */
285 				ptr[8] = 0x75;   /* 30000 ms */
286 				ptr[9] = 0x30;
287 			}
288 			ptr += 12;
289 		}
290 		nbyte = (char *)ptr - &junk[0];
291 		ptr[0] = nbyte - 4;
292 
293 		if (cdb[0] == MODE_SENSE) {
294 			data_len = min(cdb[4], csio->dxfer_len);
295 		} else {
296 			uint16_t tw = (cdb[7] << 8) | cdb[8];
297 			data_len = min(tw, csio->dxfer_len);
298 		}
299 		data_len = min(data_len, nbyte);
300 		if (data_len) {
301 			memcpy(csio->data_ptr, junk, data_len);
302 		}
303 		csio->resid = csio->dxfer_len - data_len;
304 		break;
305 	}
306 	case READ_6:
307 	case READ_10:
308 	case READ_12:
309 	case READ_16:
310 	case WRITE_6:
311 	case WRITE_10:
312 	case WRITE_12:
313 	case WRITE_16:
314 		if (vhba_rwparm(cdb, &off, &data_len, DISK_NBLKS, DISK_SHIFT)) {
315 			vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0);
316 			break;
317 		}
318 		blkcmd++;
319 		if (++vhbas->nact > vhbas->nact_high) {
320 			vhbas->nact_high = vhbas->nact;
321 			printf("%s: high block count now %d\n", __func__, vhbas->nact);
322 		}
323 		if (data_len) {
324 			if ((cdb[0] & 0xf) == 8) {
325 				memcpy(csio->data_ptr, &vhbas->disk[off], data_len);
326 			} else {
327 				memcpy(&vhbas->disk[off], csio->data_ptr, data_len);
328 			}
329 			csio->resid = csio->dxfer_len - data_len;
330 		} else {
331 			csio->resid = csio->dxfer_len;
332 		}
333 		break;
334 
335 	case READ_CAPACITY:
336 		if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) {
337 			vhba_fill_sense(csio, SSD_KEY_UNIT_ATTENTION, 0x24, 0x0);
338 			break;
339 		}
340 		if (cdb[8] & 0x1) { /* PMI */
341 			csio->data_ptr[0] = 0xff;
342 			csio->data_ptr[1] = 0xff;
343 			csio->data_ptr[2] = 0xff;
344 			csio->data_ptr[3] = 0xff;
345 		} else {
346 			uint64_t last_blk = DISK_NBLKS - 1;
347 			if (last_blk < 0xffffffffULL) {
348 			    csio->data_ptr[0] = (last_blk >> 24) & 0xff;
349 			    csio->data_ptr[1] = (last_blk >> 16) & 0xff;
350 			    csio->data_ptr[2] = (last_blk >>  8) & 0xff;
351 			    csio->data_ptr[3] = (last_blk) & 0xff;
352 			} else {
353 			    csio->data_ptr[0] = 0xff;
354 			    csio->data_ptr[1] = 0xff;
355 			    csio->data_ptr[2] = 0xff;
356 			    csio->data_ptr[3] = 0xff;
357 			}
358 		}
359 		csio->data_ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff;
360 		csio->data_ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff;
361 		csio->data_ptr[6] = ((1 << DISK_SHIFT) >>  8) & 0xff;
362 		csio->data_ptr[7] = ((1 << DISK_SHIFT)) & 0xff;
363 		break;
364 	default:
365 		vhba_default_cmd(csio, MAX_LUN, NULL);
366 		break;
367 	}
368 	if (csio->scsi_status != SCSI_STATUS_OK) {
369 		camstatus = CAM_SCSI_STATUS_ERROR;
370 		if (csio->scsi_status == SCSI_STATUS_CHECK_COND) {
371 			camstatus |= CAM_AUTOSNS_VALID;
372 		}
373 	} else {
374 		csio->scsi_status = SCSI_STATUS_OK;
375 		camstatus = CAM_REQ_CMP;
376 	}
377 	vhba_set_status(&csio->ccb_h, camstatus);
378 	if (blkcmd) {
379 		int ticks;
380 		struct timeval t;
381 
382 		TAILQ_INSERT_TAIL(&vhbas->inproc, &csio->ccb_h, sim_links.tqe);
383 		t.tv_sec = 0;
384 		t.tv_usec = (500 + arc4random());
385 		if (t.tv_usec > 10000) {
386 			t.tv_usec = 10000;
387 		}
388 		ticks = tvtohz(&t);
389 		csio->ccb_h.timeout_ch = timeout(vhba_iodelay, &csio->ccb_h, ticks);
390 	} else {
391 		TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
392 	}
393 }
394 
395 static void
396 vhba_iodelay(void *arg)
397 {
398 	struct ccb_hdr *ccbh = arg;
399 	mptest_t *vhbas = ccbh->sim_priv.entries[0].ptr;
400 
401 	mtx_lock(&vhbas->vhba->lock);
402 	TAILQ_REMOVE(&vhbas->inproc, ccbh, sim_links.tqe);
403 	TAILQ_INSERT_TAIL(&vhbas->vhba->done, ccbh, sim_links.tqe);
404 	vhbas->nact -= 1;
405 	vhba_kick(vhbas->vhba);
406 	mtx_unlock(&vhbas->vhba->lock);
407 }
408 
409 static void
410 vhba_timer(void *arg)
411 {
412 	int lun;
413 	vhba_softc_t *vhba = arg;
414 	mptest_t *vhbas = vhba->private;
415 	if (vhba_stop_lun) {
416 		lun = (vhba_stop_lun & 1)? 0 : 1;
417 		if (lun == 0 || lun == 1) {
418 			if (vhbas->luns[lun]) {
419 				struct cam_path *tp;
420 				if (vhba_notify_stop) {
421 					if (xpt_create_path(&tp, xpt_periph, cam_sim_path(vhba->sim), 0, lun) != CAM_REQ_CMP) {
422 						goto out;
423 					}
424 					vhbas->luns[lun] = 0;
425 					xpt_async(AC_LOST_DEVICE, tp, NULL);
426 					xpt_free_path(tp);
427 				} else {
428 					vhbas->luns[lun] = 0;
429 				}
430 			}
431 		}
432 		vhba_stop_lun &= ~(1 << lun);
433 	} else if (vhba_start_lun) {
434 		lun = (vhba_start_lun & 1)? 0 : 1;
435 		if (lun == 0 || lun == 1) {
436 			if (vhbas->luns[lun] == 0) {
437 				if (vhba_notify_start) {
438 					union ccb *ccb;
439 					ccb = xpt_alloc_ccb_nowait();
440 					if (ccb == NULL) {
441 						goto out;
442 					}
443 					if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, cam_sim_path(vhba->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
444 						xpt_free_ccb(ccb);
445 						goto out;
446 					}
447 					vhbas->luns[lun] = 1;
448 					xpt_rescan(ccb);
449 				} else {
450 					vhbas->luns[lun] = 1;
451 				}
452 			}
453 		}
454 		vhba_start_lun &= ~(1 << lun);
455 	}
456 out:
457 	callout_reset(&vhbas->tick, VMP_TIME, vhba_timer, vhba);
458 }
459 DEV_MODULE(vhba_mptest, vhba_modprobe, NULL);
460