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 coroutine_fn 26 virtio_blk_sect_range_ok(BlockBackend *blk, uint32_t block_size, 27 uint64_t sector, size_t size) 28 { 29 uint64_t nb_sectors; 30 uint64_t total_sectors; 31 32 if (size % VIRTIO_BLK_SECTOR_SIZE) { 33 return false; 34 } 35 36 nb_sectors = size >> VIRTIO_BLK_SECTOR_BITS; 37 38 QEMU_BUILD_BUG_ON(BDRV_SECTOR_SIZE != VIRTIO_BLK_SECTOR_SIZE); 39 if (nb_sectors > BDRV_REQUEST_MAX_SECTORS) { 40 return false; 41 } 42 if ((sector << VIRTIO_BLK_SECTOR_BITS) % block_size) { 43 return false; 44 } 45 blk_co_get_geometry(blk, &total_sectors); 46 if (sector > total_sectors || nb_sectors > total_sectors - sector) { 47 return false; 48 } 49 return true; 50 } 51 52 static int coroutine_fn 53 virtio_blk_discard_write_zeroes(VirtioBlkHandler *handler, struct iovec *iov, 54 uint32_t iovcnt, uint32_t type) 55 { 56 BlockBackend *blk = handler->blk; 57 struct virtio_blk_discard_write_zeroes desc; 58 ssize_t size; 59 uint64_t sector; 60 uint32_t num_sectors; 61 uint32_t max_sectors; 62 uint32_t flags; 63 int bytes; 64 65 /* Only one desc is currently supported */ 66 if (unlikely(iov_size(iov, iovcnt) > sizeof(desc))) { 67 return VIRTIO_BLK_S_UNSUPP; 68 } 69 70 size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc)); 71 if (unlikely(size != sizeof(desc))) { 72 error_report("Invalid size %zd, expected %zu", size, sizeof(desc)); 73 return VIRTIO_BLK_S_IOERR; 74 } 75 76 sector = le64_to_cpu(desc.sector); 77 num_sectors = le32_to_cpu(desc.num_sectors); 78 flags = le32_to_cpu(desc.flags); 79 max_sectors = (type == VIRTIO_BLK_T_WRITE_ZEROES) ? 80 VIRTIO_BLK_MAX_WRITE_ZEROES_SECTORS : 81 VIRTIO_BLK_MAX_DISCARD_SECTORS; 82 83 /* This check ensures that 'bytes' fits in an int */ 84 if (unlikely(num_sectors > max_sectors)) { 85 return VIRTIO_BLK_S_IOERR; 86 } 87 88 bytes = num_sectors << VIRTIO_BLK_SECTOR_BITS; 89 90 if (unlikely(!virtio_blk_sect_range_ok(blk, handler->logical_block_size, 91 sector, bytes))) { 92 return VIRTIO_BLK_S_IOERR; 93 } 94 95 /* 96 * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for discard 97 * and write zeroes commands if any unknown flag is set. 98 */ 99 if (unlikely(flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) { 100 return VIRTIO_BLK_S_UNSUPP; 101 } 102 103 if (type == VIRTIO_BLK_T_WRITE_ZEROES) { 104 int blk_flags = 0; 105 106 if (flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) { 107 blk_flags |= BDRV_REQ_MAY_UNMAP; 108 } 109 110 if (blk_co_pwrite_zeroes(blk, sector << VIRTIO_BLK_SECTOR_BITS, 111 bytes, blk_flags) == 0) { 112 return VIRTIO_BLK_S_OK; 113 } 114 } else if (type == VIRTIO_BLK_T_DISCARD) { 115 /* 116 * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for 117 * discard commands if the unmap flag is set. 118 */ 119 if (unlikely(flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) { 120 return VIRTIO_BLK_S_UNSUPP; 121 } 122 123 if (blk_co_pdiscard(blk, sector << VIRTIO_BLK_SECTOR_BITS, 124 bytes) == 0) { 125 return VIRTIO_BLK_S_OK; 126 } 127 } 128 129 return VIRTIO_BLK_S_IOERR; 130 } 131 132 int coroutine_fn virtio_blk_process_req(VirtioBlkHandler *handler, 133 struct iovec *in_iov, 134 struct iovec *out_iov, 135 unsigned int in_num, 136 unsigned int out_num) 137 { 138 BlockBackend *blk = handler->blk; 139 struct virtio_blk_inhdr *in; 140 struct virtio_blk_outhdr out; 141 uint32_t type; 142 int in_len; 143 144 if (out_num < 1 || in_num < 1) { 145 error_report("virtio-blk request missing headers"); 146 return -EINVAL; 147 } 148 149 if (unlikely(iov_to_buf(out_iov, out_num, 0, &out, 150 sizeof(out)) != sizeof(out))) { 151 error_report("virtio-blk request outhdr too short"); 152 return -EINVAL; 153 } 154 155 iov_discard_front(&out_iov, &out_num, sizeof(out)); 156 157 if (in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) { 158 error_report("virtio-blk request inhdr too short"); 159 return -EINVAL; 160 } 161 162 /* We always touch the last byte, so just see how big in_iov is. */ 163 in_len = iov_size(in_iov, in_num); 164 in = (void *)in_iov[in_num - 1].iov_base 165 + in_iov[in_num - 1].iov_len 166 - sizeof(struct virtio_blk_inhdr); 167 iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr)); 168 169 type = le32_to_cpu(out.type); 170 switch (type & ~VIRTIO_BLK_T_BARRIER) { 171 case VIRTIO_BLK_T_IN: 172 case VIRTIO_BLK_T_OUT: { 173 QEMUIOVector qiov; 174 int64_t offset; 175 ssize_t ret = 0; 176 bool is_write = type & VIRTIO_BLK_T_OUT; 177 int64_t sector_num = le64_to_cpu(out.sector); 178 179 if (is_write && !handler->writable) { 180 in->status = VIRTIO_BLK_S_IOERR; 181 break; 182 } 183 184 if (is_write) { 185 qemu_iovec_init_external(&qiov, out_iov, out_num); 186 } else { 187 qemu_iovec_init_external(&qiov, in_iov, in_num); 188 } 189 190 if (unlikely(!virtio_blk_sect_range_ok(blk, 191 handler->logical_block_size, 192 sector_num, qiov.size))) { 193 in->status = VIRTIO_BLK_S_IOERR; 194 break; 195 } 196 197 offset = sector_num << VIRTIO_BLK_SECTOR_BITS; 198 199 if (is_write) { 200 ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0); 201 } else { 202 ret = blk_co_preadv(blk, offset, qiov.size, &qiov, 0); 203 } 204 if (ret >= 0) { 205 in->status = VIRTIO_BLK_S_OK; 206 } else { 207 in->status = VIRTIO_BLK_S_IOERR; 208 } 209 break; 210 } 211 case VIRTIO_BLK_T_FLUSH: 212 if (blk_co_flush(blk) == 0) { 213 in->status = VIRTIO_BLK_S_OK; 214 } else { 215 in->status = VIRTIO_BLK_S_IOERR; 216 } 217 break; 218 case VIRTIO_BLK_T_GET_ID: { 219 size_t size = MIN(strlen(handler->serial) + 1, 220 MIN(iov_size(in_iov, in_num), 221 VIRTIO_BLK_ID_BYTES)); 222 iov_from_buf(in_iov, in_num, 0, handler->serial, size); 223 in->status = VIRTIO_BLK_S_OK; 224 break; 225 } 226 case VIRTIO_BLK_T_DISCARD: 227 case VIRTIO_BLK_T_WRITE_ZEROES: 228 if (!handler->writable) { 229 in->status = VIRTIO_BLK_S_IOERR; 230 break; 231 } 232 in->status = virtio_blk_discard_write_zeroes(handler, out_iov, 233 out_num, type); 234 break; 235 default: 236 in->status = VIRTIO_BLK_S_UNSUPP; 237 break; 238 } 239 240 return in_len; 241 } 242