From patchwork Tue Apr 28 09:57:14 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christoph Hellwig X-Patchwork-Id: 20388 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n3S9vXEc026181 for ; Tue, 28 Apr 2009 09:57:33 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758212AbZD1J52 (ORCPT ); Tue, 28 Apr 2009 05:57:28 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1756763AbZD1J51 (ORCPT ); Tue, 28 Apr 2009 05:57:27 -0400 Received: from verein.lst.de ([213.95.11.210]:44762 "EHLO verein.lst.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1758092AbZD1J50 (ORCPT ); Tue, 28 Apr 2009 05:57:26 -0400 Received: from verein.lst.de (localhost [127.0.0.1]) by verein.lst.de (8.12.3/8.12.3/Debian-7.1) with ESMTP id n3S9vEIF004861 (version=TLSv1/SSLv3 cipher=EDH-RSA-DES-CBC3-SHA bits=168 verify=NO); Tue, 28 Apr 2009 11:57:15 +0200 Received: (from hch@localhost) by verein.lst.de (8.12.3/8.12.3/Debian-6.6) id n3S9vEvx004859; Tue, 28 Apr 2009 11:57:14 +0200 Date: Tue, 28 Apr 2009 11:57:14 +0200 From: Christoph Hellwig To: qemu-devel@nongnu.org Cc: Christian Borntraeger , Rusty Russell , Hannes Reinecke , kvm@vger.kernel.org Subject: [PATCH 2/2] virtio-blk: add SG_IO passthru support Message-ID: <20090428095714.GA4693@lst.de> References: <20090427082606.GA32604@lst.de> <20090427082914.GA383@lst.de> Mime-Version: 1.0 Content-Disposition: inline In-Reply-To: <20090427082914.GA383@lst.de> User-Agent: Mutt/1.3.28i X-Spam-Score: 0 () X-Scanned-By: MIMEDefang 2.39 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org 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 --- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html 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 #include "virtio-blk.h" #include "block_int.h" +#ifdef __linux__ +# include +#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)