From mboxrd@z Thu Jan 1 00:00:00 1970 From: Christoph Hellwig Subject: [PATCH] virtio-blk: add SGI_IO passthru support Date: Mon, 27 Apr 2009 10:29:14 +0200 Message-ID: <20090427082914.GA383@lst.de> References: <20090427082606.GA32604@lst.de> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Cc: Rusty Russell , Hannes Reinecke , Christian Borntraeger , kvm@vger.kernel.org To: qemu-devel@nongnu.org Return-path: Received: from verein.lst.de ([213.95.11.210]:50845 "EHLO verein.lst.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752413AbZD0IaC (ORCPT ); Mon, 27 Apr 2009 04:30:02 -0400 Content-Disposition: inline In-Reply-To: <20090427082606.GA32604@lst.de> Sender: kvm-owner@vger.kernel.org List-ID: [had the qemu list address wrong the first time, reply to this message, not the previous if you were on Cc] 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. Note that currently we issue the hose SG_IO synchronously. We could easily switch to async I/O, but that would required either bloating the VirtIOBlockReq by the size of struct sg_io_hdr or an additional memory allocation for each SG_IO request. Signed-off-by: Christoph Hellwig Index: qemu/hw/virtio-blk.h =================================================================== --- qemu.orig/hw/virtio-blk.h 2009-04-26 16:50:38.154074532 +0200 +++ qemu/hw/virtio-blk.h 2009-04-26 22:51:16.838076869 +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-26 16:50:38.160074667 +0200 +++ qemu/hw/virtio-blk.c 2009-04-27 10:25:19.278074514 +0200 @@ -15,6 +15,9 @@ #include #include "virtio-blk.h" #include "block_int.h" +#ifdef __linux__ +# include +#endif typedef struct VirtIOBlock { @@ -35,6 +38,7 @@ typedef struct VirtIOBlockReq VirtQueueElement elem; struct virtio_blk_inhdr *in; struct virtio_blk_outhdr *out; + struct virtio_scsi_inhdr *scsi; QEMUIOVector qiov; struct VirtIOBlockReq *next; } VirtIOBlockReq; @@ -103,6 +107,108 @@ static VirtIOBlockReq *virtio_blk_get_re return req; } +#ifdef __linux__ +static void virtio_blk_handle_scsi(VirtIOBlockReq *req) +{ + struct sg_io_hdr hdr; + int ret, size = 0; + int status; + 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; + size = sizeof(*req->in) + sizeof(*req->scsi); + + memset(&hdr, 0, sizeof(struct sg_io_hdr)); + hdr.interface_id = 'S'; + hdr.cmd_len = req->elem.out_sg[1].iov_len; + hdr.cmdp = req->elem.out_sg[1].iov_base; + 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. + */ + hdr.dxfer_direction = SG_DXFER_TO_DEV; + hdr.iovec_count = req->elem.out_num - 2; + + for (i = 0; i < hdr.iovec_count; i++) + hdr.dxfer_len += req->elem.out_sg[i + 2].iov_len; + + 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. + */ + hdr.dxfer_direction = SG_DXFER_FROM_DEV; + hdr.iovec_count = req->elem.in_num - 3; + for (i = 0; i < hdr.iovec_count; i++) + hdr.dxfer_len += req->elem.in_sg[i].iov_len; + + hdr.dxferp = req->elem.in_sg; + size += hdr.dxfer_len; + } else { + /* + * Some SCSI commands don't actually transfer any data. + */ + hdr.dxfer_direction = SG_DXFER_NONE; + } + + hdr.sbp = req->elem.in_sg[req->elem.in_num - 3].iov_base; + hdr.mx_sb_len = req->elem.in_sg[req->elem.in_num - 3].iov_len; + size += hdr.mx_sb_len; + + ret = bdrv_ioctl(req->dev->bs, SG_IO, &hdr); + if (ret) { + status = VIRTIO_BLK_S_UNSUPP; + hdr.status = ret; + hdr.resid = hdr.dxfer_len; + } else if (hdr.status) { + status = VIRTIO_BLK_S_IOERR; + } else { + status = VIRTIO_BLK_S_OK; + } + + req->scsi->errors = hdr.status; + req->scsi->residual = hdr.resid; + req->scsi->sense_len = hdr.sb_len_wr; + req->scsi->data_len = hdr.dxfer_len; + + virtio_blk_req_complete(req, status); +} +#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 +242,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 +304,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) From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1LyMDu-0002py-M5 for qemu-devel@nongnu.org; Mon, 27 Apr 2009 04:29:58 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1LyMDp-0002mW-Uu for qemu-devel@nongnu.org; Mon, 27 Apr 2009 04:29:57 -0400 Received: from [199.232.76.173] (port=50918 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1LyMDp-0002mK-Mk for qemu-devel@nongnu.org; Mon, 27 Apr 2009 04:29:53 -0400 Received: from verein.lst.de ([213.95.11.210]:50840) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_3DES_EDE_CBC_SHA1:24) (Exim 4.60) (envelope-from ) id 1LyMDp-0000kV-2j for qemu-devel@nongnu.org; Mon, 27 Apr 2009 04:29:53 -0400 Date: Mon, 27 Apr 2009 10:29:14 +0200 From: Christoph Hellwig Message-ID: <20090427082914.GA383@lst.de> References: <20090427082606.GA32604@lst.de> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20090427082606.GA32604@lst.de> Subject: [Qemu-devel] [PATCH] virtio-blk: add SGI_IO passthru support List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: Christian Borntraeger , Rusty Russell , Hannes Reinecke , kvm@vger.kernel.org [had the qemu list address wrong the first time, reply to this message, not the previous if you were on Cc] 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. Note that currently we issue the hose SG_IO synchronously. We could easily switch to async I/O, but that would required either bloating the VirtIOBlockReq by the size of struct sg_io_hdr or an additional memory allocation for each SG_IO request. Signed-off-by: Christoph Hellwig Index: qemu/hw/virtio-blk.h =================================================================== --- qemu.orig/hw/virtio-blk.h 2009-04-26 16:50:38.154074532 +0200 +++ qemu/hw/virtio-blk.h 2009-04-26 22:51:16.838076869 +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-26 16:50:38.160074667 +0200 +++ qemu/hw/virtio-blk.c 2009-04-27 10:25:19.278074514 +0200 @@ -15,6 +15,9 @@ #include #include "virtio-blk.h" #include "block_int.h" +#ifdef __linux__ +# include +#endif typedef struct VirtIOBlock { @@ -35,6 +38,7 @@ typedef struct VirtIOBlockReq VirtQueueElement elem; struct virtio_blk_inhdr *in; struct virtio_blk_outhdr *out; + struct virtio_scsi_inhdr *scsi; QEMUIOVector qiov; struct VirtIOBlockReq *next; } VirtIOBlockReq; @@ -103,6 +107,108 @@ static VirtIOBlockReq *virtio_blk_get_re return req; } +#ifdef __linux__ +static void virtio_blk_handle_scsi(VirtIOBlockReq *req) +{ + struct sg_io_hdr hdr; + int ret, size = 0; + int status; + 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; + size = sizeof(*req->in) + sizeof(*req->scsi); + + memset(&hdr, 0, sizeof(struct sg_io_hdr)); + hdr.interface_id = 'S'; + hdr.cmd_len = req->elem.out_sg[1].iov_len; + hdr.cmdp = req->elem.out_sg[1].iov_base; + 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. + */ + hdr.dxfer_direction = SG_DXFER_TO_DEV; + hdr.iovec_count = req->elem.out_num - 2; + + for (i = 0; i < hdr.iovec_count; i++) + hdr.dxfer_len += req->elem.out_sg[i + 2].iov_len; + + 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. + */ + hdr.dxfer_direction = SG_DXFER_FROM_DEV; + hdr.iovec_count = req->elem.in_num - 3; + for (i = 0; i < hdr.iovec_count; i++) + hdr.dxfer_len += req->elem.in_sg[i].iov_len; + + hdr.dxferp = req->elem.in_sg; + size += hdr.dxfer_len; + } else { + /* + * Some SCSI commands don't actually transfer any data. + */ + hdr.dxfer_direction = SG_DXFER_NONE; + } + + hdr.sbp = req->elem.in_sg[req->elem.in_num - 3].iov_base; + hdr.mx_sb_len = req->elem.in_sg[req->elem.in_num - 3].iov_len; + size += hdr.mx_sb_len; + + ret = bdrv_ioctl(req->dev->bs, SG_IO, &hdr); + if (ret) { + status = VIRTIO_BLK_S_UNSUPP; + hdr.status = ret; + hdr.resid = hdr.dxfer_len; + } else if (hdr.status) { + status = VIRTIO_BLK_S_IOERR; + } else { + status = VIRTIO_BLK_S_OK; + } + + req->scsi->errors = hdr.status; + req->scsi->residual = hdr.resid; + req->scsi->sense_len = hdr.sb_len_wr; + req->scsi->data_len = hdr.dxfer_len; + + virtio_blk_req_complete(req, status); +} +#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 +242,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 +304,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)