2 * Handler for virtio-blk I/O
4 * Copyright (c) 2020 Red Hat, Inc.
5 * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved.
8 * Coiby Xu <coiby.xu@gmail.com>
9 * Xie Yongji <xieyongji@bytedance.com>
11 * This work is licensed under the terms of the GNU GPL, version 2 or
12 * later. See the COPYING file in the top-level directory.
15 #include "qemu/osdep.h"
16 #include "qemu/error-report.h"
17 #include "virtio-blk-handler.h"
19 #include "standard-headers/linux/virtio_blk.h"
21 struct virtio_blk_inhdr
{
25 static bool virtio_blk_sect_range_ok(BlockBackend
*blk
, uint32_t block_size
,
26 uint64_t sector
, size_t size
)
29 uint64_t total_sectors
;
31 if (size
% VIRTIO_BLK_SECTOR_SIZE
) {
35 nb_sectors
= size
>> VIRTIO_BLK_SECTOR_BITS
;
37 QEMU_BUILD_BUG_ON(BDRV_SECTOR_SIZE
!= VIRTIO_BLK_SECTOR_SIZE
);
38 if (nb_sectors
> BDRV_REQUEST_MAX_SECTORS
) {
41 if ((sector
<< VIRTIO_BLK_SECTOR_BITS
) % block_size
) {
44 blk_get_geometry(blk
, &total_sectors
);
45 if (sector
> total_sectors
|| nb_sectors
> total_sectors
- sector
) {
51 static int coroutine_fn
52 virtio_blk_discard_write_zeroes(VirtioBlkHandler
*handler
, struct iovec
*iov
,
53 uint32_t iovcnt
, uint32_t type
)
55 BlockBackend
*blk
= handler
->blk
;
56 struct virtio_blk_discard_write_zeroes desc
;
64 /* Only one desc is currently supported */
65 if (unlikely(iov_size(iov
, iovcnt
) > sizeof(desc
))) {
66 return VIRTIO_BLK_S_UNSUPP
;
69 size
= iov_to_buf(iov
, iovcnt
, 0, &desc
, sizeof(desc
));
70 if (unlikely(size
!= sizeof(desc
))) {
71 error_report("Invalid size %zd, expected %zu", size
, sizeof(desc
));
72 return VIRTIO_BLK_S_IOERR
;
75 sector
= le64_to_cpu(desc
.sector
);
76 num_sectors
= le32_to_cpu(desc
.num_sectors
);
77 flags
= le32_to_cpu(desc
.flags
);
78 max_sectors
= (type
== VIRTIO_BLK_T_WRITE_ZEROES
) ?
79 VIRTIO_BLK_MAX_WRITE_ZEROES_SECTORS
:
80 VIRTIO_BLK_MAX_DISCARD_SECTORS
;
82 /* This check ensures that 'bytes' fits in an int */
83 if (unlikely(num_sectors
> max_sectors
)) {
84 return VIRTIO_BLK_S_IOERR
;
87 bytes
= num_sectors
<< VIRTIO_BLK_SECTOR_BITS
;
89 if (unlikely(!virtio_blk_sect_range_ok(blk
, handler
->logical_block_size
,
91 return VIRTIO_BLK_S_IOERR
;
95 * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for discard
96 * and write zeroes commands if any unknown flag is set.
98 if (unlikely(flags
& ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP
)) {
99 return VIRTIO_BLK_S_UNSUPP
;
102 if (type
== VIRTIO_BLK_T_WRITE_ZEROES
) {
105 if (flags
& VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP
) {
106 blk_flags
|= BDRV_REQ_MAY_UNMAP
;
109 if (blk_co_pwrite_zeroes(blk
, sector
<< VIRTIO_BLK_SECTOR_BITS
,
110 bytes
, blk_flags
) == 0) {
111 return VIRTIO_BLK_S_OK
;
113 } else if (type
== VIRTIO_BLK_T_DISCARD
) {
115 * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for
116 * discard commands if the unmap flag is set.
118 if (unlikely(flags
& VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP
)) {
119 return VIRTIO_BLK_S_UNSUPP
;
122 if (blk_co_pdiscard(blk
, sector
<< VIRTIO_BLK_SECTOR_BITS
,
124 return VIRTIO_BLK_S_OK
;
128 return VIRTIO_BLK_S_IOERR
;
131 int coroutine_fn
virtio_blk_process_req(VirtioBlkHandler
*handler
,
132 struct iovec
*in_iov
,
133 struct iovec
*out_iov
,
135 unsigned int out_num
)
137 BlockBackend
*blk
= handler
->blk
;
138 struct virtio_blk_inhdr
*in
;
139 struct virtio_blk_outhdr out
;
143 if (out_num
< 1 || in_num
< 1) {
144 error_report("virtio-blk request missing headers");
148 if (unlikely(iov_to_buf(out_iov
, out_num
, 0, &out
,
149 sizeof(out
)) != sizeof(out
))) {
150 error_report("virtio-blk request outhdr too short");
154 iov_discard_front(&out_iov
, &out_num
, sizeof(out
));
156 if (in_iov
[in_num
- 1].iov_len
< sizeof(struct virtio_blk_inhdr
)) {
157 error_report("virtio-blk request inhdr too short");
161 /* We always touch the last byte, so just see how big in_iov is. */
162 in_len
= iov_size(in_iov
, in_num
);
163 in
= (void *)in_iov
[in_num
- 1].iov_base
164 + in_iov
[in_num
- 1].iov_len
165 - sizeof(struct virtio_blk_inhdr
);
166 iov_discard_back(in_iov
, &in_num
, sizeof(struct virtio_blk_inhdr
));
168 type
= le32_to_cpu(out
.type
);
169 switch (type
& ~VIRTIO_BLK_T_BARRIER
) {
170 case VIRTIO_BLK_T_IN
:
171 case VIRTIO_BLK_T_OUT
: {
175 bool is_write
= type
& VIRTIO_BLK_T_OUT
;
176 int64_t sector_num
= le64_to_cpu(out
.sector
);
178 if (is_write
&& !handler
->writable
) {
179 in
->status
= VIRTIO_BLK_S_IOERR
;
184 qemu_iovec_init_external(&qiov
, out_iov
, out_num
);
186 qemu_iovec_init_external(&qiov
, in_iov
, in_num
);
189 if (unlikely(!virtio_blk_sect_range_ok(blk
,
190 handler
->logical_block_size
,
191 sector_num
, qiov
.size
))) {
192 in
->status
= VIRTIO_BLK_S_IOERR
;
196 offset
= sector_num
<< VIRTIO_BLK_SECTOR_BITS
;
199 ret
= blk_co_pwritev(blk
, offset
, qiov
.size
, &qiov
, 0);
201 ret
= blk_co_preadv(blk
, offset
, qiov
.size
, &qiov
, 0);
204 in
->status
= VIRTIO_BLK_S_OK
;
206 in
->status
= VIRTIO_BLK_S_IOERR
;
210 case VIRTIO_BLK_T_FLUSH
:
211 if (blk_co_flush(blk
) == 0) {
212 in
->status
= VIRTIO_BLK_S_OK
;
214 in
->status
= VIRTIO_BLK_S_IOERR
;
217 case VIRTIO_BLK_T_GET_ID
: {
218 size_t size
= MIN(strlen(handler
->serial
) + 1,
219 MIN(iov_size(in_iov
, in_num
),
220 VIRTIO_BLK_ID_BYTES
));
221 iov_from_buf(in_iov
, in_num
, 0, handler
->serial
, size
);
222 in
->status
= VIRTIO_BLK_S_OK
;
225 case VIRTIO_BLK_T_DISCARD
:
226 case VIRTIO_BLK_T_WRITE_ZEROES
:
227 if (!handler
->writable
) {
228 in
->status
= VIRTIO_BLK_S_IOERR
;
231 in
->status
= virtio_blk_discard_write_zeroes(handler
, out_iov
,
235 in
->status
= VIRTIO_BLK_S_UNSUPP
;