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 12 #include <rte_errno.h> 13 14 #include "mlx5_common.h" 15 #include "mlx5_common_utils.h" 16 #include "mlx5_glue.h" 17 18 #ifdef MLX5_GLUE 19 const struct mlx5_glue *mlx5_glue; 20 #endif 21 22 /** 23 * Get PCI information by sysfs device path. 24 * 25 * @param dev_path 26 * Pointer to device sysfs folder name. 27 * @param[out] pci_addr 28 * PCI bus address output buffer. 29 * 30 * @return 31 * 0 on success, a negative errno value otherwise and rte_errno is set. 32 */ 33 int 34 mlx5_dev_to_pci_addr(const char *dev_path, 35 struct rte_pci_addr *pci_addr) 36 { 37 FILE *file; 38 char line[32]; 39 MKSTR(path, "%s/device/uevent", dev_path); 40 41 file = fopen(path, "rb"); 42 if (file == NULL) { 43 rte_errno = errno; 44 return -rte_errno; 45 } 46 while (fgets(line, sizeof(line), file) == line) { 47 size_t len = strlen(line); 48 int ret; 49 50 /* Truncate long lines. */ 51 if (len == (sizeof(line) - 1)) 52 while (line[(len - 1)] != '\n') { 53 ret = fgetc(file); 54 if (ret == EOF) 55 break; 56 line[(len - 1)] = ret; 57 } 58 /* Extract information. */ 59 if (sscanf(line, 60 "PCI_SLOT_NAME=" 61 "%" SCNx32 ":%" SCNx8 ":%" SCNx8 ".%" SCNx8 "\n", 62 &pci_addr->domain, 63 &pci_addr->bus, 64 &pci_addr->devid, 65 &pci_addr->function) == 4) { 66 ret = 0; 67 break; 68 } 69 } 70 fclose(file); 71 return 0; 72 } 73 74 /** 75 * Extract port name, as a number, from sysfs or netlink information. 76 * 77 * @param[in] port_name_in 78 * String representing the port name. 79 * @param[out] port_info_out 80 * Port information, including port name as a number and port name 81 * type if recognized 82 * 83 * @return 84 * port_name field set according to recognized name format. 85 */ 86 void 87 mlx5_translate_port_name(const char *port_name_in, 88 struct mlx5_switch_info *port_info_out) 89 { 90 char pf_c1, pf_c2, vf_c1, vf_c2; 91 char *end; 92 int sc_items; 93 94 /* 95 * Check for port-name as a string of the form pf0vf0 96 * (support kernel ver >= 5.0 or OFED ver >= 4.6). 97 */ 98 sc_items = sscanf(port_name_in, "%c%c%d%c%c%d", 99 &pf_c1, &pf_c2, &port_info_out->pf_num, 100 &vf_c1, &vf_c2, &port_info_out->port_name); 101 if (sc_items == 6 && 102 pf_c1 == 'p' && pf_c2 == 'f' && 103 vf_c1 == 'v' && vf_c2 == 'f') { 104 port_info_out->name_type = MLX5_PHYS_PORT_NAME_TYPE_PFVF; 105 return; 106 } 107 /* 108 * Check for port-name as a string of the form p0 109 * (support kernel ver >= 5.0, or OFED ver >= 4.6). 110 */ 111 sc_items = sscanf(port_name_in, "%c%d", 112 &pf_c1, &port_info_out->port_name); 113 if (sc_items == 2 && pf_c1 == 'p') { 114 port_info_out->name_type = MLX5_PHYS_PORT_NAME_TYPE_UPLINK; 115 return; 116 } 117 /* Check for port-name as a number (support kernel ver < 5.0 */ 118 errno = 0; 119 port_info_out->port_name = strtol(port_name_in, &end, 0); 120 if (!errno && 121 (size_t)(end - port_name_in) == strlen(port_name_in)) { 122 port_info_out->name_type = MLX5_PHYS_PORT_NAME_TYPE_LEGACY; 123 return; 124 } 125 port_info_out->name_type = MLX5_PHYS_PORT_NAME_TYPE_UNKNOWN; 126 } 127 128 #ifdef MLX5_GLUE 129 130 /** 131 * Suffix RTE_EAL_PMD_PATH with "-glue". 132 * 133 * This function performs a sanity check on RTE_EAL_PMD_PATH before 134 * suffixing its last component. 135 * 136 * @param buf[out] 137 * Output buffer, should be large enough otherwise NULL is returned. 138 * @param size 139 * Size of @p out. 140 * 141 * @return 142 * Pointer to @p buf or @p NULL in case suffix cannot be appended. 143 */ 144 static char * 145 mlx5_glue_path(char *buf, size_t size) 146 { 147 static const char *const bad[] = { "/", ".", "..", NULL }; 148 const char *path = RTE_EAL_PMD_PATH; 149 size_t len = strlen(path); 150 size_t off; 151 int i; 152 153 while (len && path[len - 1] == '/') 154 --len; 155 for (off = len; off && path[off - 1] != '/'; --off) 156 ; 157 for (i = 0; bad[i]; ++i) 158 if (!strncmp(path + off, bad[i], (int)(len - off))) 159 goto error; 160 i = snprintf(buf, size, "%.*s-glue", (int)len, path); 161 if (i == -1 || (size_t)i >= size) 162 goto error; 163 return buf; 164 error: 165 RTE_LOG(ERR, PMD, "unable to append \"-glue\" to last component of" 166 " RTE_EAL_PMD_PATH (\"" RTE_EAL_PMD_PATH "\"), please" 167 " re-configure DPDK"); 168 return NULL; 169 } 170 171 static int 172 mlx5_glue_dlopen(void) 173 { 174 char glue_path[sizeof(RTE_EAL_PMD_PATH) - 1 + sizeof("-glue")]; 175 void *handle = NULL; 176 177 char const *path[] = { 178 /* 179 * A basic security check is necessary before trusting 180 * MLX5_GLUE_PATH, which may override RTE_EAL_PMD_PATH. 181 */ 182 (geteuid() == getuid() && getegid() == getgid() ? 183 getenv("MLX5_GLUE_PATH") : NULL), 184 /* 185 * When RTE_EAL_PMD_PATH is set, use its glue-suffixed 186 * variant, otherwise let dlopen() look up libraries on its 187 * own. 188 */ 189 (*RTE_EAL_PMD_PATH ? 190 mlx5_glue_path(glue_path, sizeof(glue_path)) : ""), 191 }; 192 unsigned int i = 0; 193 void **sym; 194 const char *dlmsg; 195 196 while (!handle && i != RTE_DIM(path)) { 197 const char *end; 198 size_t len; 199 int ret; 200 201 if (!path[i]) { 202 ++i; 203 continue; 204 } 205 end = strpbrk(path[i], ":;"); 206 if (!end) 207 end = path[i] + strlen(path[i]); 208 len = end - path[i]; 209 ret = 0; 210 do { 211 char name[ret + 1]; 212 213 ret = snprintf(name, sizeof(name), "%.*s%s" MLX5_GLUE, 214 (int)len, path[i], 215 (!len || *(end - 1) == '/') ? "" : "/"); 216 if (ret == -1) 217 break; 218 if (sizeof(name) != (size_t)ret + 1) 219 continue; 220 DRV_LOG(DEBUG, "Looking for rdma-core glue as " 221 "\"%s\"", name); 222 handle = dlopen(name, RTLD_LAZY); 223 break; 224 } while (1); 225 path[i] = end + 1; 226 if (!*end) 227 ++i; 228 } 229 if (!handle) { 230 rte_errno = EINVAL; 231 dlmsg = dlerror(); 232 if (dlmsg) 233 DRV_LOG(WARNING, "Cannot load glue library: %s", dlmsg); 234 goto glue_error; 235 } 236 sym = dlsym(handle, "mlx5_glue"); 237 if (!sym || !*sym) { 238 rte_errno = EINVAL; 239 dlmsg = dlerror(); 240 if (dlmsg) 241 DRV_LOG(ERR, "Cannot resolve glue symbol: %s", dlmsg); 242 goto glue_error; 243 } 244 mlx5_glue = *sym; 245 return 0; 246 247 glue_error: 248 if (handle) 249 dlclose(handle); 250 return -1; 251 } 252 253 #endif 254 255 /** 256 * Initialization routine for run-time dependency on rdma-core. 257 */ 258 void 259 mlx5_glue_constructor(void) 260 { 261 /* 262 * RDMAV_HUGEPAGES_SAFE tells ibv_fork_init() we intend to use 263 * huge pages. Calling ibv_fork_init() during init allows 264 * applications to use fork() safely for purposes other than 265 * using this PMD, which is not supported in forked processes. 266 */ 267 setenv("RDMAV_HUGEPAGES_SAFE", "1", 1); 268 /* Match the size of Rx completion entry to the size of a cacheline. */ 269 if (RTE_CACHE_LINE_SIZE == 128) 270 setenv("MLX5_CQE_SIZE", "128", 0); 271 /* 272 * MLX5_DEVICE_FATAL_CLEANUP tells ibv_destroy functions to 273 * cleanup all the Verbs resources even when the device was removed. 274 */ 275 setenv("MLX5_DEVICE_FATAL_CLEANUP", "1", 1); 276 277 #ifdef MLX5_GLUE 278 if (mlx5_glue_dlopen() != 0) 279 goto glue_error; 280 #endif 281 282 #ifdef RTE_LIBRTE_MLX5_DEBUG 283 /* Glue structure must not contain any NULL pointers. */ 284 { 285 unsigned int i; 286 287 for (i = 0; i != sizeof(*mlx5_glue) / sizeof(void *); ++i) 288 MLX5_ASSERT(((const void *const *)mlx5_glue)[i]); 289 } 290 #endif 291 if (strcmp(mlx5_glue->version, MLX5_GLUE_VERSION)) { 292 rte_errno = EINVAL; 293 DRV_LOG(ERR, "rdma-core glue \"%s\" mismatch: \"%s\" is " 294 "required", mlx5_glue->version, MLX5_GLUE_VERSION); 295 goto glue_error; 296 } 297 mlx5_glue->fork_init(); 298 return; 299 300 glue_error: 301 DRV_LOG(WARNING, "Cannot initialize MLX5 common due to missing" 302 " run-time dependency on rdma-core libraries (libibverbs," 303 " libmlx5)"); 304 mlx5_glue = NULL; 305 } 306