linux/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * VDPA simulator for block device.
   4 *
   5 * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
   6 * Copyright (c) 2021, Red Hat Inc. All rights reserved.
   7 *
   8 */
   9
  10#include <linux/init.h>
  11#include <linux/module.h>
  12#include <linux/device.h>
  13#include <linux/kernel.h>
  14#include <linux/sched.h>
  15#include <linux/blkdev.h>
  16#include <linux/vringh.h>
  17#include <linux/vdpa.h>
  18#include <uapi/linux/virtio_blk.h>
  19
  20#include "vdpa_sim.h"
  21
  22#define DRV_VERSION  "0.1"
  23#define DRV_AUTHOR   "Max Gurtovoy <mgurtovoy@nvidia.com>"
  24#define DRV_DESC     "vDPA Device Simulator for block device"
  25#define DRV_LICENSE  "GPL v2"
  26
  27#define VDPASIM_BLK_FEATURES    (VDPASIM_FEATURES | \
  28                                 (1ULL << VIRTIO_BLK_F_SIZE_MAX) | \
  29                                 (1ULL << VIRTIO_BLK_F_SEG_MAX)  | \
  30                                 (1ULL << VIRTIO_BLK_F_BLK_SIZE) | \
  31                                 (1ULL << VIRTIO_BLK_F_TOPOLOGY) | \
  32                                 (1ULL << VIRTIO_BLK_F_MQ))
  33
  34#define VDPASIM_BLK_CAPACITY    0x40000
  35#define VDPASIM_BLK_SIZE_MAX    0x1000
  36#define VDPASIM_BLK_SEG_MAX     32
  37#define VDPASIM_BLK_VQ_NUM      1
  38
  39static char vdpasim_blk_id[VIRTIO_BLK_ID_BYTES] = "vdpa_blk_sim";
  40
  41static bool vdpasim_blk_check_range(u64 start_sector, size_t range_size)
  42{
  43        u64 range_sectors = range_size >> SECTOR_SHIFT;
  44
  45        if (range_size > VDPASIM_BLK_SIZE_MAX * VDPASIM_BLK_SEG_MAX)
  46                return false;
  47
  48        if (start_sector > VDPASIM_BLK_CAPACITY)
  49                return false;
  50
  51        if (range_sectors > VDPASIM_BLK_CAPACITY - start_sector)
  52                return false;
  53
  54        return true;
  55}
  56
  57/* Returns 'true' if the request is handled (with or without an I/O error)
  58 * and the status is correctly written in the last byte of the 'in iov',
  59 * 'false' otherwise.
  60 */
  61static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim,
  62                                   struct vdpasim_virtqueue *vq)
  63{
  64        size_t pushed = 0, to_pull, to_push;
  65        struct virtio_blk_outhdr hdr;
  66        ssize_t bytes;
  67        loff_t offset;
  68        u64 sector;
  69        u8 status;
  70        u32 type;
  71        int ret;
  72
  73        ret = vringh_getdesc_iotlb(&vq->vring, &vq->out_iov, &vq->in_iov,
  74                                   &vq->head, GFP_ATOMIC);
  75        if (ret != 1)
  76                return false;
  77
  78        if (vq->out_iov.used < 1 || vq->in_iov.used < 1) {
  79                dev_err(&vdpasim->vdpa.dev, "missing headers - out_iov: %u in_iov %u\n",
  80                        vq->out_iov.used, vq->in_iov.used);
  81                return false;
  82        }
  83
  84        if (vq->in_iov.iov[vq->in_iov.used - 1].iov_len < 1) {
  85                dev_err(&vdpasim->vdpa.dev, "request in header too short\n");
  86                return false;
  87        }
  88
  89        /* The last byte is the status and we checked if the last iov has
  90         * enough room for it.
  91         */
  92        to_push = vringh_kiov_length(&vq->in_iov) - 1;
  93
  94        to_pull = vringh_kiov_length(&vq->out_iov);
  95
  96        bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov, &hdr,
  97                                      sizeof(hdr));
  98        if (bytes != sizeof(hdr)) {
  99                dev_err(&vdpasim->vdpa.dev, "request out header too short\n");
 100                return false;
 101        }
 102
 103        to_pull -= bytes;
 104
 105        type = vdpasim32_to_cpu(vdpasim, hdr.type);
 106        sector = vdpasim64_to_cpu(vdpasim, hdr.sector);
 107        offset = sector << SECTOR_SHIFT;
 108        status = VIRTIO_BLK_S_OK;
 109
 110        switch (type) {
 111        case VIRTIO_BLK_T_IN:
 112                if (!vdpasim_blk_check_range(sector, to_push)) {
 113                        dev_err(&vdpasim->vdpa.dev,
 114                                "reading over the capacity - offset: 0x%llx len: 0x%zx\n",
 115                                offset, to_push);
 116                        status = VIRTIO_BLK_S_IOERR;
 117                        break;
 118                }
 119
 120                bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov,
 121                                              vdpasim->buffer + offset,
 122                                              to_push);
 123                if (bytes < 0) {
 124                        dev_err(&vdpasim->vdpa.dev,
 125                                "vringh_iov_push_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n",
 126                                bytes, offset, to_push);
 127                        status = VIRTIO_BLK_S_IOERR;
 128                        break;
 129                }
 130
 131                pushed += bytes;
 132                break;
 133
 134        case VIRTIO_BLK_T_OUT:
 135                if (!vdpasim_blk_check_range(sector, to_pull)) {
 136                        dev_err(&vdpasim->vdpa.dev,
 137                                "writing over the capacity - offset: 0x%llx len: 0x%zx\n",
 138                                offset, to_pull);
 139                        status = VIRTIO_BLK_S_IOERR;
 140                        break;
 141                }
 142
 143                bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov,
 144                                              vdpasim->buffer + offset,
 145                                              to_pull);
 146                if (bytes < 0) {
 147                        dev_err(&vdpasim->vdpa.dev,
 148                                "vringh_iov_pull_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n",
 149                                bytes, offset, to_pull);
 150                        status = VIRTIO_BLK_S_IOERR;
 151                        break;
 152                }
 153                break;
 154
 155        case VIRTIO_BLK_T_GET_ID:
 156                bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov,
 157                                              vdpasim_blk_id,
 158                                              VIRTIO_BLK_ID_BYTES);
 159                if (bytes < 0) {
 160                        dev_err(&vdpasim->vdpa.dev,
 161                                "vringh_iov_push_iotlb() error: %zd\n", bytes);
 162                        status = VIRTIO_BLK_S_IOERR;
 163                        break;
 164                }
 165
 166                pushed += bytes;
 167                break;
 168
 169        default:
 170                dev_warn(&vdpasim->vdpa.dev,
 171                         "Unsupported request type %d\n", type);
 172                status = VIRTIO_BLK_S_IOERR;
 173                break;
 174        }
 175
 176        /* If some operations fail, we need to skip the remaining bytes
 177         * to put the status in the last byte
 178         */
 179        if (to_push - pushed > 0)
 180                vringh_kiov_advance(&vq->in_iov, to_push - pushed);
 181
 182        /* Last byte is the status */
 183        bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov, &status, 1);
 184        if (bytes != 1)
 185                return false;
 186
 187        pushed += bytes;
 188
 189        /* Make sure data is wrote before advancing index */
 190        smp_wmb();
 191
 192        vringh_complete_iotlb(&vq->vring, vq->head, pushed);
 193
 194        return true;
 195}
 196
 197static void vdpasim_blk_work(struct work_struct *work)
 198{
 199        struct vdpasim *vdpasim = container_of(work, struct vdpasim, work);
 200        int i;
 201
 202        spin_lock(&vdpasim->lock);
 203
 204        if (!(vdpasim->status & VIRTIO_CONFIG_S_DRIVER_OK))
 205                goto out;
 206
 207        for (i = 0; i < VDPASIM_BLK_VQ_NUM; i++) {
 208                struct vdpasim_virtqueue *vq = &vdpasim->vqs[i];
 209
 210                if (!vq->ready)
 211                        continue;
 212
 213                while (vdpasim_blk_handle_req(vdpasim, vq)) {
 214                        /* Make sure used is visible before rasing the interrupt. */
 215                        smp_wmb();
 216
 217                        local_bh_disable();
 218                        if (vringh_need_notify_iotlb(&vq->vring) > 0)
 219                                vringh_notify(&vq->vring);
 220                        local_bh_enable();
 221                }
 222        }
 223out:
 224        spin_unlock(&vdpasim->lock);
 225}
 226
 227static void vdpasim_blk_get_config(struct vdpasim *vdpasim, void *config)
 228{
 229        struct virtio_blk_config *blk_config = config;
 230
 231        memset(config, 0, sizeof(struct virtio_blk_config));
 232
 233        blk_config->capacity = cpu_to_vdpasim64(vdpasim, VDPASIM_BLK_CAPACITY);
 234        blk_config->size_max = cpu_to_vdpasim32(vdpasim, VDPASIM_BLK_SIZE_MAX);
 235        blk_config->seg_max = cpu_to_vdpasim32(vdpasim, VDPASIM_BLK_SEG_MAX);
 236        blk_config->num_queues = cpu_to_vdpasim16(vdpasim, VDPASIM_BLK_VQ_NUM);
 237        blk_config->min_io_size = cpu_to_vdpasim16(vdpasim, 1);
 238        blk_config->opt_io_size = cpu_to_vdpasim32(vdpasim, 1);
 239        blk_config->blk_size = cpu_to_vdpasim32(vdpasim, SECTOR_SIZE);
 240}
 241
 242static void vdpasim_blk_mgmtdev_release(struct device *dev)
 243{
 244}
 245
 246static struct device vdpasim_blk_mgmtdev = {
 247        .init_name = "vdpasim_blk",
 248        .release = vdpasim_blk_mgmtdev_release,
 249};
 250
 251static int vdpasim_blk_dev_add(struct vdpa_mgmt_dev *mdev, const char *name)
 252{
 253        struct vdpasim_dev_attr dev_attr = {};
 254        struct vdpasim *simdev;
 255        int ret;
 256
 257        dev_attr.mgmt_dev = mdev;
 258        dev_attr.name = name;
 259        dev_attr.id = VIRTIO_ID_BLOCK;
 260        dev_attr.supported_features = VDPASIM_BLK_FEATURES;
 261        dev_attr.nvqs = VDPASIM_BLK_VQ_NUM;
 262        dev_attr.config_size = sizeof(struct virtio_blk_config);
 263        dev_attr.get_config = vdpasim_blk_get_config;
 264        dev_attr.work_fn = vdpasim_blk_work;
 265        dev_attr.buffer_size = VDPASIM_BLK_CAPACITY << SECTOR_SHIFT;
 266
 267        simdev = vdpasim_create(&dev_attr);
 268        if (IS_ERR(simdev))
 269                return PTR_ERR(simdev);
 270
 271        ret = _vdpa_register_device(&simdev->vdpa, VDPASIM_BLK_VQ_NUM);
 272        if (ret)
 273                goto put_dev;
 274
 275        return 0;
 276
 277put_dev:
 278        put_device(&simdev->vdpa.dev);
 279        return ret;
 280}
 281
 282static void vdpasim_blk_dev_del(struct vdpa_mgmt_dev *mdev,
 283                                struct vdpa_device *dev)
 284{
 285        struct vdpasim *simdev = container_of(dev, struct vdpasim, vdpa);
 286
 287        _vdpa_unregister_device(&simdev->vdpa);
 288}
 289
 290static const struct vdpa_mgmtdev_ops vdpasim_blk_mgmtdev_ops = {
 291        .dev_add = vdpasim_blk_dev_add,
 292        .dev_del = vdpasim_blk_dev_del
 293};
 294
 295static struct virtio_device_id id_table[] = {
 296        { VIRTIO_ID_BLOCK, VIRTIO_DEV_ANY_ID },
 297        { 0 },
 298};
 299
 300static struct vdpa_mgmt_dev mgmt_dev = {
 301        .device = &vdpasim_blk_mgmtdev,
 302        .id_table = id_table,
 303        .ops = &vdpasim_blk_mgmtdev_ops,
 304};
 305
 306static int __init vdpasim_blk_init(void)
 307{
 308        int ret;
 309
 310        ret = device_register(&vdpasim_blk_mgmtdev);
 311        if (ret)
 312                return ret;
 313
 314        ret = vdpa_mgmtdev_register(&mgmt_dev);
 315        if (ret)
 316                goto parent_err;
 317
 318        return 0;
 319
 320parent_err:
 321        device_unregister(&vdpasim_blk_mgmtdev);
 322        return ret;
 323}
 324
 325static void __exit vdpasim_blk_exit(void)
 326{
 327        vdpa_mgmtdev_unregister(&mgmt_dev);
 328        device_unregister(&vdpasim_blk_mgmtdev);
 329}
 330
 331module_init(vdpasim_blk_init)
 332module_exit(vdpasim_blk_exit)
 333
 334MODULE_VERSION(DRV_VERSION);
 335MODULE_LICENSE(DRV_LICENSE);
 336MODULE_AUTHOR(DRV_AUTHOR);
 337MODULE_DESCRIPTION(DRV_DESC);
 338