qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

virtio-blk-handler.c (7417B)


      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 }