Branch data Line data Source code
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 : : #include <rte_kvargs.h>
22 : :
23 : : #include <eal_export.h>
24 : : #include "eal_filesystem.h"
25 : : #include "private.h"
26 : :
27 : : /** Pathname of VMBUS devices directory. */
28 : : #define SYSFS_VMBUS_DEVICES "/sys/bus/vmbus/devices"
29 : :
30 : : /*
31 : : * GUID associated with network devices
32 : : * {f8615163-df3e-46c5-913f-f2d2f965ed0e}
33 : : */
34 : : static const rte_uuid_t vmbus_nic_uuid = {
35 : : 0xf8, 0x61, 0x51, 0x63,
36 : : 0xdf, 0x3e,
37 : : 0x46, 0xc5,
38 : : 0x91, 0x3f,
39 : : 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0xe
40 : : };
41 : :
42 : : /* Read sysfs file to get UUID */
43 : : static int
44 : 0 : parse_sysfs_uuid(const char *filename, rte_uuid_t uu)
45 : : {
46 : : char buf[BUFSIZ];
47 : : char *cp, *in = buf;
48 : : FILE *f;
49 : :
50 : 0 : f = fopen(filename, "r");
51 [ # # ]: 0 : if (f == NULL) {
52 : 0 : VMBUS_LOG(ERR, "cannot open sysfs value %s: %s",
53 : : filename, strerror(errno));
54 : 0 : return -1;
55 : : }
56 : :
57 [ # # ]: 0 : if (fgets(buf, sizeof(buf), f) == NULL) {
58 : 0 : VMBUS_LOG(ERR, "cannot read sysfs value %s",
59 : : filename);
60 : 0 : fclose(f);
61 : 0 : return -1;
62 : : }
63 : 0 : fclose(f);
64 : :
65 : 0 : cp = strchr(buf, '\n');
66 [ # # ]: 0 : if (cp)
67 : 0 : *cp = '\0';
68 : :
69 : : /* strip { } notation */
70 [ # # ]: 0 : if (buf[0] == '{') {
71 : : in = buf + 1;
72 : 0 : cp = strchr(in, '}');
73 [ # # ]: 0 : if (cp)
74 : 0 : *cp = '\0';
75 : : }
76 : :
77 [ # # ]: 0 : if (rte_uuid_parse(in, uu) < 0) {
78 : 0 : VMBUS_LOG(ERR, "%s %s not a valid UUID",
79 : : filename, buf);
80 : 0 : return -1;
81 : : }
82 : :
83 : : return 0;
84 : : }
85 : :
86 : : static int
87 : 0 : get_sysfs_string(const char *filename, char *buf, size_t buflen)
88 : : {
89 : : char *cp;
90 : : FILE *f;
91 : :
92 : 0 : f = fopen(filename, "r");
93 [ # # ]: 0 : if (f == NULL) {
94 : 0 : VMBUS_LOG(ERR, "cannot open sysfs value %s:%s",
95 : : filename, strerror(errno));
96 : 0 : return -1;
97 : : }
98 : :
99 [ # # # # ]: 0 : if (fgets(buf, buflen, f) == NULL) {
100 : 0 : VMBUS_LOG(ERR, "cannot read sysfs value %s",
101 : : filename);
102 : 0 : fclose(f);
103 : 0 : return -1;
104 : : }
105 : 0 : fclose(f);
106 : :
107 : : /* remove trailing newline */
108 : 0 : cp = memchr(buf, '\n', buflen);
109 [ # # ]: 0 : if (cp)
110 : 0 : *cp = '\0';
111 : :
112 : : return 0;
113 : : }
114 : :
115 : : static int
116 : 0 : vmbus_get_uio_dev(const struct rte_vmbus_device *dev,
117 : : char *dstbuf, size_t buflen)
118 : : {
119 : : char dirname[PATH_MAX];
120 : : unsigned int uio_num;
121 : : struct dirent *e;
122 : : DIR *dir;
123 : :
124 : : /* Assume recent kernel where uio is in uio/uioX */
125 : : snprintf(dirname, sizeof(dirname),
126 : 0 : SYSFS_VMBUS_DEVICES "/%s/uio", dev->device.name);
127 : :
128 : 0 : dir = opendir(dirname);
129 [ # # ]: 0 : if (dir == NULL)
130 : : return -1; /* Not a UIO device */
131 : :
132 : : /* take the first file starting with "uio" */
133 [ # # ]: 0 : while ((e = readdir(dir)) != NULL) {
134 : : const int prefix_len = 3;
135 : : char *endptr;
136 : :
137 [ # # ]: 0 : if (strncmp(e->d_name, "uio", prefix_len) != 0)
138 : 0 : continue;
139 : :
140 : : /* try uio%d */
141 : 0 : errno = 0;
142 : 0 : uio_num = strtoull(e->d_name + prefix_len, &endptr, 10);
143 [ # # # # ]: 0 : if (errno == 0 && endptr != (e->d_name + prefix_len)) {
144 : : snprintf(dstbuf, buflen, "%s/uio%u", dirname, uio_num);
145 : 0 : break;
146 : : }
147 : : }
148 : 0 : closedir(dir);
149 : :
150 [ # # ]: 0 : if (e == NULL)
151 : : return -1;
152 : :
153 : 0 : return uio_num;
154 : : }
155 : :
156 : : /* Check map names with kernel names */
157 : : static const char *map_names[VMBUS_MAX_RESOURCE] = {
158 : : [HV_TXRX_RING_MAP] = "txrx_rings",
159 : : [HV_INT_PAGE_MAP] = "int_page",
160 : : [HV_MON_PAGE_MAP] = "monitor_page",
161 : : [HV_RECV_BUF_MAP] = "recv:",
162 : : [HV_SEND_BUF_MAP] = "send:",
163 : : };
164 : :
165 : :
166 : : /* map the resources of a vmbus device in virtual memory */
167 : : RTE_EXPORT_SYMBOL(rte_vmbus_map_device)
168 : : int
169 : 0 : rte_vmbus_map_device(struct rte_vmbus_device *dev)
170 : : {
171 : : char uioname[PATH_MAX], filename[PATH_MAX];
172 : : char dirname[PATH_MAX], mapname[64];
173 : : int i;
174 : :
175 : 0 : dev->uio_num = vmbus_get_uio_dev(dev, uioname, sizeof(uioname));
176 [ # # ]: 0 : if (dev->uio_num < 0) {
177 : 0 : VMBUS_LOG(DEBUG, "Not managed by UIO driver, skipped");
178 : 0 : return 1;
179 : : }
180 : :
181 : : /* Extract resource value */
182 [ # # ]: 0 : for (i = 0; i < VMBUS_MAX_RESOURCE; i++) {
183 : : struct rte_mem_resource *res = &dev->resource[i];
184 : : unsigned long len, gpad = 0;
185 : : char *cp;
186 : :
187 : : snprintf(dirname, sizeof(dirname),
188 : : "%s/maps/map%d", uioname, i);
189 : :
190 : : snprintf(filename, sizeof(filename),
191 : : "%s/name", dirname);
192 : :
193 [ # # ]: 0 : if (get_sysfs_string(filename, mapname, sizeof(mapname)) < 0) {
194 : 0 : VMBUS_LOG(ERR, "could not read %s", filename);
195 : 0 : return -1;
196 : : }
197 : :
198 [ # # ]: 0 : if (strncmp(map_names[i], mapname, strlen(map_names[i])) != 0) {
199 : 0 : VMBUS_LOG(ERR,
200 : : "unexpected resource %s (expected %s)",
201 : : mapname, map_names[i]);
202 : 0 : return -1;
203 : : }
204 : :
205 : : snprintf(filename, sizeof(filename),
206 : : "%s/size", dirname);
207 [ # # ]: 0 : if (eal_parse_sysfs_value(filename, &len) < 0) {
208 : 0 : VMBUS_LOG(ERR,
209 : : "could not read %s", filename);
210 : 0 : return -1;
211 : : }
212 : 0 : res->len = len;
213 : :
214 : : /* both send and receive buffers have gpad in name */
215 : 0 : cp = memchr(mapname, ':', sizeof(mapname));
216 [ # # ]: 0 : if (cp)
217 : 0 : gpad = strtoul(cp+1, NULL, 0);
218 : :
219 : : /* put the GPAD value in physical address */
220 : 0 : res->phys_addr = gpad;
221 : : }
222 : :
223 : 0 : return vmbus_uio_map_resource(dev);
224 : : }
225 : :
226 : : RTE_EXPORT_SYMBOL(rte_vmbus_unmap_device)
227 : : void
228 : 0 : rte_vmbus_unmap_device(struct rte_vmbus_device *dev)
229 : : {
230 : 0 : vmbus_uio_unmap_resource(dev);
231 : 0 : }
232 : :
233 : : /* Check in dev args if NUMA should be used by checking for "numa_aware" in the
234 : : * device arguments.
235 : : * By default returning false, meaning this vmbus device is not NUMA aware.
236 : : */
237 : 0 : static bool vmbus_use_numa(struct rte_vmbus_device *dev)
238 : : {
239 : 0 : struct rte_devargs *devargs = dev->device.devargs;
240 : : struct rte_kvargs *kvlist;
241 : : const struct rte_kvargs_pair *pair;
242 : : unsigned long v;
243 : : unsigned int i;
244 : 0 : char *endp = NULL;
245 : : bool ret = false;
246 : :
247 [ # # ]: 0 : if (!devargs)
248 : : return ret;
249 : :
250 : 0 : VMBUS_LOG(DEBUG, "device args %s %s", devargs->name, devargs->args);
251 : :
252 : 0 : kvlist = rte_kvargs_parse(devargs->args, NULL);
253 [ # # ]: 0 : if (!kvlist) {
254 : 0 : VMBUS_LOG(ERR, "invalid parameters");
255 : 0 : return ret;
256 : : }
257 : :
258 [ # # ]: 0 : for (i = 0; i < kvlist->count; i++) {
259 : : pair = &kvlist->pairs[i];
260 [ # # ]: 0 : if (!strcmp(pair->key, NETVSC_ARG_NUMA_AWARE)) {
261 : 0 : v = strtoul(pair->value, &endp, 0);
262 [ # # # # ]: 0 : if (*pair->value == '\0' || *endp != '\0') {
263 : 0 : VMBUS_LOG(ERR, "invalid parameter %s=%s",
264 : : pair->key, pair->value);
265 : : }
266 : 0 : ret = v ? true : false;
267 : : }
268 : : }
269 : :
270 : 0 : rte_kvargs_free(kvlist);
271 : :
272 : 0 : return ret;
273 : : }
274 : :
275 : : /* Scan one vmbus sysfs entry, and fill the devices list from it. */
276 : : static int
277 : 0 : vmbus_scan_one(const char *name)
278 : : {
279 : : struct rte_vmbus_device *dev, *dev2;
280 : : char filename[PATH_MAX];
281 : : char dirname[PATH_MAX];
282 : : unsigned long tmp;
283 : :
284 : 0 : dev = calloc(1, sizeof(*dev));
285 [ # # ]: 0 : if (dev == NULL)
286 : : return -1;
287 : :
288 [ # # ]: 0 : if (rte_strscpy(dev->name, name, sizeof(dev->name)) < 0)
289 : 0 : goto error;
290 : 0 : dev->device.name = dev->name;
291 : :
292 : : /* sysfs base directory
293 : : * /sys/bus/vmbus/devices/7a08391f-f5a0-4ac0-9802-d13fd964f8df
294 : : * or on older kernel
295 : : * /sys/bus/vmbus/devices/vmbus_1
296 : : */
297 : : snprintf(dirname, sizeof(dirname), "%s/%s",
298 : : SYSFS_VMBUS_DEVICES, name);
299 : :
300 : : /* get device class */
301 : : snprintf(filename, sizeof(filename), "%s/class_id", dirname);
302 [ # # ]: 0 : if (parse_sysfs_uuid(filename, dev->class_id) < 0)
303 : 0 : goto error;
304 : :
305 : : /* skip non-network devices */
306 [ # # ]: 0 : if (rte_uuid_compare(dev->class_id, vmbus_nic_uuid) != 0) {
307 : 0 : free(dev);
308 : 0 : return 0;
309 : : }
310 : :
311 : : /* get device id */
312 : : snprintf(filename, sizeof(filename), "%s/device_id", dirname);
313 [ # # ]: 0 : if (parse_sysfs_uuid(filename, dev->device_id) < 0)
314 : 0 : goto error;
315 : :
316 : : /* get relid */
317 : : snprintf(filename, sizeof(filename), "%s/id", dirname);
318 [ # # ]: 0 : if (eal_parse_sysfs_value(filename, &tmp) < 0)
319 : 0 : goto error;
320 : 0 : dev->relid = tmp;
321 : :
322 : : /* get monitor id */
323 : : snprintf(filename, sizeof(filename), "%s/monitor_id", dirname);
324 [ # # ]: 0 : if (eal_parse_sysfs_value(filename, &tmp) >= 0) {
325 : 0 : dev->monitor_id = tmp;
326 : : } else {
327 : 0 : VMBUS_LOG(NOTICE, "monitor disabled on %s", name);
328 : 0 : dev->monitor_id = UINT8_MAX;
329 : : }
330 : :
331 : 0 : dev->device.devargs = rte_bus_find_devargs(&rte_vmbus_bus, dev->name);
332 : :
333 : 0 : dev->device.numa_node = SOCKET_ID_ANY;
334 [ # # ]: 0 : if (vmbus_use_numa(dev)) {
335 : : /* get numa node (if present) */
336 : : snprintf(filename, sizeof(filename), "%s/numa_node",
337 : : dirname);
338 : :
339 [ # # ]: 0 : if (access(filename, R_OK) == 0) {
340 [ # # ]: 0 : if (eal_parse_sysfs_value(filename, &tmp) < 0)
341 : 0 : goto error;
342 : 0 : dev->device.numa_node = tmp;
343 : : }
344 : : }
345 : :
346 : : /* device is valid, add in list (sorted) */
347 : 0 : VMBUS_LOG(DEBUG, "Adding vmbus device %s", name);
348 : :
349 [ # # ]: 0 : RTE_BUS_FOREACH_DEV(dev2, &rte_vmbus_bus) {
350 : : int ret;
351 : :
352 : 0 : ret = rte_uuid_compare(dev->device_id, dev2->device_id);
353 [ # # ]: 0 : if (ret > 0)
354 : : continue;
355 : :
356 [ # # ]: 0 : if (ret < 0) {
357 : 0 : rte_bus_insert_device(&rte_vmbus_bus, &dev2->device, &dev->device);
358 : : } else { /* already registered */
359 : 0 : VMBUS_LOG(NOTICE,
360 : : "%s already registered", name);
361 : 0 : free(dev);
362 : : }
363 : : return 0;
364 : : }
365 : :
366 : 0 : rte_bus_add_device(&rte_vmbus_bus, &dev->device);
367 : 0 : return 0;
368 : 0 : error:
369 : 0 : VMBUS_LOG(DEBUG, "failed");
370 : :
371 : 0 : free(dev);
372 : 0 : return -1;
373 : : }
374 : :
375 : : /*
376 : : * Scan the content of the vmbus, and the devices in the devices list
377 : : */
378 : : RTE_EXPORT_SYMBOL(rte_vmbus_scan)
379 : : int
380 : 225 : rte_vmbus_scan(void)
381 : : {
382 : : struct dirent *e;
383 : : DIR *dir;
384 : :
385 : 225 : dir = opendir(SYSFS_VMBUS_DEVICES);
386 [ + - ]: 225 : if (dir == NULL) {
387 [ - + ]: 225 : if (errno == ENOENT)
388 : : return 0;
389 : :
390 : 0 : VMBUS_LOG(ERR, "opendir %s failed: %s",
391 : : SYSFS_VMBUS_DEVICES, strerror(errno));
392 : 0 : return -1;
393 : : }
394 : :
395 [ # # ]: 0 : while ((e = readdir(dir)) != NULL) {
396 [ # # ]: 0 : if (e->d_name[0] == '.')
397 : 0 : continue;
398 : :
399 [ # # ]: 0 : if (vmbus_scan_one(e->d_name) < 0)
400 : 0 : goto error;
401 : : }
402 : 0 : closedir(dir);
403 : 0 : return 0;
404 : :
405 : : error:
406 : 0 : closedir(dir);
407 : 0 : return -1;
408 : : }
409 : :
410 : : RTE_EXPORT_SYMBOL(rte_vmbus_irq_mask)
411 : 0 : void rte_vmbus_irq_mask(struct rte_vmbus_device *device)
412 : : {
413 : 0 : vmbus_uio_irq_control(device, 1);
414 : 0 : }
415 : :
416 : : RTE_EXPORT_SYMBOL(rte_vmbus_irq_unmask)
417 : 0 : void rte_vmbus_irq_unmask(struct rte_vmbus_device *device)
418 : : {
419 : 0 : vmbus_uio_irq_control(device, 0);
420 : 0 : }
421 : :
422 : : RTE_EXPORT_SYMBOL(rte_vmbus_irq_read)
423 : 0 : int rte_vmbus_irq_read(struct rte_vmbus_device *device)
424 : : {
425 : 0 : return vmbus_uio_irq_read(device);
426 : : }
|