Add the VHBA package. It is here in tools because it's really a testbed.

Sponsored by:	Panasas
MFC after:	1 month
This commit is contained in:
Matt Jacob 2010-06-08 22:26:47 +00:00
parent 374d9c4d18
commit 8d7b838eee
15 changed files with 2352 additions and 0 deletions

29
tools/tools/vhba/Makefile Normal file
View File

@ -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 <bsd.subdir.mk>

16
tools/tools/vhba/README Normal file
View File

@ -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.

View File

@ -0,0 +1,7 @@
# $FreeBSD$
KMOD= vfaulty
SRCS= vhba_faulty.c vhba.c
CFLAGS += -I${.CURDIR}/.. -DVHBA_MOD=\"vfaulty\"
VPATH= ${.CURDIR}/..
.include <bsd.kmod.mk>

View File

@ -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);

View File

@ -0,0 +1,7 @@
# $FreeBSD$
KMOD= vlots
SRCS= vhba_lots.c vhba.c
CFLAGS += -I${.CURDIR}/.. -DVHBA_MOD=\"vlots\"
VPATH= ${.CURDIR}/..
.include <bsd.kmod.mk>

View File

@ -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);

View File

@ -0,0 +1,7 @@
# $FreeBSD$
KMOD= vmedium
SRCS= vhba_medium.c vhba.c
CFLAGS += -I${.CURDIR}/.. -DVHBA_MOD=\"vmedium\"
VPATH= ${.CURDIR}/..
.include <bsd.kmod.mk>

View File

@ -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);

View File

@ -0,0 +1 @@
/* $FreeBSD$ */

View File

@ -0,0 +1,7 @@
# $FreeBSD$
KMOD= vrptluns
SRCS= vhba_rptluns.c vhba.c
CFLAGS += -I${.CURDIR}/.. -DVHBA_MOD=\"vrptluns\"
VPATH= ${.CURDIR}/..
.include <bsd.kmod.mk>

View File

@ -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);

View File

@ -0,0 +1,7 @@
# $FreeBSD$
KMOD= vsimple
SRCS= vhba_simple.c vhba.c
CFLAGS += -I${.CURDIR}/.. -DVHBA_MOD=\"vsimple\"
VPATH= ${.CURDIR}/..
.include <bsd.kmod.mk>

View File

@ -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);

431
tools/tools/vhba/vhba.c Normal file
View File

@ -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);
}

116
tools/tools/vhba/vhba.h Normal file
View File

@ -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 <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/endian.h>
#include <sys/lock.h>
#include <sys/kernel.h>
#include <sys/queue.h>
#include <sys/queue.h>
#include <sys/malloc.h>
#include <sys/taskqueue.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/proc.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <machine/stdarg.h>
#include <cam/cam.h>
#include <cam/cam_debug.h>
#include <cam/cam_ccb.h>
#include <cam/cam_sim.h>
#include <cam/cam_xpt.h>
#include <cam/cam_xpt_sim.h>
#include <cam/cam_debug.h>
#include <cam/scsi/scsi_all.h>
#include <cam/scsi/scsi_message.h>
#include <sys/unistd.h>
#include <sys/kthread.h>
#include <sys/conf.h>
#include <sys/module.h>
#include <sys/ioccom.h>
#include <sys/devicestat.h>
#include <cam/cam_periph.h>
#include <cam/cam_xpt_periph.h>
#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