]> Git Repo - qemu.git/blame - hw/virtio-blk.c
PCI network qdev conversion
[qemu.git] / hw / virtio-blk.c
CommitLineData
6e02c38d
AL
1/*
2 * Virtio Block Device
3 *
4 * Copyright IBM, Corp. 2007
5 *
6 * Authors:
7 * Anthony Liguori <[email protected]>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
11 *
12 */
13
869a5c6d
AL
14#include <qemu-common.h>
15#include <sysemu.h>
6e02c38d
AL
16#include "virtio-blk.h"
17#include "block_int.h"
1063b8b1
CH
18#ifdef __linux__
19# include <scsi/sg.h>
20#endif
6e02c38d
AL
21
22typedef struct VirtIOBlock
23{
24 VirtIODevice vdev;
25 BlockDriverState *bs;
26 VirtQueue *vq;
869a5c6d 27 void *rq;
6e02c38d
AL
28} VirtIOBlock;
29
30static VirtIOBlock *to_virtio_blk(VirtIODevice *vdev)
31{
32 return (VirtIOBlock *)vdev;
33}
34
35typedef struct VirtIOBlockReq
36{
37 VirtIOBlock *dev;
38 VirtQueueElement elem;
39 struct virtio_blk_inhdr *in;
40 struct virtio_blk_outhdr *out;
1063b8b1 41 struct virtio_scsi_inhdr *scsi;
d28a1b6e 42 QEMUIOVector qiov;
869a5c6d 43 struct VirtIOBlockReq *next;
6e02c38d
AL
44} VirtIOBlockReq;
45
869a5c6d
AL
46static void virtio_blk_req_complete(VirtIOBlockReq *req, int status)
47{
48 VirtIOBlock *s = req->dev;
49
50 req->in->status = status;
d28a1b6e 51 virtqueue_push(s->vq, &req->elem, req->qiov.size + sizeof(*req->in));
869a5c6d
AL
52 virtio_notify(&s->vdev, s->vq);
53
869a5c6d
AL
54 qemu_free(req);
55}
56
57static int virtio_blk_handle_write_error(VirtIOBlockReq *req, int error)
58{
59 BlockInterfaceErrorAction action = drive_get_onerror(req->dev->bs);
60 VirtIOBlock *s = req->dev;
61
62 if (action == BLOCK_ERR_IGNORE)
63 return 0;
64
65 if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC)
66 || action == BLOCK_ERR_STOP_ANY) {
67 req->next = s->rq;
68 s->rq = req;
69 vm_stop(0);
70 } else {
71 virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
72 }
73
74 return 1;
75}
76
6e02c38d
AL
77static void virtio_blk_rw_complete(void *opaque, int ret)
78{
79 VirtIOBlockReq *req = opaque;
6e02c38d 80
d28a1b6e 81 if (ret && (req->out->type & VIRTIO_BLK_T_OUT)) {
869a5c6d
AL
82 if (virtio_blk_handle_write_error(req, -ret))
83 return;
6e02c38d
AL
84 }
85
869a5c6d
AL
86 virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
87}
6e02c38d 88
869a5c6d
AL
89static VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s)
90{
91 VirtIOBlockReq *req = qemu_mallocz(sizeof(*req));
487414f1 92 req->dev = s;
869a5c6d 93 return req;
6e02c38d
AL
94}
95
96static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s)
97{
869a5c6d 98 VirtIOBlockReq *req = virtio_blk_alloc_request(s);
6e02c38d 99
869a5c6d
AL
100 if (req != NULL) {
101 if (!virtqueue_pop(s->vq, &req->elem)) {
102 qemu_free(req);
103 return NULL;
104 }
6e02c38d
AL
105 }
106
107 return req;
108}
109
1063b8b1
CH
110#ifdef __linux__
111static 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
206static void virtio_blk_handle_scsi(VirtIOBlockReq *req)
207{
208 virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
209}
210#endif /* __linux__ */
211
d28a1b6e 212static void virtio_blk_handle_write(VirtIOBlockReq *req)
869a5c6d 213{
d28a1b6e
AL
214 bdrv_aio_writev(req->dev->bs, req->out->sector, &req->qiov,
215 req->qiov.size / 512, virtio_blk_rw_complete, req);
216}
869a5c6d 217
d28a1b6e
AL
218static void virtio_blk_handle_read(VirtIOBlockReq *req)
219{
220 bdrv_aio_readv(req->dev->bs, req->out->sector, &req->qiov,
221 req->qiov.size / 512, virtio_blk_rw_complete, req);
869a5c6d
AL
222}
223
6e02c38d
AL
224static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
225{
226 VirtIOBlock *s = to_virtio_blk(vdev);
227 VirtIOBlockReq *req;
228
229 while ((req = virtio_blk_get_request(s))) {
6e02c38d
AL
230 if (req->elem.out_num < 1 || req->elem.in_num < 1) {
231 fprintf(stderr, "virtio-blk missing headers\n");
232 exit(1);
233 }
234
235 if (req->elem.out_sg[0].iov_len < sizeof(*req->out) ||
236 req->elem.in_sg[req->elem.in_num - 1].iov_len < sizeof(*req->in)) {
237 fprintf(stderr, "virtio-blk header not in correct element\n");
238 exit(1);
239 }
240
241 req->out = (void *)req->elem.out_sg[0].iov_base;
242 req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base;
243
244 if (req->out->type & VIRTIO_BLK_T_SCSI_CMD) {
1063b8b1 245 virtio_blk_handle_scsi(req);
6e02c38d 246 } else if (req->out->type & VIRTIO_BLK_T_OUT) {
d28a1b6e
AL
247 qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1],
248 req->elem.out_num - 1);
249 virtio_blk_handle_write(req);
6e02c38d 250 } else {
d28a1b6e
AL
251 qemu_iovec_init_external(&req->qiov, &req->elem.in_sg[0],
252 req->elem.in_num - 1);
253 virtio_blk_handle_read(req);
6e02c38d
AL
254 }
255 }
256 /*
257 * FIXME: Want to check for completions before returning to guest mode,
258 * so cached reads and writes are reported as quickly as possible. But
259 * that should be done in the generic block layer.
260 */
261}
262
869a5c6d
AL
263static void virtio_blk_dma_restart_cb(void *opaque, int running, int reason)
264{
265 VirtIOBlock *s = opaque;
266 VirtIOBlockReq *req = s->rq;
267
268 if (!running)
269 return;
270
271 s->rq = NULL;
272
273 while (req) {
274 virtio_blk_handle_write(req);
275 req = req->next;
276 }
277}
278
6e02c38d
AL
279static void virtio_blk_reset(VirtIODevice *vdev)
280{
281 /*
282 * This should cancel pending requests, but can't do nicely until there
283 * are per-device request lists.
284 */
285 qemu_aio_flush();
286}
287
288static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
289{
290 VirtIOBlock *s = to_virtio_blk(vdev);
291 struct virtio_blk_config blkcfg;
292 uint64_t capacity;
293 int cylinders, heads, secs;
294
295 bdrv_get_geometry(s->bs, &capacity);
296 bdrv_get_geometry_hint(s->bs, &cylinders, &heads, &secs);
297 stq_raw(&blkcfg.capacity, capacity);
298 stl_raw(&blkcfg.seg_max, 128 - 2);
299 stw_raw(&blkcfg.cylinders, cylinders);
300 blkcfg.heads = heads;
301 blkcfg.sectors = secs;
302 memcpy(config, &blkcfg, sizeof(blkcfg));
303}
304
305static uint32_t virtio_blk_get_features(VirtIODevice *vdev)
306{
1063b8b1
CH
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;
6e02c38d
AL
316}
317
318static void virtio_blk_save(QEMUFile *f, void *opaque)
319{
320 VirtIOBlock *s = opaque;
869a5c6d
AL
321 VirtIOBlockReq *req = s->rq;
322
6e02c38d 323 virtio_save(&s->vdev, f);
869a5c6d
AL
324
325 while (req) {
326 qemu_put_sbyte(f, 1);
327 qemu_put_buffer(f, (unsigned char*)&req->elem, sizeof(req->elem));
328 req = req->next;
329 }
330 qemu_put_sbyte(f, 0);
6e02c38d
AL
331}
332
333static int virtio_blk_load(QEMUFile *f, void *opaque, int version_id)
334{
335 VirtIOBlock *s = opaque;
336
869a5c6d 337 if (version_id != 2)
6e02c38d
AL
338 return -EINVAL;
339
340 virtio_load(&s->vdev, f);
869a5c6d
AL
341 while (qemu_get_sbyte(f)) {
342 VirtIOBlockReq *req = virtio_blk_alloc_request(s);
343 qemu_get_buffer(f, (unsigned char*)&req->elem, sizeof(req->elem));
344 req->next = s->rq;
345 s->rq = req->next;
346 }
6e02c38d
AL
347
348 return 0;
349}
350
9b32d5a5 351void *virtio_blk_init(PCIBus *bus, BlockDriverState *bs)
6e02c38d
AL
352{
353 VirtIOBlock *s;
354 int cylinders, heads, secs;
355 static int virtio_blk_id;
356
9b32d5a5
AL
357 s = (VirtIOBlock *)virtio_init_pci(bus, "virtio-blk",
358 PCI_VENDOR_ID_REDHAT_QUMRANET,
359 PCI_DEVICE_ID_VIRTIO_BLOCK,
99b3718e
AL
360 PCI_VENDOR_ID_REDHAT_QUMRANET,
361 VIRTIO_ID_BLOCK,
173a543b 362 PCI_CLASS_STORAGE_OTHER, 0x00,
6e02c38d
AL
363 sizeof(struct virtio_blk_config), sizeof(VirtIOBlock));
364 if (!s)
365 return NULL;
366
367 s->vdev.get_config = virtio_blk_update_config;
368 s->vdev.get_features = virtio_blk_get_features;
369 s->vdev.reset = virtio_blk_reset;
370 s->bs = bs;
869a5c6d 371 s->rq = NULL;
b0a7b120 372 bs->private = &s->vdev.pci_dev;
6e02c38d
AL
373 bdrv_guess_geometry(s->bs, &cylinders, &heads, &secs);
374 bdrv_set_geometry_hint(s->bs, cylinders, heads, secs);
375
376 s->vq = virtio_add_queue(&s->vdev, 128, virtio_blk_handle_output);
377
869a5c6d
AL
378 qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s);
379 register_savevm("virtio-blk", virtio_blk_id++, 2,
6e02c38d
AL
380 virtio_blk_save, virtio_blk_load, s);
381
382 return s;
383}
This page took 0.134362 seconds and 4 git commands to generate.