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