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 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 f = fopen(filename, "r"); 51 if (f == NULL) { 52 VMBUS_LOG(ERR, "cannot open sysfs value %s: %s", 53 filename, strerror(errno)); 54 return -1; 55 } 56 57 if (fgets(buf, sizeof(buf), f) == NULL) { 58 VMBUS_LOG(ERR, "cannot read sysfs value %s", 59 filename); 60 fclose(f); 61 return -1; 62 } 63 fclose(f); 64 65 cp = strchr(buf, '\n'); 66 if (cp) 67 *cp = '\0'; 68 69 /* strip { } notation */ 70 if (buf[0] == '{') { 71 in = buf + 1; 72 cp = strchr(in, '}'); 73 if (cp) 74 *cp = '\0'; 75 } 76 77 if (rte_uuid_parse(in, uu) < 0) { 78 VMBUS_LOG(ERR, "%s %s not a valid UUID", 79 filename, buf); 80 return -1; 81 } 82 83 return 0; 84 } 85 86 static int 87 get_sysfs_string(const char *filename, char *buf, size_t buflen) 88 { 89 char *cp; 90 FILE *f; 91 92 f = fopen(filename, "r"); 93 if (f == NULL) { 94 VMBUS_LOG(ERR, "cannot open sysfs value %s:%s", 95 filename, strerror(errno)); 96 return -1; 97 } 98 99 if (fgets(buf, buflen, f) == NULL) { 100 VMBUS_LOG(ERR, "cannot read sysfs value %s", 101 filename); 102 fclose(f); 103 return -1; 104 } 105 fclose(f); 106 107 /* remove trailing newline */ 108 cp = memchr(buf, '\n', buflen); 109 if (cp) 110 *cp = '\0'; 111 112 return 0; 113 } 114 115 static int 116 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 SYSFS_VMBUS_DEVICES "/%s/uio", dev->device.name); 127 128 dir = opendir(dirname); 129 if (dir == NULL) 130 return -1; /* Not a UIO device */ 131 132 /* take the first file starting with "uio" */ 133 while ((e = readdir(dir)) != NULL) { 134 const int prefix_len = 3; 135 char *endptr; 136 137 if (strncmp(e->d_name, "uio", prefix_len) != 0) 138 continue; 139 140 /* try uio%d */ 141 errno = 0; 142 uio_num = strtoull(e->d_name + prefix_len, &endptr, 10); 143 if (errno == 0 && endptr != (e->d_name + prefix_len)) { 144 snprintf(dstbuf, buflen, "%s/uio%u", dirname, uio_num); 145 break; 146 } 147 } 148 closedir(dir); 149 150 if (e == NULL) 151 return -1; 152 153 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 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 dev->uio_num = vmbus_get_uio_dev(dev, uioname, sizeof(uioname)); 175 if (dev->uio_num < 0) { 176 VMBUS_LOG(DEBUG, "Not managed by UIO driver, skipped"); 177 return 1; 178 } 179 180 /* Extract resource value */ 181 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 if (get_sysfs_string(filename, mapname, sizeof(mapname)) < 0) { 193 VMBUS_LOG(ERR, "could not read %s", filename); 194 return -1; 195 } 196 197 if (strncmp(map_names[i], mapname, strlen(map_names[i])) != 0) { 198 VMBUS_LOG(ERR, 199 "unexpected resource %s (expected %s)", 200 mapname, map_names[i]); 201 return -1; 202 } 203 204 snprintf(filename, sizeof(filename), 205 "%s/size", dirname); 206 if (eal_parse_sysfs_value(filename, &len) < 0) { 207 VMBUS_LOG(ERR, 208 "could not read %s", filename); 209 return -1; 210 } 211 res->len = len; 212 213 /* both send and receive buffers have gpad in name */ 214 cp = memchr(mapname, ':', sizeof(mapname)); 215 if (cp) 216 gpad = strtoul(cp+1, NULL, 0); 217 218 /* put the GPAD value in physical address */ 219 res->phys_addr = gpad; 220 } 221 222 return vmbus_uio_map_resource(dev); 223 } 224 225 void 226 rte_vmbus_unmap_device(struct rte_vmbus_device *dev) 227 { 228 vmbus_uio_unmap_resource(dev); 229 } 230 231 /* Scan one vmbus sysfs entry, and fill the devices list from it. */ 232 static int 233 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 240 dev = calloc(1, sizeof(*dev)); 241 if (dev == NULL) 242 return -1; 243 244 dev->device.bus = &rte_vmbus_bus.bus; 245 dev->device.name = strdup(name); 246 if (!dev->device.name) 247 goto error; 248 249 /* sysfs base directory 250 * /sys/bus/vmbus/devices/7a08391f-f5a0-4ac0-9802-d13fd964f8df 251 * or on older kernel 252 * /sys/bus/vmbus/devices/vmbus_1 253 */ 254 snprintf(dirname, sizeof(dirname), "%s/%s", 255 SYSFS_VMBUS_DEVICES, name); 256 257 /* get device class */ 258 snprintf(filename, sizeof(filename), "%s/class_id", dirname); 259 if (parse_sysfs_uuid(filename, dev->class_id) < 0) 260 goto error; 261 262 /* skip non-network devices */ 263 if (rte_uuid_compare(dev->class_id, vmbus_nic_uuid) != 0) { 264 free(dev); 265 return 0; 266 } 267 268 /* get device id */ 269 snprintf(filename, sizeof(filename), "%s/device_id", dirname); 270 if (parse_sysfs_uuid(filename, dev->device_id) < 0) 271 goto error; 272 273 /* get relid */ 274 snprintf(filename, sizeof(filename), "%s/id", dirname); 275 if (eal_parse_sysfs_value(filename, &tmp) < 0) 276 goto error; 277 dev->relid = tmp; 278 279 /* get monitor id */ 280 snprintf(filename, sizeof(filename), "%s/monitor_id", dirname); 281 if (eal_parse_sysfs_value(filename, &tmp) < 0) 282 goto error; 283 dev->monitor_id = tmp; 284 285 /* get numa node (if present) */ 286 snprintf(filename, sizeof(filename), "%s/numa_node", 287 dirname); 288 289 if (access(filename, R_OK) == 0) { 290 if (eal_parse_sysfs_value(filename, &tmp) < 0) 291 goto error; 292 dev->device.numa_node = tmp; 293 } else { 294 /* if no NUMA support, set default to 0 */ 295 dev->device.numa_node = SOCKET_ID_ANY; 296 } 297 298 dev->device.devargs = vmbus_devargs_lookup(dev); 299 300 /* device is valid, add in list (sorted) */ 301 VMBUS_LOG(DEBUG, "Adding vmbus device %s", name); 302 303 TAILQ_FOREACH(dev2, &rte_vmbus_bus.device_list, next) { 304 int ret; 305 306 ret = rte_uuid_compare(dev->device_id, dev2->device_id); 307 if (ret > 0) 308 continue; 309 310 if (ret < 0) { 311 vmbus_insert_device(dev2, dev); 312 } else { /* already registered */ 313 VMBUS_LOG(NOTICE, 314 "%s already registered", name); 315 free(dev); 316 } 317 return 0; 318 } 319 320 vmbus_add_device(dev); 321 return 0; 322 error: 323 VMBUS_LOG(DEBUG, "failed"); 324 325 free(dev); 326 return -1; 327 } 328 329 /* 330 * Scan the content of the vmbus, and the devices in the devices list 331 */ 332 int 333 rte_vmbus_scan(void) 334 { 335 struct dirent *e; 336 DIR *dir; 337 338 dir = opendir(SYSFS_VMBUS_DEVICES); 339 if (dir == NULL) { 340 if (errno == ENOENT) 341 return 0; 342 343 VMBUS_LOG(ERR, "opendir %s failed: %s", 344 SYSFS_VMBUS_DEVICES, strerror(errno)); 345 return -1; 346 } 347 348 while ((e = readdir(dir)) != NULL) { 349 if (e->d_name[0] == '.') 350 continue; 351 352 if (vmbus_scan_one(e->d_name) < 0) 353 goto error; 354 } 355 closedir(dir); 356 return 0; 357 358 error: 359 closedir(dir); 360 return -1; 361 } 362 363 void rte_vmbus_irq_mask(struct rte_vmbus_device *device) 364 { 365 vmbus_uio_irq_control(device, 1); 366 } 367 368 void rte_vmbus_irq_unmask(struct rte_vmbus_device *device) 369 { 370 vmbus_uio_irq_control(device, 0); 371 } 372 373 int rte_vmbus_irq_read(struct rte_vmbus_device *device) 374 { 375 return vmbus_uio_irq_read(device); 376 } 377