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 | 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 | ... | ... |