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