Skip to content

Commit 7d189f6

Browse files
stefano-garzarellamstsirkin
authored andcommitted
vdpa_sim_blk: implement ramdisk behaviour
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> Link: https://lore.kernel.org/r/20210315163450.254396-13-sgarzare@redhat.com Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
1 parent 0c853c2 commit 7d189f6

1 file changed

Lines changed: 146 additions & 18 deletions

File tree

drivers/vdpa/vdpa_sim/vdpa_sim_blk.c

Lines changed: 146 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* VDPA simulator for block device.
44
*
55
* Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
6+
* Copyright (c) 2021, Red Hat Inc. All rights reserved.
67
*
78
*/
89

@@ -14,6 +15,7 @@
1415
#include <linux/blkdev.h>
1516
#include <linux/vringh.h>
1617
#include <linux/vdpa.h>
18+
#include <linux/blkdev.h>
1719
#include <uapi/linux/virtio_blk.h>
1820

1921
#include "vdpa_sim.h"
@@ -37,10 +39,151 @@
3739

3840
static struct vdpasim *vdpasim_blk_dev;
3941

42+
static bool vdpasim_blk_check_range(u64 start_sector, size_t range_size)
43+
{
44+
u64 range_sectors = range_size >> SECTOR_SHIFT;
45+
46+
if (range_size > VDPASIM_BLK_SIZE_MAX * VDPASIM_BLK_SEG_MAX)
47+
return false;
48+
49+
if (start_sector > VDPASIM_BLK_CAPACITY)
50+
return false;
51+
52+
if (range_sectors > VDPASIM_BLK_CAPACITY - start_sector)
53+
return false;
54+
55+
return true;
56+
}
57+
58+
/* Returns 'true' if the request is handled (with or without an I/O error)
59+
* and the status is correctly written in the last byte of the 'in iov',
60+
* 'false' otherwise.
61+
*/
62+
static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim,
63+
struct vdpasim_virtqueue *vq)
64+
{
65+
size_t pushed = 0, to_pull, to_push;
66+
struct virtio_blk_outhdr hdr;
67+
ssize_t bytes;
68+
loff_t offset;
69+
u64 sector;
70+
u8 status;
71+
u32 type;
72+
int ret;
73+
74+
ret = vringh_getdesc_iotlb(&vq->vring, &vq->out_iov, &vq->in_iov,
75+
&vq->head, GFP_ATOMIC);
76+
if (ret != 1)
77+
return false;
78+
79+
if (vq->out_iov.used < 1 || vq->in_iov.used < 1) {
80+
dev_err(&vdpasim->vdpa.dev, "missing headers - out_iov: %u in_iov %u\n",
81+
vq->out_iov.used, vq->in_iov.used);
82+
return false;
83+
}
84+
85+
if (vq->in_iov.iov[vq->in_iov.used - 1].iov_len < 1) {
86+
dev_err(&vdpasim->vdpa.dev, "request in header too short\n");
87+
return false;
88+
}
89+
90+
/* The last byte is the status and we checked if the last iov has
91+
* enough room for it.
92+
*/
93+
to_push = vringh_kiov_length(&vq->in_iov) - 1;
94+
95+
to_pull = vringh_kiov_length(&vq->out_iov);
96+
97+
bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov, &hdr,
98+
sizeof(hdr));
99+
if (bytes != sizeof(hdr)) {
100+
dev_err(&vdpasim->vdpa.dev, "request out header too short\n");
101+
return false;
102+
}
103+
104+
to_pull -= bytes;
105+
106+
type = vdpasim32_to_cpu(vdpasim, hdr.type);
107+
sector = vdpasim64_to_cpu(vdpasim, hdr.sector);
108+
offset = sector << SECTOR_SHIFT;
109+
status = VIRTIO_BLK_S_OK;
110+
111+
switch (type) {
112+
case VIRTIO_BLK_T_IN:
113+
if (!vdpasim_blk_check_range(sector, to_push)) {
114+
dev_err(&vdpasim->vdpa.dev,
115+
"reading over the capacity - offset: 0x%llx len: 0x%zx\n",
116+
offset, to_push);
117+
status = VIRTIO_BLK_S_IOERR;
118+
break;
119+
}
120+
121+
bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov,
122+
vdpasim->buffer + offset,
123+
to_push);
124+
if (bytes < 0) {
125+
dev_err(&vdpasim->vdpa.dev,
126+
"vringh_iov_push_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n",
127+
bytes, offset, to_push);
128+
status = VIRTIO_BLK_S_IOERR;
129+
break;
130+
}
131+
132+
pushed += bytes;
133+
break;
134+
135+
case VIRTIO_BLK_T_OUT:
136+
if (!vdpasim_blk_check_range(sector, to_pull)) {
137+
dev_err(&vdpasim->vdpa.dev,
138+
"writing over the capacity - offset: 0x%llx len: 0x%zx\n",
139+
offset, to_pull);
140+
status = VIRTIO_BLK_S_IOERR;
141+
break;
142+
}
143+
144+
bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov,
145+
vdpasim->buffer + offset,
146+
to_pull);
147+
if (bytes < 0) {
148+
dev_err(&vdpasim->vdpa.dev,
149+
"vringh_iov_pull_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n",
150+
bytes, offset, to_pull);
151+
status = VIRTIO_BLK_S_IOERR;
152+
break;
153+
}
154+
break;
155+
156+
default:
157+
dev_warn(&vdpasim->vdpa.dev,
158+
"Unsupported request type %d\n", type);
159+
status = VIRTIO_BLK_S_IOERR;
160+
break;
161+
}
162+
163+
/* If some operations fail, we need to skip the remaining bytes
164+
* to put the status in the last byte
165+
*/
166+
if (to_push - pushed > 0)
167+
vringh_kiov_advance(&vq->in_iov, to_push - pushed);
168+
169+
/* Last byte is the status */
170+
bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov, &status, 1);
171+
if (bytes != 1)
172+
return false;
173+
174+
pushed += bytes;
175+
176+
/* Make sure data is wrote before advancing index */
177+
smp_wmb();
178+
179+
vringh_complete_iotlb(&vq->vring, vq->head, pushed);
180+
181+
return true;
182+
}
183+
40184
static void vdpasim_blk_work(struct work_struct *work)
41185
{
42186
struct vdpasim *vdpasim = container_of(work, struct vdpasim, work);
43-
u8 status = VIRTIO_BLK_S_OK;
44187
int i;
45188

46189
spin_lock(&vdpasim->lock);
@@ -54,22 +197,7 @@ static void vdpasim_blk_work(struct work_struct *work)
54197
if (!vq->ready)
55198
continue;
56199

57-
while (vringh_getdesc_iotlb(&vq->vring, &vq->out_iov,
58-
&vq->in_iov, &vq->head,
59-
GFP_ATOMIC) > 0) {
60-
int write;
61-
62-
vq->in_iov.i = vq->in_iov.used - 1;
63-
write = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov,
64-
&status, 1);
65-
if (write <= 0)
66-
break;
67-
68-
/* Make sure data is wrote before advancing index */
69-
smp_wmb();
70-
71-
vringh_complete_iotlb(&vq->vring, vq->head, write);
72-
200+
while (vdpasim_blk_handle_req(vdpasim, vq)) {
73201
/* Make sure used is visible before rasing the interrupt. */
74202
smp_wmb();
75203

@@ -109,7 +237,7 @@ static int __init vdpasim_blk_init(void)
109237
dev_attr.config_size = sizeof(struct virtio_blk_config);
110238
dev_attr.get_config = vdpasim_blk_get_config;
111239
dev_attr.work_fn = vdpasim_blk_work;
112-
dev_attr.buffer_size = PAGE_SIZE;
240+
dev_attr.buffer_size = VDPASIM_BLK_CAPACITY << SECTOR_SHIFT;
113241

114242
vdpasim_blk_dev = vdpasim_create(&dev_attr);
115243
if (IS_ERR(vdpasim_blk_dev)) {

0 commit comments

Comments
 (0)