New upstream version 18.11-rc1
[deb_dpdk.git] / drivers / bus / vmbus / linux / vmbus_bus.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 <dirent.h>
9 #include <fcntl.h>
10 #include <sys/mman.h>
11 #include <sys/stat.h>
12
13 #include <rte_eal.h>
14 #include <rte_uuid.h>
15 #include <rte_tailq.h>
16 #include <rte_log.h>
17 #include <rte_devargs.h>
18 #include <rte_memory.h>
19 #include <rte_malloc.h>
20 #include <rte_bus_vmbus.h>
21
22 #include "eal_filesystem.h"
23 #include "private.h"
24
25 /** Pathname of VMBUS devices directory. */
26 #define SYSFS_VMBUS_DEVICES "/sys/bus/vmbus/devices"
27
28 extern struct rte_vmbus_bus rte_vmbus_bus;
29
30 /* Read sysfs file to get UUID */
31 static int
32 parse_sysfs_uuid(const char *filename, rte_uuid_t uu)
33 {
34         char buf[BUFSIZ];
35         char *cp, *in = buf;
36         FILE *f;
37
38         f = fopen(filename, "r");
39         if (f == NULL) {
40                 VMBUS_LOG(ERR, "cannot open sysfs value %s: %s",
41                           filename, strerror(errno));
42                 return -1;
43         }
44
45         if (fgets(buf, sizeof(buf), f) == NULL) {
46                 VMBUS_LOG(ERR, "cannot read sysfs value %s",
47                                 filename);
48                 fclose(f);
49                 return -1;
50         }
51         fclose(f);
52
53         cp = strchr(buf, '\n');
54         if (cp)
55                 *cp = '\0';
56
57         /* strip { } notation */
58         if (buf[0] == '{') {
59                 in = buf + 1;
60                 cp = strchr(in, '}');
61                 if (cp)
62                         *cp = '\0';
63         }
64
65         if (rte_uuid_parse(in, uu) < 0) {
66                 VMBUS_LOG(ERR, "%s %s not a valid UUID",
67                         filename, buf);
68                 return -1;
69         }
70
71         return 0;
72 }
73
74 static int
75 get_sysfs_string(const char *filename, char *buf, size_t buflen)
76 {
77         char *cp;
78         FILE *f;
79
80         f = fopen(filename, "r");
81         if (f == NULL) {
82                 VMBUS_LOG(ERR, "cannot open sysfs value %s:%s",
83                           filename, strerror(errno));
84                 return -1;
85         }
86
87         if (fgets(buf, buflen, f) == NULL) {
88                 VMBUS_LOG(ERR, "cannot read sysfs value %s",
89                                 filename);
90                 fclose(f);
91                 return -1;
92         }
93         fclose(f);
94
95         /* remove trailing newline */
96         cp = memchr(buf, '\n', buflen);
97         if (cp)
98                 *cp = '\0';
99
100         return 0;
101 }
102
103 static int
104 vmbus_get_uio_dev(const struct rte_vmbus_device *dev,
105                   char *dstbuf, size_t buflen)
106 {
107         char dirname[PATH_MAX];
108         unsigned int uio_num;
109         struct dirent *e;
110         DIR *dir;
111
112         /* Assume recent kernel where uio is in uio/uioX */
113         snprintf(dirname, sizeof(dirname),
114                  SYSFS_VMBUS_DEVICES "/%s/uio", dev->device.name);
115
116         dir = opendir(dirname);
117         if (dir == NULL)
118                 return -1; /* Not a UIO device */
119
120         /* take the first file starting with "uio" */
121         while ((e = readdir(dir)) != NULL) {
122                 const int prefix_len = 3;
123                 char *endptr;
124
125                 if (strncmp(e->d_name, "uio", prefix_len) != 0)
126                         continue;
127
128                 /* try uio%d */
129                 errno = 0;
130                 uio_num = strtoull(e->d_name + prefix_len, &endptr, 10);
131                 if (errno == 0 && endptr != (e->d_name + prefix_len)) {
132                         snprintf(dstbuf, buflen, "%s/uio%u", dirname, uio_num);
133                         break;
134                 }
135         }
136         closedir(dir);
137
138         if (e == NULL)
139                 return -1;
140
141         return uio_num;
142 }
143
144 /* Check map names with kernel names */
145 static const char *map_names[VMBUS_MAX_RESOURCE] = {
146         [HV_TXRX_RING_MAP] = "txrx_rings",
147         [HV_INT_PAGE_MAP]  = "int_page",
148         [HV_MON_PAGE_MAP]  = "monitor_page",
149         [HV_RECV_BUF_MAP]  = "recv:",
150         [HV_SEND_BUF_MAP]  = "send:",
151 };
152
153
154 /* map the resources of a vmbus device in virtual memory */
155 int
156 rte_vmbus_map_device(struct rte_vmbus_device *dev)
157 {
158         char uioname[PATH_MAX], filename[PATH_MAX];
159         char dirname[PATH_MAX], mapname[64];
160         int i;
161
162         dev->uio_num = vmbus_get_uio_dev(dev, uioname, sizeof(uioname));
163         if (dev->uio_num < 0) {
164                 VMBUS_LOG(DEBUG, "Not managed by UIO driver, skipped");
165                 return 1;
166         }
167
168         /* Extract resource value */
169         for (i = 0; i < VMBUS_MAX_RESOURCE; i++) {
170                 struct rte_mem_resource *res = &dev->resource[i];
171                 unsigned long len, gpad = 0;
172                 char *cp;
173
174                 snprintf(dirname, sizeof(dirname),
175                          "%s/maps/map%d", uioname, i);
176
177                 snprintf(filename, sizeof(filename),
178                          "%s/name", dirname);
179
180                 if (get_sysfs_string(filename, mapname, sizeof(mapname)) < 0) {
181                         VMBUS_LOG(ERR, "could not read %s", filename);
182                         return -1;
183                 }
184
185                 if (strncmp(map_names[i], mapname, strlen(map_names[i])) != 0) {
186                         VMBUS_LOG(ERR,
187                                 "unexpected resource %s (expected %s)",
188                                 mapname, map_names[i]);
189                         return -1;
190                 }
191
192                 snprintf(filename, sizeof(filename),
193                          "%s/size", dirname);
194                 if (eal_parse_sysfs_value(filename, &len) < 0) {
195                         VMBUS_LOG(ERR,
196                                 "could not read %s", filename);
197                         return -1;
198                 }
199                 res->len = len;
200
201                 /* both send and receive buffers have gpad in name */
202                 cp = memchr(mapname, ':', sizeof(mapname));
203                 if (cp)
204                         gpad = strtoul(cp+1, NULL, 0);
205
206                 /* put the GPAD value in physical address */
207                 res->phys_addr = gpad;
208         }
209
210         return vmbus_uio_map_resource(dev);
211 }
212
213 void
214 rte_vmbus_unmap_device(struct rte_vmbus_device *dev)
215 {
216         vmbus_uio_unmap_resource(dev);
217 }
218
219 /* Scan one vmbus sysfs entry, and fill the devices list from it. */
220 static int
221 vmbus_scan_one(const char *name)
222 {
223         struct rte_vmbus_device *dev, *dev2;
224         char filename[PATH_MAX];
225         char dirname[PATH_MAX];
226         unsigned long tmp;
227
228         dev = calloc(1, sizeof(*dev));
229         if (dev == NULL)
230                 return -1;
231
232         dev->device.bus = &rte_vmbus_bus.bus;
233         dev->device.name = strdup(name);
234         if (!dev->device.name)
235                 goto error;
236
237         /* sysfs base directory
238          *   /sys/bus/vmbus/devices/7a08391f-f5a0-4ac0-9802-d13fd964f8df
239          * or on older kernel
240          *   /sys/bus/vmbus/devices/vmbus_1
241          */
242         snprintf(dirname, sizeof(dirname), "%s/%s",
243                  SYSFS_VMBUS_DEVICES, name);
244
245         /* get device id */
246         snprintf(filename, sizeof(filename), "%s/device_id", dirname);
247         if (parse_sysfs_uuid(filename, dev->device_id) < 0)
248                 goto error;
249
250         /* get device class  */
251         snprintf(filename, sizeof(filename), "%s/class_id", dirname);
252         if (parse_sysfs_uuid(filename, dev->class_id) < 0)
253                 goto error;
254
255         /* get relid */
256         snprintf(filename, sizeof(filename), "%s/id", dirname);
257         if (eal_parse_sysfs_value(filename, &tmp) < 0)
258                 goto error;
259         dev->relid = tmp;
260
261         /* get monitor id */
262         snprintf(filename, sizeof(filename), "%s/monitor_id", dirname);
263         if (eal_parse_sysfs_value(filename, &tmp) < 0)
264                 goto error;
265         dev->monitor_id = tmp;
266
267         /* get numa node (if present) */
268         snprintf(filename, sizeof(filename), "%s/numa_node",
269                  dirname);
270
271         if (access(filename, R_OK) == 0) {
272                 if (eal_parse_sysfs_value(filename, &tmp) < 0)
273                         goto error;
274                 dev->device.numa_node = tmp;
275         } else {
276                 /* if no NUMA support, set default to 0 */
277                 dev->device.numa_node = SOCKET_ID_ANY;
278         }
279
280         dev->device.devargs = vmbus_devargs_lookup(dev);
281
282         /* device is valid, add in list (sorted) */
283         VMBUS_LOG(DEBUG, "Adding vmbus device %s", name);
284
285         TAILQ_FOREACH(dev2, &rte_vmbus_bus.device_list, next) {
286                 int ret;
287
288                 ret = rte_uuid_compare(dev->device_id, dev2->device_id);
289                 if (ret > 0)
290                         continue;
291
292                 if (ret < 0) {
293                         vmbus_insert_device(dev2, dev);
294                 } else { /* already registered */
295                         VMBUS_LOG(NOTICE,
296                                 "%s already registered", name);
297                         free(dev);
298                 }
299                 return 0;
300         }
301
302         vmbus_add_device(dev);
303         return 0;
304 error:
305         VMBUS_LOG(DEBUG, "failed");
306
307         free(dev);
308         return -1;
309 }
310
311 /*
312  * Scan the content of the vmbus, and the devices in the devices list
313  */
314 int
315 rte_vmbus_scan(void)
316 {
317         struct dirent *e;
318         DIR *dir;
319
320         dir = opendir(SYSFS_VMBUS_DEVICES);
321         if (dir == NULL) {
322                 if (errno == ENOENT)
323                         return 0;
324
325                 VMBUS_LOG(ERR, "opendir %s failed: %s",
326                           SYSFS_VMBUS_DEVICES, strerror(errno));
327                 return -1;
328         }
329
330         while ((e = readdir(dir)) != NULL) {
331                 if (e->d_name[0] == '.')
332                         continue;
333
334                 if (vmbus_scan_one(e->d_name) < 0)
335                         goto error;
336         }
337         closedir(dir);
338         return 0;
339
340 error:
341         closedir(dir);
342         return -1;
343 }
344
345 void rte_vmbus_irq_mask(struct rte_vmbus_device *device)
346 {
347         vmbus_uio_irq_control(device, 1);
348 }
349
350 void rte_vmbus_irq_unmask(struct rte_vmbus_device *device)
351 {
352         vmbus_uio_irq_control(device, 0);
353 }
354
355 int rte_vmbus_irq_read(struct rte_vmbus_device *device)
356 {
357         return vmbus_uio_irq_read(device);
358 }