examples/l3fwd: share queue size variables
[dpdk.git] / drivers / bus / vmbus / linux / vmbus_uio.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2018, Microsoft Corporation.
3  * All Rights Reserved.
4  */
5
6 #include <string.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <dirent.h>
10 #include <inttypes.h>
11 #include <sys/stat.h>
12 #include <sys/mman.h>
13
14 #include <rte_eal.h>
15 #include <rte_log.h>
16 #include <rte_bus.h>
17 #include <rte_memory.h>
18 #include <rte_common.h>
19 #include <rte_malloc.h>
20 #include <rte_bus_vmbus.h>
21 #include <rte_string_fns.h>
22
23 #include "private.h"
24
25 /** Pathname of VMBUS devices directory. */
26 #define SYSFS_VMBUS_DEVICES "/sys/bus/vmbus/devices"
27
28 static void *vmbus_map_addr;
29
30 /* Control interrupts */
31 void vmbus_uio_irq_control(struct rte_vmbus_device *dev, int32_t onoff)
32 {
33         if ((rte_intr_fd_get(dev->intr_handle) < 0) ||
34             write(rte_intr_fd_get(dev->intr_handle), &onoff,
35                   sizeof(onoff)) < 0) {
36                 VMBUS_LOG(ERR, "cannot write to %d:%s",
37                           rte_intr_fd_get(dev->intr_handle),
38                           strerror(errno));
39         }
40 }
41
42 int vmbus_uio_irq_read(struct rte_vmbus_device *dev)
43 {
44         int32_t count;
45         int cc;
46
47         if (rte_intr_fd_get(dev->intr_handle) < 0)
48                 return -1;
49
50         cc = read(rte_intr_fd_get(dev->intr_handle), &count,
51                   sizeof(count));
52         if (cc < (int)sizeof(count)) {
53                 if (cc < 0) {
54                         VMBUS_LOG(ERR, "IRQ read failed %s",
55                                   strerror(errno));
56                         return -errno;
57                 }
58                 VMBUS_LOG(ERR, "can't read IRQ count");
59                 return -EINVAL;
60         }
61
62         return count;
63 }
64
65 void
66 vmbus_uio_free_resource(struct rte_vmbus_device *dev,
67                 struct mapped_vmbus_resource *uio_res)
68 {
69         rte_free(uio_res);
70
71         if (rte_intr_dev_fd_get(dev->intr_handle) >= 0) {
72                 close(rte_intr_dev_fd_get(dev->intr_handle));
73                 rte_intr_dev_fd_set(dev->intr_handle, -1);
74         }
75
76         if (rte_intr_fd_get(dev->intr_handle) >= 0) {
77                 close(rte_intr_fd_get(dev->intr_handle));
78                 rte_intr_fd_set(dev->intr_handle, -1);
79                 rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UNKNOWN);
80         }
81 }
82
83 int
84 vmbus_uio_alloc_resource(struct rte_vmbus_device *dev,
85                          struct mapped_vmbus_resource **uio_res)
86 {
87         char devname[PATH_MAX]; /* contains the /dev/uioX */
88         int fd;
89
90         /* save fd if in primary process */
91         snprintf(devname, sizeof(devname), "/dev/uio%u", dev->uio_num);
92         fd = open(devname, O_RDWR);
93         if (fd < 0) {
94                 VMBUS_LOG(ERR, "Cannot open %s: %s",
95                         devname, strerror(errno));
96                 goto error;
97         }
98
99         if (rte_intr_fd_set(dev->intr_handle, fd))
100                 goto error;
101
102         if (rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UIO_INTX))
103                 goto error;
104
105         /* allocate the mapping details for secondary processes*/
106         *uio_res = rte_zmalloc("UIO_RES", sizeof(**uio_res), 0);
107         if (*uio_res == NULL) {
108                 VMBUS_LOG(ERR, "cannot store uio mmap details");
109                 goto error;
110         }
111
112         strlcpy((*uio_res)->path, devname, PATH_MAX);
113         rte_uuid_copy((*uio_res)->id, dev->device_id);
114
115         return 0;
116
117 error:
118         vmbus_uio_free_resource(dev, *uio_res);
119         return -1;
120 }
121
122 static int
123 find_max_end_va(const struct rte_memseg_list *msl, void *arg)
124 {
125         size_t sz = msl->memseg_arr.len * msl->page_sz;
126         void *end_va = RTE_PTR_ADD(msl->base_va, sz);
127         void **max_va = arg;
128
129         if (*max_va < end_va)
130                 *max_va = end_va;
131         return 0;
132 }
133
134 /*
135  * TODO: this should be part of memseg api.
136  *       code is duplicated from PCI.
137  */
138 static void *
139 vmbus_find_max_end_va(void)
140 {
141         void *va = NULL;
142
143         rte_memseg_list_walk(find_max_end_va, &va);
144         return va;
145 }
146
147 int
148 vmbus_uio_map_resource_by_index(struct rte_vmbus_device *dev, int idx,
149                                 struct mapped_vmbus_resource *uio_res,
150                                 int flags)
151 {
152         size_t size = dev->resource[idx].len;
153         struct vmbus_map *maps = uio_res->maps;
154         void *mapaddr;
155         off_t offset;
156         int fd;
157
158         /* devname for mmap  */
159         fd = open(uio_res->path, O_RDWR);
160         if (fd < 0) {
161                 VMBUS_LOG(ERR, "Cannot open %s: %s",
162                           uio_res->path, strerror(errno));
163                 return -1;
164         }
165
166         /* try mapping somewhere close to the end of hugepages */
167         if (vmbus_map_addr == NULL)
168                 vmbus_map_addr = vmbus_find_max_end_va();
169
170         /* offset is special in uio it indicates which resource */
171         offset = idx * rte_mem_page_size();
172
173         mapaddr = vmbus_map_resource(vmbus_map_addr, fd, offset, size, flags);
174         close(fd);
175
176         if (mapaddr == MAP_FAILED)
177                 return -1;
178
179         dev->resource[idx].addr = mapaddr;
180         vmbus_map_addr = RTE_PTR_ADD(mapaddr, size);
181
182         /* Record result of successful mapping for use by secondary */
183         maps[idx].addr = mapaddr;
184         maps[idx].size = size;
185
186         return 0;
187 }
188
189 static int vmbus_uio_map_primary(struct vmbus_channel *chan,
190                                  void **ring_buf, uint32_t *ring_size)
191 {
192         struct mapped_vmbus_resource *uio_res;
193
194         uio_res = vmbus_uio_find_resource(chan->device);
195         if (!uio_res) {
196                 VMBUS_LOG(ERR, "can not find resources!");
197                 return -ENOMEM;
198         }
199
200         if (uio_res->nb_maps < VMBUS_MAX_RESOURCE) {
201                 VMBUS_LOG(ERR, "VMBUS: only %u resources found!",
202                           uio_res->nb_maps);
203                 return -EINVAL;
204         }
205
206         *ring_size = uio_res->maps[HV_TXRX_RING_MAP].size / 2;
207         *ring_buf  = uio_res->maps[HV_TXRX_RING_MAP].addr;
208         return 0;
209 }
210
211 static int vmbus_uio_map_subchan(const struct rte_vmbus_device *dev,
212                                  const struct vmbus_channel *chan,
213                                  void **ring_buf, uint32_t *ring_size)
214 {
215         char ring_path[PATH_MAX];
216         size_t file_size;
217         struct stat sb;
218         void *mapaddr;
219         int fd;
220         struct mapped_vmbus_resource *uio_res;
221         int channel_idx;
222
223         uio_res = vmbus_uio_find_resource(dev);
224         if (!uio_res) {
225                 VMBUS_LOG(ERR, "can not find resources for mapping subchan");
226                 return -ENOMEM;
227         }
228
229         if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
230                 if (uio_res->nb_subchannels >= UIO_MAX_SUBCHANNEL) {
231                         VMBUS_LOG(ERR,
232                                 "exceeding max subchannels UIO_MAX_SUBCHANNEL(%d)",
233                                 UIO_MAX_SUBCHANNEL);
234                         VMBUS_LOG(ERR, "Change UIO_MAX_SUBCHANNEL and recompile");
235                         return -ENOMEM;
236                 }
237         } else {
238                 for (channel_idx = 0; channel_idx < uio_res->nb_subchannels;
239                      channel_idx++)
240                         if (uio_res->subchannel_maps[channel_idx].relid ==
241                                         chan->relid)
242                                 break;
243                 if (channel_idx == uio_res->nb_subchannels) {
244                         VMBUS_LOG(ERR,
245                                 "couldn't find sub channel %d from shared mapping in primary",
246                                 chan->relid);
247                         return -ENOMEM;
248                 }
249                 vmbus_map_addr = uio_res->subchannel_maps[channel_idx].addr;
250         }
251
252         snprintf(ring_path, sizeof(ring_path),
253                  "%s/%s/channels/%u/ring",
254                  SYSFS_VMBUS_DEVICES, dev->device.name,
255                  chan->relid);
256
257         fd = open(ring_path, O_RDWR);
258         if (fd < 0) {
259                 VMBUS_LOG(ERR, "Cannot open %s: %s",
260                           ring_path, strerror(errno));
261                 return -errno;
262         }
263
264         if (fstat(fd, &sb) < 0) {
265                 VMBUS_LOG(ERR, "Cannot state %s: %s",
266                           ring_path, strerror(errno));
267                 close(fd);
268                 return -errno;
269         }
270         file_size = sb.st_size;
271
272         if (file_size == 0 || (file_size & (rte_mem_page_size() - 1))) {
273                 VMBUS_LOG(ERR, "incorrect size %s: %zu",
274                           ring_path, file_size);
275
276                 close(fd);
277                 return -EINVAL;
278         }
279
280         mapaddr = vmbus_map_resource(vmbus_map_addr, fd,
281                                      0, file_size, 0);
282         close(fd);
283
284         if (mapaddr == MAP_FAILED)
285                 return -EIO;
286
287         if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
288
289                 /* Add this mapping to uio_res for use by secondary */
290                 uio_res->subchannel_maps[uio_res->nb_subchannels].relid =
291                         chan->relid;
292                 uio_res->subchannel_maps[uio_res->nb_subchannels].addr =
293                         mapaddr;
294                 uio_res->subchannel_maps[uio_res->nb_subchannels].size =
295                         file_size;
296                 uio_res->nb_subchannels++;
297
298                 vmbus_map_addr = RTE_PTR_ADD(mapaddr, file_size);
299         } else {
300                 if (mapaddr != vmbus_map_addr) {
301                         VMBUS_LOG(ERR, "failed to map channel %d to addr %p",
302                                         chan->relid, mapaddr);
303                         vmbus_unmap_resource(mapaddr, file_size);
304                         return -EIO;
305                 }
306         }
307
308         *ring_size = file_size / 2;
309         *ring_buf = mapaddr;
310
311         return 0;
312 }
313
314 int vmbus_uio_map_rings(struct vmbus_channel *chan)
315 {
316         const struct rte_vmbus_device *dev = chan->device;
317         uint32_t ring_size;
318         void *ring_buf;
319         int ret;
320
321         /* Primary channel */
322         if (chan->subchannel_id == 0)
323                 ret = vmbus_uio_map_primary(chan, &ring_buf, &ring_size);
324         else
325                 ret = vmbus_uio_map_subchan(dev, chan, &ring_buf, &ring_size);
326
327         if (ret)
328                 return ret;
329
330         vmbus_br_setup(&chan->txbr, ring_buf, ring_size);
331         vmbus_br_setup(&chan->rxbr, (char *)ring_buf + ring_size, ring_size);
332         return 0;
333 }
334
335 static int vmbus_uio_sysfs_read(const char *dir, const char *name,
336                                 unsigned long *val, unsigned long max_range)
337 {
338         char path[PATH_MAX];
339         FILE *f;
340         int ret;
341
342         snprintf(path, sizeof(path), "%s/%s", dir, name);
343         f = fopen(path, "r");
344         if (!f) {
345                 VMBUS_LOG(ERR, "can't open %s:%s",
346                           path, strerror(errno));
347                 return -errno;
348         }
349
350         if (fscanf(f, "%lu", val) != 1)
351                 ret = -EIO;
352         else if (*val > max_range)
353                 ret = -ERANGE;
354         else
355                 ret = 0;
356         fclose(f);
357
358         return ret;
359 }
360
361 static bool vmbus_uio_ring_present(const struct rte_vmbus_device *dev,
362                                    uint32_t relid)
363 {
364         char ring_path[PATH_MAX];
365
366         /* Check if kernel has subchannel sysfs files */
367         snprintf(ring_path, sizeof(ring_path),
368                  "%s/%s/channels/%u/ring",
369                  SYSFS_VMBUS_DEVICES, dev->device.name, relid);
370
371         return access(ring_path, R_OK|W_OK) == 0;
372 }
373
374 bool vmbus_uio_subchannels_supported(const struct rte_vmbus_device *dev,
375                                      const struct vmbus_channel *chan)
376 {
377         return vmbus_uio_ring_present(dev, chan->relid);
378 }
379
380 static bool vmbus_isnew_subchannel(struct vmbus_channel *primary,
381                                    unsigned long id)
382 {
383         const struct vmbus_channel *c;
384
385         STAILQ_FOREACH(c, &primary->subchannel_list, next) {
386                 if (c->relid == id)
387                         return false;
388         }
389         return true;
390 }
391
392 int vmbus_uio_get_subchan(struct vmbus_channel *primary,
393                           struct vmbus_channel **subchan)
394 {
395         const struct rte_vmbus_device *dev = primary->device;
396         char chan_path[PATH_MAX], subchan_path[PATH_MAX];
397         struct dirent *ent;
398         DIR *chan_dir;
399         int err;
400
401         snprintf(chan_path, sizeof(chan_path),
402                  "%s/%s/channels",
403                  SYSFS_VMBUS_DEVICES, dev->device.name);
404
405         chan_dir = opendir(chan_path);
406         if (!chan_dir) {
407                 VMBUS_LOG(ERR, "cannot open %s: %s",
408                           chan_path, strerror(errno));
409                 return -errno;
410         }
411
412         while ((ent = readdir(chan_dir))) {
413                 unsigned long relid, subid, monid;
414                 char *endp;
415
416                 if (ent->d_name[0] == '.')
417                         continue;
418
419                 errno = 0;
420                 relid = strtoul(ent->d_name, &endp, 0);
421                 if (*endp || errno != 0 || relid > UINT16_MAX) {
422                         VMBUS_LOG(NOTICE, "not a valid channel relid: %s",
423                                   ent->d_name);
424                         continue;
425                 }
426
427                 if (!vmbus_isnew_subchannel(primary, relid)) {
428                         VMBUS_LOG(DEBUG, "skip already found channel: %lu",
429                                   relid);
430                         continue;
431                 }
432
433                 if (!vmbus_uio_ring_present(dev, relid)) {
434                         VMBUS_LOG(DEBUG, "ring mmap not found (yet) for: %lu",
435                                   relid);
436                         continue;
437                 }
438
439                 snprintf(subchan_path, sizeof(subchan_path), "%s/%lu",
440                          chan_path, relid);
441                 err = vmbus_uio_sysfs_read(subchan_path, "subchannel_id",
442                                            &subid, UINT16_MAX);
443                 if (err) {
444                         VMBUS_LOG(NOTICE, "no subchannel_id in %s:%s",
445                                   subchan_path, strerror(-err));
446                         goto fail;
447                 }
448
449                 if (subid == 0)
450                         continue;       /* skip primary channel */
451
452                 err = vmbus_uio_sysfs_read(subchan_path, "monitor_id",
453                                            &monid, UINT8_MAX);
454                 if (err) {
455                         VMBUS_LOG(NOTICE, "no monitor_id in %s:%s",
456                                   subchan_path, strerror(-err));
457                         goto fail;
458                 }
459
460                 err = vmbus_chan_create(dev, relid, subid, monid, subchan);
461                 if (err) {
462                         VMBUS_LOG(ERR, "subchannel setup failed");
463                         goto fail;
464                 }
465                 break;
466         }
467         closedir(chan_dir);
468
469         return (ent == NULL) ? -ENOENT : 0;
470 fail:
471         closedir(chan_dir);
472         return err;
473 }