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