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.
16 #include "block_int.h"
19 /* from Linux's linux/virtio_blk.h */
21 /* The ID for virtio_block */
22 #define VIRTIO_ID_BLOCK 2
25 #define VIRTIO_BLK_F_BARRIER 0 /* Does host support barriers? */
26 #define VIRTIO_BLK_F_SIZE_MAX 1 /* Indicates maximum segment size */
27 #define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */
28 #define VIRTIO_BLK_F_GEOMETRY 4 /* Indicates support of legacy geometry */
30 struct virtio_blk_config
38 } __attribute__((packed
));
40 /* These two define direction. */
41 #define VIRTIO_BLK_T_IN 0
42 #define VIRTIO_BLK_T_OUT 1
44 /* This bit says it's a scsi command, not an actual read or write. */
45 #define VIRTIO_BLK_T_SCSI_CMD 2
47 /* Barrier before this op. */
48 #define VIRTIO_BLK_T_BARRIER 0x80000000
50 /* This is the first element of the read scatter-gather list. */
51 struct virtio_blk_outhdr
57 /* Sector (ie. 512 byte offset) */
61 #define VIRTIO_BLK_S_OK 0
62 #define VIRTIO_BLK_S_IOERR 1
63 #define VIRTIO_BLK_S_UNSUPP 2
65 /* This is the first element of the write scatter-gather list */
66 struct virtio_blk_inhdr
71 typedef struct VirtIOBlock
78 static VirtIOBlock
*to_virtio_blk(VirtIODevice
*vdev
)
80 return (VirtIOBlock
*)vdev
;
83 typedef struct VirtIOBlockReq
86 VirtQueueElement elem
;
87 struct virtio_blk_inhdr
*in
;
88 struct virtio_blk_outhdr
*out
;
93 static void virtio_blk_rw_complete(void *opaque
, int ret
)
95 VirtIOBlockReq
*req
= opaque
;
96 VirtIOBlock
*s
= req
->dev
;
98 /* Copy read data to the guest */
99 if (!ret
&& !(req
->out
->type
& VIRTIO_BLK_T_OUT
)) {
103 for (i
= 0; i
< req
->elem
.in_num
- 1; i
++) {
106 /* Be pretty defensive wrt malicious guests */
107 len
= MIN(req
->elem
.in_sg
[i
].iov_len
,
110 memcpy(req
->elem
.in_sg
[i
].iov_base
,
111 req
->buffer
+ offset
,
117 req
->in
->status
= ret
? VIRTIO_BLK_S_IOERR
: VIRTIO_BLK_S_OK
;
118 virtqueue_push(s
->vq
, &req
->elem
, req
->size
+ sizeof(*req
->in
));
119 virtio_notify(&s
->vdev
, s
->vq
);
121 qemu_free(req
->buffer
);
125 static VirtIOBlockReq
*virtio_blk_get_request(VirtIOBlock
*s
)
129 req
= qemu_mallocz(sizeof(*req
));
134 if (!virtqueue_pop(s
->vq
, &req
->elem
)) {
142 static void virtio_blk_handle_output(VirtIODevice
*vdev
, VirtQueue
*vq
)
144 VirtIOBlock
*s
= to_virtio_blk(vdev
);
147 while ((req
= virtio_blk_get_request(s
))) {
150 if (req
->elem
.out_num
< 1 || req
->elem
.in_num
< 1) {
151 fprintf(stderr
, "virtio-blk missing headers\n");
155 if (req
->elem
.out_sg
[0].iov_len
< sizeof(*req
->out
) ||
156 req
->elem
.in_sg
[req
->elem
.in_num
- 1].iov_len
< sizeof(*req
->in
)) {
157 fprintf(stderr
, "virtio-blk header not in correct element\n");
161 req
->out
= (void *)req
->elem
.out_sg
[0].iov_base
;
162 req
->in
= (void *)req
->elem
.in_sg
[req
->elem
.in_num
- 1].iov_base
;
164 if (req
->out
->type
& VIRTIO_BLK_T_SCSI_CMD
) {
165 unsigned int len
= sizeof(*req
->in
);
167 req
->in
->status
= VIRTIO_BLK_S_UNSUPP
;
168 virtqueue_push(vq
, &req
->elem
, len
);
169 virtio_notify(vdev
, vq
);
171 } else if (req
->out
->type
& VIRTIO_BLK_T_OUT
) {
174 for (i
= 1; i
< req
->elem
.out_num
; i
++)
175 req
->size
+= req
->elem
.out_sg
[i
].iov_len
;
177 req
->buffer
= qemu_memalign(512, req
->size
);
178 if (req
->buffer
== NULL
) {
183 /* We copy the data from the SG list to avoid splitting up the request. This helps
184 performance a lot until we can pass full sg lists as AIO operations */
186 for (i
= 1; i
< req
->elem
.out_num
; i
++) {
189 len
= MIN(req
->elem
.out_sg
[i
].iov_len
,
191 memcpy(req
->buffer
+ offset
,
192 req
->elem
.out_sg
[i
].iov_base
,
197 bdrv_aio_write(s
->bs
, req
->out
->sector
,
200 virtio_blk_rw_complete
,
203 for (i
= 0; i
< req
->elem
.in_num
- 1; i
++)
204 req
->size
+= req
->elem
.in_sg
[i
].iov_len
;
206 req
->buffer
= qemu_memalign(512, req
->size
);
207 if (req
->buffer
== NULL
) {
212 bdrv_aio_read(s
->bs
, req
->out
->sector
,
215 virtio_blk_rw_complete
,
220 * FIXME: Want to check for completions before returning to guest mode,
221 * so cached reads and writes are reported as quickly as possible. But
222 * that should be done in the generic block layer.
226 static void virtio_blk_reset(VirtIODevice
*vdev
)
229 * This should cancel pending requests, but can't do nicely until there
230 * are per-device request lists.
235 static void virtio_blk_update_config(VirtIODevice
*vdev
, uint8_t *config
)
237 VirtIOBlock
*s
= to_virtio_blk(vdev
);
238 struct virtio_blk_config blkcfg
;
240 int cylinders
, heads
, secs
;
242 bdrv_get_geometry(s
->bs
, &capacity
);
243 bdrv_get_geometry_hint(s
->bs
, &cylinders
, &heads
, &secs
);
244 blkcfg
.capacity
= cpu_to_le64(capacity
);
245 blkcfg
.seg_max
= cpu_to_le32(128 - 2);
246 blkcfg
.cylinders
= cpu_to_le16(cylinders
);
247 blkcfg
.heads
= heads
;
248 blkcfg
.sectors
= secs
;
249 memcpy(config
, &blkcfg
, sizeof(blkcfg
));
252 static uint32_t virtio_blk_get_features(VirtIODevice
*vdev
)
254 return (1 << VIRTIO_BLK_F_SEG_MAX
| 1 << VIRTIO_BLK_F_GEOMETRY
);
257 static void virtio_blk_save(QEMUFile
*f
, void *opaque
)
259 VirtIOBlock
*s
= opaque
;
260 virtio_save(&s
->vdev
, f
);
263 static int virtio_blk_load(QEMUFile
*f
, void *opaque
, int version_id
)
265 VirtIOBlock
*s
= opaque
;
270 virtio_load(&s
->vdev
, f
);
275 void *virtio_blk_init(PCIBus
*bus
, uint16_t vendor
, uint16_t device
,
276 BlockDriverState
*bs
)
279 int cylinders
, heads
, secs
;
280 static int virtio_blk_id
;
282 s
= (VirtIOBlock
*)virtio_init_pci(bus
, "virtio-blk", vendor
, device
,
285 sizeof(struct virtio_blk_config
), sizeof(VirtIOBlock
));
289 s
->vdev
.update_config
= virtio_blk_update_config
;
290 s
->vdev
.get_features
= virtio_blk_get_features
;
291 s
->vdev
.reset
= virtio_blk_reset
;
293 bs
->devfn
= s
->vdev
.pci_dev
.devfn
;
294 bdrv_guess_geometry(s
->bs
, &cylinders
, &heads
, &secs
);
295 bdrv_set_geometry_hint(s
->bs
, cylinders
, heads
, secs
);
297 s
->vq
= virtio_add_queue(&s
->vdev
, 128, virtio_blk_handle_output
);
299 register_savevm("virtio-blk", virtio_blk_id
++, 1,
300 virtio_blk_save
, virtio_blk_load
, s
);