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