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