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
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"
3739
3840static 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+
40184static 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