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
42typedef 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
51static void vhba_task(void *, int);
52static void faulty_act(faulty_t *, struct ccb_scsiio *);
53
54void
55vhba_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
67void
68vhba_fini(vhba_softc_t *vhba)
69{
70	faulty_t *vhbas = vhba->private;
71	vhba->private = NULL;
72	free(vhbas->disk, M_DEVBUF);
73}
74
75void
76vhba_kick(vhba_softc_t *vhba)
77{
78	faulty_t *vhbas = vhba->private;
79	taskqueue_enqueue(taskqueue_swi, &vhbas->qt);
80}
81
82static void
83vhba_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
104static void
105faulty_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}
349DEV_MODULE(vhba_faulty, vhba_modprobe, NULL);
350