On Mon, Jun 14, 2010 at 9:44 AM, Nicholas A. Bellinger <nab@xxxxxxxxxxxxxxx> wrote: > From: Nicholas Bellinger <nab@xxxxxxxxxxxxxxx> > > 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. Did I miss the docs? > > So far it has been tested with x86_64 host and guest using hw/megasas.c and TCM_Loop LLD > Port LUNs. Because this path 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 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 <nab@xxxxxxxxxxxxxxx> > --- > Makefile.objs | 2 +- > hw/scsi-bsg.c | 588 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 589 insertions(+), 1 deletions(-) > create mode 100644 hw/scsi-bsg.c > > diff --git a/Makefile.objs b/Makefile.objs > index 188d617..c4fcb72 100644 > --- a/Makefile.objs > +++ b/Makefile.objs > @@ -197,7 +197,7 @@ hw-obj-$(CONFIG_IDE_CMD646) += ide/cmd646.o > hw-obj-$(CONFIG_IDE_MACIO) += ide/macio.o > > # SCSI layer > -hw-obj-y += scsi-disk.o scsi-generic.o > +hw-obj-y += scsi-disk.o scsi-generic.o scsi-bsg.o Instead of '#ifdef __linux__' (which should be '#ifdef CONFIG_LINUX'), please compile the object only if CONFIG_LINUX is set, something like: hw-obj-$(CONFIG_LINUX) += scsi-bsg.o Please also check if this could be compiled in common-obj set. > hw-obj-y += lsi53c895a.o megasas.o > hw-obj-$(CONFIG_ESP) += esp.o > > diff --git a/hw/scsi-bsg.c b/hw/scsi-bsg.c > new file mode 100644 > index 0000000..fc76b76 > --- /dev/null > +++ b/hw/scsi-bsg.c > @@ -0,0 +1,588 @@ > +/* > + * block layer implementation of the sg v4 interface for Linux hosts > + * > + * Copyright (c) 2010 Rising Tide Systems > + * Written by Nicholas A. Bellinger <nab@xxxxxxxxxxxxxxx> > + * > + * 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 "dma.h" > +#include "block/raw-posix-aio.h" > + > +#ifdef __linux__ > + > +#define DEBUG_BSG > +#undef DEBUG_BSG_IO > +#undef DEBUG_BSG_MAP This should be //#define DEBUG_BSG //#define DEBUG_BSG_IO //#define DEBUG_BSG_MAP > + > +#ifdef DEBUG_BSG > +#define DPRINTF(fmt, ...) \ > +do { printf("scsi-bsg: " fmt , ## __VA_ARGS__); } while (0) > +#else > +#define DPRINTF(fmt, ...) do {} while(0) > +#endif > + > +#define BADF(fmt, ...) \ > +do { fprintf(stderr, "scsi-bsg: " fmt , ## __VA_ARGS__); } while (0) > + > +#include <stdio.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <sys/epoll.h> > +#include <unistd.h> > +#include <scsi/sg.h> > +#include <linux/bsg.h> > +#include "scsi-defs.h" > + > +#define SCSI_SENSE_BUF_SIZE 96 > + > +#define SG_ERR_DRIVER_TIMEOUT 0x06 > +#define SG_ERR_DRIVER_SENSE 0x08 > + > +#ifndef MAX_UINT > +#define MAX_UINT ((unsigned int)-1) The standard macro is UINT_MAX. > +#endif > + > +typedef struct SCSIBSGState SCSIBSGState; > + > +typedef struct SCSIBSGReq { > + SCSIRequest req; > + uint8_t *buf; > + int buflen; > + QEMUIOVector iov; > + 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 { > + printf("bsg device %d read failed, errno: %d\n", > + fd, errno); DPRINTF? > + return errno; > + } > + } > + return 0; > +} > + > +static SCSIBSGReq *bsg_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun) > +{ > + SCSIRequest *req; > + SCSIBSGReq *r; > + > + req = scsi_req_alloc(sizeof(SCSIBSGReq), d, tag, lun); > + r = DO_UPCAST(SCSIBSGReq, req, req); > + qemu_iovec_init(&r->iov, 1); > + qemu_iovec_init(&r->aio_iov, 1); > + return r; > +} > + > +static void bsg_remove_request(SCSIBSGReq *r) > +{ > + qemu_free(r->buf); > + qemu_iovec_destroy(&r->iov); > + qemu_iovec_destroy(&r->aio_iov); > + scsi_req_free(&r->req); > +} > + > +static void bsg_command_complete(void *opaque, int ret) > +{ > + SCSIBSGReq *r = (SCSIBSGReq *)opaque; Useless cast in C. > + 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) { > + scsi_req_print(&r->req); > + fprintf(stderr, "%s: ret %d (%s)\n", __FUNCTION__, > + ret, strerror(-ret)); error_report()? > + s->senselen = scsi_build_sense(SENSE_CODE(INVALID_FIELD), > + s->sensebuf, SCSI_SENSE_BUF_SIZE, 0); > + 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); > + fprintf(stderr, "%s: timeout\n", __FUNCTION__); > + r->req.status = BUSY << 1; > + } 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); > + fprintf(stderr, "%s: driver sense\n", __FUNCTION__); > + r->req.status = CHECK_CONDITION << 1; > + } else { > + r->req.status = GOOD << 1; > + } > + } > +#ifdef DEBUG_BSG_IO > + DPRINTF("Command complete 0x%p tag=0x%x status=%d\n", > + r, r->req.tag, r->req.status); Please introduce DPRINTF_BSG_IO and remove #ifdef/#endif. > +#endif > + scsi_req_complete(&r->req); > +} > + > +static int bsg_execute_command_run(SCSIBSGReq *r, > + BlockDriverCompletionFunc *complete) > +{ > + BlockDriverState *bdrv = r->req.dev->conf.dinfo->bdrv; > + 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 = MAX_UINT; > + /* [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; Does something initialize r->bsg_hdr.flags before? > + > + 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 int bsg_execute_command_buf(SCSIBSGReq *r, > + BlockDriverCompletionFunc *complete, > + uint8_t *buf, uint32_t buflen) > +{ > + if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { > + r->bsg_hdr.dout_xferp = (unsigned long)buf; > + r->bsg_hdr.dout_xfer_len = buflen; > + } else if (r->req.cmd.mode == SCSI_XFER_FROM_DEV) { > + r->bsg_hdr.din_xferp = (unsigned long)buf; > + r->bsg_hdr.din_xfer_len = buflen; > + } > +#ifdef DEBUG_BSG_IO > + DPRINTF("execute BUF: %p, dxfer_len %u\n", buf, buflen); > +#endif > + return bsg_execute_command_run(r, complete); > +} > + > +static int bsg_execute_command_iov(SCSIBSGReq *r, > + BlockDriverCompletionFunc *complete, > + QEMUIOVector *iov) > +{ > + if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { > + r->bsg_hdr.dout_iovec_count = iov->niov; > + r->bsg_hdr.dout_xferp = (unsigned long)iov->iov; > + r->bsg_hdr.dout_xfer_len = iov->size; > + } else if (r->req.cmd.mode == SCSI_XFER_FROM_DEV) { > + r->bsg_hdr.din_iovec_count = iov->niov; > + r->bsg_hdr.din_xferp = (unsigned long)iov->iov; > + r->bsg_hdr.din_xfer_len = iov->size; > + } > +#ifdef DEBUG_BSG_IO > + DPRINTF("execute IOV: iovec_count: %u, iov: %p, size: %u\n", > + iov->niov, iov->iov, (unsigned int)iov->size); You can remove the cast by using '%zu'. > +#endif > + return bsg_execute_command_run(r, complete); > +} > + > +static void bsg_write_complete(void *opaque, int ret) > +{ > + SCSIBSGReq *r = (SCSIBSGReq *)opaque; > + SCSIBSGState *s = DO_UPCAST(SCSIBSGState, qdev, r->req.dev); > +#ifdef DEBUG_BSG_IO > + DPRINTF("bsg_write_complete() ret = %d\n", ret); > +#endif > + if (ret) { > + DPRINTF("IO error\n"); > + bsg_command_complete(r, ret); > + 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 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_destroy(SCSIDevice *d) > +{ > + SCSIBSGState *s = DO_UPCAST(SCSIBSGState, qdev, d); > + SCSIBSGReq *r; > + > + while (!QTAILQ_EMPTY(&s->qdev.requests)) { > + r = DO_UPCAST(SCSIBSGReq, req, QTAILQ_FIRST(&s->qdev.requests)); > + bsg_remove_request(r); > + } > + drive_uninit(s->qdev.conf.dinfo); > +} > + > +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.dinfo || !s->qdev.conf.dinfo->bdrv) { > + error_report("scsi-bsg: drive property not set"); > + return -1; > + } > + s->bs = s->qdev.conf.dinfo->bdrv; > + > + /* 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 Dead code shouldn't be committed. > +// 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 void bsg_generic_unmap(SCSIBSGReq *r) > +{ > + int is_write = !scsi_req_is_write(&r->req); > + int i; > + > + for (i = 0; i < r->iov.niov; i++) { > + cpu_physical_memory_unmap(r->iov.iov[i].iov_base, > + r->iov.iov[i].iov_len, is_write, > + r->iov.iov[i].iov_len); > + } > + qemu_iovec_reset(&r->iov); > +} > + > +static int bsg_generic_map(SCSIBSGReq *r, QEMUSGList *sg) > +{ > + int is_write = !scsi_req_is_write(&r->req); > + target_phys_addr_t cur_addr, cur_len, cur_offset = 0; > + void *mem; > + int i; > + > + qemu_iovec_reset(&r->iov); > + for (i = 0; i < sg->nsg;) { > + cur_addr = sg->sg[i].base + cur_offset; > + cur_len = sg->sg[i].len - cur_offset; > +#ifdef DEBUG_BSG_MAP > + DPRINTF("Using cur_addr: 0x%016lx cur_len: 0x%016lx\n", > + (long unsigned int)cur_addr, (long unsigned int)cur_len); Please use TARGET_FMT_plx for cur_addr and cur_len. > +#endif > + mem = cpu_physical_memory_map(cur_addr, &cur_len, is_write); > + if (!mem) > + goto err; > +#ifdef DEBUG_BSG_MAP > + DPRINTF("Adding iovec for mem: %p len: 0x%016lx\n", mem, > + (long unsigned int)cur_len); Same here. > +#endif > + qemu_iovec_add(&r->iov, mem, cur_len); > + > + cur_offset += cur_len; > + if (cur_offset == sg->sg[i].len) { > + cur_offset = 0; > + i++; > + } > + } > + > + return 0; > + > +err: > + bsg_generic_unmap(r); > + return -1; > +} > + > +static SCSIRequest *bsg_generic_req_get(SCSIDevice *d, uint32_t tag, int lun) > +{ > + SCSIBSGReq *r; > + > + r = bsg_new_request(d, tag, lun); > + return &r->req; > +} > + > +static void bsg_generic_req_cb(void *opaque, int ret) > +{ > + SCSIRequest *req = opaque; > + SCSIBSGReq *r = DO_UPCAST(SCSIBSGReq, req, req); > + SCSIBSGState *s = DO_UPCAST(SCSIBSGState, qdev, r->req.dev); > + BlockDriverCompletionFunc *complete; > + 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) { > + printf("bsg_read() failed with ret: %d\n", err); > + return; > + } > + > + if (r->iov.niov) > + bsg_generic_unmap(r); > + > + if (scsi_req_is_write(req)) { > + req->xferlen = r->bsg_hdr.dout_xfer_len; > + complete = bsg_write_complete; > + } else { > + req->xferlen = r->bsg_hdr.din_xfer_len; > + complete = bsg_command_complete; > + } > + complete(opaque, ret); > +} > + > +static int bsg_generic_req_common(SCSIRequest *req, uint8_t *buffer) > +{ > + SCSIBSGReq *r = DO_UPCAST(SCSIBSGReq, req, req); > + SCSIBSGState *s = DO_UPCAST(SCSIBSGState, qdev, r->req.dev); > + > + bsg_req_fixup(&r->req); > + if (req->cmd.buf[0] != REQUEST_SENSE && > + (req->lun != s->lun || (req->cmd.buf[1] >> 5) != s->lun)) { > + > + DPRINTF("Unimplemented LUN %d\n", > + req->lun ? req->lun : req->cmd.buf[1] >> 5); > + s->senselen = scsi_build_sense(SENSE_CODE(LUN_NOT_SUPPORTED), > + s->sensebuf, SCSI_SENSE_BUF_SIZE, 0); > + s->driver_status = SG_ERR_DRIVER_SENSE; > + req->status = CHECK_CONDITION << 1; > + return 1; > + } > + if (r->req.cmd.buf[0] == REQUEST_SENSE && > + s->driver_status & SG_ERR_DRIVER_SENSE) { > + req->xferlen = MIN(req->cmd.xfer, s->senselen); > + memcpy(buffer, s->sensebuf, req->xferlen); > + 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", > + buffer[0], buffer[1], buffer[2], buffer[3], > + buffer[4], buffer[5], buffer[6], buffer[7]); > + req->status = GOOD; > + return 1; > + } > + return 0; > +} > + > +static int bsg_generic_req_buf(SCSIRequest *req, uint8_t *buffer) > +{ > + SCSIBSGReq *r = DO_UPCAST(SCSIBSGReq, req, req); > + int handled, ret; > + > + handled = bsg_generic_req_common(req, buffer); > + if (handled) { > + scsi_req_complete(req); > + return 0; > + } > + > + req->xferlen = req->cmd.xfer; > + ret = bsg_execute_command_buf(r, bsg_generic_req_cb, buffer, req->xferlen); > + if (ret == -1) { > + bsg_command_complete(r, -EINVAL); > + } > + return 0; > +} > + > +static int bsg_generic_req_sgl(SCSIRequest *req, QEMUSGList *sg) > +{ > + SCSIBSGReq *r = DO_UPCAST(SCSIBSGReq, req, req); > + int handled, ret; > + > + if (bsg_generic_map(r, sg) != 0) { > + /* Hmm ... */ > + abort(); > + } > + handled = bsg_generic_req_common(req, r->iov.iov[0].iov_base); > + if (handled) { > + bsg_generic_unmap(r); > + scsi_req_complete(req); > + return 0; > + } > + > + req->xferlen = req->cmd.xfer; > + ret = bsg_execute_command_iov(r, bsg_generic_req_cb, &r->iov); > + if (ret == -1) { > + bsg_generic_unmap(r); > + bsg_command_complete(r, -EINVAL); > + } > + return 0; > +} > + > +static void bsg_generic_req_put(SCSIRequest *req) > +{ > + SCSIBSGReq *r = DO_UPCAST(SCSIBSGReq, req, req); > + > + if (r->req.aiocb) { > + bdrv_aio_cancel(r->req.aiocb); > + } > + bsg_remove_request(r); > +} > + > +static SCSIDeviceInfo bsg_info = { > + .qdev.name = "scsi-bsg", > + .qdev.desc = "pass through block layer scsi generic (/dev/bsg/*)", > + .qdev.size = sizeof(SCSIBSGState), > + .init = bsg_generic_initfn, > + .destroy = bsg_destroy, > + > + /* new */ > + .request_get = bsg_generic_req_get, > + .request_buf = bsg_generic_req_buf, > + .request_sgl = bsg_generic_req_sgl, > + .request_put = bsg_generic_req_put, > + > + .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 > > > -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html