2 * Copyright (c) 2010 by Panasas, Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
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.
32 #include <sys/sysctl.h>
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");
51 #define DISK_NBLKS ((DISK_SIZE << 20) >> DISK_SHIFT)
54 #define PSEUDO_SPC (PSEUDO_SPT * PSEUDO_HDS)
63 TAILQ_HEAD(, ccb_hdr) inproc;
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 *);
73 vhba_init(vhba_softc_t *vhba)
75 static mptest_t vhbastatic;
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;
90 vhba_fini(vhba_softc_t *vhba)
92 mptest_t *vhbas = vhba->private;
93 callout_stop(&vhbas->tick);
95 free(vhbas->disk, M_DEVBUF);
99 vhba_kick(vhba_softc_t *vhba)
101 mptest_t *vhbas = vhba->private;
102 taskqueue_enqueue(taskqueue_swi, &vhbas->qt);
106 vhba_task(void *arg, int pending)
108 mptest_t *vhbas = arg;
109 struct ccb_hdr *ccbh;
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);
117 ccbh->sim_priv.entries[0].ptr = vhbas;
118 callout_handle_init(&ccbh->timeout_ch);
121 vhba_kick(vhbas->vhba);
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);
128 mtx_unlock(&vhbas->vhba->lock);
132 mptest_act(mptest_t *vhbas, struct ccb_scsiio *csio)
135 cam_status camstatus;
136 uint8_t *cdb, *ptr, status;
137 uint32_t data_len, blkcmd;
140 blkcmd = data_len = 0;
141 status = SCSI_STATUS_OK;
143 memset(&csio->sense_data, 0, sizeof (csio->sense_data));
144 cdb = csio->cdb_io.cdb_bytes;
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);
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);
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);
167 uint8_t page = cdb[2] & SMS_PAGE_CODE;
168 uint8_t pgctl = cdb[2] & SMS_PAGE_CTRL_MASK;
171 case SMS_FORMAT_DEVICE_PAGE:
172 case SMS_GEOMETRY_PAGE:
174 case SMS_CONTROL_MODE_PAGE:
175 case SMS_ALL_PAGES_PAGE:
178 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0);
179 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
182 memset(junk, 0, sizeof (junk));
183 if (cdb[1] & SMS_DBD) {
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;
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;
200 if (page == SMS_ALL_PAGES_PAGE || page == SMS_FORMAT_DEVICE_PAGE) {
201 ptr[0] = SMS_FORMAT_DEVICE_PAGE;
203 if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) {
204 /* tracks per zone */
207 /* alternate sectors per zone */
210 /* alternate tracks per zone */
213 /* alternate tracks per logical unit */
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;
225 /* track skew factor */
228 /* cylinder skew factor */
231 /* SSRC, HSEC, RMB, SURF */
236 if (page == SMS_ALL_PAGES_PAGE || page == SMS_GEOMETRY_PAGE) {
237 ptr[0] = SMS_GEOMETRY_PAGE;
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;
245 /* number of heads */
247 /* starting cylinder- write precompensation */
251 /* starting cylinder- reduced write current */
255 /* drive step rate */
258 /* landing zone cylinder */
264 /* rotational offset */
266 /* medium rotation rate - 7200 RPM */
273 if (page == SMS_ALL_PAGES_PAGE || page == SMS_CACHE_PAGE) {
274 ptr[0] = SMS_CACHE_PAGE;
280 if (page == SMS_ALL_PAGES_PAGE || page == SMS_CONTROL_MODE_PAGE) {
281 ptr[0] = SMS_CONTROL_MODE_PAGE;
283 if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) {
284 ptr[3] = 1 << 4; /* unrestricted reordering allowed */
285 ptr[8] = 0x75; /* 30000 ms */
290 nbyte = (char *)ptr - &junk[0];
293 if (cdb[0] == MODE_SENSE) {
294 data_len = min(cdb[4], csio->dxfer_len);
296 uint16_t tw = (cdb[7] << 8) | cdb[8];
297 data_len = min(tw, csio->dxfer_len);
299 data_len = min(data_len, nbyte);
301 memcpy(csio->data_ptr, junk, data_len);
303 csio->resid = csio->dxfer_len - data_len;
314 if (vhba_rwparm(cdb, &off, &data_len, DISK_NBLKS, DISK_SHIFT)) {
315 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0);
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);
324 if ((cdb[0] & 0xf) == 8) {
325 memcpy(csio->data_ptr, &vhbas->disk[off], data_len);
327 memcpy(&vhbas->disk[off], csio->data_ptr, data_len);
329 csio->resid = csio->dxfer_len - data_len;
331 csio->resid = csio->dxfer_len;
336 if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) {
337 vhba_fill_sense(csio, SSD_KEY_UNIT_ATTENTION, 0x24, 0x0);
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;
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;
353 csio->data_ptr[0] = 0xff;
354 csio->data_ptr[1] = 0xff;
355 csio->data_ptr[2] = 0xff;
356 csio->data_ptr[3] = 0xff;
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;
365 vhba_default_cmd(csio, MAX_LUN, NULL);
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;
374 csio->scsi_status = SCSI_STATUS_OK;
375 camstatus = CAM_REQ_CMP;
377 vhba_set_status(&csio->ccb_h, camstatus);
382 TAILQ_INSERT_TAIL(&vhbas->inproc, &csio->ccb_h, sim_links.tqe);
384 t.tv_usec = (500 + arc4random());
385 if (t.tv_usec > 10000) {
389 csio->ccb_h.timeout_ch = timeout(vhba_iodelay, &csio->ccb_h, ticks);
391 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
396 vhba_iodelay(void *arg)
398 struct ccb_hdr *ccbh = arg;
399 mptest_t *vhbas = ccbh->sim_priv.entries[0].ptr;
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);
405 vhba_kick(vhbas->vhba);
406 mtx_unlock(&vhbas->vhba->lock);
410 vhba_timer(void *arg)
413 vhba_softc_t *vhba = arg;
414 mptest_t *vhbas = vhba->private;
416 lun = (vhba_stop_lun & 1)? 0 : 1;
417 if (lun == 0 || lun == 1) {
418 if (vhbas->luns[lun]) {
420 if (vhba_notify_stop) {
421 if (xpt_create_path(&tp, xpt_periph, cam_sim_path(vhba->sim), 0, lun) != CAM_REQ_CMP) {
424 vhbas->luns[lun] = 0;
425 xpt_async(AC_LOST_DEVICE, tp, NULL);
428 vhbas->luns[lun] = 0;
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) {
439 ccb = xpt_alloc_ccb_nowait();
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) {
447 vhbas->luns[lun] = 1;
450 vhbas->luns[lun] = 1;
454 vhba_start_lun &= ~(1 << lun);
457 callout_reset(&vhbas->tick, VMP_TIME, vhba_timer, vhba);
459 DEV_MODULE(vhba_mptest, vhba_modprobe, NULL);