1 /*- 2 * BSD LICENSE 3 * 4 * Copyright 2015 6WIND S.A. 5 * Copyright 2015 Mellanox. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of 6WIND S.A. nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <stddef.h> 35 #include <unistd.h> 36 #include <stdint.h> 37 #include <stdio.h> 38 #include <string.h> 39 #include <stdlib.h> 40 #include <errno.h> 41 #include <dirent.h> 42 #include <net/if.h> 43 #include <sys/ioctl.h> 44 #include <sys/socket.h> 45 #include <netinet/in.h> 46 #include <linux/if.h> 47 48 /* DPDK headers don't like -pedantic. */ 49 #ifdef PEDANTIC 50 #pragma GCC diagnostic ignored "-pedantic" 51 #endif 52 #include <rte_atomic.h> 53 #include <rte_ethdev.h> 54 #include <rte_mbuf.h> 55 #include <rte_common.h> 56 #ifdef PEDANTIC 57 #pragma GCC diagnostic error "-pedantic" 58 #endif 59 60 #include "mlx5.h" 61 #include "mlx5_utils.h" 62 63 /** 64 * Get interface name from private structure. 65 * 66 * @param[in] priv 67 * Pointer to private structure. 68 * @param[out] ifname 69 * Interface name output buffer. 70 * 71 * @return 72 * 0 on success, -1 on failure and errno is set. 73 */ 74 int 75 priv_get_ifname(const struct priv *priv, char (*ifname)[IF_NAMESIZE]) 76 { 77 DIR *dir; 78 struct dirent *dent; 79 unsigned int dev_type = 0; 80 unsigned int dev_port_prev = ~0u; 81 char match[IF_NAMESIZE] = ""; 82 83 { 84 MKSTR(path, "%s/device/net", priv->ctx->device->ibdev_path); 85 86 dir = opendir(path); 87 if (dir == NULL) 88 return -1; 89 } 90 while ((dent = readdir(dir)) != NULL) { 91 char *name = dent->d_name; 92 FILE *file; 93 unsigned int dev_port; 94 int r; 95 96 if ((name[0] == '.') && 97 ((name[1] == '\0') || 98 ((name[1] == '.') && (name[2] == '\0')))) 99 continue; 100 101 MKSTR(path, "%s/device/net/%s/%s", 102 priv->ctx->device->ibdev_path, name, 103 (dev_type ? "dev_id" : "dev_port")); 104 105 file = fopen(path, "rb"); 106 if (file == NULL) { 107 if (errno != ENOENT) 108 continue; 109 /* 110 * Switch to dev_id when dev_port does not exist as 111 * is the case with Linux kernel versions < 3.15. 112 */ 113 try_dev_id: 114 match[0] = '\0'; 115 if (dev_type) 116 break; 117 dev_type = 1; 118 dev_port_prev = ~0u; 119 rewinddir(dir); 120 continue; 121 } 122 r = fscanf(file, (dev_type ? "%x" : "%u"), &dev_port); 123 fclose(file); 124 if (r != 1) 125 continue; 126 /* 127 * Switch to dev_id when dev_port returns the same value for 128 * all ports. May happen when using a MOFED release older than 129 * 3.0 with a Linux kernel >= 3.15. 130 */ 131 if (dev_port == dev_port_prev) 132 goto try_dev_id; 133 dev_port_prev = dev_port; 134 if (dev_port == (priv->port - 1u)) 135 snprintf(match, sizeof(match), "%s", name); 136 } 137 closedir(dir); 138 if (match[0] == '\0') 139 return -1; 140 strncpy(*ifname, match, sizeof(*ifname)); 141 return 0; 142 } 143 144 /** 145 * Read from sysfs entry. 146 * 147 * @param[in] priv 148 * Pointer to private structure. 149 * @param[in] entry 150 * Entry name relative to sysfs path. 151 * @param[out] buf 152 * Data output buffer. 153 * @param size 154 * Buffer size. 155 * 156 * @return 157 * 0 on success, -1 on failure and errno is set. 158 */ 159 static int 160 priv_sysfs_read(const struct priv *priv, const char *entry, 161 char *buf, size_t size) 162 { 163 char ifname[IF_NAMESIZE]; 164 FILE *file; 165 int ret; 166 int err; 167 168 if (priv_get_ifname(priv, &ifname)) 169 return -1; 170 171 MKSTR(path, "%s/device/net/%s/%s", priv->ctx->device->ibdev_path, 172 ifname, entry); 173 174 file = fopen(path, "rb"); 175 if (file == NULL) 176 return -1; 177 ret = fread(buf, 1, size, file); 178 err = errno; 179 if (((size_t)ret < size) && (ferror(file))) 180 ret = -1; 181 else 182 ret = size; 183 fclose(file); 184 errno = err; 185 return ret; 186 } 187 188 /** 189 * Write to sysfs entry. 190 * 191 * @param[in] priv 192 * Pointer to private structure. 193 * @param[in] entry 194 * Entry name relative to sysfs path. 195 * @param[in] buf 196 * Data buffer. 197 * @param size 198 * Buffer size. 199 * 200 * @return 201 * 0 on success, -1 on failure and errno is set. 202 */ 203 static int 204 priv_sysfs_write(const struct priv *priv, const char *entry, 205 char *buf, size_t size) 206 { 207 char ifname[IF_NAMESIZE]; 208 FILE *file; 209 int ret; 210 int err; 211 212 if (priv_get_ifname(priv, &ifname)) 213 return -1; 214 215 MKSTR(path, "%s/device/net/%s/%s", priv->ctx->device->ibdev_path, 216 ifname, entry); 217 218 file = fopen(path, "wb"); 219 if (file == NULL) 220 return -1; 221 ret = fwrite(buf, 1, size, file); 222 err = errno; 223 if (((size_t)ret < size) || (ferror(file))) 224 ret = -1; 225 else 226 ret = size; 227 fclose(file); 228 errno = err; 229 return ret; 230 } 231 232 /** 233 * Get unsigned long sysfs property. 234 * 235 * @param priv 236 * Pointer to private structure. 237 * @param[in] name 238 * Entry name relative to sysfs path. 239 * @param[out] value 240 * Value output buffer. 241 * 242 * @return 243 * 0 on success, -1 on failure and errno is set. 244 */ 245 static int 246 priv_get_sysfs_ulong(struct priv *priv, const char *name, unsigned long *value) 247 { 248 int ret; 249 unsigned long value_ret; 250 char value_str[32]; 251 252 ret = priv_sysfs_read(priv, name, value_str, (sizeof(value_str) - 1)); 253 if (ret == -1) { 254 DEBUG("cannot read %s value from sysfs: %s", 255 name, strerror(errno)); 256 return -1; 257 } 258 value_str[ret] = '\0'; 259 errno = 0; 260 value_ret = strtoul(value_str, NULL, 0); 261 if (errno) { 262 DEBUG("invalid %s value `%s': %s", name, value_str, 263 strerror(errno)); 264 return -1; 265 } 266 *value = value_ret; 267 return 0; 268 } 269 270 /** 271 * Set unsigned long sysfs property. 272 * 273 * @param priv 274 * Pointer to private structure. 275 * @param[in] name 276 * Entry name relative to sysfs path. 277 * @param value 278 * Value to set. 279 * 280 * @return 281 * 0 on success, -1 on failure and errno is set. 282 */ 283 static int 284 priv_set_sysfs_ulong(struct priv *priv, const char *name, unsigned long value) 285 { 286 int ret; 287 MKSTR(value_str, "%lu", value); 288 289 ret = priv_sysfs_write(priv, name, value_str, (sizeof(value_str) - 1)); 290 if (ret == -1) { 291 DEBUG("cannot write %s `%s' (%lu) to sysfs: %s", 292 name, value_str, value, strerror(errno)); 293 return -1; 294 } 295 return 0; 296 } 297 298 /** 299 * Perform ifreq ioctl() on associated Ethernet device. 300 * 301 * @param[in] priv 302 * Pointer to private structure. 303 * @param req 304 * Request number to pass to ioctl(). 305 * @param[out] ifr 306 * Interface request structure output buffer. 307 * 308 * @return 309 * 0 on success, -1 on failure and errno is set. 310 */ 311 int 312 priv_ifreq(const struct priv *priv, int req, struct ifreq *ifr) 313 { 314 int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); 315 int ret = -1; 316 317 if (sock == -1) 318 return ret; 319 if (priv_get_ifname(priv, &ifr->ifr_name) == 0) 320 ret = ioctl(sock, req, ifr); 321 close(sock); 322 return ret; 323 } 324 325 /** 326 * Get device MTU. 327 * 328 * @param priv 329 * Pointer to private structure. 330 * @param[out] mtu 331 * MTU value output buffer. 332 * 333 * @return 334 * 0 on success, -1 on failure and errno is set. 335 */ 336 int 337 priv_get_mtu(struct priv *priv, uint16_t *mtu) 338 { 339 unsigned long ulong_mtu; 340 341 if (priv_get_sysfs_ulong(priv, "mtu", &ulong_mtu) == -1) 342 return -1; 343 *mtu = ulong_mtu; 344 return 0; 345 } 346 347 /** 348 * Set device flags. 349 * 350 * @param priv 351 * Pointer to private structure. 352 * @param keep 353 * Bitmask for flags that must remain untouched. 354 * @param flags 355 * Bitmask for flags to modify. 356 * 357 * @return 358 * 0 on success, -1 on failure and errno is set. 359 */ 360 int 361 priv_set_flags(struct priv *priv, unsigned int keep, unsigned int flags) 362 { 363 unsigned long tmp; 364 365 if (priv_get_sysfs_ulong(priv, "flags", &tmp) == -1) 366 return -1; 367 tmp &= keep; 368 tmp |= flags; 369 return priv_set_sysfs_ulong(priv, "flags", tmp); 370 } 371 372 /** 373 * Get PCI information from struct ibv_device. 374 * 375 * @param device 376 * Pointer to Ethernet device structure. 377 * @param[out] pci_addr 378 * PCI bus address output buffer. 379 * 380 * @return 381 * 0 on success, -1 on failure and errno is set. 382 */ 383 int 384 mlx5_ibv_device_to_pci_addr(const struct ibv_device *device, 385 struct rte_pci_addr *pci_addr) 386 { 387 FILE *file; 388 char line[32]; 389 MKSTR(path, "%s/device/uevent", device->ibdev_path); 390 391 file = fopen(path, "rb"); 392 if (file == NULL) 393 return -1; 394 while (fgets(line, sizeof(line), file) == line) { 395 size_t len = strlen(line); 396 int ret; 397 398 /* Truncate long lines. */ 399 if (len == (sizeof(line) - 1)) 400 while (line[(len - 1)] != '\n') { 401 ret = fgetc(file); 402 if (ret == EOF) 403 break; 404 line[(len - 1)] = ret; 405 } 406 /* Extract information. */ 407 if (sscanf(line, 408 "PCI_SLOT_NAME=" 409 "%" SCNx16 ":%" SCNx8 ":%" SCNx8 ".%" SCNx8 "\n", 410 &pci_addr->domain, 411 &pci_addr->bus, 412 &pci_addr->devid, 413 &pci_addr->function) == 4) { 414 ret = 0; 415 break; 416 } 417 } 418 fclose(file); 419 return 0; 420 } 421