Commit 1063b8b15fb49fcf88ffa282b19aaaf7ca9c678c

Authored by Christoph Hellwig
Committed by Anthony Liguori
1 parent 451c4abd

virtio-blk: add SGI_IO passthru support

[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 <hch@lst.de>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
hw/virtio-blk.c
... ... @@ -15,6 +15,9 @@
15 15 #include <sysemu.h>
16 16 #include "virtio-blk.h"
17 17 #include "block_int.h"
  18 +#ifdef __linux__
  19 +# include <scsi/sg.h>
  20 +#endif
18 21  
19 22 typedef struct VirtIOBlock
20 23 {
... ... @@ -35,6 +38,7 @@ typedef struct VirtIOBlockReq
35 38 VirtQueueElement elem;
36 39 struct virtio_blk_inhdr *in;
37 40 struct virtio_blk_outhdr *out;
  41 + struct virtio_scsi_inhdr *scsi;
38 42 QEMUIOVector qiov;
39 43 struct VirtIOBlockReq *next;
40 44 } VirtIOBlockReq;
... ... @@ -103,6 +107,108 @@ static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s)
103 107 return req;
104 108 }
105 109  
  110 +#ifdef __linux__
  111 +static void virtio_blk_handle_scsi(VirtIOBlockReq *req)
  112 +{
  113 + struct sg_io_hdr hdr;
  114 + int ret, size = 0;
  115 + int status;
  116 + int i;
  117 +
  118 + /*
  119 + * We require at least one output segment each for the virtio_blk_outhdr
  120 + * and the SCSI command block.
  121 + *
  122 + * We also at least require the virtio_blk_inhdr, the virtio_scsi_inhdr
  123 + * and the sense buffer pointer in the input segments.
  124 + */
  125 + if (req->elem.out_num < 2 || req->elem.in_num < 3) {
  126 + virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
  127 + return;
  128 + }
  129 +
  130 + /*
  131 + * No support for bidirection commands yet.
  132 + */
  133 + if (req->elem.out_num > 2 && req->elem.in_num > 3) {
  134 + virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
  135 + return;
  136 + }
  137 +
  138 + /*
  139 + * The scsi inhdr is placed in the second-to-last input segment, just
  140 + * before the regular inhdr.
  141 + */
  142 + req->scsi = (void *)req->elem.in_sg[req->elem.in_num - 2].iov_base;
  143 + size = sizeof(*req->in) + sizeof(*req->scsi);
  144 +
  145 + memset(&hdr, 0, sizeof(struct sg_io_hdr));
  146 + hdr.interface_id = 'S';
  147 + hdr.cmd_len = req->elem.out_sg[1].iov_len;
  148 + hdr.cmdp = req->elem.out_sg[1].iov_base;
  149 + hdr.dxfer_len = 0;
  150 +
  151 + if (req->elem.out_num > 2) {
  152 + /*
  153 + * If there are more than the minimally required 2 output segments
  154 + * there is write payload starting from the third iovec.
  155 + */
  156 + hdr.dxfer_direction = SG_DXFER_TO_DEV;
  157 + hdr.iovec_count = req->elem.out_num - 2;
  158 +
  159 + for (i = 0; i < hdr.iovec_count; i++)
  160 + hdr.dxfer_len += req->elem.out_sg[i + 2].iov_len;
  161 +
  162 + hdr.dxferp = req->elem.out_sg + 2;
  163 +
  164 + } else if (req->elem.in_num > 3) {
  165 + /*
  166 + * If we have more than 3 input segments the guest wants to actually
  167 + * read data.
  168 + */
  169 + hdr.dxfer_direction = SG_DXFER_FROM_DEV;
  170 + hdr.iovec_count = req->elem.in_num - 3;
  171 + for (i = 0; i < hdr.iovec_count; i++)
  172 + hdr.dxfer_len += req->elem.in_sg[i].iov_len;
  173 +
  174 + hdr.dxferp = req->elem.in_sg;
  175 + size += hdr.dxfer_len;
  176 + } else {
  177 + /*
  178 + * Some SCSI commands don't actually transfer any data.
  179 + */
  180 + hdr.dxfer_direction = SG_DXFER_NONE;
  181 + }
  182 +
  183 + hdr.sbp = req->elem.in_sg[req->elem.in_num - 3].iov_base;
  184 + hdr.mx_sb_len = req->elem.in_sg[req->elem.in_num - 3].iov_len;
  185 + size += hdr.mx_sb_len;
  186 +
  187 + ret = bdrv_ioctl(req->dev->bs, SG_IO, &hdr);
  188 + if (ret) {
  189 + status = VIRTIO_BLK_S_UNSUPP;
  190 + hdr.status = ret;
  191 + hdr.resid = hdr.dxfer_len;
  192 + } else if (hdr.status) {
  193 + status = VIRTIO_BLK_S_IOERR;
  194 + } else {
  195 + status = VIRTIO_BLK_S_OK;
  196 + }
  197 +
  198 + req->scsi->errors = hdr.status;
  199 + req->scsi->residual = hdr.resid;
  200 + req->scsi->sense_len = hdr.sb_len_wr;
  201 + req->scsi->data_len = hdr.dxfer_len;
  202 +
  203 + virtio_blk_req_complete(req, status);
  204 +}
  205 +#else
  206 +static void virtio_blk_handle_scsi(VirtIOBlockReq *req)
  207 +{
  208 + virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
  209 +}
  210 +#endif /* __linux__ */
  211 +
106 212 static void virtio_blk_handle_write(VirtIOBlockReq *req)
107 213 {
108 214 bdrv_aio_writev(req->dev->bs, req->out->sector, &req->qiov,
... ... @@ -136,12 +242,7 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
136 242 req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base;
137 243  
138 244 if (req->out->type & VIRTIO_BLK_T_SCSI_CMD) {
139   - unsigned int len = sizeof(*req->in);
140   -
141   - req->in->status = VIRTIO_BLK_S_UNSUPP;
142   - virtqueue_push(vq, &req->elem, len);
143   - virtio_notify(vdev, vq);
144   - qemu_free(req);
  245 + virtio_blk_handle_scsi(req);
145 246 } else if (req->out->type & VIRTIO_BLK_T_OUT) {
146 247 qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1],
147 248 req->elem.out_num - 1);
... ... @@ -203,7 +304,15 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
203 304  
204 305 static uint32_t virtio_blk_get_features(VirtIODevice *vdev)
205 306 {
206   - return (1 << VIRTIO_BLK_F_SEG_MAX | 1 << VIRTIO_BLK_F_GEOMETRY);
  307 + uint32_t features = 0;
  308 +
  309 + features |= (1 << VIRTIO_BLK_F_SEG_MAX);
  310 + features |= (1 << VIRTIO_BLK_F_GEOMETRY);
  311 +#ifdef __linux__
  312 + features |= (1 << VIRTIO_BLK_F_SCSI);
  313 +#endif
  314 +
  315 + return features;
207 316 }
208 317  
209 318 static void virtio_blk_save(QEMUFile *f, void *opaque)
... ...
hw/virtio-blk.h
... ... @@ -28,6 +28,9 @@
28 28 #define VIRTIO_BLK_F_SIZE_MAX 1 /* Indicates maximum segment size */
29 29 #define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */
30 30 #define VIRTIO_BLK_F_GEOMETRY 4 /* Indicates support of legacy geometry */
  31 +#define VIRTIO_BLK_F_RO 5 /* Disk is read-only */
  32 +#define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/
  33 +#define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */
31 34  
32 35 struct virtio_blk_config
33 36 {
... ... @@ -70,6 +73,15 @@ struct virtio_blk_inhdr
70 73 unsigned char status;
71 74 };
72 75  
  76 +/* SCSI pass-through header */
  77 +struct virtio_scsi_inhdr
  78 +{
  79 + uint32_t errors;
  80 + uint32_t data_len;
  81 + uint32_t sense_len;
  82 + uint32_t residual;
  83 +};
  84 +
73 85 void *virtio_blk_init(PCIBus *bus, BlockDriverState *bs);
74 86  
75 87 #endif
... ...