1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright 2020 Mellanox Technologies, Ltd 3 */ 4 5 #include <unistd.h> 6 #include <string.h> 7 #include <stdio.h> 8 #ifdef RTE_IBVERBS_LINK_DLOPEN 9 #include <dlfcn.h> 10 #endif 11 #include <dirent.h> 12 #include <net/if.h> 13 14 #include <rte_errno.h> 15 #include <rte_string_fns.h> 16 17 #include "mlx5_common.h" 18 #include "mlx5_common_utils.h" 19 #include "mlx5_glue.h" 20 21 #ifdef MLX5_GLUE 22 const struct mlx5_glue *mlx5_glue; 23 #endif 24 25 /** 26 * Get PCI information by sysfs device path. 27 * 28 * @param dev_path 29 * Pointer to device sysfs folder name. 30 * @param[out] pci_addr 31 * PCI bus address output buffer. 32 * 33 * @return 34 * 0 on success, a negative errno value otherwise and rte_errno is set. 35 */ 36 int 37 mlx5_dev_to_pci_addr(const char *dev_path, 38 struct rte_pci_addr *pci_addr) 39 { 40 FILE *file; 41 char line[32]; 42 int rc = -ENOENT; 43 MKSTR(path, "%s/device/uevent", dev_path); 44 45 file = fopen(path, "rb"); 46 if (file == NULL) { 47 rte_errno = errno; 48 return -rte_errno; 49 } 50 while (fgets(line, sizeof(line), file) == line) { 51 size_t len = strlen(line); 52 53 /* Truncate long lines. */ 54 if (len == (sizeof(line) - 1)) { 55 while (line[(len - 1)] != '\n') { 56 int ret = fgetc(file); 57 58 if (ret == EOF) 59 goto exit; 60 line[(len - 1)] = ret; 61 } 62 /* No match for long lines. */ 63 continue; 64 } 65 /* Extract information. */ 66 if (sscanf(line, 67 "PCI_SLOT_NAME=" 68 "%" SCNx32 ":%" SCNx8 ":%" SCNx8 ".%" SCNx8 "\n", 69 &pci_addr->domain, 70 &pci_addr->bus, 71 &pci_addr->devid, 72 &pci_addr->function) == 4) { 73 rc = 0; 74 break; 75 } 76 } 77 exit: 78 fclose(file); 79 if (rc) 80 rte_errno = -rc; 81 return rc; 82 } 83 84 /** 85 * Extract port name, as a number, from sysfs or netlink information. 86 * 87 * @param[in] port_name_in 88 * String representing the port name. 89 * @param[out] port_info_out 90 * Port information, including port name as a number and port name 91 * type if recognized 92 * 93 * @return 94 * port_name field set according to recognized name format. 95 */ 96 void 97 mlx5_translate_port_name(const char *port_name_in, 98 struct mlx5_switch_info *port_info_out) 99 { 100 char pf_c1, pf_c2, vf_c1, vf_c2, eol; 101 char *end; 102 int sc_items; 103 104 /* 105 * Check for port-name as a string of the form pf0vf0 106 * (support kernel ver >= 5.0 or OFED ver >= 4.6). 107 */ 108 sc_items = sscanf(port_name_in, "%c%c%d%c%c%d%c", 109 &pf_c1, &pf_c2, &port_info_out->pf_num, 110 &vf_c1, &vf_c2, &port_info_out->port_name, &eol); 111 if (sc_items == 6 && 112 pf_c1 == 'p' && pf_c2 == 'f' && 113 vf_c1 == 'v' && vf_c2 == 'f') { 114 port_info_out->name_type = MLX5_PHYS_PORT_NAME_TYPE_PFVF; 115 return; 116 } 117 /* 118 * Check for port-name as a string of the form p0 119 * (support kernel ver >= 5.0, or OFED ver >= 4.6). 120 */ 121 sc_items = sscanf(port_name_in, "%c%d%c", 122 &pf_c1, &port_info_out->port_name, &eol); 123 if (sc_items == 2 && pf_c1 == 'p') { 124 port_info_out->name_type = MLX5_PHYS_PORT_NAME_TYPE_UPLINK; 125 return; 126 } 127 /* 128 * Check for port-name as a string of the form pf0 129 * (support kernel ver >= 5.7 for HPF representor on BF). 130 */ 131 sc_items = sscanf(port_name_in, "%c%c%d%c", 132 &pf_c1, &pf_c2, &port_info_out->pf_num, &eol); 133 if (sc_items == 3 && pf_c1 == 'p' && pf_c2 == 'f') { 134 port_info_out->port_name = -1; 135 port_info_out->name_type = MLX5_PHYS_PORT_NAME_TYPE_PFHPF; 136 return; 137 } 138 /* Check for port-name as a number (support kernel ver < 5.0 */ 139 errno = 0; 140 port_info_out->port_name = strtol(port_name_in, &end, 0); 141 if (!errno && 142 (size_t)(end - port_name_in) == strlen(port_name_in)) { 143 port_info_out->name_type = MLX5_PHYS_PORT_NAME_TYPE_LEGACY; 144 return; 145 } 146 port_info_out->name_type = MLX5_PHYS_PORT_NAME_TYPE_UNKNOWN; 147 } 148 149 /** 150 * Get kernel interface name from IB device path. 151 * 152 * @param[in] ibdev_path 153 * Pointer to IB device path. 154 * @param[out] ifname 155 * Interface name output buffer. 156 * 157 * @return 158 * 0 on success, a negative errno value otherwise and rte_errno is set. 159 */ 160 int 161 mlx5_get_ifname_sysfs(const char *ibdev_path, char *ifname) 162 { 163 DIR *dir; 164 struct dirent *dent; 165 unsigned int dev_type = 0; 166 unsigned int dev_port_prev = ~0u; 167 char match[IF_NAMESIZE] = ""; 168 169 MLX5_ASSERT(ibdev_path); 170 { 171 MKSTR(path, "%s/device/net", ibdev_path); 172 173 dir = opendir(path); 174 if (dir == NULL) { 175 rte_errno = errno; 176 return -rte_errno; 177 } 178 } 179 while ((dent = readdir(dir)) != NULL) { 180 char *name = dent->d_name; 181 FILE *file; 182 unsigned int dev_port; 183 int r; 184 185 if ((name[0] == '.') && 186 ((name[1] == '\0') || 187 ((name[1] == '.') && (name[2] == '\0')))) 188 continue; 189 190 MKSTR(path, "%s/device/net/%s/%s", 191 ibdev_path, name, 192 (dev_type ? "dev_id" : "dev_port")); 193 194 file = fopen(path, "rb"); 195 if (file == NULL) { 196 if (errno != ENOENT) 197 continue; 198 /* 199 * Switch to dev_id when dev_port does not exist as 200 * is the case with Linux kernel versions < 3.15. 201 */ 202 try_dev_id: 203 match[0] = '\0'; 204 if (dev_type) 205 break; 206 dev_type = 1; 207 dev_port_prev = ~0u; 208 rewinddir(dir); 209 continue; 210 } 211 r = fscanf(file, (dev_type ? "%x" : "%u"), &dev_port); 212 fclose(file); 213 if (r != 1) 214 continue; 215 /* 216 * Switch to dev_id when dev_port returns the same value for 217 * all ports. May happen when using a MOFED release older than 218 * 3.0 with a Linux kernel >= 3.15. 219 */ 220 if (dev_port == dev_port_prev) 221 goto try_dev_id; 222 dev_port_prev = dev_port; 223 if (dev_port == 0) 224 strlcpy(match, name, IF_NAMESIZE); 225 } 226 closedir(dir); 227 if (match[0] == '\0') { 228 rte_errno = ENOENT; 229 return -rte_errno; 230 } 231 strncpy(ifname, match, IF_NAMESIZE); 232 return 0; 233 } 234 235 #ifdef MLX5_GLUE 236 237 /** 238 * Suffix RTE_EAL_PMD_PATH with "-glue". 239 * 240 * This function performs a sanity check on RTE_EAL_PMD_PATH before 241 * suffixing its last component. 242 * 243 * @param buf[out] 244 * Output buffer, should be large enough otherwise NULL is returned. 245 * @param size 246 * Size of @p out. 247 * 248 * @return 249 * Pointer to @p buf or @p NULL in case suffix cannot be appended. 250 */ 251 static char * 252 mlx5_glue_path(char *buf, size_t size) 253 { 254 static const char *const bad[] = { "/", ".", "..", NULL }; 255 const char *path = RTE_EAL_PMD_PATH; 256 size_t len = strlen(path); 257 size_t off; 258 int i; 259 260 while (len && path[len - 1] == '/') 261 --len; 262 for (off = len; off && path[off - 1] != '/'; --off) 263 ; 264 for (i = 0; bad[i]; ++i) 265 if (!strncmp(path + off, bad[i], (int)(len - off))) 266 goto error; 267 i = snprintf(buf, size, "%.*s-glue", (int)len, path); 268 if (i == -1 || (size_t)i >= size) 269 goto error; 270 return buf; 271 error: 272 RTE_LOG(ERR, PMD, "unable to append \"-glue\" to last component of" 273 " RTE_EAL_PMD_PATH (\"" RTE_EAL_PMD_PATH "\"), please" 274 " re-configure DPDK"); 275 return NULL; 276 } 277 278 static int 279 mlx5_glue_dlopen(void) 280 { 281 char glue_path[sizeof(RTE_EAL_PMD_PATH) - 1 + sizeof("-glue")]; 282 void *handle = NULL; 283 284 char const *path[] = { 285 /* 286 * A basic security check is necessary before trusting 287 * MLX5_GLUE_PATH, which may override RTE_EAL_PMD_PATH. 288 */ 289 (geteuid() == getuid() && getegid() == getgid() ? 290 getenv("MLX5_GLUE_PATH") : NULL), 291 /* 292 * When RTE_EAL_PMD_PATH is set, use its glue-suffixed 293 * variant, otherwise let dlopen() look up libraries on its 294 * own. 295 */ 296 (*RTE_EAL_PMD_PATH ? 297 mlx5_glue_path(glue_path, sizeof(glue_path)) : ""), 298 }; 299 unsigned int i = 0; 300 void **sym; 301 const char *dlmsg; 302 303 while (!handle && i != RTE_DIM(path)) { 304 const char *end; 305 size_t len; 306 int ret; 307 308 if (!path[i]) { 309 ++i; 310 continue; 311 } 312 end = strpbrk(path[i], ":;"); 313 if (!end) 314 end = path[i] + strlen(path[i]); 315 len = end - path[i]; 316 ret = 0; 317 do { 318 char name[ret + 1]; 319 320 ret = snprintf(name, sizeof(name), "%.*s%s" MLX5_GLUE, 321 (int)len, path[i], 322 (!len || *(end - 1) == '/') ? "" : "/"); 323 if (ret == -1) 324 break; 325 if (sizeof(name) != (size_t)ret + 1) 326 continue; 327 DRV_LOG(DEBUG, "Looking for rdma-core glue as " 328 "\"%s\"", name); 329 handle = dlopen(name, RTLD_LAZY); 330 break; 331 } while (1); 332 path[i] = end + 1; 333 if (!*end) 334 ++i; 335 } 336 if (!handle) { 337 rte_errno = EINVAL; 338 dlmsg = dlerror(); 339 if (dlmsg) 340 DRV_LOG(WARNING, "Cannot load glue library: %s", dlmsg); 341 goto glue_error; 342 } 343 sym = dlsym(handle, "mlx5_glue"); 344 if (!sym || !*sym) { 345 rte_errno = EINVAL; 346 dlmsg = dlerror(); 347 if (dlmsg) 348 DRV_LOG(ERR, "Cannot resolve glue symbol: %s", dlmsg); 349 goto glue_error; 350 } 351 mlx5_glue = *sym; 352 return 0; 353 354 glue_error: 355 if (handle) 356 dlclose(handle); 357 return -1; 358 } 359 360 #endif 361 362 /** 363 * Initialization routine for run-time dependency on rdma-core. 364 */ 365 void 366 mlx5_glue_constructor(void) 367 { 368 /* 369 * RDMAV_HUGEPAGES_SAFE tells ibv_fork_init() we intend to use 370 * huge pages. Calling ibv_fork_init() during init allows 371 * applications to use fork() safely for purposes other than 372 * using this PMD, which is not supported in forked processes. 373 */ 374 setenv("RDMAV_HUGEPAGES_SAFE", "1", 1); 375 /* Match the size of Rx completion entry to the size of a cacheline. */ 376 if (RTE_CACHE_LINE_SIZE == 128) 377 setenv("MLX5_CQE_SIZE", "128", 0); 378 /* 379 * MLX5_DEVICE_FATAL_CLEANUP tells ibv_destroy functions to 380 * cleanup all the Verbs resources even when the device was removed. 381 */ 382 setenv("MLX5_DEVICE_FATAL_CLEANUP", "1", 1); 383 384 #ifdef MLX5_GLUE 385 if (mlx5_glue_dlopen() != 0) 386 goto glue_error; 387 #endif 388 389 #ifdef RTE_LIBRTE_MLX5_DEBUG 390 /* Glue structure must not contain any NULL pointers. */ 391 { 392 unsigned int i; 393 394 for (i = 0; i != sizeof(*mlx5_glue) / sizeof(void *); ++i) 395 MLX5_ASSERT(((const void *const *)mlx5_glue)[i]); 396 } 397 #endif 398 if (strcmp(mlx5_glue->version, MLX5_GLUE_VERSION)) { 399 rte_errno = EINVAL; 400 DRV_LOG(ERR, "rdma-core glue \"%s\" mismatch: \"%s\" is " 401 "required", mlx5_glue->version, MLX5_GLUE_VERSION); 402 goto glue_error; 403 } 404 mlx5_glue->fork_init(); 405 return; 406 407 glue_error: 408 DRV_LOG(WARNING, "Cannot initialize MLX5 common due to missing" 409 " run-time dependency on rdma-core libraries (libibverbs," 410 " libmlx5)"); 411 mlx5_glue = NULL; 412 } 413 414