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