Changed command interface between kernel and userspace.
[qemu/ovp.git] / qemu-vio-blk.c
blobe45e5666c1b06779518be4a116290b23e6fcf2a8
1 #include <stdio.h>
2 #include <stdint.h>
5 #include <qemu-common.h>
6 #include <sysemu.h>
7 #include "qemu-vio-blk.h"
8 #include "block_int.h"
9 #ifdef __linux__
10 # include <scsi/sg.h>
11 #endif
13 char *progname;
14 extern unsigned long int num_reads;
15 extern unsigned long int num_writes;
16 extern unsigned long int num_scsi_cmds;
17 extern unsigned long int num_flushs;
18 extern unsigned long int bytes_wrote;
19 extern unsigned long int bytes_read;
21 //#define trace() printf("qemu-vio-blk: %s-%s: %s(%d)\n",__TIME__, __FILE__,__func__, __LINE__)
22 #define trace() ;
25 static void qemu_vio_blk_req_complete(VirtIOBlockReq *req, int status)
28 if (req->out->type & VIRTIO_BLK_T_FLUSH) {
29 bytes_wrote+=req->qiov.size;
30 } else if (req->out->type & VIRTIO_BLK_T_SCSI_CMD) {
31 bytes_wrote+=req->qiov.size;
32 } else if (req->out->type & VIRTIO_BLK_T_OUT) {
33 bytes_wrote+=req->qiov.size;
34 } else {
35 bytes_read+=req->qiov.size;
38 VirtIOBlock *s = req->dev;
39 trace();
40 req->in->status = status;
41 virtqueue_push(s->vq, &req->elem, req->qiov.size + sizeof(*req->in));
43 if (!req)
44 free(req);
47 static void qemu_vio_blk_flush_complete(void *opaque, int ret)
49 VirtIOBlockReq *req = opaque;
50 trace();
51 qemu_vio_blk_req_complete(req, ret ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK);
54 #ifdef __linux__
55 static void qemu_vio_blk_handle_scsi(VirtIOBlockReq *req)
57 struct sg_io_hdr hdr;
58 int ret, size = 0;
59 int status;
60 int i;
61 trace();
63 * We require at least one output segment each for the virtio_blk_outhdr
64 * and the SCSI command block.
66 * We also at least require the virtio_blk_inhdr, the virtio_scsi_inhdr
67 * and the sense buffer pointer in the input segments.
69 if (req->elem.out_num < 2 || req->elem.in_num < 3) {
70 qemu_vio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
71 return;
75 * No support for bidirection commands yet.
77 if (req->elem.out_num > 2 && req->elem.in_num > 3) {
78 qemu_vio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
79 return;
83 * The scsi inhdr is placed in the second-to-last input segment, just
84 * before the regular inhdr.
86 req->scsi = (void *)req->elem.in_sg[req->elem.in_num - 2].iov_base;
87 size = sizeof(*req->in) + sizeof(*req->scsi);
89 memset(&hdr, 0, sizeof(struct sg_io_hdr));
90 hdr.interface_id = 'S';
91 hdr.cmd_len = req->elem.out_sg[1].iov_len;
92 hdr.cmdp = req->elem.out_sg[1].iov_base;
93 hdr.dxfer_len = 0;
95 if (req->elem.out_num > 2) {
97 * If there are more than the minimally required 2 output segments
98 * there is write payload starting from the third iovec.
100 hdr.dxfer_direction = SG_DXFER_TO_DEV;
101 hdr.iovec_count = req->elem.out_num - 2;
103 for (i = 0; i < hdr.iovec_count; i++)
104 hdr.dxfer_len += req->elem.out_sg[i + 2].iov_len;
106 hdr.dxferp = req->elem.out_sg + 2;
108 } else if (req->elem.in_num > 3) {
110 * If we have more than 3 input segments the guest wants to actually
111 * read data.
113 hdr.dxfer_direction = SG_DXFER_FROM_DEV;
114 hdr.iovec_count = req->elem.in_num - 3;
115 for (i = 0; i < hdr.iovec_count; i++)
116 hdr.dxfer_len += req->elem.in_sg[i].iov_len;
118 hdr.dxferp = req->elem.in_sg;
119 size += hdr.dxfer_len;
120 } else {
122 * Some SCSI commands don't actually transfer any data.
124 hdr.dxfer_direction = SG_DXFER_NONE;
127 hdr.sbp = req->elem.in_sg[req->elem.in_num - 3].iov_base;
128 hdr.mx_sb_len = req->elem.in_sg[req->elem.in_num - 3].iov_len;
129 size += hdr.mx_sb_len;
131 ret = bdrv_ioctl(req->dev->bs, SG_IO, &hdr);
132 if (ret) {
133 status = VIRTIO_BLK_S_UNSUPP;
134 hdr.status = ret;
135 hdr.resid = hdr.dxfer_len;
136 } else if (hdr.status) {
137 status = VIRTIO_BLK_S_IOERR;
138 } else {
139 status = VIRTIO_BLK_S_OK;
142 req->scsi->errors = hdr.status;
143 req->scsi->residual = hdr.resid;
144 req->scsi->sense_len = hdr.sb_len_wr;
145 req->scsi->data_len = hdr.dxfer_len;
147 qemu_vio_blk_req_complete(req, status);
149 #else
150 static void qemu_vio_blk_handle_scsi(VirtIOBlockReq *req)
152 qemu_vio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
154 #endif /* __linux__ */
156 BlockErrorAction drive_get_on_error(
157 BlockDriverState *bdrv, int is_read)
159 trace();
161 DriveInfo *dinfo;
163 QTAILQ_FOREACH(dinfo, &drives, next) {
164 if (dinfo->bdrv == bdrv)
165 return is_read ? dinfo->on_read_error : dinfo->on_write_error;
168 return is_read ? BLOCK_ERR_REPORT : BLOCK_ERR_STOP_ENOSPC;
170 return BLOCK_ERR_REPORT;
173 static void q_vm_stop(int reason)
175 trace();
179 static int qemu_vio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
180 int is_read)
182 trace();
183 BlockErrorAction action =
184 drive_get_on_error(req->dev->bs, is_read);
185 VirtIOBlock *s = req->dev;
188 if (action == BLOCK_ERR_IGNORE)
189 return 0;
191 if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC)
192 || action == BLOCK_ERR_STOP_ANY) {
193 req->next = s->rq;
194 s->rq = req;
195 q_vm_stop(0);
196 } else {
198 qemu_vio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
203 return 1;
206 static void qemu_vio_blk_rw_complete(void *opaque, int ret)
208 VirtIOBlockReq *req = opaque;
209 trace();
212 if (ret) {
214 int is_read = !(req->out->type & VIRTIO_BLK_T_OUT);
216 if (qemu_vio_blk_handle_rw_error(req, -ret, is_read))
217 return;
220 qemu_vio_blk_req_complete(req, VIRTIO_BLK_S_OK);
223 void do_multiwrite(BlockDriverState *bs, BlockRequest *blkreq,
224 int num_writes)
226 int i, ret;
227 trace();
229 for (i = 0; i < num_writes; i++) {
231 ret = bdrv_write(bs,blkreq[i].sector,blkreq[i].qiov->iov->iov_base,blkreq[i].nb_sectors);
232 blkreq[i].cb(blkreq[i].opaque,ret);
235 return;
237 //FIX: How to use bdrv_aio_multiwrite inside qemu-vio?
238 ret = bdrv_aio_multiwrite(bs, blkreq, num_writes);
240 trace();
241 if (ret != 0) {
242 for (i = 0; i < num_writes; i++) {
243 if (blkreq[i].error) {
244 qemu_vio_blk_rw_complete(blkreq[i].opaque, -EIO);
250 static void qemu_vio_blk_handle_write(BlockRequest *blkreq, int *num_writes,
251 VirtIOBlockReq *req, BlockDriverState **old_bs)
253 trace();
254 if (req->dev->bs != *old_bs || *num_writes == 32) {
255 if (*old_bs != NULL) {
256 do_multiwrite(*old_bs, blkreq, *num_writes);
258 *num_writes = 0;
259 *old_bs = req->dev->bs;
263 blkreq[*num_writes].sector = req->out->sector;
264 blkreq[*num_writes].nb_sectors = req->qiov.size / 512;
265 blkreq[*num_writes].qiov = &req->qiov;
266 blkreq[*num_writes].cb = qemu_vio_blk_rw_complete;
267 blkreq[*num_writes].opaque = req;
268 blkreq[*num_writes].error = 0;
270 (*num_writes)++;
273 static void qemu_vio_blk_handle_read(VirtIOBlockReq *req)
275 int ret,i, read_offset=0, remaining;
276 BlockDriverAIOCB *acb;
277 struct iovec *vec;
279 trace();
281 remaining = req->qiov.size;
283 for (i = 0; i<req->qiov.niov;i++) {
284 vec = &req->qiov.iov[i];
285 ret = bdrv_read(req->dev->bs,req->out->sector+read_offset,
286 vec->iov_base,vec->iov_len/512);
288 read_offset += vec->iov_len/512;
290 remaining = remaining - vec->iov_len;
292 if (remaining == 0) {
293 qemu_vio_blk_rw_complete(req,ret);
294 return;
298 qemu_vio_blk_rw_complete(req, -EIO);
300 return;
302 acb = bdrv_aio_readv(req->dev->bs, req->out->sector, &req->qiov,
303 req->qiov.size / 512, qemu_vio_blk_rw_complete, req);
306 trace();
307 if (!acb) {
308 qemu_vio_blk_rw_complete(req, -EIO);
310 trace();
313 static void qemu_vio_blk_handle_flush(BlockRequest *blkreq, int *num_writes,
314 VirtIOBlockReq *req, BlockDriverState **old_bs)
316 BlockDriverAIOCB *acb;
317 trace();
319 * Make sure all outstanding writes are posted to the backing device.
321 if (*old_bs != NULL) {
323 do_multiwrite(*old_bs, blkreq, *num_writes);
325 *num_writes = 0;
326 *old_bs = req->dev->bs;
328 acb = bdrv_aio_flush(req->dev->bs, qemu_vio_blk_flush_complete, req);
329 if (!acb) {
330 qemu_vio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
334 VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s)
337 VirtIOBlockReq *req = malloc(sizeof(VirtIOBlockReq));
339 trace();
341 bzero(req,sizeof(VirtIOBlockReq));
343 req->dev = s;
344 req->dev->bs = s->bs;
346 if (req != NULL) {
347 if (!virtqueue_pop(s->vq, &req->elem)) {
348 free(req);
349 return NULL;
353 return req;
357 void virtio_blk_handle_request(VirtIOBlockReq *req,
358 MultiReqBuffer *mrb)
361 trace();
362 if (req->elem.out_num < 1 || req->elem.in_num < 1) {
363 fprintf(stderr, "qemu-vio-blk: missing headers\n");
364 exit(1);
367 if (req->elem.out_sg[0].iov_len < sizeof(*req->out) ||
368 req->elem.in_sg[req->elem.in_num - 1].iov_len < sizeof(*req->in)) {
369 fprintf(stderr, "qemu-vio-blk: header not in correct element\n");
370 exit(1);
373 req->out = (void *)req->elem.out_sg[0].iov_base;
374 req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base;
376 if (req->out->type & VIRTIO_BLK_T_FLUSH) {
377 num_flushs++;
378 qemu_vio_blk_handle_flush(mrb->blkreq, &mrb->num_writes,
379 req, &mrb->old_bs);
380 } else if (req->out->type & VIRTIO_BLK_T_SCSI_CMD) {
381 num_scsi_cmds++;
382 qemu_vio_blk_handle_scsi(req);
383 } else if (req->out->type & VIRTIO_BLK_T_OUT) {
384 num_writes++;
385 qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1],
386 req->elem.out_num - 1);
387 qemu_vio_blk_handle_write(mrb->blkreq, &mrb->num_writes,
388 req, &mrb->old_bs);
389 } else {
390 num_reads++;
391 qemu_iovec_init_external(&req->qiov, &req->elem.in_sg[0],
392 req->elem.in_num - 1);
393 qemu_vio_blk_handle_read(req);
397 void qemu_vio_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs)
399 bdrv_guess_geometry(bs, pcyls, pheads, psecs);
402 int64_t qemu_vio_getlength(BlockDriverState *bs)
404 return bdrv_getlength(bs) / 512;