4 * Copyright IBM, Corp. 2007
7 * Anthony Liguori <aliguori@us.ibm.com>
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
14 #include <qemu-common.h>
16 #include "virtio-blk.h"
17 #include "block_int.h"
19 typedef struct VirtIOBlock
27 static VirtIOBlock
*to_virtio_blk(VirtIODevice
*vdev
)
29 return (VirtIOBlock
*)vdev
;
32 typedef struct VirtIOBlockReq
35 VirtQueueElement elem
;
36 struct virtio_blk_inhdr
*in
;
37 struct virtio_blk_outhdr
*out
;
40 struct VirtIOBlockReq
*next
;
43 static void virtio_blk_req_complete(VirtIOBlockReq
*req
, int status
)
45 VirtIOBlock
*s
= req
->dev
;
47 req
->in
->status
= status
;
48 virtqueue_push(s
->vq
, &req
->elem
, req
->size
+ sizeof(*req
->in
));
49 virtio_notify(&s
->vdev
, s
->vq
);
51 qemu_free(req
->buffer
);
55 static int virtio_blk_handle_write_error(VirtIOBlockReq
*req
, int error
)
57 BlockInterfaceErrorAction action
= drive_get_onerror(req
->dev
->bs
);
58 VirtIOBlock
*s
= req
->dev
;
60 if (action
== BLOCK_ERR_IGNORE
)
63 if ((error
== ENOSPC
&& action
== BLOCK_ERR_STOP_ENOSPC
)
64 || action
== BLOCK_ERR_STOP_ANY
) {
69 virtio_blk_req_complete(req
, VIRTIO_BLK_S_IOERR
);
75 static void virtio_blk_rw_complete(void *opaque
, int ret
)
77 VirtIOBlockReq
*req
= opaque
;
79 /* Copy read data to the guest */
80 if (!ret
&& !(req
->out
->type
& VIRTIO_BLK_T_OUT
)) {
84 for (i
= 0; i
< req
->elem
.in_num
- 1; i
++) {
87 /* Be pretty defensive wrt malicious guests */
88 len
= MIN(req
->elem
.in_sg
[i
].iov_len
,
91 memcpy(req
->elem
.in_sg
[i
].iov_base
,
96 } else if (ret
&& (req
->out
->type
& VIRTIO_BLK_T_OUT
)) {
97 if (virtio_blk_handle_write_error(req
, -ret
))
101 virtio_blk_req_complete(req
, VIRTIO_BLK_S_OK
);
104 static VirtIOBlockReq
*virtio_blk_alloc_request(VirtIOBlock
*s
)
106 VirtIOBlockReq
*req
= qemu_mallocz(sizeof(*req
));
112 static VirtIOBlockReq
*virtio_blk_get_request(VirtIOBlock
*s
)
114 VirtIOBlockReq
*req
= virtio_blk_alloc_request(s
);
117 if (!virtqueue_pop(s
->vq
, &req
->elem
)) {
126 static int virtio_blk_handle_write(VirtIOBlockReq
*req
)
132 for (i
= 1; i
< req
->elem
.out_num
; i
++)
133 req
->size
+= req
->elem
.out_sg
[i
].iov_len
;
135 req
->buffer
= qemu_memalign(512, req
->size
);
136 if (req
->buffer
== NULL
) {
141 /* We copy the data from the SG list to avoid splitting up the request.
142 This helps performance a lot until we can pass full sg lists as AIO
144 for (i
= 1; i
< req
->elem
.out_num
; i
++) {
147 len
= MIN(req
->elem
.out_sg
[i
].iov_len
,
149 memcpy(req
->buffer
+ offset
,
150 req
->elem
.out_sg
[i
].iov_base
,
156 bdrv_aio_write(req
->dev
->bs
, req
->out
->sector
, req
->buffer
, req
->size
/ 512,
157 virtio_blk_rw_complete
, req
);
161 static void virtio_blk_handle_output(VirtIODevice
*vdev
, VirtQueue
*vq
)
163 VirtIOBlock
*s
= to_virtio_blk(vdev
);
166 while ((req
= virtio_blk_get_request(s
))) {
169 if (req
->elem
.out_num
< 1 || req
->elem
.in_num
< 1) {
170 fprintf(stderr
, "virtio-blk missing headers\n");
174 if (req
->elem
.out_sg
[0].iov_len
< sizeof(*req
->out
) ||
175 req
->elem
.in_sg
[req
->elem
.in_num
- 1].iov_len
< sizeof(*req
->in
)) {
176 fprintf(stderr
, "virtio-blk header not in correct element\n");
180 req
->out
= (void *)req
->elem
.out_sg
[0].iov_base
;
181 req
->in
= (void *)req
->elem
.in_sg
[req
->elem
.in_num
- 1].iov_base
;
183 if (req
->out
->type
& VIRTIO_BLK_T_SCSI_CMD
) {
184 unsigned int len
= sizeof(*req
->in
);
186 req
->in
->status
= VIRTIO_BLK_S_UNSUPP
;
187 virtqueue_push(vq
, &req
->elem
, len
);
188 virtio_notify(vdev
, vq
);
190 } else if (req
->out
->type
& VIRTIO_BLK_T_OUT
) {
191 if (virtio_blk_handle_write(req
) < 0)
194 for (i
= 0; i
< req
->elem
.in_num
- 1; i
++)
195 req
->size
+= req
->elem
.in_sg
[i
].iov_len
;
197 req
->buffer
= qemu_memalign(512, req
->size
);
198 if (req
->buffer
== NULL
) {
203 bdrv_aio_read(s
->bs
, req
->out
->sector
,
206 virtio_blk_rw_complete
,
211 * FIXME: Want to check for completions before returning to guest mode,
212 * so cached reads and writes are reported as quickly as possible. But
213 * that should be done in the generic block layer.
217 static void virtio_blk_dma_restart_cb(void *opaque
, int running
, int reason
)
219 VirtIOBlock
*s
= opaque
;
220 VirtIOBlockReq
*req
= s
->rq
;
228 virtio_blk_handle_write(req
);
233 static void virtio_blk_reset(VirtIODevice
*vdev
)
236 * This should cancel pending requests, but can't do nicely until there
237 * are per-device request lists.
242 static void virtio_blk_update_config(VirtIODevice
*vdev
, uint8_t *config
)
244 VirtIOBlock
*s
= to_virtio_blk(vdev
);
245 struct virtio_blk_config blkcfg
;
247 int cylinders
, heads
, secs
;
249 bdrv_get_geometry(s
->bs
, &capacity
);
250 bdrv_get_geometry_hint(s
->bs
, &cylinders
, &heads
, &secs
);
251 stq_raw(&blkcfg
.capacity
, capacity
);
252 stl_raw(&blkcfg
.seg_max
, 128 - 2);
253 stw_raw(&blkcfg
.cylinders
, cylinders
);
254 blkcfg
.heads
= heads
;
255 blkcfg
.sectors
= secs
;
256 memcpy(config
, &blkcfg
, sizeof(blkcfg
));
259 static uint32_t virtio_blk_get_features(VirtIODevice
*vdev
)
261 return (1 << VIRTIO_BLK_F_SEG_MAX
| 1 << VIRTIO_BLK_F_GEOMETRY
);
264 static void virtio_blk_save(QEMUFile
*f
, void *opaque
)
266 VirtIOBlock
*s
= opaque
;
267 VirtIOBlockReq
*req
= s
->rq
;
269 virtio_save(&s
->vdev
, f
);
272 qemu_put_sbyte(f
, 1);
273 qemu_put_buffer(f
, (unsigned char*)&req
->elem
, sizeof(req
->elem
));
276 qemu_put_sbyte(f
, 0);
279 static int virtio_blk_load(QEMUFile
*f
, void *opaque
, int version_id
)
281 VirtIOBlock
*s
= opaque
;
286 virtio_load(&s
->vdev
, f
);
287 while (qemu_get_sbyte(f
)) {
288 VirtIOBlockReq
*req
= virtio_blk_alloc_request(s
);
289 qemu_get_buffer(f
, (unsigned char*)&req
->elem
, sizeof(req
->elem
));
297 void *virtio_blk_init(PCIBus
*bus
, BlockDriverState
*bs
)
300 int cylinders
, heads
, secs
;
301 static int virtio_blk_id
;
303 s
= (VirtIOBlock
*)virtio_init_pci(bus
, "virtio-blk",
304 PCI_VENDOR_ID_REDHAT_QUMRANET
,
305 PCI_DEVICE_ID_VIRTIO_BLOCK
,
306 PCI_VENDOR_ID_REDHAT_QUMRANET
,
309 sizeof(struct virtio_blk_config
), sizeof(VirtIOBlock
));
313 s
->vdev
.get_config
= virtio_blk_update_config
;
314 s
->vdev
.get_features
= virtio_blk_get_features
;
315 s
->vdev
.reset
= virtio_blk_reset
;
318 bdrv_guess_geometry(s
->bs
, &cylinders
, &heads
, &secs
);
319 bdrv_set_geometry_hint(s
->bs
, cylinders
, heads
, secs
);
321 s
->vq
= virtio_add_queue(&s
->vdev
, 128, virtio_blk_handle_output
);
323 qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb
, s
);
324 register_savevm("virtio-blk", virtio_blk_id
++, 2,
325 virtio_blk_save
, virtio_blk_load
, s
);