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