New upstream version 18.08
[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.name = strdup(name);
233         if (!dev->device.name)
234                 goto error;
235
236         /* sysfs base directory
237          *   /sys/bus/vmbus/devices/7a08391f-f5a0-4ac0-9802-d13fd964f8df
238          * or on older kernel
239          *   /sys/bus/vmbus/devices/vmbus_1
240          */
241         snprintf(dirname, sizeof(dirname), "%s/%s",
242                  SYSFS_VMBUS_DEVICES, name);
243
244         /* get device id */
245         snprintf(filename, sizeof(filename), "%s/device_id", dirname);
246         if (parse_sysfs_uuid(filename, dev->device_id) < 0)
247                 goto error;
248
249         /* get device class  */
250         snprintf(filename, sizeof(filename), "%s/class_id", dirname);
251         if (parse_sysfs_uuid(filename, dev->class_id) < 0)
252                 goto error;
253
254         /* get relid */
255         snprintf(filename, sizeof(filename), "%s/id", dirname);
256         if (eal_parse_sysfs_value(filename, &tmp) < 0)
257                 goto error;
258         dev->relid = tmp;
259
260         /* get monitor id */
261         snprintf(filename, sizeof(filename), "%s/monitor_id", dirname);
262         if (eal_parse_sysfs_value(filename, &tmp) < 0)
263                 goto error;
264         dev->monitor_id = tmp;
265
266         /* get numa node (if present) */
267         snprintf(filename, sizeof(filename), "%s/numa_node",
268                  dirname);
269
270         if (access(filename, R_OK) == 0) {
271                 if (eal_parse_sysfs_value(filename, &tmp) < 0)
272                         goto error;
273                 dev->device.numa_node = tmp;
274         } else {
275                 /* if no NUMA support, set default to 0 */
276                 dev->device.numa_node = SOCKET_ID_ANY;
277         }
278
279         /* device is valid, add in list (sorted) */
280         VMBUS_LOG(DEBUG, "Adding vmbus device %s", name);
281
282         TAILQ_FOREACH(dev2, &rte_vmbus_bus.device_list, next) {
283                 int ret;
284
285                 ret = rte_uuid_compare(dev->device_id, dev2->device_id);
286                 if (ret > 0)
287                         continue;
288
289                 if (ret < 0) {
290                         vmbus_insert_device(dev2, dev);
291                 } else { /* already registered */
292                         VMBUS_LOG(NOTICE,
293                                 "%s already registered", name);
294                         free(dev);
295                 }
296                 return 0;
297         }
298
299         vmbus_add_device(dev);
300         return 0;
301 error:
302         VMBUS_LOG(DEBUG, "failed");
303
304         free(dev);
305         return -1;
306 }
307
308 /*
309  * Scan the content of the vmbus, and the devices in the devices list
310  */
311 int
312 rte_vmbus_scan(void)
313 {
314         struct dirent *e;
315         DIR *dir;
316
317         dir = opendir(SYSFS_VMBUS_DEVICES);
318         if (dir == NULL) {
319                 if (errno == ENOENT)
320                         return 0;
321
322                 VMBUS_LOG(ERR, "opendir %s failed: %s",
323                           SYSFS_VMBUS_DEVICES, strerror(errno));
324                 return -1;
325         }
326
327         while ((e = readdir(dir)) != NULL) {
328                 if (e->d_name[0] == '.')
329                         continue;
330
331                 if (vmbus_scan_one(e->d_name) < 0)
332                         goto error;
333         }
334         closedir(dir);
335         return 0;
336
337 error:
338         closedir(dir);
339         return -1;
340 }
341
342 void rte_vmbus_irq_mask(struct rte_vmbus_device *device)
343 {
344         vmbus_uio_irq_control(device, 1);
345 }
346
347 void rte_vmbus_irq_unmask(struct rte_vmbus_device *device)
348 {
349         vmbus_uio_irq_control(device, 0);
350 }
351
352 int rte_vmbus_irq_read(struct rte_vmbus_device *device)
353 {
354         return vmbus_uio_irq_read(device);
355 }