1831dba47SStephen Hemminger /* SPDX-License-Identifier: BSD-3-Clause
2831dba47SStephen Hemminger * Copyright (c) 2018, Microsoft Corporation.
3831dba47SStephen Hemminger * All Rights Reserved.
4831dba47SStephen Hemminger */
5831dba47SStephen Hemminger
6831dba47SStephen Hemminger #include <string.h>
7831dba47SStephen Hemminger #include <unistd.h>
8831dba47SStephen Hemminger #include <dirent.h>
9831dba47SStephen Hemminger #include <fcntl.h>
10831dba47SStephen Hemminger #include <sys/mman.h>
11831dba47SStephen Hemminger #include <sys/stat.h>
12831dba47SStephen Hemminger
13831dba47SStephen Hemminger #include <rte_eal.h>
14831dba47SStephen Hemminger #include <rte_uuid.h>
15831dba47SStephen Hemminger #include <rte_tailq.h>
16831dba47SStephen Hemminger #include <rte_log.h>
17831dba47SStephen Hemminger #include <rte_devargs.h>
18831dba47SStephen Hemminger #include <rte_memory.h>
19831dba47SStephen Hemminger #include <rte_malloc.h>
20831dba47SStephen Hemminger #include <rte_bus_vmbus.h>
21831dba47SStephen Hemminger
22831dba47SStephen Hemminger #include "eal_filesystem.h"
23831dba47SStephen Hemminger #include "private.h"
24831dba47SStephen Hemminger
25831dba47SStephen Hemminger /** Pathname of VMBUS devices directory. */
26831dba47SStephen Hemminger #define SYSFS_VMBUS_DEVICES "/sys/bus/vmbus/devices"
27831dba47SStephen Hemminger
28b11b76c2SStephen Hemminger /*
29b11b76c2SStephen Hemminger * GUID associated with network devices
30b11b76c2SStephen Hemminger * {f8615163-df3e-46c5-913f-f2d2f965ed0e}
31b11b76c2SStephen Hemminger */
32b11b76c2SStephen Hemminger static const rte_uuid_t vmbus_nic_uuid = {
33b11b76c2SStephen Hemminger 0xf8, 0x61, 0x51, 0x63,
34b11b76c2SStephen Hemminger 0xdf, 0x3e,
35b11b76c2SStephen Hemminger 0x46, 0xc5,
36b11b76c2SStephen Hemminger 0x91, 0x3f,
37b11b76c2SStephen Hemminger 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0xe
38b11b76c2SStephen Hemminger };
39b11b76c2SStephen Hemminger
40831dba47SStephen Hemminger extern struct rte_vmbus_bus rte_vmbus_bus;
41831dba47SStephen Hemminger
42831dba47SStephen Hemminger /* Read sysfs file to get UUID */
43831dba47SStephen Hemminger static int
parse_sysfs_uuid(const char * filename,rte_uuid_t uu)44831dba47SStephen Hemminger parse_sysfs_uuid(const char *filename, rte_uuid_t uu)
45831dba47SStephen Hemminger {
46831dba47SStephen Hemminger char buf[BUFSIZ];
47831dba47SStephen Hemminger char *cp, *in = buf;
48831dba47SStephen Hemminger FILE *f;
49831dba47SStephen Hemminger
50831dba47SStephen Hemminger f = fopen(filename, "r");
51831dba47SStephen Hemminger if (f == NULL) {
52831dba47SStephen Hemminger VMBUS_LOG(ERR, "cannot open sysfs value %s: %s",
53831dba47SStephen Hemminger filename, strerror(errno));
54831dba47SStephen Hemminger return -1;
55831dba47SStephen Hemminger }
56831dba47SStephen Hemminger
57831dba47SStephen Hemminger if (fgets(buf, sizeof(buf), f) == NULL) {
58831dba47SStephen Hemminger VMBUS_LOG(ERR, "cannot read sysfs value %s",
59831dba47SStephen Hemminger filename);
60831dba47SStephen Hemminger fclose(f);
61831dba47SStephen Hemminger return -1;
62831dba47SStephen Hemminger }
63831dba47SStephen Hemminger fclose(f);
64831dba47SStephen Hemminger
65831dba47SStephen Hemminger cp = strchr(buf, '\n');
66831dba47SStephen Hemminger if (cp)
67831dba47SStephen Hemminger *cp = '\0';
68831dba47SStephen Hemminger
69831dba47SStephen Hemminger /* strip { } notation */
70831dba47SStephen Hemminger if (buf[0] == '{') {
71831dba47SStephen Hemminger in = buf + 1;
72831dba47SStephen Hemminger cp = strchr(in, '}');
73831dba47SStephen Hemminger if (cp)
74831dba47SStephen Hemminger *cp = '\0';
75831dba47SStephen Hemminger }
76831dba47SStephen Hemminger
77831dba47SStephen Hemminger if (rte_uuid_parse(in, uu) < 0) {
78831dba47SStephen Hemminger VMBUS_LOG(ERR, "%s %s not a valid UUID",
79831dba47SStephen Hemminger filename, buf);
80831dba47SStephen Hemminger return -1;
81831dba47SStephen Hemminger }
82831dba47SStephen Hemminger
83831dba47SStephen Hemminger return 0;
84831dba47SStephen Hemminger }
85831dba47SStephen Hemminger
86831dba47SStephen Hemminger static int
get_sysfs_string(const char * filename,char * buf,size_t buflen)87831dba47SStephen Hemminger get_sysfs_string(const char *filename, char *buf, size_t buflen)
88831dba47SStephen Hemminger {
89831dba47SStephen Hemminger char *cp;
90831dba47SStephen Hemminger FILE *f;
91831dba47SStephen Hemminger
92831dba47SStephen Hemminger f = fopen(filename, "r");
93831dba47SStephen Hemminger if (f == NULL) {
94831dba47SStephen Hemminger VMBUS_LOG(ERR, "cannot open sysfs value %s:%s",
95831dba47SStephen Hemminger filename, strerror(errno));
96831dba47SStephen Hemminger return -1;
97831dba47SStephen Hemminger }
98831dba47SStephen Hemminger
99831dba47SStephen Hemminger if (fgets(buf, buflen, f) == NULL) {
100831dba47SStephen Hemminger VMBUS_LOG(ERR, "cannot read sysfs value %s",
101831dba47SStephen Hemminger filename);
102831dba47SStephen Hemminger fclose(f);
103831dba47SStephen Hemminger return -1;
104831dba47SStephen Hemminger }
105831dba47SStephen Hemminger fclose(f);
106831dba47SStephen Hemminger
107831dba47SStephen Hemminger /* remove trailing newline */
108831dba47SStephen Hemminger cp = memchr(buf, '\n', buflen);
109831dba47SStephen Hemminger if (cp)
110831dba47SStephen Hemminger *cp = '\0';
111831dba47SStephen Hemminger
112831dba47SStephen Hemminger return 0;
113831dba47SStephen Hemminger }
114831dba47SStephen Hemminger
115831dba47SStephen Hemminger static int
vmbus_get_uio_dev(const struct rte_vmbus_device * dev,char * dstbuf,size_t buflen)116831dba47SStephen Hemminger vmbus_get_uio_dev(const struct rte_vmbus_device *dev,
117831dba47SStephen Hemminger char *dstbuf, size_t buflen)
118831dba47SStephen Hemminger {
119831dba47SStephen Hemminger char dirname[PATH_MAX];
120831dba47SStephen Hemminger unsigned int uio_num;
121831dba47SStephen Hemminger struct dirent *e;
122831dba47SStephen Hemminger DIR *dir;
123831dba47SStephen Hemminger
124831dba47SStephen Hemminger /* Assume recent kernel where uio is in uio/uioX */
125831dba47SStephen Hemminger snprintf(dirname, sizeof(dirname),
126831dba47SStephen Hemminger SYSFS_VMBUS_DEVICES "/%s/uio", dev->device.name);
127831dba47SStephen Hemminger
128831dba47SStephen Hemminger dir = opendir(dirname);
129831dba47SStephen Hemminger if (dir == NULL)
130831dba47SStephen Hemminger return -1; /* Not a UIO device */
131831dba47SStephen Hemminger
132831dba47SStephen Hemminger /* take the first file starting with "uio" */
133831dba47SStephen Hemminger while ((e = readdir(dir)) != NULL) {
134831dba47SStephen Hemminger const int prefix_len = 3;
135831dba47SStephen Hemminger char *endptr;
136831dba47SStephen Hemminger
137831dba47SStephen Hemminger if (strncmp(e->d_name, "uio", prefix_len) != 0)
138831dba47SStephen Hemminger continue;
139831dba47SStephen Hemminger
140831dba47SStephen Hemminger /* try uio%d */
141831dba47SStephen Hemminger errno = 0;
142831dba47SStephen Hemminger uio_num = strtoull(e->d_name + prefix_len, &endptr, 10);
143831dba47SStephen Hemminger if (errno == 0 && endptr != (e->d_name + prefix_len)) {
144831dba47SStephen Hemminger snprintf(dstbuf, buflen, "%s/uio%u", dirname, uio_num);
145831dba47SStephen Hemminger break;
146831dba47SStephen Hemminger }
147831dba47SStephen Hemminger }
148831dba47SStephen Hemminger closedir(dir);
149831dba47SStephen Hemminger
150831dba47SStephen Hemminger if (e == NULL)
151831dba47SStephen Hemminger return -1;
152831dba47SStephen Hemminger
153831dba47SStephen Hemminger return uio_num;
154831dba47SStephen Hemminger }
155831dba47SStephen Hemminger
156831dba47SStephen Hemminger /* Check map names with kernel names */
157831dba47SStephen Hemminger static const char *map_names[VMBUS_MAX_RESOURCE] = {
158831dba47SStephen Hemminger [HV_TXRX_RING_MAP] = "txrx_rings",
159831dba47SStephen Hemminger [HV_INT_PAGE_MAP] = "int_page",
160831dba47SStephen Hemminger [HV_MON_PAGE_MAP] = "monitor_page",
161831dba47SStephen Hemminger [HV_RECV_BUF_MAP] = "recv:",
162831dba47SStephen Hemminger [HV_SEND_BUF_MAP] = "send:",
163831dba47SStephen Hemminger };
164831dba47SStephen Hemminger
165831dba47SStephen Hemminger
166831dba47SStephen Hemminger /* map the resources of a vmbus device in virtual memory */
167831dba47SStephen Hemminger int
rte_vmbus_map_device(struct rte_vmbus_device * dev)168831dba47SStephen Hemminger rte_vmbus_map_device(struct rte_vmbus_device *dev)
169831dba47SStephen Hemminger {
170831dba47SStephen Hemminger char uioname[PATH_MAX], filename[PATH_MAX];
171831dba47SStephen Hemminger char dirname[PATH_MAX], mapname[64];
172831dba47SStephen Hemminger int i;
173831dba47SStephen Hemminger
174831dba47SStephen Hemminger dev->uio_num = vmbus_get_uio_dev(dev, uioname, sizeof(uioname));
175831dba47SStephen Hemminger if (dev->uio_num < 0) {
176831dba47SStephen Hemminger VMBUS_LOG(DEBUG, "Not managed by UIO driver, skipped");
177831dba47SStephen Hemminger return 1;
178831dba47SStephen Hemminger }
179831dba47SStephen Hemminger
180831dba47SStephen Hemminger /* Extract resource value */
181831dba47SStephen Hemminger for (i = 0; i < VMBUS_MAX_RESOURCE; i++) {
182831dba47SStephen Hemminger struct rte_mem_resource *res = &dev->resource[i];
183831dba47SStephen Hemminger unsigned long len, gpad = 0;
184831dba47SStephen Hemminger char *cp;
185831dba47SStephen Hemminger
186831dba47SStephen Hemminger snprintf(dirname, sizeof(dirname),
187831dba47SStephen Hemminger "%s/maps/map%d", uioname, i);
188831dba47SStephen Hemminger
189831dba47SStephen Hemminger snprintf(filename, sizeof(filename),
190831dba47SStephen Hemminger "%s/name", dirname);
191831dba47SStephen Hemminger
192831dba47SStephen Hemminger if (get_sysfs_string(filename, mapname, sizeof(mapname)) < 0) {
193831dba47SStephen Hemminger VMBUS_LOG(ERR, "could not read %s", filename);
194831dba47SStephen Hemminger return -1;
195831dba47SStephen Hemminger }
196831dba47SStephen Hemminger
197831dba47SStephen Hemminger if (strncmp(map_names[i], mapname, strlen(map_names[i])) != 0) {
198831dba47SStephen Hemminger VMBUS_LOG(ERR,
199831dba47SStephen Hemminger "unexpected resource %s (expected %s)",
200831dba47SStephen Hemminger mapname, map_names[i]);
201831dba47SStephen Hemminger return -1;
202831dba47SStephen Hemminger }
203831dba47SStephen Hemminger
204831dba47SStephen Hemminger snprintf(filename, sizeof(filename),
205831dba47SStephen Hemminger "%s/size", dirname);
206831dba47SStephen Hemminger if (eal_parse_sysfs_value(filename, &len) < 0) {
207831dba47SStephen Hemminger VMBUS_LOG(ERR,
208831dba47SStephen Hemminger "could not read %s", filename);
209831dba47SStephen Hemminger return -1;
210831dba47SStephen Hemminger }
211831dba47SStephen Hemminger res->len = len;
212831dba47SStephen Hemminger
213831dba47SStephen Hemminger /* both send and receive buffers have gpad in name */
214831dba47SStephen Hemminger cp = memchr(mapname, ':', sizeof(mapname));
215831dba47SStephen Hemminger if (cp)
216831dba47SStephen Hemminger gpad = strtoul(cp+1, NULL, 0);
217831dba47SStephen Hemminger
218831dba47SStephen Hemminger /* put the GPAD value in physical address */
219831dba47SStephen Hemminger res->phys_addr = gpad;
220831dba47SStephen Hemminger }
221831dba47SStephen Hemminger
222831dba47SStephen Hemminger return vmbus_uio_map_resource(dev);
223831dba47SStephen Hemminger }
224831dba47SStephen Hemminger
225831dba47SStephen Hemminger void
rte_vmbus_unmap_device(struct rte_vmbus_device * dev)226831dba47SStephen Hemminger rte_vmbus_unmap_device(struct rte_vmbus_device *dev)
227831dba47SStephen Hemminger {
228831dba47SStephen Hemminger vmbus_uio_unmap_resource(dev);
229831dba47SStephen Hemminger }
230831dba47SStephen Hemminger
231831dba47SStephen Hemminger /* Scan one vmbus sysfs entry, and fill the devices list from it. */
232831dba47SStephen Hemminger static int
vmbus_scan_one(const char * name)233831dba47SStephen Hemminger vmbus_scan_one(const char *name)
234831dba47SStephen Hemminger {
235831dba47SStephen Hemminger struct rte_vmbus_device *dev, *dev2;
236831dba47SStephen Hemminger char filename[PATH_MAX];
237831dba47SStephen Hemminger char dirname[PATH_MAX];
238831dba47SStephen Hemminger unsigned long tmp;
239c6c865d7SDavid Marchand char *dev_name;
240831dba47SStephen Hemminger
241831dba47SStephen Hemminger dev = calloc(1, sizeof(*dev));
242831dba47SStephen Hemminger if (dev == NULL)
243831dba47SStephen Hemminger return -1;
244831dba47SStephen Hemminger
2456844d146SThomas Monjalon dev->device.bus = &rte_vmbus_bus.bus;
246c6c865d7SDavid Marchand dev->device.name = dev_name = strdup(name);
247831dba47SStephen Hemminger if (!dev->device.name)
248831dba47SStephen Hemminger goto error;
249831dba47SStephen Hemminger
250831dba47SStephen Hemminger /* sysfs base directory
251831dba47SStephen Hemminger * /sys/bus/vmbus/devices/7a08391f-f5a0-4ac0-9802-d13fd964f8df
252831dba47SStephen Hemminger * or on older kernel
253831dba47SStephen Hemminger * /sys/bus/vmbus/devices/vmbus_1
254831dba47SStephen Hemminger */
255831dba47SStephen Hemminger snprintf(dirname, sizeof(dirname), "%s/%s",
256831dba47SStephen Hemminger SYSFS_VMBUS_DEVICES, name);
257831dba47SStephen Hemminger
258831dba47SStephen Hemminger /* get device class */
259831dba47SStephen Hemminger snprintf(filename, sizeof(filename), "%s/class_id", dirname);
260831dba47SStephen Hemminger if (parse_sysfs_uuid(filename, dev->class_id) < 0)
261831dba47SStephen Hemminger goto error;
262831dba47SStephen Hemminger
263b11b76c2SStephen Hemminger /* skip non-network devices */
264b11b76c2SStephen Hemminger if (rte_uuid_compare(dev->class_id, vmbus_nic_uuid) != 0) {
265c6c865d7SDavid Marchand free(dev_name);
266b11b76c2SStephen Hemminger free(dev);
267b11b76c2SStephen Hemminger return 0;
268b11b76c2SStephen Hemminger }
269b11b76c2SStephen Hemminger
270b11b76c2SStephen Hemminger /* get device id */
271b11b76c2SStephen Hemminger snprintf(filename, sizeof(filename), "%s/device_id", dirname);
272b11b76c2SStephen Hemminger if (parse_sysfs_uuid(filename, dev->device_id) < 0)
273b11b76c2SStephen Hemminger goto error;
274b11b76c2SStephen Hemminger
275831dba47SStephen Hemminger /* get relid */
276831dba47SStephen Hemminger snprintf(filename, sizeof(filename), "%s/id", dirname);
277831dba47SStephen Hemminger if (eal_parse_sysfs_value(filename, &tmp) < 0)
278831dba47SStephen Hemminger goto error;
279831dba47SStephen Hemminger dev->relid = tmp;
280831dba47SStephen Hemminger
281831dba47SStephen Hemminger /* get monitor id */
282831dba47SStephen Hemminger snprintf(filename, sizeof(filename), "%s/monitor_id", dirname);
283831dba47SStephen Hemminger if (eal_parse_sysfs_value(filename, &tmp) < 0)
284831dba47SStephen Hemminger goto error;
285831dba47SStephen Hemminger dev->monitor_id = tmp;
286831dba47SStephen Hemminger
287831dba47SStephen Hemminger /* get numa node (if present) */
288831dba47SStephen Hemminger snprintf(filename, sizeof(filename), "%s/numa_node",
289831dba47SStephen Hemminger dirname);
290831dba47SStephen Hemminger
291831dba47SStephen Hemminger if (access(filename, R_OK) == 0) {
292831dba47SStephen Hemminger if (eal_parse_sysfs_value(filename, &tmp) < 0)
293831dba47SStephen Hemminger goto error;
294831dba47SStephen Hemminger dev->device.numa_node = tmp;
295831dba47SStephen Hemminger } else {
296831dba47SStephen Hemminger dev->device.numa_node = SOCKET_ID_ANY;
297831dba47SStephen Hemminger }
298831dba47SStephen Hemminger
299cf2b9075SStephen Hemminger dev->device.devargs = vmbus_devargs_lookup(dev);
300cf2b9075SStephen Hemminger
301*d61138d4SHarman Kalra /* Allocate interrupt handle instance */
302*d61138d4SHarman Kalra dev->intr_handle =
303*d61138d4SHarman Kalra rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
304*d61138d4SHarman Kalra if (dev->intr_handle == NULL)
305*d61138d4SHarman Kalra goto error;
306*d61138d4SHarman Kalra
307831dba47SStephen Hemminger /* device is valid, add in list (sorted) */
308831dba47SStephen Hemminger VMBUS_LOG(DEBUG, "Adding vmbus device %s", name);
309831dba47SStephen Hemminger
310831dba47SStephen Hemminger TAILQ_FOREACH(dev2, &rte_vmbus_bus.device_list, next) {
311831dba47SStephen Hemminger int ret;
312831dba47SStephen Hemminger
313831dba47SStephen Hemminger ret = rte_uuid_compare(dev->device_id, dev2->device_id);
314831dba47SStephen Hemminger if (ret > 0)
315831dba47SStephen Hemminger continue;
316831dba47SStephen Hemminger
317831dba47SStephen Hemminger if (ret < 0) {
318831dba47SStephen Hemminger vmbus_insert_device(dev2, dev);
319831dba47SStephen Hemminger } else { /* already registered */
320831dba47SStephen Hemminger VMBUS_LOG(NOTICE,
321831dba47SStephen Hemminger "%s already registered", name);
322c6c865d7SDavid Marchand free(dev_name);
323831dba47SStephen Hemminger free(dev);
324831dba47SStephen Hemminger }
325831dba47SStephen Hemminger return 0;
326831dba47SStephen Hemminger }
327831dba47SStephen Hemminger
328831dba47SStephen Hemminger vmbus_add_device(dev);
329831dba47SStephen Hemminger return 0;
330831dba47SStephen Hemminger error:
331831dba47SStephen Hemminger VMBUS_LOG(DEBUG, "failed");
332831dba47SStephen Hemminger
333c6c865d7SDavid Marchand free(dev_name);
334831dba47SStephen Hemminger free(dev);
335831dba47SStephen Hemminger return -1;
336831dba47SStephen Hemminger }
337831dba47SStephen Hemminger
338831dba47SStephen Hemminger /*
339831dba47SStephen Hemminger * Scan the content of the vmbus, and the devices in the devices list
340831dba47SStephen Hemminger */
341831dba47SStephen Hemminger int
rte_vmbus_scan(void)342831dba47SStephen Hemminger rte_vmbus_scan(void)
343831dba47SStephen Hemminger {
344831dba47SStephen Hemminger struct dirent *e;
345831dba47SStephen Hemminger DIR *dir;
346831dba47SStephen Hemminger
347831dba47SStephen Hemminger dir = opendir(SYSFS_VMBUS_DEVICES);
348831dba47SStephen Hemminger if (dir == NULL) {
349831dba47SStephen Hemminger if (errno == ENOENT)
350831dba47SStephen Hemminger return 0;
351831dba47SStephen Hemminger
352831dba47SStephen Hemminger VMBUS_LOG(ERR, "opendir %s failed: %s",
353831dba47SStephen Hemminger SYSFS_VMBUS_DEVICES, strerror(errno));
354831dba47SStephen Hemminger return -1;
355831dba47SStephen Hemminger }
356831dba47SStephen Hemminger
357831dba47SStephen Hemminger while ((e = readdir(dir)) != NULL) {
358831dba47SStephen Hemminger if (e->d_name[0] == '.')
359831dba47SStephen Hemminger continue;
360831dba47SStephen Hemminger
361831dba47SStephen Hemminger if (vmbus_scan_one(e->d_name) < 0)
362831dba47SStephen Hemminger goto error;
363831dba47SStephen Hemminger }
364831dba47SStephen Hemminger closedir(dir);
365831dba47SStephen Hemminger return 0;
366831dba47SStephen Hemminger
367831dba47SStephen Hemminger error:
368831dba47SStephen Hemminger closedir(dir);
369831dba47SStephen Hemminger return -1;
370831dba47SStephen Hemminger }
371831dba47SStephen Hemminger
rte_vmbus_irq_mask(struct rte_vmbus_device * device)372831dba47SStephen Hemminger void rte_vmbus_irq_mask(struct rte_vmbus_device *device)
373831dba47SStephen Hemminger {
374831dba47SStephen Hemminger vmbus_uio_irq_control(device, 1);
375831dba47SStephen Hemminger }
376831dba47SStephen Hemminger
rte_vmbus_irq_unmask(struct rte_vmbus_device * device)377831dba47SStephen Hemminger void rte_vmbus_irq_unmask(struct rte_vmbus_device *device)
378831dba47SStephen Hemminger {
379831dba47SStephen Hemminger vmbus_uio_irq_control(device, 0);
380831dba47SStephen Hemminger }
381831dba47SStephen Hemminger
rte_vmbus_irq_read(struct rte_vmbus_device * device)382831dba47SStephen Hemminger int rte_vmbus_irq_read(struct rte_vmbus_device *device)
383831dba47SStephen Hemminger {
384831dba47SStephen Hemminger return vmbus_uio_irq_read(device);
385831dba47SStephen Hemminger }
386