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_filesystem.h"
23 : : #include "private.h"
24 : :
25 : : /** Pathname of VMBUS devices directory. */
26 : : #define SYSFS_VMBUS_DEVICES "/sys/bus/vmbus/devices"
27 : :
28 : : /*
29 : : * GUID associated with network devices
30 : : * {f8615163-df3e-46c5-913f-f2d2f965ed0e}
31 : : */
32 : : static const rte_uuid_t vmbus_nic_uuid = {
33 : : 0xf8, 0x61, 0x51, 0x63,
34 : : 0xdf, 0x3e,
35 : : 0x46, 0xc5,
36 : : 0x91, 0x3f,
37 : : 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0xe
38 : : };
39 : :
40 : : extern struct rte_vmbus_bus rte_vmbus_bus;
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 : : int
168 : 0 : rte_vmbus_map_device(struct rte_vmbus_device *dev)
169 : : {
170 : : char uioname[PATH_MAX], filename[PATH_MAX];
171 : : char dirname[PATH_MAX], mapname[64];
172 : : int i;
173 : :
174 : 0 : dev->uio_num = vmbus_get_uio_dev(dev, uioname, sizeof(uioname));
175 [ # # ]: 0 : if (dev->uio_num < 0) {
176 : 0 : VMBUS_LOG(DEBUG, "Not managed by UIO driver, skipped");
177 : 0 : return 1;
178 : : }
179 : :
180 : : /* Extract resource value */
181 [ # # ]: 0 : for (i = 0; i < VMBUS_MAX_RESOURCE; i++) {
182 : : struct rte_mem_resource *res = &dev->resource[i];
183 : : unsigned long len, gpad = 0;
184 : : char *cp;
185 : :
186 : : snprintf(dirname, sizeof(dirname),
187 : : "%s/maps/map%d", uioname, i);
188 : :
189 : : snprintf(filename, sizeof(filename),
190 : : "%s/name", dirname);
191 : :
192 [ # # ]: 0 : if (get_sysfs_string(filename, mapname, sizeof(mapname)) < 0) {
193 : 0 : VMBUS_LOG(ERR, "could not read %s", filename);
194 : 0 : return -1;
195 : : }
196 : :
197 [ # # ]: 0 : if (strncmp(map_names[i], mapname, strlen(map_names[i])) != 0) {
198 : 0 : VMBUS_LOG(ERR,
199 : : "unexpected resource %s (expected %s)",
200 : : mapname, map_names[i]);
201 : 0 : return -1;
202 : : }
203 : :
204 : : snprintf(filename, sizeof(filename),
205 : : "%s/size", dirname);
206 [ # # ]: 0 : if (eal_parse_sysfs_value(filename, &len) < 0) {
207 : 0 : VMBUS_LOG(ERR,
208 : : "could not read %s", filename);
209 : 0 : return -1;
210 : : }
211 : 0 : res->len = len;
212 : :
213 : : /* both send and receive buffers have gpad in name */
214 : 0 : cp = memchr(mapname, ':', sizeof(mapname));
215 [ # # ]: 0 : if (cp)
216 : 0 : gpad = strtoul(cp+1, NULL, 0);
217 : :
218 : : /* put the GPAD value in physical address */
219 : 0 : res->phys_addr = gpad;
220 : : }
221 : :
222 : 0 : return vmbus_uio_map_resource(dev);
223 : : }
224 : :
225 : : void
226 : 0 : rte_vmbus_unmap_device(struct rte_vmbus_device *dev)
227 : : {
228 : 0 : vmbus_uio_unmap_resource(dev);
229 : 0 : }
230 : :
231 : : /* Scan one vmbus sysfs entry, and fill the devices list from it. */
232 : : static int
233 : 0 : vmbus_scan_one(const char *name)
234 : : {
235 : : struct rte_vmbus_device *dev, *dev2;
236 : : char filename[PATH_MAX];
237 : : char dirname[PATH_MAX];
238 : : unsigned long tmp;
239 : : char *dev_name;
240 : :
241 : 0 : dev = calloc(1, sizeof(*dev));
242 [ # # ]: 0 : if (dev == NULL)
243 : : return -1;
244 : :
245 : 0 : dev->device.bus = &rte_vmbus_bus.bus;
246 : 0 : dev->device.name = dev_name = strdup(name);
247 [ # # ]: 0 : if (!dev->device.name)
248 : 0 : goto error;
249 : :
250 : : /* sysfs base directory
251 : : * /sys/bus/vmbus/devices/7a08391f-f5a0-4ac0-9802-d13fd964f8df
252 : : * or on older kernel
253 : : * /sys/bus/vmbus/devices/vmbus_1
254 : : */
255 : : snprintf(dirname, sizeof(dirname), "%s/%s",
256 : : SYSFS_VMBUS_DEVICES, name);
257 : :
258 : : /* get device class */
259 : : snprintf(filename, sizeof(filename), "%s/class_id", dirname);
260 [ # # ]: 0 : if (parse_sysfs_uuid(filename, dev->class_id) < 0)
261 : 0 : goto error;
262 : :
263 : : /* skip non-network devices */
264 [ # # ]: 0 : if (rte_uuid_compare(dev->class_id, vmbus_nic_uuid) != 0) {
265 : 0 : free(dev_name);
266 : 0 : free(dev);
267 : 0 : return 0;
268 : : }
269 : :
270 : : /* get device id */
271 : : snprintf(filename, sizeof(filename), "%s/device_id", dirname);
272 [ # # ]: 0 : if (parse_sysfs_uuid(filename, dev->device_id) < 0)
273 : 0 : goto error;
274 : :
275 : : /* get relid */
276 : : snprintf(filename, sizeof(filename), "%s/id", dirname);
277 [ # # ]: 0 : if (eal_parse_sysfs_value(filename, &tmp) < 0)
278 : 0 : goto error;
279 : 0 : dev->relid = tmp;
280 : :
281 : : /* get monitor id */
282 : : snprintf(filename, sizeof(filename), "%s/monitor_id", dirname);
283 [ # # ]: 0 : if (eal_parse_sysfs_value(filename, &tmp) < 0)
284 : 0 : goto error;
285 : 0 : dev->monitor_id = tmp;
286 : :
287 : : /* get numa node (if present) */
288 : : snprintf(filename, sizeof(filename), "%s/numa_node",
289 : : dirname);
290 : :
291 [ # # ]: 0 : if (access(filename, R_OK) == 0) {
292 [ # # ]: 0 : if (eal_parse_sysfs_value(filename, &tmp) < 0)
293 : 0 : goto error;
294 : 0 : dev->device.numa_node = tmp;
295 : : } else {
296 : 0 : dev->device.numa_node = SOCKET_ID_ANY;
297 : : }
298 : :
299 : 0 : dev->device.devargs = vmbus_devargs_lookup(dev);
300 : :
301 : : /* Allocate interrupt handle instance */
302 : 0 : dev->intr_handle =
303 : 0 : rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
304 [ # # ]: 0 : if (dev->intr_handle == NULL)
305 : 0 : goto error;
306 : :
307 : : /* device is valid, add in list (sorted) */
308 : 0 : VMBUS_LOG(DEBUG, "Adding vmbus device %s", name);
309 : :
310 [ # # ]: 0 : TAILQ_FOREACH(dev2, &rte_vmbus_bus.device_list, next) {
311 : : int ret;
312 : :
313 : 0 : ret = rte_uuid_compare(dev->device_id, dev2->device_id);
314 [ # # ]: 0 : if (ret > 0)
315 : : continue;
316 : :
317 [ # # ]: 0 : if (ret < 0) {
318 : 0 : vmbus_insert_device(dev2, dev);
319 : : } else { /* already registered */
320 : 0 : VMBUS_LOG(NOTICE,
321 : : "%s already registered", name);
322 : 0 : free(dev_name);
323 : 0 : free(dev);
324 : : }
325 : : return 0;
326 : : }
327 : :
328 : 0 : vmbus_add_device(dev);
329 : 0 : return 0;
330 : 0 : error:
331 : 0 : VMBUS_LOG(DEBUG, "failed");
332 : :
333 : 0 : free(dev_name);
334 : 0 : free(dev);
335 : 0 : return -1;
336 : : }
337 : :
338 : : /*
339 : : * Scan the content of the vmbus, and the devices in the devices list
340 : : */
341 : : int
342 : 185 : rte_vmbus_scan(void)
343 : : {
344 : : struct dirent *e;
345 : : DIR *dir;
346 : :
347 : 185 : dir = opendir(SYSFS_VMBUS_DEVICES);
348 [ + - ]: 185 : if (dir == NULL) {
349 [ - + ]: 185 : if (errno == ENOENT)
350 : : return 0;
351 : :
352 : 0 : VMBUS_LOG(ERR, "opendir %s failed: %s",
353 : : SYSFS_VMBUS_DEVICES, strerror(errno));
354 : 0 : return -1;
355 : : }
356 : :
357 [ # # ]: 0 : while ((e = readdir(dir)) != NULL) {
358 [ # # ]: 0 : if (e->d_name[0] == '.')
359 : 0 : continue;
360 : :
361 [ # # ]: 0 : if (vmbus_scan_one(e->d_name) < 0)
362 : 0 : goto error;
363 : : }
364 : 0 : closedir(dir);
365 : 0 : return 0;
366 : :
367 : : error:
368 : 0 : closedir(dir);
369 : 0 : return -1;
370 : : }
371 : :
372 : 0 : void rte_vmbus_irq_mask(struct rte_vmbus_device *device)
373 : : {
374 : 0 : vmbus_uio_irq_control(device, 1);
375 : 0 : }
376 : :
377 : 0 : void rte_vmbus_irq_unmask(struct rte_vmbus_device *device)
378 : : {
379 : 0 : vmbus_uio_irq_control(device, 0);
380 : 0 : }
381 : :
382 : 0 : int rte_vmbus_irq_read(struct rte_vmbus_device *device)
383 : : {
384 : 0 : return vmbus_uio_irq_read(device);
385 : : }
|