diff --git a/tools/tools/vhba/Makefile b/tools/tools/vhba/Makefile new file mode 100644 index 000000000000..01e23733fb20 --- /dev/null +++ b/tools/tools/vhba/Makefile @@ -0,0 +1,29 @@ +# $FreeBSD$ +# +# Copyright (c) 2010 by Panasas Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice immediately at the beginning of the file, without modification, +# this list of conditions, and the following disclaimer. +# 2. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +SUBDIR= simple medium lots faulty rptluns + +.include diff --git a/tools/tools/vhba/README b/tools/tools/vhba/README new file mode 100644 index 000000000000..ced0b5db46f7 --- /dev/null +++ b/tools/tools/vhba/README @@ -0,0 +1,16 @@ +$FreeBSD$ +Tue Jun 8 15:02:02 PDT 2010 + +This packages is a testbed for a number of purposes and consists +of two pieces. + +The first piece is a simple SIM driver for FreeBSD. It provides +*just enough* framework to be useful, plus some nominally common +code responses for code sharing purposes. + +The second piece(s) are underlying implementations which make various +virtual devices implemented under the VHBA itself. The current ones +are pretty much used to stress and test the FreeBSD CAM framework +itself- this is why this is in the tool directory. + +Clearly other connections and possibilities exist as well. diff --git a/tools/tools/vhba/faulty/Makefile b/tools/tools/vhba/faulty/Makefile new file mode 100644 index 000000000000..bbbb158b4102 --- /dev/null +++ b/tools/tools/vhba/faulty/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ +KMOD= vfaulty +SRCS= vhba_faulty.c vhba.c +CFLAGS += -I${.CURDIR}/.. -DVHBA_MOD=\"vfaulty\" +VPATH= ${.CURDIR}/.. + +.include diff --git a/tools/tools/vhba/faulty/vhba_faulty.c b/tools/tools/vhba/faulty/vhba_faulty.c new file mode 100644 index 000000000000..49cbb4b1065e --- /dev/null +++ b/tools/tools/vhba/faulty/vhba_faulty.c @@ -0,0 +1,349 @@ +/*- + * Copyright (c) 2010 by Panasas, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $FreeBSD$ */ +/* + * "Faulty" Device. Victimize random commands with a Selection Timeout. + */ +#include "vhba.h" + +#define MAX_TGT VHBA_MAXTGT +#define MAX_LUN 4 + +#define DISK_SIZE 32 +#define DISK_SHIFT 9 +#define DISK_NBLKS ((DISK_SIZE << 20) >> DISK_SHIFT) +#define PSEUDO_SPT 64 +#define PSEUDO_HDS 64 +#define PSEUDO_SPC (PSEUDO_SPT * PSEUDO_HDS) + +typedef struct { + vhba_softc_t * vhba; + uint8_t * disk; + size_t disk_size; + uint32_t ctr; + uint32_t dead; + struct task qt; +} faulty_t; + +static void vhba_task(void *, int); +static void faulty_act(faulty_t *, struct ccb_scsiio *); + +void +vhba_init(vhba_softc_t *vhba) +{ + static faulty_t vhbastatic; + vhbastatic.vhba = vhba; + vhbastatic.disk_size = DISK_SIZE << 20; + vhbastatic.disk = malloc(vhbastatic.disk_size, M_DEVBUF, M_WAITOK|M_ZERO); + vhba->private = &vhbastatic; + vhbastatic.ctr = (arc4random() & 0xffff) + 1; + TASK_INIT(&vhbastatic.qt, 0, vhba_task, &vhbastatic); +} + + +void +vhba_fini(vhba_softc_t *vhba) +{ + faulty_t *vhbas = vhba->private; + vhba->private = NULL; + free(vhbas->disk, M_DEVBUF); +} + +void +vhba_kick(vhba_softc_t *vhba) +{ + faulty_t *vhbas = vhba->private; + taskqueue_enqueue(taskqueue_swi, &vhbas->qt); +} + +static void +vhba_task(void *arg, int pending) +{ + faulty_t *vhbas = arg; + struct ccb_hdr *ccbh; + + mtx_lock(&vhbas->vhba->lock); + while ((ccbh = TAILQ_FIRST(&vhbas->vhba->actv)) != NULL) { + TAILQ_REMOVE(&vhbas->vhba->actv, ccbh, sim_links.tqe); + faulty_act(vhbas, (struct ccb_scsiio *)ccbh); + if (--vhbas->ctr == 0) { + vhbas->dead = 1; + vhbas->ctr = (arc4random() & 0xff) + 1; + } + } + while ((ccbh = TAILQ_FIRST(&vhbas->vhba->done)) != NULL) { + TAILQ_REMOVE(&vhbas->vhba->done, ccbh, sim_links.tqe); + xpt_done((union ccb *)ccbh); + } + mtx_unlock(&vhbas->vhba->lock); +} + +static void +faulty_act(faulty_t *vhbas, struct ccb_scsiio *csio) +{ + char junk[128]; + cam_status camstatus; + uint8_t *cdb, *ptr, status; + uint32_t data_len; + uint64_t off; + + data_len = 0; + status = SCSI_STATUS_OK; + + memset(&csio->sense_data, 0, sizeof (csio->sense_data)); + cdb = csio->cdb_io.cdb_bytes; + + if (csio->ccb_h.target_id >= MAX_TGT) { + vhba_set_status(&csio->ccb_h, CAM_SEL_TIMEOUT); + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); + return; + } + if (vhbas->dead) { + vhbas->dead = 0; + vhba_set_status(&csio->ccb_h, CAM_SEL_TIMEOUT); + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); + return; + } + if (csio->ccb_h.target_lun >= MAX_LUN && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) { + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0); + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); + return; + } + + switch (cdb[0]) { + case MODE_SENSE: + case MODE_SENSE_10: + { + unsigned int nbyte; + uint8_t page = cdb[2] & SMS_PAGE_CODE; + uint8_t pgctl = cdb[2] & SMS_PAGE_CTRL_MASK; + + switch (page) { + case SMS_FORMAT_DEVICE_PAGE: + case SMS_GEOMETRY_PAGE: + case SMS_CACHE_PAGE: + case SMS_CONTROL_MODE_PAGE: + case SMS_ALL_PAGES_PAGE: + break; + default: + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); + return; + } + memset(junk, 0, sizeof (junk)); + if (cdb[1] & SMS_DBD) { + ptr = &junk[4]; + } else { + ptr = junk; + ptr[3] = 8; + ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; + ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; + ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; + ptr[7] = ((1 << DISK_SHIFT)) & 0xff; + + ptr[8] = (DISK_NBLKS >> 24) & 0xff; + ptr[9] = (DISK_NBLKS >> 16) & 0xff; + ptr[10] = (DISK_NBLKS >> 8) & 0xff; + ptr[11] = DISK_NBLKS & 0xff; + ptr += 12; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_FORMAT_DEVICE_PAGE) { + ptr[0] = SMS_FORMAT_DEVICE_PAGE; + ptr[1] = 24; + if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { + /* tracks per zone */ + /* ptr[2] = 0; */ + /* ptr[3] = 0; */ + /* alternate sectors per zone */ + /* ptr[4] = 0; */ + /* ptr[5] = 0; */ + /* alternate tracks per zone */ + /* ptr[6] = 0; */ + /* ptr[7] = 0; */ + /* alternate tracks per logical unit */ + /* ptr[8] = 0; */ + /* ptr[9] = 0; */ + /* sectors per track */ + ptr[10] = (PSEUDO_SPT >> 8) & 0xff; + ptr[11] = PSEUDO_SPT & 0xff; + /* data bytes per physical sector */ + ptr[12] = ((1 << DISK_SHIFT) >> 8) & 0xff; + ptr[13] = (1 << DISK_SHIFT) & 0xff; + /* interleave */ + /* ptr[14] = 0; */ + /* ptr[15] = 1; */ + /* track skew factor */ + /* ptr[16] = 0; */ + /* ptr[17] = 0; */ + /* cylinder skew factor */ + /* ptr[18] = 0; */ + /* ptr[19] = 0; */ + /* SSRC, HSEC, RMB, SURF */ + } + ptr += 26; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_GEOMETRY_PAGE) { + ptr[0] = SMS_GEOMETRY_PAGE; + ptr[1] = 24; + if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { + uint32_t cyl = (DISK_NBLKS + ((PSEUDO_SPC - 1))) / PSEUDO_SPC; + /* number of cylinders */ + ptr[2] = (cyl >> 24) & 0xff; + ptr[3] = (cyl >> 16) & 0xff; + ptr[4] = cyl & 0xff; + /* number of heads */ + ptr[5] = PSEUDO_HDS; + /* starting cylinder- write precompensation */ + /* ptr[6] = 0; */ + /* ptr[7] = 0; */ + /* ptr[8] = 0; */ + /* starting cylinder- reduced write current */ + /* ptr[9] = 0; */ + /* ptr[10] = 0; */ + /* ptr[11] = 0; */ + /* drive step rate */ + /* ptr[12] = 0; */ + /* ptr[13] = 0; */ + /* landing zone cylinder */ + /* ptr[14] = 0; */ + /* ptr[15] = 0; */ + /* ptr[16] = 0; */ + /* RPL */ + /* ptr[17] = 0; */ + /* rotational offset */ + /* ptr[18] = 0; */ + /* medium rotation rate - 7200 RPM */ + ptr[20] = 0x1c; + ptr[21] = 0x20; + } + ptr += 26; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_CACHE_PAGE) { + ptr[0] = SMS_CACHE_PAGE; + ptr[1] = 18; + ptr[2] = 1 << 2; + ptr += 20; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_CONTROL_MODE_PAGE) { + ptr[0] = SMS_CONTROL_MODE_PAGE; + ptr[1] = 10; + if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { + ptr[3] = 1 << 4; /* unrestricted reordering allowed */ + ptr[8] = 0x75; /* 30000 ms */ + ptr[9] = 0x30; + } + ptr += 12; + } + nbyte = (char *)ptr - &junk[0]; + ptr[0] = nbyte - 4; + + if (cdb[0] == MODE_SENSE) { + data_len = min(cdb[4], csio->dxfer_len); + } else { + uint16_t tw = (cdb[7] << 8) | cdb[8]; + data_len = min(tw, csio->dxfer_len); + } + data_len = min(data_len, nbyte); + if (data_len) { + memcpy(csio->data_ptr, junk, data_len); + } + csio->resid = csio->dxfer_len - data_len; + break; + } + case READ_6: + case READ_10: + case READ_12: + case READ_16: + case WRITE_6: + case WRITE_10: + case WRITE_12: + case WRITE_16: + if (vhba_rwparm(cdb, &off, &data_len, DISK_NBLKS, DISK_SHIFT)) { + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); + break; + } + if (data_len) { + if ((cdb[0] & 0xf) == 8) { + memcpy(csio->data_ptr, &vhbas->disk[off], data_len); + } else { + memcpy(&vhbas->disk[off], csio->data_ptr, data_len); + } + csio->resid = csio->dxfer_len - data_len; + } else { + csio->resid = csio->dxfer_len; + } + break; + + case READ_CAPACITY: + if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) { + vhba_fill_sense(csio, SSD_KEY_UNIT_ATTENTION, 0x24, 0x0); + break; + } + if (cdb[8] & 0x1) { /* PMI */ + csio->data_ptr[0] = 0xff; + csio->data_ptr[1] = 0xff; + csio->data_ptr[2] = 0xff; + csio->data_ptr[3] = 0xff; + } else { + uint64_t last_blk = DISK_NBLKS - 1; + if (last_blk < 0xffffffffULL) { + csio->data_ptr[0] = (last_blk >> 24) & 0xff; + csio->data_ptr[1] = (last_blk >> 16) & 0xff; + csio->data_ptr[2] = (last_blk >> 8) & 0xff; + csio->data_ptr[3] = (last_blk) & 0xff; + } else { + csio->data_ptr[0] = 0xff; + csio->data_ptr[1] = 0xff; + csio->data_ptr[2] = 0xff; + csio->data_ptr[3] = 0xff; + } + } + csio->data_ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; + csio->data_ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; + csio->data_ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; + csio->data_ptr[7] = ((1 << DISK_SHIFT)) & 0xff; + break; + default: + vhba_default_cmd(csio, MAX_LUN, NULL); + break; + } + if (csio->scsi_status != SCSI_STATUS_OK) { + camstatus = CAM_SCSI_STATUS_ERROR; + if (csio->scsi_status == SCSI_STATUS_CHECK_COND) { + camstatus |= CAM_AUTOSNS_VALID; + } + } else { + csio->scsi_status = SCSI_STATUS_OK; + camstatus = CAM_REQ_CMP; + } + vhba_set_status(&csio->ccb_h, camstatus); + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); +} +DEV_MODULE(vhba_faulty, vhba_modprobe, NULL); diff --git a/tools/tools/vhba/lots/Makefile b/tools/tools/vhba/lots/Makefile new file mode 100644 index 000000000000..37c05c234c37 --- /dev/null +++ b/tools/tools/vhba/lots/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ +KMOD= vlots +SRCS= vhba_lots.c vhba.c +CFLAGS += -I${.CURDIR}/.. -DVHBA_MOD=\"vlots\" +VPATH= ${.CURDIR}/.. + +.include diff --git a/tools/tools/vhba/lots/vhba_lots.c b/tools/tools/vhba/lots/vhba_lots.c new file mode 100644 index 000000000000..07126663c42d --- /dev/null +++ b/tools/tools/vhba/lots/vhba_lots.c @@ -0,0 +1,335 @@ +/*- + * Copyright (c) 2010 by Panasas, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $FreeBSD$ */ +/* + * VHBA device that just reate boatloads of devices. + */ +#include "vhba.h" + +#define MAX_TGT VHBA_MAXTGT +#define MAX_LUN 32 + +#define DISK_SIZE 32 +#define DISK_SHIFT 9 +#define DISK_NBLKS ((DISK_SIZE << 20) >> DISK_SHIFT) +#define PSEUDO_SPT 64 +#define PSEUDO_HDS 64 +#define PSEUDO_SPC (PSEUDO_SPT * PSEUDO_HDS) + +typedef struct { + vhba_softc_t * vhba; + uint8_t * disk; + size_t disk_size; + struct task qt; +} vhbalots_t; + +static void vhba_task(void *, int); +static void vhbalots_act(vhbalots_t *, struct ccb_scsiio *); + +void +vhba_init(vhba_softc_t *vhba) +{ + static vhbalots_t vhbas; + vhbas.vhba = vhba; + vhbas.disk_size = DISK_SIZE << 20; + vhbas.disk = malloc(vhbas.disk_size, M_DEVBUF, M_WAITOK|M_ZERO); + vhba->private = &vhbas; + TASK_INIT(&vhbas.qt, 0, vhba_task, &vhbas); +} + + +void +vhba_fini(vhba_softc_t *vhba) +{ + vhbalots_t *vhbas = vhba->private; + vhba->private = NULL; + free(vhbas->disk, M_DEVBUF); +} + +void +vhba_kick(vhba_softc_t *vhba) +{ + vhbalots_t *vhbas = vhba->private; + taskqueue_enqueue(taskqueue_swi, &vhbas->qt); +} + +static void +vhba_task(void *arg, int pending) +{ + vhbalots_t *vhbas = arg; + struct ccb_hdr *ccbh; + + mtx_lock(&vhbas->vhba->lock); + while ((ccbh = TAILQ_FIRST(&vhbas->vhba->actv)) != NULL) { + TAILQ_REMOVE(&vhbas->vhba->actv, ccbh, sim_links.tqe); + vhbalots_act(vhbas, (struct ccb_scsiio *)ccbh); + } + while ((ccbh = TAILQ_FIRST(&vhbas->vhba->done)) != NULL) { + TAILQ_REMOVE(&vhbas->vhba->done, ccbh, sim_links.tqe); + xpt_done((union ccb *)ccbh); + } + mtx_unlock(&vhbas->vhba->lock); +} + +static void +vhbalots_act(vhbalots_t *vhbas, struct ccb_scsiio *csio) +{ + char junk[128]; + uint8_t *cdb, *ptr, status; + uint32_t data_len; + uint64_t off; + + data_len = 0; + status = SCSI_STATUS_OK; + + memset(&csio->sense_data, 0, sizeof (csio->sense_data)); + cdb = csio->cdb_io.cdb_bytes; + + if (csio->ccb_h.target_id >= MAX_TGT) { + csio->ccb_h.status = CAM_SEL_TIMEOUT; + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); + return; + } + if (csio->ccb_h.target_lun >= MAX_LUN && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) { + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0); + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); + return; + } + + switch (cdb[0]) { + case MODE_SENSE: + case MODE_SENSE_10: + { + unsigned int nbyte; + uint8_t page = cdb[2] & SMS_PAGE_CODE; + uint8_t pgctl = cdb[2] & SMS_PAGE_CTRL_MASK; + + switch (page) { + case SMS_FORMAT_DEVICE_PAGE: + case SMS_GEOMETRY_PAGE: + case SMS_CACHE_PAGE: + case SMS_CONTROL_MODE_PAGE: + case SMS_ALL_PAGES_PAGE: + break; + default: + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); + return; + } + memset(junk, 0, sizeof (junk)); + if (cdb[1] & SMS_DBD) { + ptr = &junk[4]; + } else { + ptr = junk; + ptr[3] = 8; + ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; + ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; + ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; + ptr[7] = ((1 << DISK_SHIFT)) & 0xff; + + ptr[8] = (DISK_NBLKS >> 24) & 0xff; + ptr[9] = (DISK_NBLKS >> 16) & 0xff; + ptr[10] = (DISK_NBLKS >> 8) & 0xff; + ptr[11] = DISK_NBLKS & 0xff; + ptr += 12; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_FORMAT_DEVICE_PAGE) { + ptr[0] = SMS_FORMAT_DEVICE_PAGE; + ptr[1] = 24; + if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { + /* tracks per zone */ + /* ptr[2] = 0; */ + /* ptr[3] = 0; */ + /* alternate sectors per zone */ + /* ptr[4] = 0; */ + /* ptr[5] = 0; */ + /* alternate tracks per zone */ + /* ptr[6] = 0; */ + /* ptr[7] = 0; */ + /* alternate tracks per logical unit */ + /* ptr[8] = 0; */ + /* ptr[9] = 0; */ + /* sectors per track */ + ptr[10] = (PSEUDO_SPT >> 8) & 0xff; + ptr[11] = PSEUDO_SPT & 0xff; + /* data bytes per physical sector */ + ptr[12] = ((1 << DISK_SHIFT) >> 8) & 0xff; + ptr[13] = (1 << DISK_SHIFT) & 0xff; + /* interleave */ + /* ptr[14] = 0; */ + /* ptr[15] = 1; */ + /* track skew factor */ + /* ptr[16] = 0; */ + /* ptr[17] = 0; */ + /* cylinder skew factor */ + /* ptr[18] = 0; */ + /* ptr[19] = 0; */ + /* SSRC, HSEC, RMB, SURF */ + } + ptr += 26; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_GEOMETRY_PAGE) { + ptr[0] = SMS_GEOMETRY_PAGE; + ptr[1] = 24; + if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { + uint32_t cyl = (DISK_NBLKS + ((PSEUDO_SPC - 1))) / PSEUDO_SPC; + /* number of cylinders */ + ptr[2] = (cyl >> 24) & 0xff; + ptr[3] = (cyl >> 16) & 0xff; + ptr[4] = cyl & 0xff; + /* number of heads */ + ptr[5] = PSEUDO_HDS; + /* starting cylinder- write precompensation */ + /* ptr[6] = 0; */ + /* ptr[7] = 0; */ + /* ptr[8] = 0; */ + /* starting cylinder- reduced write current */ + /* ptr[9] = 0; */ + /* ptr[10] = 0; */ + /* ptr[11] = 0; */ + /* drive step rate */ + /* ptr[12] = 0; */ + /* ptr[13] = 0; */ + /* landing zone cylinder */ + /* ptr[14] = 0; */ + /* ptr[15] = 0; */ + /* ptr[16] = 0; */ + /* RPL */ + /* ptr[17] = 0; */ + /* rotational offset */ + /* ptr[18] = 0; */ + /* medium rotation rate - 7200 RPM */ + ptr[20] = 0x1c; + ptr[21] = 0x20; + } + ptr += 26; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_CACHE_PAGE) { + ptr[0] = SMS_CACHE_PAGE; + ptr[1] = 18; + ptr[2] = 1 << 2; + ptr += 20; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_CONTROL_MODE_PAGE) { + ptr[0] = SMS_CONTROL_MODE_PAGE; + ptr[1] = 10; + if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { + ptr[3] = 1 << 4; /* unrestricted reordering allowed */ + ptr[8] = 0x75; /* 30000 ms */ + ptr[9] = 0x30; + } + ptr += 12; + } + nbyte = (char *)ptr - &junk[0]; + ptr[0] = nbyte - 4; + + if (cdb[0] == MODE_SENSE) { + data_len = min(cdb[4], csio->dxfer_len); + } else { + uint16_t tw = (cdb[7] << 8) | cdb[8]; + data_len = min(tw, csio->dxfer_len); + } + data_len = min(data_len, nbyte); + if (data_len) { + memcpy(csio->data_ptr, junk, data_len); + } + csio->resid = csio->dxfer_len - data_len; + break; + } + case READ_6: + case READ_10: + case READ_12: + case READ_16: + case WRITE_6: + case WRITE_10: + case WRITE_12: + case WRITE_16: + if (vhba_rwparm(cdb, &off, &data_len, DISK_NBLKS, DISK_SHIFT)) { + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); + break; + } + if (data_len) { + if ((cdb[0] & 0xf) == 8) { + memcpy(csio->data_ptr, &vhbas->disk[off], data_len); + } else { + memcpy(&vhbas->disk[off], csio->data_ptr, data_len); + } + csio->resid = csio->dxfer_len - data_len; + } else { + csio->resid = csio->dxfer_len; + } + break; + + case READ_CAPACITY: + if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) { + vhba_fill_sense(csio, SSD_KEY_UNIT_ATTENTION, 0x24, 0x0); + break; + } + if (cdb[8] & 0x1) { /* PMI */ + csio->data_ptr[0] = 0xff; + csio->data_ptr[1] = 0xff; + csio->data_ptr[2] = 0xff; + csio->data_ptr[3] = 0xff; + } else { + uint64_t last_blk = DISK_NBLKS - 1; + if (last_blk < 0xffffffffULL) { + csio->data_ptr[0] = (last_blk >> 24) & 0xff; + csio->data_ptr[1] = (last_blk >> 16) & 0xff; + csio->data_ptr[2] = (last_blk >> 8) & 0xff; + csio->data_ptr[3] = (last_blk) & 0xff; + } else { + csio->data_ptr[0] = 0xff; + csio->data_ptr[1] = 0xff; + csio->data_ptr[2] = 0xff; + csio->data_ptr[3] = 0xff; + } + } + csio->data_ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; + csio->data_ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; + csio->data_ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; + csio->data_ptr[7] = ((1 << DISK_SHIFT)) & 0xff; + break; + default: + vhba_default_cmd(csio, MAX_LUN, NULL); + break; + } + csio->ccb_h.status &= ~CAM_STATUS_MASK; + if (csio->scsi_status != SCSI_STATUS_OK) { + csio->ccb_h.status |= CAM_SCSI_STATUS_ERROR; + if (csio->scsi_status == SCSI_STATUS_CHECK_COND) { + csio->ccb_h.status |= CAM_AUTOSNS_VALID; + } + } else { + csio->scsi_status = SCSI_STATUS_OK; + csio->ccb_h.status |= CAM_REQ_CMP; + } + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); +} +DEV_MODULE(vhba_lots, vhba_modprobe, NULL); diff --git a/tools/tools/vhba/medium/Makefile b/tools/tools/vhba/medium/Makefile new file mode 100644 index 000000000000..091e1399aa3a --- /dev/null +++ b/tools/tools/vhba/medium/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ +KMOD= vmedium +SRCS= vhba_medium.c vhba.c +CFLAGS += -I${.CURDIR}/.. -DVHBA_MOD=\"vmedium\" +VPATH= ${.CURDIR}/.. + +.include diff --git a/tools/tools/vhba/medium/vhba_medium.c b/tools/tools/vhba/medium/vhba_medium.c new file mode 100644 index 000000000000..2fc9f926af78 --- /dev/null +++ b/tools/tools/vhba/medium/vhba_medium.c @@ -0,0 +1,337 @@ +/*- + * Copyright (c) 2010 by Panasas, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $FreeBSD$ */ +/* + * A VHBA device that has a medium number of device. + */ +#include "vhba.h" + +#define MAX_TGT 4 +#define MAX_LUN 10 + +#define DISK_SIZE 32 +#define DISK_SHIFT 9 +#define DISK_NBLKS ((DISK_SIZE << 20) >> DISK_SHIFT) +#define PSEUDO_SPT 64 +#define PSEUDO_HDS 64 +#define PSEUDO_SPC (PSEUDO_SPT * PSEUDO_HDS) + +typedef struct { + vhba_softc_t * vhba; + uint8_t * disk; + size_t disk_size; + struct task qt; +} vhbamedium_t; + +static void vhba_task(void *, int); +static void vhbamedium_act(vhbamedium_t *, struct ccb_scsiio *); + +void +vhba_init(vhba_softc_t *vhba) +{ + static vhbamedium_t vhbas; + vhbas.vhba = vhba; + vhbas.disk_size = DISK_SIZE << 20; + vhbas.disk = malloc(vhbas.disk_size, M_DEVBUF, M_WAITOK|M_ZERO); + vhba->private = &vhbas; + TASK_INIT(&vhbas.qt, 0, vhba_task, &vhbas); +} + + +void +vhba_fini(vhba_softc_t *vhba) +{ + vhbamedium_t *vhbas = vhba->private; + vhba->private = NULL; + free(vhbas->disk, M_DEVBUF); +} + +void +vhba_kick(vhba_softc_t *vhba) +{ + vhbamedium_t *vhbas = vhba->private; + taskqueue_enqueue(taskqueue_swi, &vhbas->qt); +} + +static void +vhba_task(void *arg, int pending) +{ + vhbamedium_t *vhbas = arg; + struct ccb_hdr *ccbh; + + mtx_lock(&vhbas->vhba->lock); + while ((ccbh = TAILQ_FIRST(&vhbas->vhba->actv)) != NULL) { + TAILQ_REMOVE(&vhbas->vhba->actv, ccbh, sim_links.tqe); + vhbamedium_act(vhbas, (struct ccb_scsiio *)ccbh); + } + while ((ccbh = TAILQ_FIRST(&vhbas->vhba->done)) != NULL) { + TAILQ_REMOVE(&vhbas->vhba->done, ccbh, sim_links.tqe); + xpt_done((union ccb *)ccbh); + } + mtx_unlock(&vhbas->vhba->lock); +} + +static void +vhbamedium_act(vhbamedium_t *vhbas, struct ccb_scsiio *csio) +{ + char junk[128]; + uint8_t *cdb, *ptr, status; + uint32_t data_len; + uint64_t off; + + data_len = 0; + status = SCSI_STATUS_OK; + + memset(&csio->sense_data, 0, sizeof (csio->sense_data)); + cdb = csio->cdb_io.cdb_bytes; + + if (csio->ccb_h.target_id >= MAX_TGT) { + csio->ccb_h.status = CAM_SEL_TIMEOUT; + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); + return; + } + if (csio->ccb_h.target_lun >= MAX_LUN && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) { + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0); + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); + return; + } + + switch (cdb[0]) { + case MODE_SENSE: + case MODE_SENSE_10: + { + unsigned int nbyte; + uint8_t page = cdb[2] & SMS_PAGE_CODE; + uint8_t pgctl = cdb[2] & SMS_PAGE_CTRL_MASK; + + switch (page) { + case SMS_FORMAT_DEVICE_PAGE: + case SMS_GEOMETRY_PAGE: + case SMS_CACHE_PAGE: + case SMS_CONTROL_MODE_PAGE: + case SMS_ALL_PAGES_PAGE: + break; + default: + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); + return; + } + memset(junk, 0, sizeof (junk)); + if (cdb[1] & SMS_DBD) { + ptr = &junk[4]; + } else { + ptr = junk; + ptr[3] = 8; + ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; + ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; + ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; + ptr[7] = ((1 << DISK_SHIFT)) & 0xff; + + ptr[8] = (DISK_NBLKS >> 24) & 0xff; + ptr[9] = (DISK_NBLKS >> 16) & 0xff; + ptr[10] = (DISK_NBLKS >> 8) & 0xff; + ptr[11] = DISK_NBLKS & 0xff; + ptr += 12; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_FORMAT_DEVICE_PAGE) { + ptr[0] = SMS_FORMAT_DEVICE_PAGE; + ptr[1] = 24; + if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { + /* tracks per zone */ + /* ptr[2] = 0; */ + /* ptr[3] = 0; */ + /* alternate sectors per zone */ + /* ptr[4] = 0; */ + /* ptr[5] = 0; */ + /* alternate tracks per zone */ + /* ptr[6] = 0; */ + /* ptr[7] = 0; */ + /* alternate tracks per logical unit */ + /* ptr[8] = 0; */ + /* ptr[9] = 0; */ + /* sectors per track */ + ptr[10] = (PSEUDO_SPT >> 8) & 0xff; + ptr[11] = PSEUDO_SPT & 0xff; + /* data bytes per physical sector */ + ptr[12] = ((1 << DISK_SHIFT) >> 8) & 0xff; + ptr[13] = (1 << DISK_SHIFT) & 0xff; + /* interleave */ + /* ptr[14] = 0; */ + /* ptr[15] = 1; */ + /* track skew factor */ + /* ptr[16] = 0; */ + /* ptr[17] = 0; */ + /* cylinder skew factor */ + /* ptr[18] = 0; */ + /* ptr[19] = 0; */ + /* SSRC, HSEC, RMB, SURF */ + } + ptr += 26; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_GEOMETRY_PAGE) { + ptr[0] = SMS_GEOMETRY_PAGE; + ptr[1] = 24; + if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { + uint32_t cyl = (DISK_NBLKS + ((PSEUDO_SPC - 1))) / PSEUDO_SPC; + /* number of cylinders */ + ptr[2] = (cyl >> 24) & 0xff; + ptr[3] = (cyl >> 16) & 0xff; + ptr[4] = cyl & 0xff; + /* number of heads */ + ptr[5] = PSEUDO_HDS; + /* starting cylinder- write precompensation */ + /* ptr[6] = 0; */ + /* ptr[7] = 0; */ + /* ptr[8] = 0; */ + /* starting cylinder- reduced write current */ + /* ptr[9] = 0; */ + /* ptr[10] = 0; */ + /* ptr[11] = 0; */ + /* drive step rate */ + /* ptr[12] = 0; */ + /* ptr[13] = 0; */ + /* landing zone cylinder */ + /* ptr[14] = 0; */ + /* ptr[15] = 0; */ + /* ptr[16] = 0; */ + /* RPL */ + /* ptr[17] = 0; */ + /* rotational offset */ + /* ptr[18] = 0; */ + /* medium rotation rate - 7200 RPM */ + ptr[20] = 0x1c; + ptr[21] = 0x20; + } + ptr += 26; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_CACHE_PAGE) { + ptr[0] = SMS_CACHE_PAGE; + ptr[1] = 18; + ptr[2] = 1 << 2; + ptr += 20; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_CONTROL_MODE_PAGE) { + ptr[0] = SMS_CONTROL_MODE_PAGE; + ptr[1] = 10; + if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { + ptr[3] = 1 << 4; /* unrestricted reordering allowed */ + ptr[8] = 0x75; /* 30000 ms */ + ptr[9] = 0x30; + } + ptr += 12; + } + nbyte = (char *)ptr - &junk[0]; + ptr[0] = nbyte - 4; + + if (cdb[0] == MODE_SENSE) { + data_len = min(cdb[4], csio->dxfer_len); + } else { + uint16_t tw = (cdb[7] << 8) | cdb[8]; + data_len = min(tw, csio->dxfer_len); + } + data_len = min(data_len, nbyte); + if (data_len) { + memcpy(csio->data_ptr, junk, data_len); + } + csio->resid = csio->dxfer_len - data_len; + break; + } + case READ_6: + case READ_10: + case READ_12: + case READ_16: + case WRITE_6: + case WRITE_10: + case WRITE_12: + case WRITE_16: + if (vhba_rwparm(cdb, &off, &data_len, DISK_NBLKS, DISK_SHIFT)) { + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); + break; + } + if (data_len) { + if ((cdb[0] & 0xf) == 8) { + memcpy(csio->data_ptr, &vhbas->disk[off], data_len); + } else { + memcpy(&vhbas->disk[off], csio->data_ptr, data_len); + } + csio->resid = csio->dxfer_len - data_len; + } else { + csio->resid = csio->dxfer_len; + } + break; + break; + + case READ_CAPACITY: + if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) { + vhba_fill_sense(csio, SSD_KEY_UNIT_ATTENTION, 0x24, 0x0); + break; + } + if (cdb[8] & 0x1) { /* PMI */ + csio->data_ptr[0] = 0xff; + csio->data_ptr[1] = 0xff; + csio->data_ptr[2] = 0xff; + csio->data_ptr[3] = 0xff; + } else { + uint64_t last_blk = DISK_NBLKS - 1; + if (last_blk < 0xffffffffULL) { + csio->data_ptr[0] = (last_blk >> 24) & 0xff; + csio->data_ptr[1] = (last_blk >> 16) & 0xff; + csio->data_ptr[2] = (last_blk >> 8) & 0xff; + csio->data_ptr[3] = (last_blk) & 0xff; + } else { + csio->data_ptr[0] = 0xff; + csio->data_ptr[1] = 0xff; + csio->data_ptr[2] = 0xff; + csio->data_ptr[3] = 0xff; + } + } + csio->data_ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; + csio->data_ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; + csio->data_ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; + csio->data_ptr[7] = ((1 << DISK_SHIFT)) & 0xff; + break; + + default: + vhba_default_cmd(csio, MAX_LUN, NULL); + break; + } + csio->ccb_h.status &= ~CAM_STATUS_MASK; + if (csio->scsi_status != SCSI_STATUS_OK) { + csio->ccb_h.status |= CAM_SCSI_STATUS_ERROR; + if (csio->scsi_status == SCSI_STATUS_CHECK_COND) { + csio->ccb_h.status |= CAM_AUTOSNS_VALID; + } + } else { + csio->scsi_status = SCSI_STATUS_OK; + csio->ccb_h.status |= CAM_REQ_CMP; + } + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); +} +DEV_MODULE(vhba_mediaum, vhba_modprobe, NULL); diff --git a/tools/tools/vhba/opt_cam.h b/tools/tools/vhba/opt_cam.h new file mode 100644 index 000000000000..da23dbe43a4f --- /dev/null +++ b/tools/tools/vhba/opt_cam.h @@ -0,0 +1 @@ +/* $FreeBSD$ */ diff --git a/tools/tools/vhba/rptluns/Makefile b/tools/tools/vhba/rptluns/Makefile new file mode 100644 index 000000000000..a7d81fbc07d8 --- /dev/null +++ b/tools/tools/vhba/rptluns/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ +KMOD= vrptluns +SRCS= vhba_rptluns.c vhba.c +CFLAGS += -I${.CURDIR}/.. -DVHBA_MOD=\"vrptluns\" +VPATH= ${.CURDIR}/.. + +.include diff --git a/tools/tools/vhba/rptluns/vhba_rptluns.c b/tools/tools/vhba/rptluns/vhba_rptluns.c new file mode 100644 index 000000000000..28424c4facd3 --- /dev/null +++ b/tools/tools/vhba/rptluns/vhba_rptluns.c @@ -0,0 +1,366 @@ +/*- + * Copyright (c) 2010 by Panasas, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $FreeBSD$ */ +/* + * A VHBA device to test REPORT LUN functionality. + */ +#include "vhba.h" + +#define MAX_TGT 1 +#define MAX_LUN 1024 + +#define DISK_SIZE 32 +#define DISK_SHIFT 9 +#define DISK_NBLKS ((DISK_SIZE << 20) >> DISK_SHIFT) +#define PSEUDO_SPT 64 +#define PSEUDO_HDS 64 +#define PSEUDO_SPC (PSEUDO_SPT * PSEUDO_HDS) + +typedef struct { + vhba_softc_t * vhba; + uint8_t * disk; + size_t disk_size; + struct task qt; + uint8_t rpbitmap[MAX_LUN >> 3]; +} vhbarptluns_t; + +static void vhba_task(void *, int); +static void vhbarptluns_act(vhbarptluns_t *, struct ccb_scsiio *); + +void +vhba_init(vhba_softc_t *vhba) +{ + static vhbarptluns_t vhbas; + struct timeval now; + int i; + + vhbas.vhba = vhba; + vhbas.disk_size = DISK_SIZE << 20; + vhbas.disk = malloc(vhbas.disk_size, M_DEVBUF, M_WAITOK|M_ZERO); + vhba->private = &vhbas; + printf("setting luns"); + getmicrotime(&now); + if (now.tv_usec & 0x1) { + vhbas.rpbitmap[0] |= 1; + } + for (i = 1; i < 8; i++) { + if (arc4random() & 1) { + printf(" %d", i); + vhbas.rpbitmap[0] |= (1 << i); + } + } + for (i = 8; i < MAX_LUN; i++) { + if ((arc4random() % i) == 0) { + vhbas.rpbitmap[i >> 3] |= (1 << (i & 0x7)); + printf(" %d", i); + } + } + printf("\n"); + TASK_INIT(&vhbas.qt, 0, vhba_task, &vhbas); +} + +void +vhba_fini(vhba_softc_t *vhba) +{ + vhbarptluns_t *vhbas = vhba->private; + vhba->private = NULL; + free(vhbas->disk, M_DEVBUF); +} + +void +vhba_kick(vhba_softc_t *vhba) +{ + vhbarptluns_t *vhbas = vhba->private; + taskqueue_enqueue(taskqueue_swi, &vhbas->qt); +} + +static void +vhba_task(void *arg, int pending) +{ + vhbarptluns_t *vhbas = arg; + struct ccb_hdr *ccbh; + + mtx_lock(&vhbas->vhba->lock); + while ((ccbh = TAILQ_FIRST(&vhbas->vhba->actv)) != NULL) { + TAILQ_REMOVE(&vhbas->vhba->actv, ccbh, sim_links.tqe); + vhbarptluns_act(vhbas, (struct ccb_scsiio *)ccbh); + } + while ((ccbh = TAILQ_FIRST(&vhbas->vhba->done)) != NULL) { + TAILQ_REMOVE(&vhbas->vhba->done, ccbh, sim_links.tqe); + xpt_done((union ccb *)ccbh); + } + mtx_unlock(&vhbas->vhba->lock); +} + +static void +vhbarptluns_act(vhbarptluns_t *vhbas, struct ccb_scsiio *csio) +{ + char junk[128]; + uint8_t *cdb, *ptr, status; + uint32_t data_len; + uint64_t off; + int i, attached_lun = 0; + + data_len = 0; + status = SCSI_STATUS_OK; + + memset(&csio->sense_data, 0, sizeof (csio->sense_data)); + cdb = csio->cdb_io.cdb_bytes; + + if (csio->ccb_h.target_id >= MAX_TGT) { + csio->ccb_h.status = CAM_SEL_TIMEOUT; + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); + return; + } + + if (csio->ccb_h.target_lun < MAX_LUN) { + i = csio->ccb_h.target_lun & 0x7; + if (vhbas->rpbitmap[csio->ccb_h.target_lun >> 3] & (1 << i)) { + attached_lun = 1; + } + } + if (attached_lun == 0 && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) { + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0); + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); + return; + } + + switch (cdb[0]) { + case MODE_SENSE: + case MODE_SENSE_10: + { + unsigned int nbyte; + uint8_t page = cdb[2] & SMS_PAGE_CODE; + uint8_t pgctl = cdb[2] & SMS_PAGE_CTRL_MASK; + + switch (page) { + case SMS_FORMAT_DEVICE_PAGE: + case SMS_GEOMETRY_PAGE: + case SMS_CACHE_PAGE: + case SMS_CONTROL_MODE_PAGE: + case SMS_ALL_PAGES_PAGE: + break; + default: + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); + return; + } + memset(junk, 0, sizeof (junk)); + if (cdb[1] & SMS_DBD) { + ptr = &junk[4]; + } else { + ptr = junk; + ptr[3] = 8; + ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; + ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; + ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; + ptr[7] = ((1 << DISK_SHIFT)) & 0xff; + + ptr[8] = (DISK_NBLKS >> 24) & 0xff; + ptr[9] = (DISK_NBLKS >> 16) & 0xff; + ptr[10] = (DISK_NBLKS >> 8) & 0xff; + ptr[11] = DISK_NBLKS & 0xff; + ptr += 12; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_FORMAT_DEVICE_PAGE) { + ptr[0] = SMS_FORMAT_DEVICE_PAGE; + ptr[1] = 24; + if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { + /* tracks per zone */ + /* ptr[2] = 0; */ + /* ptr[3] = 0; */ + /* alternate sectors per zone */ + /* ptr[4] = 0; */ + /* ptr[5] = 0; */ + /* alternate tracks per zone */ + /* ptr[6] = 0; */ + /* ptr[7] = 0; */ + /* alternate tracks per logical unit */ + /* ptr[8] = 0; */ + /* ptr[9] = 0; */ + /* sectors per track */ + ptr[10] = (PSEUDO_SPT >> 8) & 0xff; + ptr[11] = PSEUDO_SPT & 0xff; + /* data bytes per physical sector */ + ptr[12] = ((1 << DISK_SHIFT) >> 8) & 0xff; + ptr[13] = (1 << DISK_SHIFT) & 0xff; + /* interleave */ + /* ptr[14] = 0; */ + /* ptr[15] = 1; */ + /* track skew factor */ + /* ptr[16] = 0; */ + /* ptr[17] = 0; */ + /* cylinder skew factor */ + /* ptr[18] = 0; */ + /* ptr[19] = 0; */ + /* SSRC, HSEC, RMB, SURF */ + } + ptr += 26; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_GEOMETRY_PAGE) { + ptr[0] = SMS_GEOMETRY_PAGE; + ptr[1] = 24; + if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { + uint32_t cyl = (DISK_NBLKS + ((PSEUDO_SPC - 1))) / PSEUDO_SPC; + /* number of cylinders */ + ptr[2] = (cyl >> 24) & 0xff; + ptr[3] = (cyl >> 16) & 0xff; + ptr[4] = cyl & 0xff; + /* number of heads */ + ptr[5] = PSEUDO_HDS; + /* starting cylinder- write precompensation */ + /* ptr[6] = 0; */ + /* ptr[7] = 0; */ + /* ptr[8] = 0; */ + /* starting cylinder- reduced write current */ + /* ptr[9] = 0; */ + /* ptr[10] = 0; */ + /* ptr[11] = 0; */ + /* drive step rate */ + /* ptr[12] = 0; */ + /* ptr[13] = 0; */ + /* landing zone cylinder */ + /* ptr[14] = 0; */ + /* ptr[15] = 0; */ + /* ptr[16] = 0; */ + /* RPL */ + /* ptr[17] = 0; */ + /* rotational offset */ + /* ptr[18] = 0; */ + /* medium rotation rate - 7200 RPM */ + ptr[20] = 0x1c; + ptr[21] = 0x20; + } + ptr += 26; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_CACHE_PAGE) { + ptr[0] = SMS_CACHE_PAGE; + ptr[1] = 18; + ptr[2] = 1 << 2; + ptr += 20; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_CONTROL_MODE_PAGE) { + ptr[0] = SMS_CONTROL_MODE_PAGE; + ptr[1] = 10; + if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { + ptr[3] = 1 << 4; /* unrestricted reordering allowed */ + ptr[8] = 0x75; /* 30000 ms */ + ptr[9] = 0x30; + } + ptr += 12; + } + nbyte = (char *)ptr - &junk[0]; + ptr[0] = nbyte - 4; + + if (cdb[0] == MODE_SENSE) { + data_len = min(cdb[4], csio->dxfer_len); + } else { + uint16_t tw = (cdb[7] << 8) | cdb[8]; + data_len = min(tw, csio->dxfer_len); + } + data_len = min(data_len, nbyte); + if (data_len) { + memcpy(csio->data_ptr, junk, data_len); + } + csio->resid = csio->dxfer_len - data_len; + break; + } + case READ_6: + case READ_10: + case READ_12: + case READ_16: + case WRITE_6: + case WRITE_10: + case WRITE_12: + case WRITE_16: + if (vhba_rwparm(cdb, &off, &data_len, DISK_NBLKS, DISK_SHIFT)) { + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); + break; + } + if (data_len) { + if ((cdb[0] & 0xf) == 8) { + memcpy(csio->data_ptr, &vhbas->disk[off], data_len); + } else { + memcpy(&vhbas->disk[off], csio->data_ptr, data_len); + } + csio->resid = csio->dxfer_len - data_len; + } else { + csio->resid = csio->dxfer_len; + } + break; + break; + + case READ_CAPACITY: + if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) { + vhba_fill_sense(csio, SSD_KEY_UNIT_ATTENTION, 0x24, 0x0); + break; + } + if (cdb[8] & 0x1) { /* PMI */ + csio->data_ptr[0] = 0xff; + csio->data_ptr[1] = 0xff; + csio->data_ptr[2] = 0xff; + csio->data_ptr[3] = 0xff; + } else { + uint64_t last_blk = DISK_NBLKS - 1; + if (last_blk < 0xffffffffULL) { + csio->data_ptr[0] = (last_blk >> 24) & 0xff; + csio->data_ptr[1] = (last_blk >> 16) & 0xff; + csio->data_ptr[2] = (last_blk >> 8) & 0xff; + csio->data_ptr[3] = (last_blk) & 0xff; + } else { + csio->data_ptr[0] = 0xff; + csio->data_ptr[1] = 0xff; + csio->data_ptr[2] = 0xff; + csio->data_ptr[3] = 0xff; + } + } + csio->data_ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; + csio->data_ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; + csio->data_ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; + csio->data_ptr[7] = ((1 << DISK_SHIFT)) & 0xff; + break; + + default: + vhba_default_cmd(csio, MAX_LUN, vhbas->rpbitmap); + break; + } + csio->ccb_h.status &= ~CAM_STATUS_MASK; + if (csio->scsi_status != SCSI_STATUS_OK) { + csio->ccb_h.status |= CAM_SCSI_STATUS_ERROR; + if (csio->scsi_status == SCSI_STATUS_CHECK_COND) { + csio->ccb_h.status |= CAM_AUTOSNS_VALID; + } + } else { + csio->scsi_status = SCSI_STATUS_OK; + csio->ccb_h.status |= CAM_REQ_CMP; + } + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); +} +DEV_MODULE(vhba_rtpluns, vhba_modprobe, NULL); diff --git a/tools/tools/vhba/simple/Makefile b/tools/tools/vhba/simple/Makefile new file mode 100644 index 000000000000..5aa9950a6e60 --- /dev/null +++ b/tools/tools/vhba/simple/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ +KMOD= vsimple +SRCS= vhba_simple.c vhba.c +CFLAGS += -I${.CURDIR}/.. -DVHBA_MOD=\"vsimple\" +VPATH= ${.CURDIR}/.. + +.include diff --git a/tools/tools/vhba/simple/vhba_simple.c b/tools/tools/vhba/simple/vhba_simple.c new file mode 100644 index 000000000000..bc40bea77f8d --- /dev/null +++ b/tools/tools/vhba/simple/vhba_simple.c @@ -0,0 +1,337 @@ +/*- + * Copyright (c) 2010 by Panasas, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $FreeBSD$ */ +/* + * "Simple" VHBA device + */ +#include "vhba.h" + +#define MAX_TGT 1 +#define MAX_LUN 1 + +#define DISK_SIZE 32 +#define DISK_SHIFT 9 +#define DISK_NBLKS ((DISK_SIZE << 20) >> DISK_SHIFT) +#define PSEUDO_SPT 64 +#define PSEUDO_HDS 64 +#define PSEUDO_SPC (PSEUDO_SPT * PSEUDO_HDS) + +typedef struct { + vhba_softc_t * vhba; + uint8_t * disk; + size_t disk_size; + struct task qt; +} vhbasimple_t; + +static void vhba_task(void *, int); +static void vhbasimple_act(vhbasimple_t *, struct ccb_scsiio *); + +void +vhba_init(vhba_softc_t *vhba) +{ + static vhbasimple_t vhbas; + vhbas.vhba = vhba; + vhbas.disk_size = DISK_SIZE << 20; + vhbas.disk = malloc(vhbas.disk_size, M_DEVBUF, M_WAITOK|M_ZERO); + vhba->private = &vhbas; + TASK_INIT(&vhbas.qt, 0, vhba_task, &vhbas); +} + + +void +vhba_fini(vhba_softc_t *vhba) +{ + vhbasimple_t *vhbas = vhba->private; + vhba->private = NULL; + free(vhbas->disk, M_DEVBUF); +} + +void +vhba_kick(vhba_softc_t *vhba) +{ + vhbasimple_t *vhbas = vhba->private; + taskqueue_enqueue(taskqueue_swi, &vhbas->qt); +} + +static void +vhba_task(void *arg, int pending) +{ + vhbasimple_t *vhbas = arg; + struct ccb_hdr *ccbh; + + mtx_lock(&vhbas->vhba->lock); + while ((ccbh = TAILQ_FIRST(&vhbas->vhba->actv)) != NULL) { + TAILQ_REMOVE(&vhbas->vhba->actv, ccbh, sim_links.tqe); + vhbasimple_act(vhbas, (struct ccb_scsiio *)ccbh); + } + while ((ccbh = TAILQ_FIRST(&vhbas->vhba->done)) != NULL) { + TAILQ_REMOVE(&vhbas->vhba->done, ccbh, sim_links.tqe); + xpt_done((union ccb *)ccbh); + } + mtx_unlock(&vhbas->vhba->lock); +} + +static void +vhbasimple_act(vhbasimple_t *vhbas, struct ccb_scsiio *csio) +{ + char junk[128]; + uint8_t *cdb, *ptr, status; + uint32_t data_len; + uint64_t off; + + data_len = 0; + status = SCSI_STATUS_OK; + + memset(&csio->sense_data, 0, sizeof (csio->sense_data)); + cdb = csio->cdb_io.cdb_bytes; + + if (csio->ccb_h.target_id >= MAX_TGT) { + csio->ccb_h.status = CAM_SEL_TIMEOUT; + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); + return; + } + if (csio->ccb_h.target_lun >= MAX_LUN && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) { + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0); + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); + return; + } + + switch (cdb[0]) { + case MODE_SENSE: + case MODE_SENSE_10: + { + unsigned int nbyte; + uint8_t page = cdb[2] & SMS_PAGE_CODE; + uint8_t pgctl = cdb[2] & SMS_PAGE_CTRL_MASK; + + switch (page) { + case SMS_FORMAT_DEVICE_PAGE: + case SMS_GEOMETRY_PAGE: + case SMS_CACHE_PAGE: + case SMS_CONTROL_MODE_PAGE: + case SMS_ALL_PAGES_PAGE: + break; + default: + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); + return; + } + memset(junk, 0, sizeof (junk)); + if (cdb[1] & SMS_DBD) { + ptr = &junk[4]; + } else { + ptr = junk; + ptr[3] = 8; + ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; + ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; + ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; + ptr[7] = ((1 << DISK_SHIFT)) & 0xff; + + ptr[8] = (DISK_NBLKS >> 24) & 0xff; + ptr[9] = (DISK_NBLKS >> 16) & 0xff; + ptr[10] = (DISK_NBLKS >> 8) & 0xff; + ptr[11] = DISK_NBLKS & 0xff; + ptr += 12; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_FORMAT_DEVICE_PAGE) { + ptr[0] = SMS_FORMAT_DEVICE_PAGE; + ptr[1] = 24; + if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { + /* tracks per zone */ + /* ptr[2] = 0; */ + /* ptr[3] = 0; */ + /* alternate sectors per zone */ + /* ptr[4] = 0; */ + /* ptr[5] = 0; */ + /* alternate tracks per zone */ + /* ptr[6] = 0; */ + /* ptr[7] = 0; */ + /* alternate tracks per logical unit */ + /* ptr[8] = 0; */ + /* ptr[9] = 0; */ + /* sectors per track */ + ptr[10] = (PSEUDO_SPT >> 8) & 0xff; + ptr[11] = PSEUDO_SPT & 0xff; + /* data bytes per physical sector */ + ptr[12] = ((1 << DISK_SHIFT) >> 8) & 0xff; + ptr[13] = (1 << DISK_SHIFT) & 0xff; + /* interleave */ + /* ptr[14] = 0; */ + /* ptr[15] = 1; */ + /* track skew factor */ + /* ptr[16] = 0; */ + /* ptr[17] = 0; */ + /* cylinder skew factor */ + /* ptr[18] = 0; */ + /* ptr[19] = 0; */ + /* SSRC, HSEC, RMB, SURF */ + } + ptr += 26; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_GEOMETRY_PAGE) { + ptr[0] = SMS_GEOMETRY_PAGE; + ptr[1] = 24; + if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { + uint32_t cyl = (DISK_NBLKS + ((PSEUDO_SPC - 1))) / PSEUDO_SPC; + /* number of cylinders */ + ptr[2] = (cyl >> 24) & 0xff; + ptr[3] = (cyl >> 16) & 0xff; + ptr[4] = cyl & 0xff; + /* number of heads */ + ptr[5] = PSEUDO_HDS; + /* starting cylinder- write precompensation */ + /* ptr[6] = 0; */ + /* ptr[7] = 0; */ + /* ptr[8] = 0; */ + /* starting cylinder- reduced write current */ + /* ptr[9] = 0; */ + /* ptr[10] = 0; */ + /* ptr[11] = 0; */ + /* drive step rate */ + /* ptr[12] = 0; */ + /* ptr[13] = 0; */ + /* landing zone cylinder */ + /* ptr[14] = 0; */ + /* ptr[15] = 0; */ + /* ptr[16] = 0; */ + /* RPL */ + /* ptr[17] = 0; */ + /* rotational offset */ + /* ptr[18] = 0; */ + /* medium rotation rate - 7200 RPM */ + ptr[20] = 0x1c; + ptr[21] = 0x20; + } + ptr += 26; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_CACHE_PAGE) { + ptr[0] = SMS_CACHE_PAGE; + ptr[1] = 18; + ptr[2] = 1 << 2; + ptr += 20; + } + + if (page == SMS_ALL_PAGES_PAGE || page == SMS_CONTROL_MODE_PAGE) { + ptr[0] = SMS_CONTROL_MODE_PAGE; + ptr[1] = 10; + if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { + ptr[3] = 1 << 4; /* unrestricted reordering allowed */ + ptr[8] = 0x75; /* 30000 ms */ + ptr[9] = 0x30; + } + ptr += 12; + } + nbyte = (char *)ptr - &junk[0]; + ptr[0] = nbyte - 4; + + if (cdb[0] == MODE_SENSE) { + data_len = min(cdb[4], csio->dxfer_len); + } else { + uint16_t tw = (cdb[7] << 8) | cdb[8]; + data_len = min(tw, csio->dxfer_len); + } + data_len = min(data_len, nbyte); + if (data_len) { + memcpy(csio->data_ptr, junk, data_len); + } + csio->resid = csio->dxfer_len - data_len; + break; + } + case READ_6: + case READ_10: + case READ_12: + case READ_16: + case WRITE_6: + case WRITE_10: + case WRITE_12: + case WRITE_16: + if (vhba_rwparm(cdb, &off, &data_len, DISK_NBLKS, DISK_SHIFT)) { + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); + break; + } + if (data_len) { + if ((cdb[0] & 0xf) == 8) { + memcpy(csio->data_ptr, &vhbas->disk[off], data_len); + } else { + memcpy(&vhbas->disk[off], csio->data_ptr, data_len); + } + csio->resid = csio->dxfer_len - data_len; + } else { + csio->resid = csio->dxfer_len; + } + break; + break; + + case READ_CAPACITY: + if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) { + vhba_fill_sense(csio, SSD_KEY_UNIT_ATTENTION, 0x24, 0x0); + break; + } + if (cdb[8] & 0x1) { /* PMI */ + csio->data_ptr[0] = 0xff; + csio->data_ptr[1] = 0xff; + csio->data_ptr[2] = 0xff; + csio->data_ptr[3] = 0xff; + } else { + uint64_t last_blk = DISK_NBLKS - 1; + if (last_blk < 0xffffffffULL) { + csio->data_ptr[0] = (last_blk >> 24) & 0xff; + csio->data_ptr[1] = (last_blk >> 16) & 0xff; + csio->data_ptr[2] = (last_blk >> 8) & 0xff; + csio->data_ptr[3] = (last_blk) & 0xff; + } else { + csio->data_ptr[0] = 0xff; + csio->data_ptr[1] = 0xff; + csio->data_ptr[2] = 0xff; + csio->data_ptr[3] = 0xff; + } + } + csio->data_ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; + csio->data_ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; + csio->data_ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; + csio->data_ptr[7] = ((1 << DISK_SHIFT)) & 0xff; + break; + + default: + vhba_default_cmd(csio, MAX_LUN, NULL); + break; + } + csio->ccb_h.status &= ~CAM_STATUS_MASK; + if (csio->scsi_status != SCSI_STATUS_OK) { + csio->ccb_h.status |= CAM_SCSI_STATUS_ERROR; + if (csio->scsi_status == SCSI_STATUS_CHECK_COND) { + csio->ccb_h.status |= CAM_AUTOSNS_VALID; + } + } else { + csio->scsi_status = SCSI_STATUS_OK; + csio->ccb_h.status |= CAM_REQ_CMP; + } + TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); +} +DEV_MODULE(vhba_simple, vhba_modprobe, NULL); diff --git a/tools/tools/vhba/vhba.c b/tools/tools/vhba/vhba.c new file mode 100644 index 000000000000..fd0dce6f5bb7 --- /dev/null +++ b/tools/tools/vhba/vhba.c @@ -0,0 +1,431 @@ +/*- + * Copyright (c) 2010 by Panasas, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $FreeBSD$ */ +/* + * Virtual HBA infrastructure, to be used for testing as well as other cute hacks. + */ +#include "vhba.h" +static vhba_softc_t *vhba; + +#ifndef VHBA_MOD +#define VHBA_MOD "vhba" +#endif + +static void vhba_action(struct cam_sim *, union ccb *); +static void vhba_poll(struct cam_sim *); + +static int +vhba_attach(vhba_softc_t *vhba) +{ + TAILQ_INIT(&vhba->actv); + TAILQ_INIT(&vhba->done); + vhba->devq = cam_simq_alloc(VHBA_MAXCMDS); + if (vhba->devq == NULL) { + return (ENOMEM); + } + vhba->sim = cam_sim_alloc(vhba_action, vhba_poll, VHBA_MOD, vhba, 0, &vhba->lock, VHBA_MAXCMDS, VHBA_MAXCMDS, vhba->devq); + if (vhba->sim == NULL) { + cam_simq_free(vhba->devq); + return (ENOMEM); + } + vhba_init(vhba); + mtx_lock(&vhba->lock); + if (xpt_bus_register(vhba->sim, 0, 0) != CAM_SUCCESS) { + cam_sim_free(vhba->sim, TRUE); + mtx_unlock(&vhba->lock); + return (EIO); + } + mtx_unlock(&vhba->lock); + return (0); +} + +static void +vhba_detach(vhba_softc_t *vhba) +{ + /* + * We can't be called with anything queued up. + */ + vhba_fini(vhba); + xpt_bus_deregister(cam_sim_path(vhba->sim)); + cam_sim_free(vhba->sim, TRUE); +} + +static void +vhba_poll(struct cam_sim *sim) +{ + vhba_softc_t *vhba = cam_sim_softc(sim); + vhba_kick(vhba); +} + +static void +vhba_action(struct cam_sim *sim, union ccb *ccb) +{ + struct ccb_trans_settings *cts; + vhba_softc_t *vhba; + + vhba = cam_sim_softc(sim); + if (vhba->private == NULL) { + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + xpt_done(ccb); + return; + } + switch (ccb->ccb_h.func_code) { + case XPT_SCSI_IO: + ccb->ccb_h.status &= ~CAM_STATUS_MASK; + ccb->ccb_h.status |= CAM_REQ_INPROG; + TAILQ_INSERT_TAIL(&vhba->actv, &ccb->ccb_h, sim_links.tqe); + vhba_kick(vhba); + return; + + case XPT_RESET_DEV: + ccb->ccb_h.status = CAM_REQ_CMP; + break; + + case XPT_GET_TRAN_SETTINGS: + cts = &ccb->cts; + cts->protocol_version = SCSI_REV_SPC2; + cts->protocol = PROTO_SCSI; + cts->transport_version = 0; + cts->transport = XPORT_PPB; + ccb->ccb_h.status = CAM_REQ_CMP; + break; + + case XPT_CALC_GEOMETRY: + cam_calc_geometry(&ccb->ccg, 1); + break; + + case XPT_RESET_BUS: /* Reset the specified bus */ + ccb->ccb_h.status = CAM_REQ_CMP; + break; + + case XPT_PATH_INQ: /* Path routing inquiry */ + { + struct ccb_pathinq *cpi = &ccb->cpi; + + cpi->version_num = 1; + cpi->max_target = VHBA_MAXTGT - 1; + cpi->max_lun = 16383; + cpi->hba_misc = PIM_NOBUSRESET; + cpi->initiator_id = cpi->max_target + 1; + cpi->transport = XPORT_PPB; + cpi->base_transfer_speed = 1000000; + cpi->protocol = PROTO_SCSI; + cpi->protocol_version = SCSI_REV_SPC2; + strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strlcpy(cpi->hba_vid, "FakeHBA", HBA_IDLEN); + strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); + cpi->unit_number = cam_sim_unit(sim); + cpi->ccb_h.status = CAM_REQ_CMP; + break; + } + default: + ccb->ccb_h.status = CAM_REQ_INVALID; + break; + } + xpt_done(ccb); +} + +/* + * Common support + */ +void +vhba_fill_sense(struct ccb_scsiio *csio, uint8_t key, uint8_t asc, uint8_t ascq) +{ + csio->ccb_h.status = CAM_SCSI_STATUS_ERROR|CAM_AUTOSNS_VALID; + csio->scsi_status = SCSI_STATUS_CHECK_COND; + csio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR; + csio->sense_data.flags = key; + csio->sense_data.extra_len = 10; + csio->sense_data.add_sense_code = asc; + csio->sense_data.add_sense_code_qual = ascq; + csio->sense_len = sizeof (csio->sense_data); +} + +int +vhba_rwparm(uint8_t *cdb, uint64_t *offset, uint32_t *tl, uint64_t nblks, uint32_t blk_shift) +{ + uint32_t cnt; + uint64_t lba; + + switch (cdb[0]) { + case WRITE_16: + case READ_16: + cnt = (((uint32_t)cdb[10]) << 24) | + (((uint32_t)cdb[11]) << 16) | + (((uint32_t)cdb[12]) << 8) | + ((uint32_t)cdb[13]); + + lba = (((uint64_t)cdb[2]) << 56) | + (((uint64_t)cdb[3]) << 48) | + (((uint64_t)cdb[4]) << 40) | + (((uint64_t)cdb[5]) << 32) | + (((uint64_t)cdb[6]) << 24) | + (((uint64_t)cdb[7]) << 16) | + (((uint64_t)cdb[8]) << 8) | + ((uint64_t)cdb[9]); + break; + case WRITE_12: + case READ_12: + cnt = (((uint32_t)cdb[6]) << 16) | + (((uint32_t)cdb[7]) << 8) | + ((u_int32_t)cdb[8]); + + lba = (((uint32_t)cdb[2]) << 24) | + (((uint32_t)cdb[3]) << 16) | + (((uint32_t)cdb[4]) << 8) | + ((uint32_t)cdb[5]); + break; + case WRITE_10: + case READ_10: + cnt = (((uint32_t)cdb[7]) << 8) | + ((u_int32_t)cdb[8]); + + lba = (((uint32_t)cdb[2]) << 24) | + (((uint32_t)cdb[3]) << 16) | + (((uint32_t)cdb[4]) << 8) | + ((uint32_t)cdb[5]); + break; + case WRITE_6: + case READ_6: + cnt = cdb[4]; + if (cnt == 0) { + cnt = 256; + } + lba = (((uint32_t)cdb[1] & 0x1f) << 16) | + (((uint32_t)cdb[2]) << 8) | + ((uint32_t)cdb[3]); + break; + default: + return (-1); + } + + if (lba + cnt > nblks) { + return (-1); + } + *tl = cnt << blk_shift; + *offset = lba << blk_shift; + return (0); +} + +void +vhba_default_cmd(struct ccb_scsiio *csio, lun_id_t max_lun, uint8_t *sparse_lun_map) +{ + char junk[128]; + const uint8_t niliqd[SHORT_INQUIRY_LENGTH] = { + 0x7f, 0x0, 0x5, 0x2, 32, 0, 0, 0x32, + 'P', 'A', 'N', 'A', 'S', 'A', 'S', ' ', + 'N', 'U', 'L', 'L', ' ', 'D', 'E', 'V', + 'I', 'C', 'E', ' ', ' ', ' ', ' ', ' ', + '0', '0', '0', '1' + }; + const uint8_t iqd[SHORT_INQUIRY_LENGTH] = { + 0, 0x0, 0x5, 0x2, 32, 0, 0, 0x32, + 'P', 'A', 'N', 'A', 'S', 'A', 'S', ' ', + 'V', 'I', 'R', 'T', ' ', 'M', 'E', 'M', + 'O', 'R', 'Y', ' ', 'D', 'I', 'S', 'K', + '0', '0', '0', '1' + }; + const uint8_t vp0data[6] = { 0, 0, 0, 0x2, 0, 0x80 }; + const uint8_t vp80data[36] = { 0, 0x80, 0, 0x20 }; + int i, attached_lun; + uint8_t *cdb, *ptr, status; + uint32_t data_len, nlun; + + data_len = 0; + status = SCSI_STATUS_OK; + + memset(&csio->sense_data, 0, sizeof (csio->sense_data)); + cdb = csio->cdb_io.cdb_bytes; + + attached_lun = 1; + if (csio->ccb_h.target_lun >= max_lun) { + attached_lun = 0; + } else if (sparse_lun_map) { + i = csio->ccb_h.target_lun & 0x7; + if ((sparse_lun_map[csio->ccb_h.target_lun >> 3] & (1 << i)) == 0) { + attached_lun = 0; + } + } + if (attached_lun == 0 && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) { + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0); + return; + } + + switch (cdb[0]) { + case REQUEST_SENSE: + data_len = csio->dxfer_len; + if (cdb[4] < csio->dxfer_len) + data_len = cdb[4]; + if (data_len) { + memset(junk, 0, sizeof (junk)); + junk[0] = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR; + junk[2] = SSD_KEY_NO_SENSE; + junk[7] = 10; + memcpy(csio->data_ptr, junk, + (data_len > sizeof junk)? sizeof junk : data_len); + } + csio->resid = csio->dxfer_len - data_len; + break; + case INQUIRY: + i = 0; + if ((cdb[1] & 0x1f) == SI_EVPD) { + if ((cdb[2] != 0 && cdb[2] != 0x80) || cdb[3] || cdb[5]) { + i = 1; + } + } else if ((cdb[1] & 0x1f) || cdb[2] || cdb[3] || cdb[5]) { + i = 1; + } + if (i) { + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); + break; + } + if (attached_lun == 0) { + if (cdb[1] & 0x1f) { + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); + break; + } + memcpy(junk, niliqd, sizeof (niliqd)); + data_len = sizeof (niliqd); + } else if (cdb[1] & 0x1f) { + if (cdb[2] == 0) { + memcpy(junk, vp0data, sizeof (vp0data)); + data_len = sizeof (vp0data); + } else { + memcpy(junk, vp80data, sizeof (vp80data)); + snprintf(&junk[4], sizeof (vp80data) - 4, "TGT%dLUN%d", csio->ccb_h.target_id, csio->ccb_h.target_lun); + for (i = 0; i < sizeof (vp80data); i++) { + if (junk[i] == 0) { + junk[i] = ' '; + } + } + } + data_len = sizeof (vp80data); + } else { + memcpy(junk, iqd, sizeof (iqd)); + data_len = sizeof (iqd); + } + if (data_len > cdb[4]) { + data_len = cdb[4]; + } + if (data_len) { + memcpy(csio->data_ptr, junk, data_len); + } + csio->resid = csio->dxfer_len - data_len; + break; + case TEST_UNIT_READY: + case SYNCHRONIZE_CACHE: + case START_STOP: + case RESERVE: + case RELEASE: + break; + + case REPORT_LUNS: + if (csio->dxfer_len) { + memset(csio->data_ptr, 0, csio->dxfer_len); + } + ptr = NULL; + for (nlun = i = 0; i < max_lun; i++) { + if (sparse_lun_map) { + if ((sparse_lun_map[i >> 3] & (1 << (i & 0x7))) == 0) { + continue; + } + } + ptr = &csio->data_ptr[8 + ((nlun++) << 3)]; + if ((ptr + 8) > &csio->data_ptr[csio->dxfer_len]) { + continue; + } + if (i >= 256) { + ptr[0] = 0x40 | ((i >> 8) & 0x3f); + } + ptr[1] = i; + } + junk[0] = (nlun << 3) >> 24; + junk[1] = (nlun << 3) >> 16; + junk[2] = (nlun << 3) >> 8; + junk[3] = (nlun << 3); + memset(junk+4, 0, 4); + if (csio->dxfer_len) { + u_int amt; + + amt = MIN(csio->dxfer_len, 8); + memcpy(csio->data_ptr, junk, amt); + amt = MIN((nlun << 3) + 8, csio->dxfer_len); + csio->resid = csio->dxfer_len - amt; + } + break; + + default: + vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x20, 0x0); + break; + } +} + +void +vhba_set_status(struct ccb_hdr *ccbh, cam_status status) +{ + ccbh->status &= ~CAM_STATUS_MASK; + ccbh->status |= status; + if (status != CAM_REQ_CMP) { + if ((ccbh->status & CAM_DEV_QFRZN) == 0) { + ccbh->status |= CAM_DEV_QFRZN; + xpt_freeze_devq(ccbh->path, 1); + } + } +} + +int +vhba_modprobe(module_t mod, int cmd, void *arg) +{ + int error = 0; + + switch (cmd) { + case MOD_LOAD: + vhba = malloc(sizeof (*vhba), M_DEVBUF, M_WAITOK|M_ZERO); + mtx_init(&vhba->lock, "vhba", NULL, MTX_DEF); + error = vhba_attach(vhba); + if (error) { + mtx_destroy(&vhba->lock); + free(vhba, M_DEVBUF); + } + break; + case MOD_UNLOAD: + mtx_lock(&vhba->lock); + if (TAILQ_FIRST(&vhba->done) || TAILQ_FIRST(&vhba->actv)) { + error = EBUSY; + mtx_unlock(&vhba->lock); + break; + } + vhba_detach(vhba); + mtx_unlock(&vhba->lock); + mtx_destroy(&vhba->lock); + free(vhba, M_DEVBUF); + break; + default: + error = EOPNOTSUPP; + break; + } + return (error); +} diff --git a/tools/tools/vhba/vhba.h b/tools/tools/vhba/vhba.h new file mode 100644 index 000000000000..c09bd000de3e --- /dev/null +++ b/tools/tools/vhba/vhba.h @@ -0,0 +1,116 @@ +/*- + * Copyright (c) 2010 by Panasas, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $FreeBSD$ */ +/* + * Virtual HBA defines + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include + +#define VHBA_MAXTGT 64 +#define VHBA_MAXCMDS 256 + +typedef struct { + struct mtx lock; + struct cam_sim * sim; + struct cam_devq * devq; + TAILQ_HEAD(, ccb_hdr) actv; + TAILQ_HEAD(, ccb_hdr) done; + void * private; +} vhba_softc_t; + +/* + * Each different instantiation of a fake HBA needs to + * provide these as function entry points. It's responsible + * for setting up some thread or regular timeout that will + * dequeue things from the actv queue and put done items + * on the done queue. + */ +void vhba_init(vhba_softc_t *); +void vhba_fini(vhba_softc_t *); +void vhba_kick(vhba_softc_t *); + +/* + * Support functions + */ +void vhba_fill_sense(struct ccb_scsiio *, uint8_t, uint8_t, uint8_t); +int vhba_rwparm(uint8_t *, uint64_t *, uint32_t *, uint64_t, uint32_t); +void vhba_default_cmd(struct ccb_scsiio *, lun_id_t, uint8_t *); +void vhba_set_status(struct ccb_hdr *, cam_status); + +/* + * Common module loader function + */ +int vhba_modprobe(module_t, int, void *); + +/* + * retrofits + */ +#ifndef MODE_SENSE +#define MODE_SENSE 0x1a +#endif +#ifndef SMS_FORMAT_DEVICE_PAGE +#define SMS_FORMAT_DEVICE_PAGE 0x03 +#endif +#ifndef SMS_GEOMETRY_PAGE +#define SMS_GEOMETRY_PAGE 0x04 +#endif