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