1 /*
2  * Handler for virtio-blk I/O
3  *
4  * Copyright (c) 2020 Red Hat, Inc.
5  * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved.
6  *
7  * Author:
8  *   Coiby Xu <coiby.xu@gmail.com>
9  *   Xie Yongji <xieyongji@bytedance.com>
10  *
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.
13  */
14 
15 #include "qemu/osdep.h"
16 #include "qemu/error-report.h"
17 #include "virtio-blk-handler.h"
18 
19 #include "standard-headers/linux/virtio_blk.h"
20 
21 struct virtio_blk_inhdr {
22     unsigned char status;
23 };
24 
25 static bool virtio_blk_sect_range_ok(BlockBackend *blk, uint32_t block_size,
26                                      uint64_t sector, size_t size)
27 {
28     uint64_t nb_sectors;
29     uint64_t total_sectors;
30 
31     if (size % VIRTIO_BLK_SECTOR_SIZE) {
32         return false;
33     }
34 
35     nb_sectors = size >> VIRTIO_BLK_SECTOR_BITS;
36 
37     QEMU_BUILD_BUG_ON(BDRV_SECTOR_SIZE != VIRTIO_BLK_SECTOR_SIZE);
38     if (nb_sectors > BDRV_REQUEST_MAX_SECTORS) {
39         return false;
40     }
41     if ((sector << VIRTIO_BLK_SECTOR_BITS) % block_size) {
42         return false;
43     }
44     blk_get_geometry(blk, &total_sectors);
45     if (sector > total_sectors || nb_sectors > total_sectors - sector) {
46         return false;
47     }
48     return true;
49 }
50 
51 static int coroutine_fn
52 virtio_blk_discard_write_zeroes(VirtioBlkHandler *handler, struct iovec *iov,
53                                 uint32_t iovcnt, uint32_t type)
54 {
55     BlockBackend *blk = handler->blk;
56     struct virtio_blk_discard_write_zeroes desc;
57     ssize_t size;
58     uint64_t sector;
59     uint32_t num_sectors;
60     uint32_t max_sectors;
61     uint32_t flags;
62     int bytes;
63 
64     /* Only one desc is currently supported */
65     if (unlikely(iov_size(iov, iovcnt) > sizeof(desc))) {
66         return VIRTIO_BLK_S_UNSUPP;
67     }
68 
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;
73     }
74 
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;
81 
82     /* This check ensures that 'bytes' fits in an int */
83     if (unlikely(num_sectors > max_sectors)) {
84         return VIRTIO_BLK_S_IOERR;
85     }
86 
87     bytes = num_sectors << VIRTIO_BLK_SECTOR_BITS;
88 
89     if (unlikely(!virtio_blk_sect_range_ok(blk, handler->logical_block_size,
90                                            sector, bytes))) {
91         return VIRTIO_BLK_S_IOERR;
92     }
93 
94     /*
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.
97      */
98     if (unlikely(flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) {
99         return VIRTIO_BLK_S_UNSUPP;
100     }
101 
102     if (type == VIRTIO_BLK_T_WRITE_ZEROES) {
103         int blk_flags = 0;
104 
105         if (flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) {
106             blk_flags |= BDRV_REQ_MAY_UNMAP;
107         }
108 
109         if (blk_co_pwrite_zeroes(blk, sector << VIRTIO_BLK_SECTOR_BITS,
110                                  bytes, blk_flags) == 0) {
111             return VIRTIO_BLK_S_OK;
112         }
113     } else if (type == VIRTIO_BLK_T_DISCARD) {
114         /*
115          * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for
116          * discard commands if the unmap flag is set.
117          */
118         if (unlikely(flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) {
119             return VIRTIO_BLK_S_UNSUPP;
120         }
121 
122         if (blk_co_pdiscard(blk, sector << VIRTIO_BLK_SECTOR_BITS,
123                             bytes) == 0) {
124             return VIRTIO_BLK_S_OK;
125         }
126     }
127 
128     return VIRTIO_BLK_S_IOERR;
129 }
130 
131 int coroutine_fn virtio_blk_process_req(VirtioBlkHandler *handler,
132                                         struct iovec *in_iov,
133                                         struct iovec *out_iov,
134                                         unsigned int in_num,
135                                         unsigned int out_num)
136 {
137     BlockBackend *blk = handler->blk;
138     struct virtio_blk_inhdr *in;
139     struct virtio_blk_outhdr out;
140     uint32_t type;
141     int in_len;
142 
143     if (out_num < 1 || in_num < 1) {
144         error_report("virtio-blk request missing headers");
145         return -EINVAL;
146     }
147 
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");
151         return -EINVAL;
152     }
153 
154     iov_discard_front(&out_iov, &out_num, sizeof(out));
155 
156     if (in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) {
157         error_report("virtio-blk request inhdr too short");
158         return -EINVAL;
159     }
160 
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));
167 
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: {
172         QEMUIOVector qiov;
173         int64_t offset;
174         ssize_t ret = 0;
175         bool is_write = type & VIRTIO_BLK_T_OUT;
176         int64_t sector_num = le64_to_cpu(out.sector);
177 
178         if (is_write && !handler->writable) {
179             in->status = VIRTIO_BLK_S_IOERR;
180             break;
181         }
182 
183         if (is_write) {
184             qemu_iovec_init_external(&qiov, out_iov, out_num);
185         } else {
186             qemu_iovec_init_external(&qiov, in_iov, in_num);
187         }
188 
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;
193             break;
194         }
195 
196         offset = sector_num << VIRTIO_BLK_SECTOR_BITS;
197 
198         if (is_write) {
199             ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0);
200         } else {
201             ret = blk_co_preadv(blk, offset, qiov.size, &qiov, 0);
202         }
203         if (ret >= 0) {
204             in->status = VIRTIO_BLK_S_OK;
205         } else {
206             in->status = VIRTIO_BLK_S_IOERR;
207         }
208         break;
209     }
210     case VIRTIO_BLK_T_FLUSH:
211         if (blk_co_flush(blk) == 0) {
212             in->status = VIRTIO_BLK_S_OK;
213         } else {
214             in->status = VIRTIO_BLK_S_IOERR;
215         }
216         break;
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;
223         break;
224     }
225     case VIRTIO_BLK_T_DISCARD:
226     case VIRTIO_BLK_T_WRITE_ZEROES:
227         if (!handler->writable) {
228             in->status = VIRTIO_BLK_S_IOERR;
229             break;
230         }
231         in->status = virtio_blk_discard_write_zeroes(handler, out_iov,
232                                                      out_num, type);
233         break;
234     default:
235         in->status = VIRTIO_BLK_S_UNSUPP;
236         break;
237     }
238 
239     return in_len;
240 }
241