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