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 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 static int 75 mlx5_class_check_handler(__rte_unused const char *key, const char *value, 76 void *opaque) 77 { 78 enum mlx5_class *ret = opaque; 79 80 if (strcmp(value, "vdpa") == 0) { 81 *ret = MLX5_CLASS_VDPA; 82 } else if (strcmp(value, "net") == 0) { 83 *ret = MLX5_CLASS_NET; 84 } else { 85 DRV_LOG(ERR, "Invalid mlx5 class %s. Maybe typo in device" 86 " class argument setting?", value); 87 *ret = MLX5_CLASS_INVALID; 88 } 89 return 0; 90 } 91 92 enum mlx5_class 93 mlx5_class_get(struct rte_devargs *devargs) 94 { 95 struct rte_kvargs *kvlist; 96 const char *key = MLX5_CLASS_ARG_NAME; 97 enum mlx5_class ret = MLX5_CLASS_NET; 98 99 if (devargs == NULL) 100 return ret; 101 kvlist = rte_kvargs_parse(devargs->args, NULL); 102 if (kvlist == NULL) 103 return ret; 104 if (rte_kvargs_count(kvlist, key)) 105 rte_kvargs_process(kvlist, key, mlx5_class_check_handler, &ret); 106 rte_kvargs_free(kvlist); 107 return ret; 108 } 109 110 /** 111 * Extract port name, as a number, from sysfs or netlink information. 112 * 113 * @param[in] port_name_in 114 * String representing the port name. 115 * @param[out] port_info_out 116 * Port information, including port name as a number and port name 117 * type if recognized 118 * 119 * @return 120 * port_name field set according to recognized name format. 121 */ 122 void 123 mlx5_translate_port_name(const char *port_name_in, 124 struct mlx5_switch_info *port_info_out) 125 { 126 char pf_c1, pf_c2, vf_c1, vf_c2; 127 char *end; 128 int sc_items; 129 130 /* 131 * Check for port-name as a string of the form pf0vf0 132 * (support kernel ver >= 5.0 or OFED ver >= 4.6). 133 */ 134 sc_items = sscanf(port_name_in, "%c%c%d%c%c%d", 135 &pf_c1, &pf_c2, &port_info_out->pf_num, 136 &vf_c1, &vf_c2, &port_info_out->port_name); 137 if (sc_items == 6 && 138 pf_c1 == 'p' && pf_c2 == 'f' && 139 vf_c1 == 'v' && vf_c2 == 'f') { 140 port_info_out->name_type = MLX5_PHYS_PORT_NAME_TYPE_PFVF; 141 return; 142 } 143 /* 144 * Check for port-name as a string of the form p0 145 * (support kernel ver >= 5.0, or OFED ver >= 4.6). 146 */ 147 sc_items = sscanf(port_name_in, "%c%d", 148 &pf_c1, &port_info_out->port_name); 149 if (sc_items == 2 && pf_c1 == 'p') { 150 port_info_out->name_type = MLX5_PHYS_PORT_NAME_TYPE_UPLINK; 151 return; 152 } 153 /* Check for port-name as a number (support kernel ver < 5.0 */ 154 errno = 0; 155 port_info_out->port_name = strtol(port_name_in, &end, 0); 156 if (!errno && 157 (size_t)(end - port_name_in) == strlen(port_name_in)) { 158 port_info_out->name_type = MLX5_PHYS_PORT_NAME_TYPE_LEGACY; 159 return; 160 } 161 port_info_out->name_type = MLX5_PHYS_PORT_NAME_TYPE_UNKNOWN; 162 return; 163 } 164 165 #ifdef RTE_IBVERBS_LINK_DLOPEN 166 167 /** 168 * Suffix RTE_EAL_PMD_PATH with "-glue". 169 * 170 * This function performs a sanity check on RTE_EAL_PMD_PATH before 171 * suffixing its last component. 172 * 173 * @param buf[out] 174 * Output buffer, should be large enough otherwise NULL is returned. 175 * @param size 176 * Size of @p out. 177 * 178 * @return 179 * Pointer to @p buf or @p NULL in case suffix cannot be appended. 180 */ 181 static char * 182 mlx5_glue_path(char *buf, size_t size) 183 { 184 static const char *const bad[] = { "/", ".", "..", NULL }; 185 const char *path = RTE_EAL_PMD_PATH; 186 size_t len = strlen(path); 187 size_t off; 188 int i; 189 190 while (len && path[len - 1] == '/') 191 --len; 192 for (off = len; off && path[off - 1] != '/'; --off) 193 ; 194 for (i = 0; bad[i]; ++i) 195 if (!strncmp(path + off, bad[i], (int)(len - off))) 196 goto error; 197 i = snprintf(buf, size, "%.*s-glue", (int)len, path); 198 if (i == -1 || (size_t)i >= size) 199 goto error; 200 return buf; 201 error: 202 RTE_LOG(ERR, PMD, "unable to append \"-glue\" to last component of" 203 " RTE_EAL_PMD_PATH (\"" RTE_EAL_PMD_PATH "\"), please" 204 " re-configure DPDK"); 205 return NULL; 206 } 207 #endif 208 209 /** 210 * Initialization routine for run-time dependency on rdma-core. 211 */ 212 RTE_INIT_PRIO(mlx5_glue_init, CLASS) 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 void *handle = NULL; 237 238 const char *path[] = { 239 /* 240 * A basic security check is necessary before trusting 241 * MLX5_GLUE_PATH, which may override RTE_EAL_PMD_PATH. 242 */ 243 (geteuid() == getuid() && getegid() == getgid() ? 244 getenv("MLX5_GLUE_PATH") : NULL), 245 /* 246 * When RTE_EAL_PMD_PATH is set, use its glue-suffixed 247 * variant, otherwise let dlopen() look up libraries on its 248 * own. 249 */ 250 (*RTE_EAL_PMD_PATH ? 251 mlx5_glue_path(glue_path, sizeof(glue_path)) : ""), 252 }; 253 unsigned int i = 0; 254 void **sym; 255 const char *dlmsg; 256 257 while (!handle && i != RTE_DIM(path)) { 258 const char *end; 259 size_t len; 260 int ret; 261 262 if (!path[i]) { 263 ++i; 264 continue; 265 } 266 end = strpbrk(path[i], ":;"); 267 if (!end) 268 end = path[i] + strlen(path[i]); 269 len = end - path[i]; 270 ret = 0; 271 do { 272 char name[ret + 1]; 273 274 ret = snprintf(name, sizeof(name), "%.*s%s" MLX5_GLUE, 275 (int)len, path[i], 276 (!len || *(end - 1) == '/') ? "" : "/"); 277 if (ret == -1) 278 break; 279 if (sizeof(name) != (size_t)ret + 1) 280 continue; 281 DRV_LOG(DEBUG, "Looking for rdma-core glue as " 282 "\"%s\"", name); 283 handle = dlopen(name, RTLD_LAZY); 284 break; 285 } while (1); 286 path[i] = end + 1; 287 if (!*end) 288 ++i; 289 } 290 if (!handle) { 291 rte_errno = EINVAL; 292 dlmsg = dlerror(); 293 if (dlmsg) 294 DRV_LOG(WARNING, "Cannot load glue library: %s", dlmsg); 295 goto glue_error; 296 } 297 sym = dlsym(handle, "mlx5_glue"); 298 if (!sym || !*sym) { 299 rte_errno = EINVAL; 300 dlmsg = dlerror(); 301 if (dlmsg) 302 DRV_LOG(ERR, "Cannot resolve glue symbol: %s", dlmsg); 303 goto glue_error; 304 } 305 mlx5_glue = *sym; 306 #endif /* RTE_IBVERBS_LINK_DLOPEN */ 307 #ifdef RTE_LIBRTE_MLX5_DEBUG 308 /* Glue structure must not contain any NULL pointers. */ 309 { 310 unsigned int i; 311 312 for (i = 0; i != sizeof(*mlx5_glue) / sizeof(void *); ++i) 313 MLX5_ASSERT(((const void *const *)mlx5_glue)[i]); 314 } 315 #endif 316 if (strcmp(mlx5_glue->version, MLX5_GLUE_VERSION)) { 317 rte_errno = EINVAL; 318 DRV_LOG(ERR, "rdma-core glue \"%s\" mismatch: \"%s\" is " 319 "required", mlx5_glue->version, MLX5_GLUE_VERSION); 320 goto glue_error; 321 } 322 mlx5_glue->fork_init(); 323 return; 324 glue_error: 325 #ifdef RTE_IBVERBS_LINK_DLOPEN 326 if (handle) 327 dlclose(handle); 328 #endif 329 DRV_LOG(WARNING, "Cannot initialize MLX5 common due to missing" 330 " run-time dependency on rdma-core libraries (libibverbs," 331 " libmlx5)"); 332 mlx5_glue = NULL; 333 return; 334 } 335