Commit 1063b8b15fb49fcf88ffa282b19aaaf7ca9c678c
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>
Showing
2 changed files
with
128 additions
and
7 deletions
hw/virtio-blk.c
| @@ -15,6 +15,9 @@ | @@ -15,6 +15,9 @@ | ||
| 15 | #include <sysemu.h> | 15 | #include <sysemu.h> |
| 16 | #include "virtio-blk.h" | 16 | #include "virtio-blk.h" |
| 17 | #include "block_int.h" | 17 | #include "block_int.h" |
| 18 | +#ifdef __linux__ | ||
| 19 | +# include <scsi/sg.h> | ||
| 20 | +#endif | ||
| 18 | 21 | ||
| 19 | typedef struct VirtIOBlock | 22 | typedef struct VirtIOBlock |
| 20 | { | 23 | { |
| @@ -35,6 +38,7 @@ typedef struct VirtIOBlockReq | @@ -35,6 +38,7 @@ typedef struct VirtIOBlockReq | ||
| 35 | VirtQueueElement elem; | 38 | VirtQueueElement elem; |
| 36 | struct virtio_blk_inhdr *in; | 39 | struct virtio_blk_inhdr *in; |
| 37 | struct virtio_blk_outhdr *out; | 40 | struct virtio_blk_outhdr *out; |
| 41 | + struct virtio_scsi_inhdr *scsi; | ||
| 38 | QEMUIOVector qiov; | 42 | QEMUIOVector qiov; |
| 39 | struct VirtIOBlockReq *next; | 43 | struct VirtIOBlockReq *next; |
| 40 | } VirtIOBlockReq; | 44 | } VirtIOBlockReq; |
| @@ -103,6 +107,108 @@ static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s) | @@ -103,6 +107,108 @@ static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s) | ||
| 103 | return req; | 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 | static void virtio_blk_handle_write(VirtIOBlockReq *req) | 212 | static void virtio_blk_handle_write(VirtIOBlockReq *req) |
| 107 | { | 213 | { |
| 108 | bdrv_aio_writev(req->dev->bs, req->out->sector, &req->qiov, | 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,12 +242,7 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) | ||
| 136 | req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base; | 242 | req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base; |
| 137 | 243 | ||
| 138 | if (req->out->type & VIRTIO_BLK_T_SCSI_CMD) { | 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 | } else if (req->out->type & VIRTIO_BLK_T_OUT) { | 246 | } else if (req->out->type & VIRTIO_BLK_T_OUT) { |
| 146 | qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1], | 247 | qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1], |
| 147 | req->elem.out_num - 1); | 248 | req->elem.out_num - 1); |
| @@ -203,7 +304,15 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) | @@ -203,7 +304,15 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) | ||
| 203 | 304 | ||
| 204 | static uint32_t virtio_blk_get_features(VirtIODevice *vdev) | 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 | static void virtio_blk_save(QEMUFile *f, void *opaque) | 318 | static void virtio_blk_save(QEMUFile *f, void *opaque) |
hw/virtio-blk.h
| @@ -28,6 +28,9 @@ | @@ -28,6 +28,9 @@ | ||
| 28 | #define VIRTIO_BLK_F_SIZE_MAX 1 /* Indicates maximum segment size */ | 28 | #define VIRTIO_BLK_F_SIZE_MAX 1 /* Indicates maximum segment size */ |
| 29 | #define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */ | 29 | #define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */ |
| 30 | #define VIRTIO_BLK_F_GEOMETRY 4 /* Indicates support of legacy geometry */ | 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 | struct virtio_blk_config | 35 | struct virtio_blk_config |
| 33 | { | 36 | { |
| @@ -70,6 +73,15 @@ struct virtio_blk_inhdr | @@ -70,6 +73,15 @@ struct virtio_blk_inhdr | ||
| 70 | unsigned char status; | 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 | void *virtio_blk_init(PCIBus *bus, BlockDriverState *bs); | 85 | void *virtio_blk_init(PCIBus *bus, BlockDriverState *bs); |
| 74 | 86 | ||
| 75 | #endif | 87 | #endif |