From: Stefano Garzarella <sgarzare@redhat.com> To: virtualization@lists.linux-foundation.org Cc: netdev@vger.kernel.org, Xie Yongji <xieyongji@bytedance.com>, Laurent Vivier <lvivier@redhat.com>, Stefan Hajnoczi <stefanha@redhat.com>, linux-kernel@vger.kernel.org, Max Gurtovoy <mgurtovoy@nvidia.com>, Jason Wang <jasowang@redhat.com>, Parav Pandit <parav@nvidia.com>, "Michael S. Tsirkin" <mst@redhat.com>, kvm@vger.kernel.org Subject: [PATCH v4 12/14] vdpa_sim_blk: implement ramdisk behaviour Date: Mon, 15 Mar 2021 17:34:48 +0100 [thread overview] Message-ID: <20210315163450.254396-13-sgarzare@redhat.com> (raw) In-Reply-To: <20210315163450.254396-1-sgarzare@redhat.com> The previous implementation wrote only the status of each request. This patch implements a more accurate block device simulator, providing a ramdisk-like behavior and adding input validation. Acked-by: Jason Wang <jasowang@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Stefano Garzarella <sgarzare@redhat.com> --- v2: - used %zd %zx to print size_t and ssize_t variables in dev_err() - removed unnecessary new line [Jason] - moved VIRTIO_BLK_T_GET_ID in another patch [Jason] - used push/pull instead of write/read terminology - added vdpasim_blk_check_range() to avoid overflows [Stefan] - use vdpasim*_to_cpu instead of le*_to_cpu - used vringh_kiov_length() helper [Jason] --- drivers/vdpa/vdpa_sim/vdpa_sim_blk.c | 164 ++++++++++++++++++++++++--- 1 file changed, 146 insertions(+), 18 deletions(-) diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c b/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c index 64926a70af5e..a31964e3e5a4 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c @@ -3,6 +3,7 @@ * VDPA simulator for block device. * * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2021, Red Hat Inc. All rights reserved. * */ @@ -14,6 +15,7 @@ #include <linux/blkdev.h> #include <linux/vringh.h> #include <linux/vdpa.h> +#include <linux/blkdev.h> #include <uapi/linux/virtio_blk.h> #include "vdpa_sim.h" @@ -37,10 +39,151 @@ static struct vdpasim *vdpasim_blk_dev; +static bool vdpasim_blk_check_range(u64 start_sector, size_t range_size) +{ + u64 range_sectors = range_size >> SECTOR_SHIFT; + + if (range_size > VDPASIM_BLK_SIZE_MAX * VDPASIM_BLK_SEG_MAX) + return false; + + if (start_sector > VDPASIM_BLK_CAPACITY) + return false; + + if (range_sectors > VDPASIM_BLK_CAPACITY - start_sector) + return false; + + return true; +} + +/* Returns 'true' if the request is handled (with or without an I/O error) + * and the status is correctly written in the last byte of the 'in iov', + * 'false' otherwise. + */ +static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, + struct vdpasim_virtqueue *vq) +{ + size_t pushed = 0, to_pull, to_push; + struct virtio_blk_outhdr hdr; + ssize_t bytes; + loff_t offset; + u64 sector; + u8 status; + u32 type; + int ret; + + ret = vringh_getdesc_iotlb(&vq->vring, &vq->out_iov, &vq->in_iov, + &vq->head, GFP_ATOMIC); + if (ret != 1) + return false; + + if (vq->out_iov.used < 1 || vq->in_iov.used < 1) { + dev_err(&vdpasim->vdpa.dev, "missing headers - out_iov: %u in_iov %u\n", + vq->out_iov.used, vq->in_iov.used); + return false; + } + + if (vq->in_iov.iov[vq->in_iov.used - 1].iov_len < 1) { + dev_err(&vdpasim->vdpa.dev, "request in header too short\n"); + return false; + } + + /* The last byte is the status and we checked if the last iov has + * enough room for it. + */ + to_push = vringh_kiov_length(&vq->in_iov) - 1; + + to_pull = vringh_kiov_length(&vq->out_iov); + + bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov, &hdr, + sizeof(hdr)); + if (bytes != sizeof(hdr)) { + dev_err(&vdpasim->vdpa.dev, "request out header too short\n"); + return false; + } + + to_pull -= bytes; + + type = vdpasim32_to_cpu(vdpasim, hdr.type); + sector = vdpasim64_to_cpu(vdpasim, hdr.sector); + offset = sector << SECTOR_SHIFT; + status = VIRTIO_BLK_S_OK; + + switch (type) { + case VIRTIO_BLK_T_IN: + if (!vdpasim_blk_check_range(sector, to_push)) { + dev_err(&vdpasim->vdpa.dev, + "reading over the capacity - offset: 0x%llx len: 0x%zx\n", + offset, to_push); + status = VIRTIO_BLK_S_IOERR; + break; + } + + bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov, + vdpasim->buffer + offset, + to_push); + if (bytes < 0) { + dev_err(&vdpasim->vdpa.dev, + "vringh_iov_push_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n", + bytes, offset, to_push); + status = VIRTIO_BLK_S_IOERR; + break; + } + + pushed += bytes; + break; + + case VIRTIO_BLK_T_OUT: + if (!vdpasim_blk_check_range(sector, to_pull)) { + dev_err(&vdpasim->vdpa.dev, + "writing over the capacity - offset: 0x%llx len: 0x%zx\n", + offset, to_pull); + status = VIRTIO_BLK_S_IOERR; + break; + } + + bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov, + vdpasim->buffer + offset, + to_pull); + if (bytes < 0) { + dev_err(&vdpasim->vdpa.dev, + "vringh_iov_pull_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n", + bytes, offset, to_pull); + status = VIRTIO_BLK_S_IOERR; + break; + } + break; + + default: + dev_warn(&vdpasim->vdpa.dev, + "Unsupported request type %d\n", type); + status = VIRTIO_BLK_S_IOERR; + break; + } + + /* If some operations fail, we need to skip the remaining bytes + * to put the status in the last byte + */ + if (to_push - pushed > 0) + vringh_kiov_advance(&vq->in_iov, to_push - pushed); + + /* Last byte is the status */ + bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov, &status, 1); + if (bytes != 1) + return false; + + pushed += bytes; + + /* Make sure data is wrote before advancing index */ + smp_wmb(); + + vringh_complete_iotlb(&vq->vring, vq->head, pushed); + + return true; +} + static void vdpasim_blk_work(struct work_struct *work) { struct vdpasim *vdpasim = container_of(work, struct vdpasim, work); - u8 status = VIRTIO_BLK_S_OK; int i; spin_lock(&vdpasim->lock); @@ -54,22 +197,7 @@ static void vdpasim_blk_work(struct work_struct *work) if (!vq->ready) continue; - while (vringh_getdesc_iotlb(&vq->vring, &vq->out_iov, - &vq->in_iov, &vq->head, - GFP_ATOMIC) > 0) { - int write; - - vq->in_iov.i = vq->in_iov.used - 1; - write = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov, - &status, 1); - if (write <= 0) - break; - - /* Make sure data is wrote before advancing index */ - smp_wmb(); - - vringh_complete_iotlb(&vq->vring, vq->head, write); - + while (vdpasim_blk_handle_req(vdpasim, vq)) { /* Make sure used is visible before rasing the interrupt. */ smp_wmb(); @@ -109,7 +237,7 @@ static int __init vdpasim_blk_init(void) dev_attr.config_size = sizeof(struct virtio_blk_config); dev_attr.get_config = vdpasim_blk_get_config; dev_attr.work_fn = vdpasim_blk_work; - dev_attr.buffer_size = PAGE_SIZE; + dev_attr.buffer_size = VDPASIM_BLK_CAPACITY << SECTOR_SHIFT; vdpasim_blk_dev = vdpasim_create(&dev_attr); if (IS_ERR(vdpasim_blk_dev)) { -- 2.30.2
WARNING: multiple messages have this Message-ID (diff)
From: Stefano Garzarella <sgarzare@redhat.com> To: virtualization@lists.linux-foundation.org Cc: Laurent Vivier <lvivier@redhat.com>, Max Gurtovoy <mgurtovoy@nvidia.com>, kvm@vger.kernel.org, "Michael S. Tsirkin" <mst@redhat.com>, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Xie Yongji <xieyongji@bytedance.com>, Stefan Hajnoczi <stefanha@redhat.com> Subject: [PATCH v4 12/14] vdpa_sim_blk: implement ramdisk behaviour Date: Mon, 15 Mar 2021 17:34:48 +0100 [thread overview] Message-ID: <20210315163450.254396-13-sgarzare@redhat.com> (raw) In-Reply-To: <20210315163450.254396-1-sgarzare@redhat.com> The previous implementation wrote only the status of each request. This patch implements a more accurate block device simulator, providing a ramdisk-like behavior and adding input validation. Acked-by: Jason Wang <jasowang@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Stefano Garzarella <sgarzare@redhat.com> --- v2: - used %zd %zx to print size_t and ssize_t variables in dev_err() - removed unnecessary new line [Jason] - moved VIRTIO_BLK_T_GET_ID in another patch [Jason] - used push/pull instead of write/read terminology - added vdpasim_blk_check_range() to avoid overflows [Stefan] - use vdpasim*_to_cpu instead of le*_to_cpu - used vringh_kiov_length() helper [Jason] --- drivers/vdpa/vdpa_sim/vdpa_sim_blk.c | 164 ++++++++++++++++++++++++--- 1 file changed, 146 insertions(+), 18 deletions(-) diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c b/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c index 64926a70af5e..a31964e3e5a4 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c @@ -3,6 +3,7 @@ * VDPA simulator for block device. * * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2021, Red Hat Inc. All rights reserved. * */ @@ -14,6 +15,7 @@ #include <linux/blkdev.h> #include <linux/vringh.h> #include <linux/vdpa.h> +#include <linux/blkdev.h> #include <uapi/linux/virtio_blk.h> #include "vdpa_sim.h" @@ -37,10 +39,151 @@ static struct vdpasim *vdpasim_blk_dev; +static bool vdpasim_blk_check_range(u64 start_sector, size_t range_size) +{ + u64 range_sectors = range_size >> SECTOR_SHIFT; + + if (range_size > VDPASIM_BLK_SIZE_MAX * VDPASIM_BLK_SEG_MAX) + return false; + + if (start_sector > VDPASIM_BLK_CAPACITY) + return false; + + if (range_sectors > VDPASIM_BLK_CAPACITY - start_sector) + return false; + + return true; +} + +/* Returns 'true' if the request is handled (with or without an I/O error) + * and the status is correctly written in the last byte of the 'in iov', + * 'false' otherwise. + */ +static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, + struct vdpasim_virtqueue *vq) +{ + size_t pushed = 0, to_pull, to_push; + struct virtio_blk_outhdr hdr; + ssize_t bytes; + loff_t offset; + u64 sector; + u8 status; + u32 type; + int ret; + + ret = vringh_getdesc_iotlb(&vq->vring, &vq->out_iov, &vq->in_iov, + &vq->head, GFP_ATOMIC); + if (ret != 1) + return false; + + if (vq->out_iov.used < 1 || vq->in_iov.used < 1) { + dev_err(&vdpasim->vdpa.dev, "missing headers - out_iov: %u in_iov %u\n", + vq->out_iov.used, vq->in_iov.used); + return false; + } + + if (vq->in_iov.iov[vq->in_iov.used - 1].iov_len < 1) { + dev_err(&vdpasim->vdpa.dev, "request in header too short\n"); + return false; + } + + /* The last byte is the status and we checked if the last iov has + * enough room for it. + */ + to_push = vringh_kiov_length(&vq->in_iov) - 1; + + to_pull = vringh_kiov_length(&vq->out_iov); + + bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov, &hdr, + sizeof(hdr)); + if (bytes != sizeof(hdr)) { + dev_err(&vdpasim->vdpa.dev, "request out header too short\n"); + return false; + } + + to_pull -= bytes; + + type = vdpasim32_to_cpu(vdpasim, hdr.type); + sector = vdpasim64_to_cpu(vdpasim, hdr.sector); + offset = sector << SECTOR_SHIFT; + status = VIRTIO_BLK_S_OK; + + switch (type) { + case VIRTIO_BLK_T_IN: + if (!vdpasim_blk_check_range(sector, to_push)) { + dev_err(&vdpasim->vdpa.dev, + "reading over the capacity - offset: 0x%llx len: 0x%zx\n", + offset, to_push); + status = VIRTIO_BLK_S_IOERR; + break; + } + + bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov, + vdpasim->buffer + offset, + to_push); + if (bytes < 0) { + dev_err(&vdpasim->vdpa.dev, + "vringh_iov_push_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n", + bytes, offset, to_push); + status = VIRTIO_BLK_S_IOERR; + break; + } + + pushed += bytes; + break; + + case VIRTIO_BLK_T_OUT: + if (!vdpasim_blk_check_range(sector, to_pull)) { + dev_err(&vdpasim->vdpa.dev, + "writing over the capacity - offset: 0x%llx len: 0x%zx\n", + offset, to_pull); + status = VIRTIO_BLK_S_IOERR; + break; + } + + bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov, + vdpasim->buffer + offset, + to_pull); + if (bytes < 0) { + dev_err(&vdpasim->vdpa.dev, + "vringh_iov_pull_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n", + bytes, offset, to_pull); + status = VIRTIO_BLK_S_IOERR; + break; + } + break; + + default: + dev_warn(&vdpasim->vdpa.dev, + "Unsupported request type %d\n", type); + status = VIRTIO_BLK_S_IOERR; + break; + } + + /* If some operations fail, we need to skip the remaining bytes + * to put the status in the last byte + */ + if (to_push - pushed > 0) + vringh_kiov_advance(&vq->in_iov, to_push - pushed); + + /* Last byte is the status */ + bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov, &status, 1); + if (bytes != 1) + return false; + + pushed += bytes; + + /* Make sure data is wrote before advancing index */ + smp_wmb(); + + vringh_complete_iotlb(&vq->vring, vq->head, pushed); + + return true; +} + static void vdpasim_blk_work(struct work_struct *work) { struct vdpasim *vdpasim = container_of(work, struct vdpasim, work); - u8 status = VIRTIO_BLK_S_OK; int i; spin_lock(&vdpasim->lock); @@ -54,22 +197,7 @@ static void vdpasim_blk_work(struct work_struct *work) if (!vq->ready) continue; - while (vringh_getdesc_iotlb(&vq->vring, &vq->out_iov, - &vq->in_iov, &vq->head, - GFP_ATOMIC) > 0) { - int write; - - vq->in_iov.i = vq->in_iov.used - 1; - write = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov, - &status, 1); - if (write <= 0) - break; - - /* Make sure data is wrote before advancing index */ - smp_wmb(); - - vringh_complete_iotlb(&vq->vring, vq->head, write); - + while (vdpasim_blk_handle_req(vdpasim, vq)) { /* Make sure used is visible before rasing the interrupt. */ smp_wmb(); @@ -109,7 +237,7 @@ static int __init vdpasim_blk_init(void) dev_attr.config_size = sizeof(struct virtio_blk_config); dev_attr.get_config = vdpasim_blk_get_config; dev_attr.work_fn = vdpasim_blk_work; - dev_attr.buffer_size = PAGE_SIZE; + dev_attr.buffer_size = VDPASIM_BLK_CAPACITY << SECTOR_SHIFT; vdpasim_blk_dev = vdpasim_create(&dev_attr); if (IS_ERR(vdpasim_blk_dev)) { -- 2.30.2 _______________________________________________ Virtualization mailing list Virtualization@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/virtualization
next prev parent reply other threads:[~2021-03-15 16:37 UTC|newest] Thread overview: 44+ messages / expand[flat|nested] mbox.gz Atom feed top 2021-03-15 16:34 [PATCH v4 00/14] vdpa: add vdpa simulator for block device Stefano Garzarella 2021-03-15 16:34 ` Stefano Garzarella 2021-03-15 16:34 ` [PATCH v4 01/14] vdpa_sim: use iova module to allocate IOVA addresses Stefano Garzarella 2021-03-15 16:34 ` Stefano Garzarella 2021-03-15 16:34 ` [PATCH v4 02/14] vringh: add 'iotlb_lock' to synchronize iotlb accesses Stefano Garzarella 2021-03-15 16:34 ` Stefano Garzarella 2021-03-15 16:34 ` [PATCH v4 03/14] vringh: reset kiov 'consumed' field in __vringh_iov() Stefano Garzarella 2021-03-15 16:34 ` Stefano Garzarella 2021-03-15 16:34 ` [PATCH v4 04/14] vringh: explain more about cleaning riov and wiov Stefano Garzarella 2021-03-15 16:34 ` Stefano Garzarella 2021-03-15 16:34 ` [PATCH v4 05/14] vringh: implement vringh_kiov_advance() Stefano Garzarella 2021-03-15 16:34 ` Stefano Garzarella 2021-03-15 16:34 ` [PATCH v4 06/14] vringh: add vringh_kiov_length() helper Stefano Garzarella 2021-03-15 16:34 ` Stefano Garzarella 2021-03-15 16:51 ` Laurent Vivier 2021-03-15 16:51 ` Laurent Vivier 2021-03-15 17:06 ` Stefano Garzarella 2021-03-15 17:06 ` Stefano Garzarella 2021-03-15 16:34 ` [PATCH v4 07/14] vdpa_sim: cleanup kiovs in vdpasim_free() Stefano Garzarella 2021-03-15 16:34 ` Stefano Garzarella 2021-03-15 16:34 ` [PATCH v4 08/14] vdpa: add get_config_size callback in vdpa_config_ops Stefano Garzarella 2021-03-15 16:34 ` Stefano Garzarella 2021-03-18 3:21 ` Jason Wang 2021-03-18 3:21 ` Jason Wang 2021-03-15 16:34 ` [PATCH v4 09/14] vhost/vdpa: use get_config_size callback in vhost_vdpa_config_validate() Stefano Garzarella 2021-03-15 16:34 ` Stefano Garzarella 2021-03-18 3:22 ` Jason Wang 2021-03-18 3:22 ` Jason Wang 2021-03-15 16:34 ` [PATCH v4 10/14] vhost/vdpa: Remove the restriction that only supports virtio-net devices Stefano Garzarella 2021-03-15 16:34 ` Stefano Garzarella 2021-03-18 3:24 ` Jason Wang 2021-03-18 3:24 ` Jason Wang 2021-03-15 16:34 ` [PATCH v4 11/14] vdpa: add vdpa simulator for block device Stefano Garzarella 2021-03-15 16:34 ` Stefano Garzarella 2021-03-15 16:34 ` Stefano Garzarella [this message] 2021-03-15 16:34 ` [PATCH v4 12/14] vdpa_sim_blk: implement ramdisk behaviour Stefano Garzarella 2021-03-15 16:34 ` [PATCH v4 13/14] vdpa_sim_blk: handle VIRTIO_BLK_T_GET_ID Stefano Garzarella 2021-03-15 16:34 ` Stefano Garzarella 2021-03-15 16:34 ` [PATCH v4 14/14] vdpa_sim_blk: add support for vdpa management tool Stefano Garzarella 2021-03-15 16:34 ` Stefano Garzarella 2021-03-18 3:31 ` Jason Wang 2021-03-18 3:31 ` Jason Wang 2021-04-12 8:18 ` [PATCH v4 00/14] vdpa: add vdpa simulator for block device Stefano Garzarella 2021-04-12 8:18 ` Stefano Garzarella
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20210315163450.254396-13-sgarzare@redhat.com \ --to=sgarzare@redhat.com \ --cc=jasowang@redhat.com \ --cc=kvm@vger.kernel.org \ --cc=linux-kernel@vger.kernel.org \ --cc=lvivier@redhat.com \ --cc=mgurtovoy@nvidia.com \ --cc=mst@redhat.com \ --cc=netdev@vger.kernel.org \ --cc=parav@nvidia.com \ --cc=stefanha@redhat.com \ --cc=virtualization@lists.linux-foundation.org \ --cc=xieyongji@bytedance.com \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.