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