]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - tools/tools/vhba/mptest/vhba_mptest.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / tools / tools / vhba / mptest / vhba_mptest.c
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);