From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from [140.186.70.92] (port=43575 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PLAuA-0000iB-It for qemu-devel@nongnu.org; Wed, 24 Nov 2010 03:40:45 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PLAu8-0007Lv-Ar for qemu-devel@nongnu.org; Wed, 24 Nov 2010 03:40:42 -0500 Received: from nm18.bullet.mail.ne1.yahoo.com ([98.138.90.81]:47286) by eggs.gnu.org with smtp (Exim 4.71) (envelope-from ) id 1PLAu7-0007Ll-In for qemu-devel@nongnu.org; Wed, 24 Nov 2010 03:40:40 -0500 From: "Nicholas A. Bellinger" Date: Wed, 24 Nov 2010 00:40:35 -0800 Message-Id: <1290588035-8950-1-git-send-email-nab@linux-iscsi.org> Subject: [Qemu-devel] [PATCH 5/5] scsi-bsg: Add initial support for BSG based SCSIDeviceInfo List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Hannes Reinecke , Kevin Wolf Cc: qemu-devel , Stefan Hajnoczi , Gerd Hoffmann , Nicholas Bellinger , Paolo Bonzini From: Nicholas Bellinger This patch adds initial support for using the Linux BSG interface with write/read vectored AIO as a QEMU backstore (SCSIDeviceInfo) with hw/scsi-bus.c compatible HBA emulation. So far it has been tested with x86_64 host and guest using hw/megasas.c and TCM_Loop LLD Port LUNs. Because this patch uses struct iovec for struct sg_io_v4->d[out,in]_xferp payloads, which currently requires a patch to linux/block/bsg.c:bsg_map_hdr() in order to setup the user -> kernel iovecs. This patch can be found in lio-core-2.6.git here: commit fec4e8457c744de50e1ead69a36d5c4ee089d1ac Author: Nicholas Bellinger Date: Sun Jun 13 23:13:20 2010 -0700 [BSG]: Add support for struct sg_io_v4->d[out,in]_iovec_count This also will only currently work with paired user/kernel (eg: 64bit user / 64bit kernel) because of different pointer sizes in struct iovec->iov_base. There are also two FIXMEs in hw/scsi-bsg.c:bsg_generic_initfn() related to extraction of SCSI LUN and device type values using BSG and required by QEMU-KVM. Signed-off-by: Nicholas A. Bellinger --- Makefile.objs | 2 +- hw/scsi-bsg.c | 720 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 721 insertions(+), 1 deletions(-) create mode 100644 hw/scsi-bsg.c diff --git a/Makefile.objs b/Makefile.objs index 0443c7e..842e70c 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -79,7 +79,7 @@ common-obj-$(CONFIG_DS1338) += ds1338.o common-obj-y += i2c.o smbus.o smbus_eeprom.o common-obj-y += eeprom93xx.o common-obj-y += scsi-disk.o cdrom.o -common-obj-y += scsi-generic.o scsi-bus.o +common-obj-y += scsi-generic.o scsi-bsg.o scsi-bus.o common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o common-obj-y += usb-serial.o usb-net.o usb-bus.o common-obj-$(CONFIG_SSI) += ssi.o diff --git a/hw/scsi-bsg.c b/hw/scsi-bsg.c new file mode 100644 index 0000000..93936e0 --- /dev/null +++ b/hw/scsi-bsg.c @@ -0,0 +1,720 @@ +/* + * block layer implementation of the sg v4 interface for Linux hosts + * + * Copyright (c) 2010 Rising Tide Systems + * Written by Nicholas A. Bellinger + * + * Based on hw/scsi-generic code by Laurent Vivier, Paul Brook, and Fabrice Bellard + * + * This code is licenced under the LGPL. + */ + +#include "qemu-common.h" +#include "qemu-error.h" +#include "block.h" +#include "scsi.h" +#include "block/raw-posix-aio.h" + +#ifdef __linux__ + +#define DEBUG_BSG +//#define DEBUG_BSG_IO + +#ifdef DEBUG_BSG +#define DPRINTF(fmt, ...) \ +do { printf("scsi-bsg: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while(0) +#endif + +#ifdef DEBUG_BSG_IO +#define DPRINTF_BSG_IO(fmt, ...) DPRINTF(fmt , ## __VA_ARGS__) +#else +#define DPRINTF_BSG_IO(fmt, ...) +#endif + +#define BADF(fmt, ...) \ +do { fprintf(stderr, "scsi-bsg: " fmt , ## __VA_ARGS__); } while (0) + +#include +#include +#include +#include +#include +#include +#include +#include "scsi-defs.h" + +#define SCSI_SENSE_BUF_SIZE 96 + +#define SG_ERR_DRIVER_TIMEOUT 0x06 +#define SG_ERR_DRIVER_SENSE 0x08 + +typedef struct SCSIBSGState SCSIBSGState; + +typedef struct SCSIBSGReq { + SCSIRequest req; + uint8_t *buf; + int buflen; + int len; +// QEMUIOVector iov; + struct iovec *saved_iov; + uint32_t saved_iov_num; + QEMUIOVector aio_iov; + struct sg_io_v4 bsg_hdr; +} SCSIBSGReq; + +struct SCSIBSGState { + SCSIDevice qdev; + BlockDriverState *bs; + int lun; + int driver_status; + uint8_t sensebuf[SCSI_SENSE_BUF_SIZE]; + uint8_t senselen; +}; + +static int bsg_read(int fd, void *p_read, int to_read) +{ + int err; + + while (to_read > 0) { + err = read(fd, p_read, to_read); + if (err >= 0) { + to_read -= err; + p_read += err; + } else if (errno == EINTR) + continue; + else { + error_report("bsg device %d read failed, errno: %d\n", + fd, errno); + return errno; + } + } + return 0; +} + +static int bsg_set_sense(SCSIBSGState *s, SCSISense sense) +{ + int len; + + len = scsi_build_sense(sense, s->sensebuf, SCSI_SENSE_BUF_SIZE, 0); + s->driver_status = SG_ERR_DRIVER_SENSE; + + return len; +} + +static void bsg_clear_sense(SCSIBSGState *s) +{ + memset(s->sensebuf, 0, SCSI_SENSE_BUF_SIZE); + s->senselen = 0; + s->driver_status = 0; +} + +static int bsg_get_sense(SCSIRequest *req, uint8_t *outbuf, int len) +{ + SCSIBSGState *s = DO_UPCAST(SCSIBSGState, qdev, req->dev); + int size = SCSI_SENSE_BUF_SIZE; + + if (s->driver_status & SG_ERR_DRIVER_SENSE) { + if (len < SCSI_SENSE_BUF_SIZE) + size = len; + else + size = SCSI_SENSE_BUF_SIZE; + + memcpy(outbuf, s->sensebuf, size); + } + + return size; +} + +static void bsg_command_complete(void *opaque, int ret) +{ + SCSIBSGReq *r = opaque; + SCSIBSGState *s = DO_UPCAST(SCSIBSGState, qdev, r->req.dev); + + s->driver_status = r->bsg_hdr.driver_status; + if (s->driver_status) + s->senselen = SCSI_SENSE_BUF_SIZE; + + if (ret != 0) { + switch (ret) { + case -ENODEV: + s->senselen = bsg_set_sense(s, SENSE_CODE(LUN_NOT_SUPPORTED)); + break; + case -EINVAL: + s->senselen = bsg_set_sense(s, SENSE_CODE(INVALID_FIELD)); + break; + case -EBADR: + s->senselen = bsg_set_sense(s, SENSE_CODE(TARGET_FAILURE)); + break; + default: + s->senselen = bsg_set_sense(s, SENSE_CODE(IO_ERROR)); + break; + } + scsi_req_print(&r->req); + error_report("%s: ret %d (%s)\n", __FUNCTION__, + ret, strerror(-ret)); + s->driver_status = SG_ERR_DRIVER_SENSE; + r->req.status = CHECK_CONDITION; + } else { + if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) { + scsi_req_print(&r->req); + r->req.status = BUSY; + error_report("%s: timeout\n", __FUNCTION__); + } else if (r->bsg_hdr.device_status) { + r->req.status = r->bsg_hdr.device_status; + } else if (s->driver_status & SG_ERR_DRIVER_SENSE) { + scsi_req_print(&r->req); + error_report("%s: driver sense\n", __FUNCTION__); + r->req.status = CHECK_CONDITION; + } else { + r->req.status = GOOD; + } + } + DPRINTF_BSG_IO("Command complete 0x%p tag=0x%x status=%d\n", + r, r->req.tag, r->req.status); + + scsi_req_complete(&r->req); +} + +static int bsg_execute_command(BlockDriverState *bdrv, + SCSIBSGReq *r, + BlockDriverCompletionFunc *complete) +{ + SCSIBSGState *s = DO_UPCAST(SCSIBSGState, qdev, r->req.dev); + /* + * Following linux/include/linux/bsg.h + */ + /* [i] 'Q' to differentiate from v3 */ + r->bsg_hdr.guard = 'Q'; + r->bsg_hdr.protocol = BSG_PROTOCOL_SCSI; + r->bsg_hdr.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; + r->bsg_hdr.request_len = r->req.cmd.len; + r->bsg_hdr.request = (unsigned long)r->req.cmd.buf; + r->bsg_hdr.max_response_len = sizeof(s->sensebuf); + /* SCSI: (auto)sense data */ + r->bsg_hdr.response = (unsigned long)s->sensebuf; + /* Unlimited timeout */ + r->bsg_hdr.timeout = UINT_MAX; + /* [i->o] unused internally */ + r->bsg_hdr.usr_ptr = (unsigned long)r; + /* Bsg does Q_AT_HEAD by default */ + r->bsg_hdr.flags = BSG_FLAG_Q_AT_TAIL; + + qemu_iovec_reset(&r->aio_iov); + qemu_iovec_add(&r->aio_iov, &r->bsg_hdr, sizeof(r->bsg_hdr)); + + r->req.aiocb = paio_submit_len(bdrv, bdrv->fd, 0, &r->aio_iov, + sizeof(r->bsg_hdr), complete, r, QEMU_AIO_WRITE); + if (r->req.aiocb == NULL) { + BADF("execute_command: paio_submit_len() failed\n"); + return -1; + } + + return 0; +} + +static void bsg_setup_command(SCSIBSGReq *r) +{ + if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { + r->bsg_hdr.dout_xferp = (unsigned long)r->buf; + r->bsg_hdr.dout_xfer_len = r->buflen; + } else if (r->req.cmd.mode == SCSI_XFER_FROM_DEV) { + r->bsg_hdr.din_xferp = (unsigned long)r->buf; + r->bsg_hdr.din_xfer_len = r->buflen; + } + DPRINTF_BSG_IO("setup BUF: %p, dxfer_len %u\n", r->buf, r->buflen); +} + +static void bsg_setup_command_iov(SCSIBSGReq *r) +{ + struct iovec *iov = r->saved_iov; + int i; + + if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { + r->bsg_hdr.dout_iovec_count = r->saved_iov_num; + r->bsg_hdr.dout_xferp = (unsigned long)r->saved_iov; + r->bsg_hdr.dout_xfer_len = 0; + for (i = 0; i < r->saved_iov_num; i++) + r->bsg_hdr.dout_xfer_len += iov[i].iov_len; + + } else if (r->req.cmd.mode == SCSI_XFER_FROM_DEV) { + r->bsg_hdr.din_iovec_count = r->saved_iov_num; + r->bsg_hdr.din_xferp = (unsigned long)r->saved_iov; + r->bsg_hdr.din_xfer_len = 0; + for (i = 0; i < r->saved_iov_num; i++) + r->bsg_hdr.din_xfer_len += iov[i].iov_len; + } + + DPRINTF_BSG_IO("setup IOV: iovec_num: %u, iov: %p," + " dout_xfer_len: %u din_xfer_len: %u\n", + r->saved_iov_num, r->saved_iov, + r->bsg_hdr.dout_xfer_len, r->bsg_hdr.din_xfer_len); +} + +static void bsg_read_complete(void * opaque, int ret) +{ + SCSIBSGReq *r = (SCSIBSGReq *)opaque; + SCSIBSGState *s = DO_UPCAST(SCSIBSGState, qdev, r->req.dev); + struct sg_io_v4 io_hdr; + int err, len; + + if (ret) { + DPRINTF("IO error ret %d\n", ret); + bsg_command_complete(r, ret); + return; + } + + r->req.aiocb = NULL; + + memset(&io_hdr, 0, sizeof(io_hdr)); + /* [i] 'Q' to differentiate from v3 */ + io_hdr.guard = 'Q'; + err = bsg_read(s->bs->fd, &io_hdr, sizeof(io_hdr)); + if (err) { + DPRINTF("bsg_read() failed with ret: %d\n", err); + bsg_command_complete(r, EBADR); + return; + } + + len = r->bsg_hdr.din_xfer_len - r->bsg_hdr.din_resid; + DPRINTF_BSG_IO("BSG READ Data ready tag=0x%x len=%d\n", r->req.tag, len); + + r->len = -1; + r->req.bus->complete(&r->req, SCSI_REASON_DATA, len); +} + +/* Read more data from scsi device into buffer. */ +static void bsg_read_data(SCSIRequest *req) +{ + SCSIBSGState *s = DO_UPCAST(SCSIBSGState, qdev, req->dev); + SCSIBSGReq *r = DO_UPCAST(SCSIBSGReq, req, req); + int ret; + + DPRINTF_BSG_IO("bsg_read_data 0x%x\n", req->tag); + if (r->len == -1) { + bsg_command_complete(r, 0); + return; + } + + if (r->req.cmd.buf[0] == REQUEST_SENSE && + s->driver_status & SG_ERR_DRIVER_SENSE) { + s->senselen = MIN(r->len, s->senselen); + memcpy(r->buf, s->sensebuf, s->senselen); + r->bsg_hdr.driver_status = 0; + r->bsg_hdr.device_status = 0; + r->bsg_hdr.max_response_len = s->senselen; + r->len = -1; + DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, s->senselen); + DPRINTF("Sense: %d %d %d %d %d %d %d %d\n", + r->buf[0], r->buf[1], r->buf[2], r->buf[3], + r->buf[4], r->buf[5], r->buf[6], r->buf[7]); + r->req.bus->complete(&r->req, SCSI_REASON_DATA, s->senselen); + /* Clear sensebuf after REQUEST_SENSE */ + bsg_clear_sense(s); + return; + } + + if (r->buf != NULL) + bsg_setup_command(r); + else + bsg_setup_command_iov(r); + + ret = bsg_execute_command(s->bs, r, bsg_read_complete); + if (ret == -1) { + bsg_command_complete(r, -EBADR); + return; + } +} + +static void bsg_write_complete(void *opaque, int ret) +{ + SCSIBSGReq *r = (SCSIBSGReq *)opaque; + SCSIBSGState *s = DO_UPCAST(SCSIBSGState, qdev, r->req.dev); + struct sg_io_v4 io_hdr; + int err; + + DPRINTF_BSG_IO("bsg_write_complete() ret = %d\n", ret); + if (ret) { + DPRINTF("IO error\n"); + bsg_command_complete(r, ret); + return; + } + + r->req.aiocb = NULL; + + memset(&io_hdr, 0, sizeof(io_hdr)); + /* [i] 'Q' to differentiate from v3 */ + io_hdr.guard = 'Q'; + err = bsg_read(s->bs->fd, &io_hdr, sizeof(io_hdr)); + if (err) { + DPRINTF("bsg_read() failed with ret: %d\n", err); + bsg_command_complete(r, EBADR); + return; + } + + /* + * Copied from hw/scsi-generic.c:scsi_write_complete(), is this still + * necessary for BSG..? + */ + if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 && + s->qdev.type == TYPE_TAPE) { + s->qdev.blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11]; + DPRINTF("block size %d\n", s->qdev.blocksize); + } + + bsg_command_complete(r, ret); +} + +static int bsg_write_data(SCSIRequest *req) +{ + SCSIBSGState *s = DO_UPCAST(SCSIBSGState, qdev, req->dev); + SCSIBSGReq *r = DO_UPCAST(SCSIBSGReq, req, req); + int ret; + + DPRINTF_BSG_IO("bsg_write_data 0x%x\n", req->tag); + + if (r->len == 0 && r->bsg_hdr.dout_xfer_len == 0) { + r->len = r->buflen; + r->req.bus->complete(&r->req, SCSI_REASON_DATA, r->len); + return 0; + } + + ret = bsg_execute_command(s->bs, r, bsg_write_complete); + if (ret == -1) { + bsg_command_complete(r, -EBADR); + return 1; + } + + return 0; +} + +static void bsg_req_fixup(SCSIRequest *req) +{ + return; +} + +static int bsg_get_blocksize(BlockDriverState *bdrv) +{ + uint8_t cmd[10]; + uint8_t buf[8]; + uint8_t sensebuf[8]; + struct sg_io_v4 bsg_hdr; + int ret; + + memset(cmd, 0, sizeof(cmd)); + memset(buf, 0, sizeof(buf)); + cmd[0] = READ_CAPACITY; + + memset(&bsg_hdr, 0, sizeof(bsg_hdr)); + bsg_hdr.guard = 'Q'; + bsg_hdr.protocol = BSG_PROTOCOL_SCSI; + bsg_hdr.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; + bsg_hdr.request_len = sizeof(cmd); + bsg_hdr.request = (unsigned long)cmd; + bsg_hdr.din_xfer_len = sizeof(buf); + bsg_hdr.din_xferp = (unsigned long)buf; + bsg_hdr.max_response_len = sizeof(sensebuf); + bsg_hdr.response = (unsigned long)sensebuf; + bsg_hdr.timeout = 6000; /* XXX */ + + ret = bdrv_ioctl(bdrv, SG_IO, (void *)&bsg_hdr); + if (ret < 0) + return -1; + + return (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; +} + +static int bsg_get_stream_blocksize(BlockDriverState *bdrv) +{ + uint8_t cmd[6]; + uint8_t buf[12]; + uint8_t sensebuf[8]; + struct sg_io_v4 bsg_hdr; + int ret; + + memset(cmd, 0, sizeof(cmd)); + memset(buf, 0, sizeof(buf)); + cmd[0] = MODE_SENSE; + cmd[4] = sizeof(buf); + + memset(&bsg_hdr, 0, sizeof(bsg_hdr)); + bsg_hdr.guard = 'Q'; + bsg_hdr.protocol = BSG_PROTOCOL_SCSI; + bsg_hdr.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; + bsg_hdr.request_len = sizeof(cmd); + bsg_hdr.request = (unsigned long)cmd; + bsg_hdr.din_xfer_len = sizeof(buf); + bsg_hdr.din_xferp = (unsigned long)buf; + bsg_hdr.max_response_len = sizeof(sensebuf); + bsg_hdr.response = (unsigned long)sensebuf; + bsg_hdr.timeout = 6000; /* XXX */ + + ret = bdrv_ioctl(bdrv, SG_IO, (void *)&bsg_hdr); + if (ret < 0) + return -1; + + return (buf[9] << 16) | (buf[10] << 8) | buf[11]; +} + +static void bsg_remove_request(SCSIRequest *); + +static void bsg_purge_requests(SCSIBSGState *s) +{ + SCSIBSGReq *r; + + while (!QTAILQ_EMPTY(&s->qdev.requests)) { + r = DO_UPCAST(SCSIBSGReq, req, QTAILQ_FIRST(&s->qdev.requests)); + if (r->req.aiocb) { + bdrv_aio_cancel(r->req.aiocb); + } + bsg_remove_request(&r->req); + } +} + +static void bsg_reset(DeviceState *dev) +{ + SCSIBSGState *s = DO_UPCAST(SCSIBSGState, qdev.qdev, dev); + + bsg_purge_requests(s); +} + +static void bsg_destroy(SCSIDevice *d) +{ + SCSIBSGState *s = DO_UPCAST(SCSIBSGState, qdev, d); + + bsg_purge_requests(s); + blockdev_mark_auto_del(s->qdev.conf.bs); +} + +struct scsi_idlun { + uint32_t dev_id; + uint32_t host_unique_id; +}; + +static int bsg_generic_initfn(SCSIDevice *dev) +{ + SCSIBSGState *s = DO_UPCAST(SCSIBSGState, qdev, dev); + + if (!s->qdev.conf.bs) { + error_report("scsi-bsg: drive property not set"); + return -1; + } + s->bs = s->qdev.conf.bs; + + /* check we are really using a /dev/bsg/ * file */ + if (!bdrv_is_bsg(s->bs)) { + error_report("scsi-bsg: not BSG*"); + return -1; + } +#if 0 + /* get LUN of the BSG */ + if (bdrv_ioctl(s->bs, SG_GET_SCSI_ID, &scsiid)) { + error_report("scsi-bsg: SG_GET_SCSI_ID ioctl failed"); + return -1; + } +#endif +// FIXME: Get SCSI lun from BSG + s->lun = 0; +// FIXME: Get SCSI device type from BSG INQUIRY + s->qdev.type = TYPE_DISK; + DPRINTF("LUN %d\n", s->lun); + DPRINTF("device type %d\n", s->qdev.type); + + if (s->qdev.type == TYPE_TAPE) { + s->qdev.blocksize = bsg_get_stream_blocksize(s->bs); + if (s->qdev.blocksize == -1) + s->qdev.blocksize = 0; + } else { + s->qdev.blocksize = bsg_get_blocksize(s->bs); + /* removable media returns 0 if not present */ + if (s->qdev.blocksize <= 0) { + if (s->qdev.type == TYPE_ROM || s->qdev.type == TYPE_WORM) + s->qdev.blocksize = 2048; + else + s->qdev.blocksize = 512; + } + } + DPRINTF("block size %d\n", s->qdev.blocksize); + s->driver_status = 0; + memset(s->sensebuf, 0, sizeof(s->sensebuf)); + return 0; +} + +static SCSIRequest *bsg_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun) +{ + return scsi_req_alloc(sizeof(SCSIBSGReq), d, tag, lun); +} + +static SCSIRequest *bsg_new_request_iovec(SCSIDevice *d, uint32_t tag, + uint32_t lun, struct iovec *iov, int iov_num) +{ + SCSIRequest *req; + SCSIBSGReq *r; + + req = scsi_req_alloc(sizeof(SCSIBSGReq), d, tag, lun); + if (!req) + return NULL; + + r = DO_UPCAST(SCSIBSGReq, req, req); + r->saved_iov = iov; + r->saved_iov_num = iov_num; + + qemu_iovec_init(&r->aio_iov, 1); + + r->buf = NULL; + r->buflen = 0; + + return req; +} + +static void bsg_remove_request(SCSIRequest *req) +{ + SCSIBSGReq *r = DO_UPCAST(SCSIBSGReq, req, req); + + qemu_free(r->buf); + qemu_iovec_destroy(&r->aio_iov); + scsi_req_free(&r->req); +} + +/* + * Used as a QEMU block completion callback by bsg_send_command() + * for SG_DXFER_NONE and zero-length SCSI_XFER_TO_DEV. + */ +static void bsg_complete_none(void *opaque, int ret) +{ + SCSIRequest *req = opaque; + SCSIBSGReq *r = DO_UPCAST(SCSIBSGReq, req, req); + SCSIBSGState *s = DO_UPCAST(SCSIBSGState, qdev, r->req.dev); + struct sg_io_v4 io_hdr; + int err; + + req->aiocb = NULL; + + memset(&io_hdr, 0, sizeof(io_hdr)); + /* [i] 'Q' to differentiate from v3 */ + io_hdr.guard = 'Q'; + + err = bsg_read(s->bs->fd, &io_hdr, sizeof(io_hdr)); + if (err) { + DPRINTF("bsg_read() failed with ret: %d\n", err); + return; + } + + bsg_command_complete(opaque, ret); +} + +static int32_t bsg_send_command(SCSIRequest *req, uint8_t *cmd) +{ + SCSIBSGReq *r = DO_UPCAST(SCSIBSGReq, req, req); + SCSIBSGState *s = DO_UPCAST(SCSIBSGState, qdev, r->req.dev); + int ret; + + if (cmd[0] != REQUEST_SENSE && + (req->lun != s->lun || (cmd[1] >> 5) != s->lun)) { + + DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : cmd[1] >> 5); + bsg_command_complete(r, -ENODEV); + return 0; + } + + if (-1 == scsi_req_parse(&r->req, cmd)) { + BADF("Unsupported command length, command %x\n", cmd[0]); + bsg_command_complete(r, -EINVAL); + return 0; + } + bsg_req_fixup(&r->req); + + DPRINTF_BSG_IO("bsg_send_command: lun=%d tag=0x%x len %zd data=0x%02x", + req->lun, req->tag, r->req.cmd.xfer, cmd[0]); + +#ifdef DEBUG_BSG_IO + { + int i; + for (i = 1; i < r->req.cmd.len; i++) { + printf(" 0x%02x", cmd[i]); + } + printf("\n"); + } +#endif + + if (r->req.cmd.xfer == 0) { + if (r->buf != NULL) + qemu_free(r->buf); + r->buflen = 0; + r->buf = NULL; + ret = bsg_execute_command(s->bs, r, bsg_complete_none); + if (ret == -1) { + bsg_command_complete(r, -EBADR); + return 0; + } + return 0; + } + + if (!r->saved_iov_num) { + if (r->buflen != r->req.cmd.xfer) { + if (r->buf != NULL) + qemu_free(r->buf); + + r->buf = qemu_malloc(r->req.cmd.xfer); + r->buflen = r->req.cmd.xfer; + } + + memset(r->buf, 0, r->buflen); + } + r->len = r->req.cmd.xfer; + if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { + r->len = 0; + return r->req.cmd.xfer; + } + + return r->req.cmd.xfer; +} + +/* Return a pointer to the data buffer. */ +static uint8_t *bsg_get_buf(SCSIRequest *req) +{ + SCSIBSGReq *r = DO_UPCAST(SCSIBSGReq, req, req); + + return r->buf; +} + +static void bsg_cancel_io(SCSIRequest *req) +{ + SCSIBSGReq *r = DO_UPCAST(SCSIBSGReq, req, req); + + if (r->req.aiocb) { + bdrv_aio_cancel(r->req.aiocb); + r->req.aiocb = NULL; + } +} + +static SCSIDeviceInfo bsg_info = { + .qdev.name = "scsi-bsg", + .qdev.desc = "pass through block layer scsi generic (/dev/bsg/*)", + .qdev.size = sizeof(SCSIBSGState), + .qdev.reset = bsg_reset, + .init = bsg_generic_initfn, + .destroy = bsg_destroy, + .alloc_req = bsg_new_request, + .alloc_req_iov = bsg_new_request_iovec, + .free_req = bsg_remove_request, + .send_command = bsg_send_command, + .read_data = bsg_read_data, + .write_data = bsg_write_data, + .cancel_io = bsg_cancel_io, + .get_buf = bsg_get_buf, + .get_sense = bsg_get_sense, + .qdev.props = (Property[]) { + DEFINE_BLOCK_PROPERTIES(SCSIBSGState, qdev.conf), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void bsg_register_devices(void) +{ + scsi_qdev_register(&bsg_info); +} +device_init(bsg_register_devices) + +#endif /* __linux__ */ -- 1.5.6.5