Fix warning
[qemu] / hw / virtio-blk.c
1 /*
2  * Virtio Block Device
3  *
4  * Copyright IBM, Corp. 2007
5  *
6  * Authors:
7  *  Anthony Liguori   <aliguori@us.ibm.com>
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
14 #include <qemu-common.h>
15 #include <sysemu.h>
16 #include "virtio-blk.h"
17 #include "block_int.h"
18
19 typedef struct VirtIOBlock
20 {
21     VirtIODevice vdev;
22     BlockDriverState *bs;
23     VirtQueue *vq;
24     void *rq;
25 } VirtIOBlock;
26
27 static VirtIOBlock *to_virtio_blk(VirtIODevice *vdev)
28 {
29     return (VirtIOBlock *)vdev;
30 }
31
32 typedef struct VirtIOBlockReq
33 {
34     VirtIOBlock *dev;
35     VirtQueueElement elem;
36     struct virtio_blk_inhdr *in;
37     struct virtio_blk_outhdr *out;
38     QEMUIOVector qiov;
39     struct VirtIOBlockReq *next;
40 } VirtIOBlockReq;
41
42 static void virtio_blk_req_complete(VirtIOBlockReq *req, int status)
43 {
44     VirtIOBlock *s = req->dev;
45
46     req->in->status = status;
47     virtqueue_push(s->vq, &req->elem, req->qiov.size + sizeof(*req->in));
48     virtio_notify(&s->vdev, s->vq);
49
50     qemu_free(req);
51 }
52
53 static int virtio_blk_handle_write_error(VirtIOBlockReq *req, int error)
54 {
55     BlockInterfaceErrorAction action = drive_get_onerror(req->dev->bs);
56     VirtIOBlock *s = req->dev;
57
58     if (action == BLOCK_ERR_IGNORE)
59         return 0;
60
61     if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC)
62             || action == BLOCK_ERR_STOP_ANY) {
63         req->next = s->rq;
64         s->rq = req;
65         vm_stop(0);
66     } else {
67         virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
68     }
69
70     return 1;
71 }
72
73 static void virtio_blk_rw_complete(void *opaque, int ret)
74 {
75     VirtIOBlockReq *req = opaque;
76
77     if (ret && (req->out->type & VIRTIO_BLK_T_OUT)) {
78         if (virtio_blk_handle_write_error(req, -ret))
79             return;
80     }
81
82     virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
83 }
84
85 static VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s)
86 {
87     VirtIOBlockReq *req = qemu_mallocz(sizeof(*req));
88     req->dev = s;
89     return req;
90 }
91
92 static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s)
93 {
94     VirtIOBlockReq *req = virtio_blk_alloc_request(s);
95
96     if (req != NULL) {
97         if (!virtqueue_pop(s->vq, &req->elem)) {
98             qemu_free(req);
99             return NULL;
100         }
101     }
102
103     return req;
104 }
105
106 static void virtio_blk_handle_write(VirtIOBlockReq *req)
107 {
108     bdrv_aio_writev(req->dev->bs, req->out->sector, &req->qiov,
109                     req->qiov.size / 512, virtio_blk_rw_complete, req);
110 }
111
112 static void virtio_blk_handle_read(VirtIOBlockReq *req)
113 {
114     bdrv_aio_readv(req->dev->bs, req->out->sector, &req->qiov,
115                    req->qiov.size / 512, virtio_blk_rw_complete, req);
116 }
117
118 static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
119 {
120     VirtIOBlock *s = to_virtio_blk(vdev);
121     VirtIOBlockReq *req;
122
123     while ((req = virtio_blk_get_request(s))) {
124         if (req->elem.out_num < 1 || req->elem.in_num < 1) {
125             fprintf(stderr, "virtio-blk missing headers\n");
126             exit(1);
127         }
128
129         if (req->elem.out_sg[0].iov_len < sizeof(*req->out) ||
130             req->elem.in_sg[req->elem.in_num - 1].iov_len < sizeof(*req->in)) {
131             fprintf(stderr, "virtio-blk header not in correct element\n");
132             exit(1);
133         }
134
135         req->out = (void *)req->elem.out_sg[0].iov_base;
136         req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base;
137
138         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);
145         } else if (req->out->type & VIRTIO_BLK_T_OUT) {
146             qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1],
147                                      req->elem.out_num - 1);
148             virtio_blk_handle_write(req);
149         } else {
150             qemu_iovec_init_external(&req->qiov, &req->elem.in_sg[0],
151                                      req->elem.in_num - 1);
152             virtio_blk_handle_read(req);
153         }
154     }
155     /*
156      * FIXME: Want to check for completions before returning to guest mode,
157      * so cached reads and writes are reported as quickly as possible. But
158      * that should be done in the generic block layer.
159      */
160 }
161
162 static void virtio_blk_dma_restart_cb(void *opaque, int running, int reason)
163 {
164     VirtIOBlock *s = opaque;
165     VirtIOBlockReq *req = s->rq;
166
167     if (!running)
168         return;
169
170     s->rq = NULL;
171
172     while (req) {
173         virtio_blk_handle_write(req);
174         req = req->next;
175     }
176 }
177
178 static void virtio_blk_reset(VirtIODevice *vdev)
179 {
180     /*
181      * This should cancel pending requests, but can't do nicely until there
182      * are per-device request lists.
183      */
184     qemu_aio_flush();
185 }
186
187 static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
188 {
189     VirtIOBlock *s = to_virtio_blk(vdev);
190     struct virtio_blk_config blkcfg;
191     uint64_t capacity;
192     int cylinders, heads, secs;
193
194     bdrv_get_geometry(s->bs, &capacity);
195     bdrv_get_geometry_hint(s->bs, &cylinders, &heads, &secs);
196     stq_raw(&blkcfg.capacity, capacity);
197     stl_raw(&blkcfg.seg_max, 128 - 2);
198     stw_raw(&blkcfg.cylinders, cylinders);
199     blkcfg.heads = heads;
200     blkcfg.sectors = secs;
201     memcpy(config, &blkcfg, sizeof(blkcfg));
202 }
203
204 static uint32_t virtio_blk_get_features(VirtIODevice *vdev)
205 {
206     return (1 << VIRTIO_BLK_F_SEG_MAX | 1 << VIRTIO_BLK_F_GEOMETRY);
207 }
208
209 static void virtio_blk_save(QEMUFile *f, void *opaque)
210 {
211     VirtIOBlock *s = opaque;
212     VirtIOBlockReq *req = s->rq;
213
214     virtio_save(&s->vdev, f);
215     
216     while (req) {
217         qemu_put_sbyte(f, 1);
218         qemu_put_buffer(f, (unsigned char*)&req->elem, sizeof(req->elem));
219         req = req->next;
220     }
221     qemu_put_sbyte(f, 0);
222 }
223
224 static int virtio_blk_load(QEMUFile *f, void *opaque, int version_id)
225 {
226     VirtIOBlock *s = opaque;
227
228     if (version_id != 2)
229         return -EINVAL;
230
231     virtio_load(&s->vdev, f);
232     while (qemu_get_sbyte(f)) {
233         VirtIOBlockReq *req = virtio_blk_alloc_request(s);
234         qemu_get_buffer(f, (unsigned char*)&req->elem, sizeof(req->elem));
235         req->next = s->rq;
236         s->rq = req->next;
237     }
238
239     return 0;
240 }
241
242 void *virtio_blk_init(PCIBus *bus, BlockDriverState *bs)
243 {
244     VirtIOBlock *s;
245     int cylinders, heads, secs;
246     static int virtio_blk_id;
247
248     s = (VirtIOBlock *)virtio_init_pci(bus, "virtio-blk",
249                                        PCI_VENDOR_ID_REDHAT_QUMRANET,
250                                        PCI_DEVICE_ID_VIRTIO_BLOCK,
251                                        PCI_VENDOR_ID_REDHAT_QUMRANET,
252                                        VIRTIO_ID_BLOCK,
253                                        PCI_CLASS_STORAGE_OTHER, 0x00,
254                                        sizeof(struct virtio_blk_config), sizeof(VirtIOBlock));
255     if (!s)
256         return NULL;
257
258     s->vdev.get_config = virtio_blk_update_config;
259     s->vdev.get_features = virtio_blk_get_features;
260     s->vdev.reset = virtio_blk_reset;
261     s->bs = bs;
262     s->rq = NULL;
263     bs->private = &s->vdev.pci_dev;
264     bdrv_guess_geometry(s->bs, &cylinders, &heads, &secs);
265     bdrv_set_geometry_hint(s->bs, cylinders, heads, secs);
266
267     s->vq = virtio_add_queue(&s->vdev, 128, virtio_blk_handle_output);
268
269     qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s);
270     register_savevm("virtio-blk", virtio_blk_id++, 2,
271                     virtio_blk_save, virtio_blk_load, s);
272
273     return s;
274 }