mm, tracing: unify PFN format strings
[linux-2.6-microblaze.git] / drivers / vdpa / vdpa_sim / vdpa_sim_blk.c
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 <linux/blkdev.h>
19 #include <uapi/linux/virtio_blk.h>
20
21 #include "vdpa_sim.h"
22
23 #define DRV_VERSION  "0.1"
24 #define DRV_AUTHOR   "Max Gurtovoy <mgurtovoy@nvidia.com>"
25 #define DRV_DESC     "vDPA Device Simulator for block device"
26 #define DRV_LICENSE  "GPL v2"
27
28 #define VDPASIM_BLK_FEATURES    (VDPASIM_FEATURES | \
29                                  (1ULL << VIRTIO_BLK_F_SIZE_MAX) | \
30                                  (1ULL << VIRTIO_BLK_F_SEG_MAX)  | \
31                                  (1ULL << VIRTIO_BLK_F_BLK_SIZE) | \
32                                  (1ULL << VIRTIO_BLK_F_TOPOLOGY) | \
33                                  (1ULL << VIRTIO_BLK_F_MQ))
34
35 #define VDPASIM_BLK_CAPACITY    0x40000
36 #define VDPASIM_BLK_SIZE_MAX    0x1000
37 #define VDPASIM_BLK_SEG_MAX     32
38 #define VDPASIM_BLK_VQ_NUM      1
39
40 static char vdpasim_blk_id[VIRTIO_BLK_ID_BYTES] = "vdpa_blk_sim";
41
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         case VIRTIO_BLK_T_GET_ID:
157                 bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov,
158                                               vdpasim_blk_id,
159                                               VIRTIO_BLK_ID_BYTES);
160                 if (bytes < 0) {
161                         dev_err(&vdpasim->vdpa.dev,
162                                 "vringh_iov_push_iotlb() error: %zd\n", bytes);
163                         status = VIRTIO_BLK_S_IOERR;
164                         break;
165                 }
166
167                 pushed += bytes;
168                 break;
169
170         default:
171                 dev_warn(&vdpasim->vdpa.dev,
172                          "Unsupported request type %d\n", type);
173                 status = VIRTIO_BLK_S_IOERR;
174                 break;
175         }
176
177         /* If some operations fail, we need to skip the remaining bytes
178          * to put the status in the last byte
179          */
180         if (to_push - pushed > 0)
181                 vringh_kiov_advance(&vq->in_iov, to_push - pushed);
182
183         /* Last byte is the status */
184         bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov, &status, 1);
185         if (bytes != 1)
186                 return false;
187
188         pushed += bytes;
189
190         /* Make sure data is wrote before advancing index */
191         smp_wmb();
192
193         vringh_complete_iotlb(&vq->vring, vq->head, pushed);
194
195         return true;
196 }
197
198 static void vdpasim_blk_work(struct work_struct *work)
199 {
200         struct vdpasim *vdpasim = container_of(work, struct vdpasim, work);
201         int i;
202
203         spin_lock(&vdpasim->lock);
204
205         if (!(vdpasim->status & VIRTIO_CONFIG_S_DRIVER_OK))
206                 goto out;
207
208         for (i = 0; i < VDPASIM_BLK_VQ_NUM; i++) {
209                 struct vdpasim_virtqueue *vq = &vdpasim->vqs[i];
210
211                 if (!vq->ready)
212                         continue;
213
214                 while (vdpasim_blk_handle_req(vdpasim, vq)) {
215                         /* Make sure used is visible before rasing the interrupt. */
216                         smp_wmb();
217
218                         local_bh_disable();
219                         if (vringh_need_notify_iotlb(&vq->vring) > 0)
220                                 vringh_notify(&vq->vring);
221                         local_bh_enable();
222                 }
223         }
224 out:
225         spin_unlock(&vdpasim->lock);
226 }
227
228 static void vdpasim_blk_get_config(struct vdpasim *vdpasim, void *config)
229 {
230         struct virtio_blk_config *blk_config = config;
231
232         memset(config, 0, sizeof(struct virtio_blk_config));
233
234         blk_config->capacity = cpu_to_vdpasim64(vdpasim, VDPASIM_BLK_CAPACITY);
235         blk_config->size_max = cpu_to_vdpasim32(vdpasim, VDPASIM_BLK_SIZE_MAX);
236         blk_config->seg_max = cpu_to_vdpasim32(vdpasim, VDPASIM_BLK_SEG_MAX);
237         blk_config->num_queues = cpu_to_vdpasim16(vdpasim, VDPASIM_BLK_VQ_NUM);
238         blk_config->min_io_size = cpu_to_vdpasim16(vdpasim, 1);
239         blk_config->opt_io_size = cpu_to_vdpasim32(vdpasim, 1);
240         blk_config->blk_size = cpu_to_vdpasim32(vdpasim, SECTOR_SIZE);
241 }
242
243 static void vdpasim_blk_mgmtdev_release(struct device *dev)
244 {
245 }
246
247 static struct device vdpasim_blk_mgmtdev = {
248         .init_name = "vdpasim_blk",
249         .release = vdpasim_blk_mgmtdev_release,
250 };
251
252 static int vdpasim_blk_dev_add(struct vdpa_mgmt_dev *mdev, const char *name)
253 {
254         struct vdpasim_dev_attr dev_attr = {};
255         struct vdpasim *simdev;
256         int ret;
257
258         dev_attr.mgmt_dev = mdev;
259         dev_attr.name = name;
260         dev_attr.id = VIRTIO_ID_BLOCK;
261         dev_attr.supported_features = VDPASIM_BLK_FEATURES;
262         dev_attr.nvqs = VDPASIM_BLK_VQ_NUM;
263         dev_attr.config_size = sizeof(struct virtio_blk_config);
264         dev_attr.get_config = vdpasim_blk_get_config;
265         dev_attr.work_fn = vdpasim_blk_work;
266         dev_attr.buffer_size = VDPASIM_BLK_CAPACITY << SECTOR_SHIFT;
267
268         simdev = vdpasim_create(&dev_attr);
269         if (IS_ERR(simdev))
270                 return PTR_ERR(simdev);
271
272         ret = _vdpa_register_device(&simdev->vdpa, VDPASIM_BLK_VQ_NUM);
273         if (ret)
274                 goto put_dev;
275
276         return 0;
277
278 put_dev:
279         put_device(&simdev->vdpa.dev);
280         return ret;
281 }
282
283 static void vdpasim_blk_dev_del(struct vdpa_mgmt_dev *mdev,
284                                 struct vdpa_device *dev)
285 {
286         struct vdpasim *simdev = container_of(dev, struct vdpasim, vdpa);
287
288         _vdpa_unregister_device(&simdev->vdpa);
289 }
290
291 static const struct vdpa_mgmtdev_ops vdpasim_blk_mgmtdev_ops = {
292         .dev_add = vdpasim_blk_dev_add,
293         .dev_del = vdpasim_blk_dev_del
294 };
295
296 static struct virtio_device_id id_table[] = {
297         { VIRTIO_ID_BLOCK, VIRTIO_DEV_ANY_ID },
298         { 0 },
299 };
300
301 static struct vdpa_mgmt_dev mgmt_dev = {
302         .device = &vdpasim_blk_mgmtdev,
303         .id_table = id_table,
304         .ops = &vdpasim_blk_mgmtdev_ops,
305 };
306
307 static int __init vdpasim_blk_init(void)
308 {
309         int ret;
310
311         ret = device_register(&vdpasim_blk_mgmtdev);
312         if (ret)
313                 return ret;
314
315         ret = vdpa_mgmtdev_register(&mgmt_dev);
316         if (ret)
317                 goto parent_err;
318
319         return 0;
320
321 parent_err:
322         device_unregister(&vdpasim_blk_mgmtdev);
323         return ret;
324 }
325
326 static void __exit vdpasim_blk_exit(void)
327 {
328         vdpa_mgmtdev_unregister(&mgmt_dev);
329         device_unregister(&vdpasim_blk_mgmtdev);
330 }
331
332 module_init(vdpasim_blk_init)
333 module_exit(vdpasim_blk_exit)
334
335 MODULE_VERSION(DRV_VERSION);
336 MODULE_LICENSE(DRV_LICENSE);
337 MODULE_AUTHOR(DRV_AUTHOR);
338 MODULE_DESCRIPTION(DRV_DESC);