Add support for SG_IO passthru (packet commands) to the virtio-blk backend. Conceptually based on an older patch from Hannes Reinecke but largely rewritten to match the code structure and layering in virtio-blk aswell as doing asynchronous I/O. Signed-off-by: Christoph Hellwig <hch@xxxxxx> Index: qemu/hw/virtio-blk.h =================================================================== --- qemu.orig/hw/virtio-blk.h 2009-04-28 11:42:14.059074434 +0200 +++ qemu/hw/virtio-blk.h 2009-04-28 11:44:24.930074531 +0200 @@ -28,6 +28,9 @@ #define VIRTIO_BLK_F_SIZE_MAX 1 /* Indicates maximum segment size */ #define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */ #define VIRTIO_BLK_F_GEOMETRY 4 /* Indicates support of legacy geometry */ +#define VIRTIO_BLK_F_RO 5 /* Disk is read-only */ +#define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/ +#define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */ struct virtio_blk_config { @@ -70,6 +73,15 @@ struct virtio_blk_inhdr unsigned char status; }; +/* SCSI pass-through header */ +struct virtio_scsi_inhdr +{ + uint32_t errors; + uint32_t data_len; + uint32_t sense_len; + uint32_t residual; +}; + void *virtio_blk_init(PCIBus *bus, BlockDriverState *bs); #endif Index: qemu/hw/virtio-blk.c =================================================================== --- qemu.orig/hw/virtio-blk.c 2009-04-28 11:42:14.066074487 +0200 +++ qemu/hw/virtio-blk.c 2009-04-28 11:52:45.836079580 +0200 @@ -15,6 +15,9 @@ #include <sysemu.h> #include "virtio-blk.h" #include "block_int.h" +#ifdef __linux__ +# include <scsi/sg.h> +#endif typedef struct VirtIOBlock { @@ -35,6 +38,8 @@ typedef struct VirtIOBlockReq VirtQueueElement elem; struct virtio_blk_inhdr *in; struct virtio_blk_outhdr *out; + struct virtio_scsi_inhdr *scsi; + struct sg_io_hdr scsi_hdr; QEMUIOVector qiov; struct VirtIOBlockReq *next; } VirtIOBlockReq; @@ -103,6 +108,108 @@ static VirtIOBlockReq *virtio_blk_get_re return req; } +#ifdef __linux__ +static void virtio_blk_scsi_complete(void *opaque, int ret) +{ + VirtIOBlockReq *req = opaque; + int status; + + if (ret) { + status = VIRTIO_BLK_S_UNSUPP; + req->scsi_hdr.status = -ret; + req->scsi_hdr.resid = req->scsi_hdr.dxfer_len; + } else if (req->scsi_hdr.status) { + status = VIRTIO_BLK_S_IOERR; + } else { + status = VIRTIO_BLK_S_OK; + } + + req->scsi->errors = req->scsi_hdr.status; + req->scsi->residual = req->scsi_hdr.resid; + req->scsi->sense_len = req->scsi_hdr.sb_len_wr; + req->scsi->data_len = req->scsi_hdr.dxfer_len; + + virtio_blk_req_complete(req, status); +} + +static void virtio_blk_handle_scsi(VirtIOBlockReq *req) +{ + int i; + + /* + * We require at least one output segment each for the virtio_blk_outhdr + * and the SCSI command block. + * + * We also at least require the virtio_blk_inhdr, the virtio_scsi_inhdr + * and the sense buffer pointer in the input segments. + */ + if (req->elem.out_num < 2 || req->elem.in_num < 3) { + virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); + return; + } + + /* + * No support for bidirection commands yet. + */ + if (req->elem.out_num > 2 && req->elem.in_num > 3) { + virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); + return; + } + + /* + * The scsi inhdr is placed in the second-to-last input segment, just + * before the regular inhdr. + */ + req->scsi = (void *)req->elem.in_sg[req->elem.in_num - 2].iov_base; + + memset(&req->scsi_hdr, 0, sizeof(struct sg_io_hdr)); + req->scsi_hdr.interface_id = 'S'; + req->scsi_hdr.cmd_len = req->elem.out_sg[1].iov_len; + req->scsi_hdr.cmdp = req->elem.out_sg[1].iov_base; + req->scsi_hdr.dxfer_len = 0; + + if (req->elem.out_num > 2) { + /* + * If there are more than the minimally required 2 output segments + * there is write payload starting from the third iovec. + */ + req->scsi_hdr.dxfer_direction = SG_DXFER_TO_DEV; + req->scsi_hdr.iovec_count = req->elem.out_num - 2; + + for (i = 0; i < req->scsi_hdr.iovec_count; i++) + req->scsi_hdr.dxfer_len += req->elem.out_sg[i + 2].iov_len; + req->scsi_hdr.dxferp = req->elem.out_sg + 2; + } else if (req->elem.in_num > 3) { + /* + * If we have more than 3 input segments the guest wants to actually + * read data. + */ + req->scsi_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + req->scsi_hdr.iovec_count = req->elem.in_num - 3; + + for (i = 0; i < req->scsi_hdr.iovec_count; i++) + req->scsi_hdr.dxfer_len += req->elem.in_sg[i].iov_len; + req->scsi_hdr.dxferp = req->elem.in_sg; + } else { + /* + * Some SCSI commands don't actually transfer any data. + */ + req->scsi_hdr.dxfer_direction = SG_DXFER_NONE; + } + + req->scsi_hdr.sbp = req->elem.in_sg[req->elem.in_num - 3].iov_base; + req->scsi_hdr.mx_sb_len = req->elem.in_sg[req->elem.in_num - 3].iov_len; + + bdrv_aio_ioctl(req->dev->bs, SG_IO, &req->scsi_hdr, + virtio_blk_scsi_complete, req); +} +#else +static void virtio_blk_handle_scsi(VirtIOBlockReq *req) +{ + virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); +} +#endif /* __linux__ */ + static void virtio_blk_handle_write(VirtIOBlockReq *req) { bdrv_aio_writev(req->dev->bs, req->out->sector, &req->qiov, @@ -136,12 +243,7 @@ static void virtio_blk_handle_output(Vir req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base; if (req->out->type & VIRTIO_BLK_T_SCSI_CMD) { - unsigned int len = sizeof(*req->in); - - req->in->status = VIRTIO_BLK_S_UNSUPP; - virtqueue_push(vq, &req->elem, len); - virtio_notify(vdev, vq); - qemu_free(req); + virtio_blk_handle_scsi(req); } else if (req->out->type & VIRTIO_BLK_T_OUT) { qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1], req->elem.out_num - 1); @@ -203,7 +305,15 @@ static void virtio_blk_update_config(Vir static uint32_t virtio_blk_get_features(VirtIODevice *vdev) { - return (1 << VIRTIO_BLK_F_SEG_MAX | 1 << VIRTIO_BLK_F_GEOMETRY); + uint32_t features = 0; + + features |= (1 << VIRTIO_BLK_F_SEG_MAX); + features |= (1 << VIRTIO_BLK_F_GEOMETRY); +#ifdef __linux__ + features |= (1 << VIRTIO_BLK_F_SCSI); +#endif + + return features; } static void virtio_blk_save(QEMUFile *f, void *opaque) -- 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