Statistics
| Branch: | Revision:

root / hw / virtio-blk.c @ 9b32d5a5

History | View | Annotate | Download (6.9 kB)

1 6e02c38d aliguori
/*
2 6e02c38d aliguori
 * Virtio Block Device
3 6e02c38d aliguori
 *
4 6e02c38d aliguori
 * Copyright IBM, Corp. 2007
5 6e02c38d aliguori
 *
6 6e02c38d aliguori
 * Authors:
7 6e02c38d aliguori
 *  Anthony Liguori   <aliguori@us.ibm.com>
8 6e02c38d aliguori
 *
9 6e02c38d aliguori
 * This work is licensed under the terms of the GNU GPL, version 2.  See
10 6e02c38d aliguori
 * the COPYING file in the top-level directory.
11 6e02c38d aliguori
 *
12 6e02c38d aliguori
 */
13 6e02c38d aliguori
14 6e02c38d aliguori
#include "virtio-blk.h"
15 6e02c38d aliguori
#include "block_int.h"
16 6e02c38d aliguori
17 6e02c38d aliguori
typedef struct VirtIOBlock
18 6e02c38d aliguori
{
19 6e02c38d aliguori
    VirtIODevice vdev;
20 6e02c38d aliguori
    BlockDriverState *bs;
21 6e02c38d aliguori
    VirtQueue *vq;
22 6e02c38d aliguori
} VirtIOBlock;
23 6e02c38d aliguori
24 6e02c38d aliguori
static VirtIOBlock *to_virtio_blk(VirtIODevice *vdev)
25 6e02c38d aliguori
{
26 6e02c38d aliguori
    return (VirtIOBlock *)vdev;
27 6e02c38d aliguori
}
28 6e02c38d aliguori
29 6e02c38d aliguori
typedef struct VirtIOBlockReq
30 6e02c38d aliguori
{
31 6e02c38d aliguori
    VirtIOBlock *dev;
32 6e02c38d aliguori
    VirtQueueElement elem;
33 6e02c38d aliguori
    struct virtio_blk_inhdr *in;
34 6e02c38d aliguori
    struct virtio_blk_outhdr *out;
35 6e02c38d aliguori
    size_t size;
36 6e02c38d aliguori
    uint8_t *buffer;
37 6e02c38d aliguori
} VirtIOBlockReq;
38 6e02c38d aliguori
39 6e02c38d aliguori
static void virtio_blk_rw_complete(void *opaque, int ret)
40 6e02c38d aliguori
{
41 6e02c38d aliguori
    VirtIOBlockReq *req = opaque;
42 6e02c38d aliguori
    VirtIOBlock *s = req->dev;
43 6e02c38d aliguori
44 6e02c38d aliguori
    /* Copy read data to the guest */
45 6e02c38d aliguori
    if (!ret && !(req->out->type & VIRTIO_BLK_T_OUT)) {
46 6e02c38d aliguori
        size_t offset = 0;
47 6e02c38d aliguori
        int i;
48 6e02c38d aliguori
49 6e02c38d aliguori
        for (i = 0; i < req->elem.in_num - 1; i++) {
50 6e02c38d aliguori
            size_t len;
51 6e02c38d aliguori
52 6e02c38d aliguori
            /* Be pretty defensive wrt malicious guests */
53 6e02c38d aliguori
            len = MIN(req->elem.in_sg[i].iov_len,
54 6e02c38d aliguori
                      req->size - offset);
55 6e02c38d aliguori
56 6e02c38d aliguori
            memcpy(req->elem.in_sg[i].iov_base,
57 6e02c38d aliguori
                   req->buffer + offset,
58 6e02c38d aliguori
                   len);
59 6e02c38d aliguori
            offset += len;
60 6e02c38d aliguori
        }
61 6e02c38d aliguori
    }
62 6e02c38d aliguori
63 6e02c38d aliguori
    req->in->status = ret ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
64 6e02c38d aliguori
    virtqueue_push(s->vq, &req->elem, req->size + sizeof(*req->in));
65 6e02c38d aliguori
    virtio_notify(&s->vdev, s->vq);
66 6e02c38d aliguori
67 6e02c38d aliguori
    qemu_free(req->buffer);
68 6e02c38d aliguori
    qemu_free(req);
69 6e02c38d aliguori
}
70 6e02c38d aliguori
71 6e02c38d aliguori
static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s)
72 6e02c38d aliguori
{
73 6e02c38d aliguori
    VirtIOBlockReq *req;
74 6e02c38d aliguori
75 6e02c38d aliguori
    req = qemu_mallocz(sizeof(*req));
76 6e02c38d aliguori
    if (req == NULL)
77 6e02c38d aliguori
        return NULL;
78 6e02c38d aliguori
79 6e02c38d aliguori
    req->dev = s;
80 6e02c38d aliguori
    if (!virtqueue_pop(s->vq, &req->elem)) {
81 6e02c38d aliguori
        qemu_free(req);
82 6e02c38d aliguori
        return NULL;
83 6e02c38d aliguori
    }
84 6e02c38d aliguori
85 6e02c38d aliguori
    return req;
86 6e02c38d aliguori
}
87 6e02c38d aliguori
88 6e02c38d aliguori
static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
89 6e02c38d aliguori
{
90 6e02c38d aliguori
    VirtIOBlock *s = to_virtio_blk(vdev);
91 6e02c38d aliguori
    VirtIOBlockReq *req;
92 6e02c38d aliguori
93 6e02c38d aliguori
    while ((req = virtio_blk_get_request(s))) {
94 6e02c38d aliguori
        int i;
95 6e02c38d aliguori
96 6e02c38d aliguori
        if (req->elem.out_num < 1 || req->elem.in_num < 1) {
97 6e02c38d aliguori
            fprintf(stderr, "virtio-blk missing headers\n");
98 6e02c38d aliguori
            exit(1);
99 6e02c38d aliguori
        }
100 6e02c38d aliguori
101 6e02c38d aliguori
        if (req->elem.out_sg[0].iov_len < sizeof(*req->out) ||
102 6e02c38d aliguori
            req->elem.in_sg[req->elem.in_num - 1].iov_len < sizeof(*req->in)) {
103 6e02c38d aliguori
            fprintf(stderr, "virtio-blk header not in correct element\n");
104 6e02c38d aliguori
            exit(1);
105 6e02c38d aliguori
        }
106 6e02c38d aliguori
107 6e02c38d aliguori
        req->out = (void *)req->elem.out_sg[0].iov_base;
108 6e02c38d aliguori
        req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base;
109 6e02c38d aliguori
110 6e02c38d aliguori
        if (req->out->type & VIRTIO_BLK_T_SCSI_CMD) {
111 6e02c38d aliguori
            unsigned int len = sizeof(*req->in);
112 6e02c38d aliguori
113 6e02c38d aliguori
            req->in->status = VIRTIO_BLK_S_UNSUPP;
114 6e02c38d aliguori
            virtqueue_push(vq, &req->elem, len);
115 6e02c38d aliguori
            virtio_notify(vdev, vq);
116 6e02c38d aliguori
            qemu_free(req);
117 6e02c38d aliguori
        } else if (req->out->type & VIRTIO_BLK_T_OUT) {
118 6e02c38d aliguori
            size_t offset;
119 6e02c38d aliguori
120 6e02c38d aliguori
            for (i = 1; i < req->elem.out_num; i++)
121 6e02c38d aliguori
                req->size += req->elem.out_sg[i].iov_len;
122 6e02c38d aliguori
123 6e02c38d aliguori
            req->buffer = qemu_memalign(512, req->size);
124 6e02c38d aliguori
            if (req->buffer == NULL) {
125 6e02c38d aliguori
                qemu_free(req);
126 6e02c38d aliguori
                break;
127 6e02c38d aliguori
            }
128 6e02c38d aliguori
129 6e02c38d aliguori
            /* We copy the data from the SG list to avoid splitting up the request.  This helps
130 6e02c38d aliguori
               performance a lot until we can pass full sg lists as AIO operations */
131 6e02c38d aliguori
            offset = 0;
132 6e02c38d aliguori
            for (i = 1; i < req->elem.out_num; i++) {
133 6e02c38d aliguori
                size_t len;
134 6e02c38d aliguori
135 6e02c38d aliguori
                len = MIN(req->elem.out_sg[i].iov_len,
136 6e02c38d aliguori
                          req->size - offset);
137 6e02c38d aliguori
                memcpy(req->buffer + offset,
138 6e02c38d aliguori
                       req->elem.out_sg[i].iov_base,
139 6e02c38d aliguori
                       len);
140 6e02c38d aliguori
                offset += len;
141 6e02c38d aliguori
            }
142 6e02c38d aliguori
143 6e02c38d aliguori
            bdrv_aio_write(s->bs, req->out->sector,
144 6e02c38d aliguori
                           req->buffer,
145 6e02c38d aliguori
                           req->size / 512,
146 6e02c38d aliguori
                           virtio_blk_rw_complete,
147 6e02c38d aliguori
                           req);
148 6e02c38d aliguori
        } else {
149 6e02c38d aliguori
            for (i = 0; i < req->elem.in_num - 1; i++)
150 6e02c38d aliguori
                req->size += req->elem.in_sg[i].iov_len;
151 6e02c38d aliguori
152 6e02c38d aliguori
            req->buffer = qemu_memalign(512, req->size);
153 6e02c38d aliguori
            if (req->buffer == NULL) {
154 6e02c38d aliguori
                qemu_free(req);
155 6e02c38d aliguori
                break;
156 6e02c38d aliguori
            }
157 6e02c38d aliguori
158 6e02c38d aliguori
            bdrv_aio_read(s->bs, req->out->sector,
159 6e02c38d aliguori
                          req->buffer,
160 6e02c38d aliguori
                          req->size / 512,
161 6e02c38d aliguori
                          virtio_blk_rw_complete,
162 6e02c38d aliguori
                          req);
163 6e02c38d aliguori
        }
164 6e02c38d aliguori
    }
165 6e02c38d aliguori
    /*
166 6e02c38d aliguori
     * FIXME: Want to check for completions before returning to guest mode,
167 6e02c38d aliguori
     * so cached reads and writes are reported as quickly as possible. But
168 6e02c38d aliguori
     * that should be done in the generic block layer.
169 6e02c38d aliguori
     */
170 6e02c38d aliguori
}
171 6e02c38d aliguori
172 6e02c38d aliguori
static void virtio_blk_reset(VirtIODevice *vdev)
173 6e02c38d aliguori
{
174 6e02c38d aliguori
    /*
175 6e02c38d aliguori
     * This should cancel pending requests, but can't do nicely until there
176 6e02c38d aliguori
     * are per-device request lists.
177 6e02c38d aliguori
     */
178 6e02c38d aliguori
    qemu_aio_flush();
179 6e02c38d aliguori
}
180 6e02c38d aliguori
181 6e02c38d aliguori
static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
182 6e02c38d aliguori
{
183 6e02c38d aliguori
    VirtIOBlock *s = to_virtio_blk(vdev);
184 6e02c38d aliguori
    struct virtio_blk_config blkcfg;
185 6e02c38d aliguori
    uint64_t capacity;
186 6e02c38d aliguori
    int cylinders, heads, secs;
187 6e02c38d aliguori
188 6e02c38d aliguori
    bdrv_get_geometry(s->bs, &capacity);
189 6e02c38d aliguori
    bdrv_get_geometry_hint(s->bs, &cylinders, &heads, &secs);
190 6e02c38d aliguori
    stq_raw(&blkcfg.capacity, capacity);
191 6e02c38d aliguori
    stl_raw(&blkcfg.seg_max, 128 - 2);
192 6e02c38d aliguori
    stw_raw(&blkcfg.cylinders, cylinders);
193 6e02c38d aliguori
    blkcfg.heads = heads;
194 6e02c38d aliguori
    blkcfg.sectors = secs;
195 6e02c38d aliguori
    memcpy(config, &blkcfg, sizeof(blkcfg));
196 6e02c38d aliguori
}
197 6e02c38d aliguori
198 6e02c38d aliguori
static uint32_t virtio_blk_get_features(VirtIODevice *vdev)
199 6e02c38d aliguori
{
200 6e02c38d aliguori
    return (1 << VIRTIO_BLK_F_SEG_MAX | 1 << VIRTIO_BLK_F_GEOMETRY);
201 6e02c38d aliguori
}
202 6e02c38d aliguori
203 6e02c38d aliguori
static void virtio_blk_save(QEMUFile *f, void *opaque)
204 6e02c38d aliguori
{
205 6e02c38d aliguori
    VirtIOBlock *s = opaque;
206 6e02c38d aliguori
    virtio_save(&s->vdev, f);
207 6e02c38d aliguori
}
208 6e02c38d aliguori
209 6e02c38d aliguori
static int virtio_blk_load(QEMUFile *f, void *opaque, int version_id)
210 6e02c38d aliguori
{
211 6e02c38d aliguori
    VirtIOBlock *s = opaque;
212 6e02c38d aliguori
213 6e02c38d aliguori
    if (version_id != 1)
214 6e02c38d aliguori
        return -EINVAL;
215 6e02c38d aliguori
216 6e02c38d aliguori
    virtio_load(&s->vdev, f);
217 6e02c38d aliguori
218 6e02c38d aliguori
    return 0;
219 6e02c38d aliguori
}
220 6e02c38d aliguori
221 9b32d5a5 aliguori
void *virtio_blk_init(PCIBus *bus, BlockDriverState *bs)
222 6e02c38d aliguori
{
223 6e02c38d aliguori
    VirtIOBlock *s;
224 6e02c38d aliguori
    int cylinders, heads, secs;
225 6e02c38d aliguori
    static int virtio_blk_id;
226 6e02c38d aliguori
227 9b32d5a5 aliguori
    s = (VirtIOBlock *)virtio_init_pci(bus, "virtio-blk",
228 9b32d5a5 aliguori
                                       PCI_VENDOR_ID_REDHAT_QUMRANET,
229 9b32d5a5 aliguori
                                       PCI_DEVICE_ID_VIRTIO_BLOCK,
230 6e02c38d aliguori
                                       0, VIRTIO_ID_BLOCK,
231 6e02c38d aliguori
                                       0x01, 0x80, 0x00,
232 6e02c38d aliguori
                                       sizeof(struct virtio_blk_config), sizeof(VirtIOBlock));
233 6e02c38d aliguori
    if (!s)
234 6e02c38d aliguori
        return NULL;
235 6e02c38d aliguori
236 6e02c38d aliguori
    s->vdev.get_config = virtio_blk_update_config;
237 6e02c38d aliguori
    s->vdev.get_features = virtio_blk_get_features;
238 6e02c38d aliguori
    s->vdev.reset = virtio_blk_reset;
239 6e02c38d aliguori
    s->bs = bs;
240 6e02c38d aliguori
    bdrv_guess_geometry(s->bs, &cylinders, &heads, &secs);
241 6e02c38d aliguori
    bdrv_set_geometry_hint(s->bs, cylinders, heads, secs);
242 6e02c38d aliguori
243 6e02c38d aliguori
    s->vq = virtio_add_queue(&s->vdev, 128, virtio_blk_handle_output);
244 6e02c38d aliguori
245 6e02c38d aliguori
    register_savevm("virtio-blk", virtio_blk_id++, 1,
246 6e02c38d aliguori
                    virtio_blk_save, virtio_blk_load, s);
247 6e02c38d aliguori
248 6e02c38d aliguori
    return s;
249 6e02c38d aliguori
}