1 /*- 2 * BSD LICENSE 3 * 4 * Copyright 2017 6WIND S.A. 5 * Copyright 2017 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 <fcntl.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 #include <errno.h> 40 41 #include <rte_debug.h> 42 #include <rte_devargs.h> 43 #include <rte_malloc.h> 44 #include <rte_kvargs.h> 45 46 #include "failsafe_private.h" 47 48 /* Callback used when a new device is found in devargs */ 49 typedef int (parse_cb)(struct rte_eth_dev *dev, const char *params, 50 uint8_t head); 51 52 uint64_t hotplug_poll = FAILSAFE_HOTPLUG_DEFAULT_TIMEOUT_MS; 53 int mac_from_arg = 0; 54 55 const char *pmd_failsafe_init_parameters[] = { 56 PMD_FAILSAFE_HOTPLUG_POLL_KVARG, 57 PMD_FAILSAFE_MAC_KVARG, 58 NULL, 59 }; 60 61 /* 62 * input: text. 63 * output: 0: if text[0] != '(', 64 * 0: if there are no corresponding ')' 65 * n: distance to corresponding ')' otherwise 66 */ 67 static size_t 68 closing_paren(const char *text) 69 { 70 int nb_open = 0; 71 size_t i = 0; 72 73 while (text[i] != '\0') { 74 if (text[i] == '(') 75 nb_open++; 76 if (text[i] == ')') 77 nb_open--; 78 if (nb_open == 0) 79 return i; 80 i++; 81 } 82 return 0; 83 } 84 85 static int 86 fs_parse_device(struct sub_device *sdev, char *args) 87 { 88 struct rte_devargs *d; 89 int ret; 90 91 d = &sdev->devargs; 92 DEBUG("%s", args); 93 ret = rte_eal_devargs_parse(args, d); 94 if (ret) { 95 DEBUG("devargs parsing failed with code %d", ret); 96 return ret; 97 } 98 sdev->bus = d->bus; 99 sdev->state = DEV_PARSED; 100 return 0; 101 } 102 103 static void 104 fs_sanitize_cmdline(char *args) 105 { 106 char *nl; 107 108 nl = strrchr(args, '\n'); 109 if (nl) 110 nl[0] = '\0'; 111 } 112 113 static int 114 fs_execute_cmd(struct sub_device *sdev, char *cmdline) 115 { 116 FILE *fp; 117 /* store possible newline as well */ 118 char output[DEVARGS_MAXLEN + 1]; 119 size_t len; 120 int ret; 121 122 RTE_ASSERT(cmdline != NULL || sdev->cmdline != NULL); 123 if (sdev->cmdline == NULL) { 124 size_t i; 125 126 len = strlen(cmdline) + 1; 127 sdev->cmdline = calloc(1, len); 128 if (sdev->cmdline == NULL) { 129 ERROR("Command line allocation failed"); 130 return -ENOMEM; 131 } 132 snprintf(sdev->cmdline, len, "%s", cmdline); 133 /* Replace all commas in the command line by spaces */ 134 for (i = 0; i < len; i++) 135 if (sdev->cmdline[i] == ',') 136 sdev->cmdline[i] = ' '; 137 } 138 DEBUG("'%s'", sdev->cmdline); 139 fp = popen(sdev->cmdline, "r"); 140 if (fp == NULL) { 141 ret = -errno; 142 ERROR("popen: %s", strerror(errno)); 143 return ret; 144 } 145 /* We only read one line */ 146 if (fgets(output, sizeof(output) - 1, fp) == NULL) { 147 DEBUG("Could not read command output"); 148 ret = -ENODEV; 149 goto ret_pclose; 150 } 151 fs_sanitize_cmdline(output); 152 if (output[0] == '\0') { 153 ret = -ENODEV; 154 goto ret_pclose; 155 } 156 ret = fs_parse_device(sdev, output); 157 if (ret) 158 ERROR("Parsing device '%s' failed", output); 159 ret_pclose: 160 if (pclose(fp) == -1) 161 ERROR("pclose: %s", strerror(errno)); 162 return ret; 163 } 164 165 static int 166 fs_read_fd(struct sub_device *sdev, char *fd_str) 167 { 168 FILE *fp = NULL; 169 int fd = -1; 170 /* store possible newline as well */ 171 char output[DEVARGS_MAXLEN + 1]; 172 int err = -ENODEV; 173 int oflags; 174 int lcount; 175 176 RTE_ASSERT(fd_str != NULL || sdev->fd_str != NULL); 177 if (sdev->fd_str == NULL) { 178 sdev->fd_str = strdup(fd_str); 179 if (sdev->fd_str == NULL) { 180 ERROR("Command line allocation failed"); 181 return -ENOMEM; 182 } 183 } 184 errno = 0; 185 fd = strtol(fd_str, &fd_str, 0); 186 if (errno || *fd_str || fd < 0) { 187 ERROR("Parsing FD number failed"); 188 goto error; 189 } 190 /* Fiddle with copy of file descriptor */ 191 fd = dup(fd); 192 if (fd == -1) 193 goto error; 194 oflags = fcntl(fd, F_GETFL); 195 if (oflags == -1) 196 goto error; 197 if (fcntl(fd, F_SETFL, oflags | O_NONBLOCK) == -1) 198 goto error; 199 fp = fdopen(fd, "r"); 200 if (fp == NULL) 201 goto error; 202 fd = -1; 203 /* Only take the last line into account */ 204 lcount = 0; 205 while (fgets(output, sizeof(output), fp)) 206 ++lcount; 207 if (lcount == 0) 208 goto error; 209 else if (ferror(fp) && errno != EAGAIN) 210 goto error; 211 /* Line must end with a newline character */ 212 fs_sanitize_cmdline(output); 213 if (output[0] == '\0') 214 goto error; 215 err = fs_parse_device(sdev, output); 216 if (err) 217 ERROR("Parsing device '%s' failed", output); 218 error: 219 if (fp) 220 fclose(fp); 221 if (fd != -1) 222 close(fd); 223 return err; 224 } 225 226 static int 227 fs_parse_device_param(struct rte_eth_dev *dev, const char *param, 228 uint8_t head) 229 { 230 struct fs_priv *priv; 231 struct sub_device *sdev; 232 char *args = NULL; 233 size_t a, b; 234 int ret; 235 236 priv = PRIV(dev); 237 a = 0; 238 b = 0; 239 ret = 0; 240 while (param[b] != '(' && 241 param[b] != '\0') 242 b++; 243 a = b; 244 b += closing_paren(¶m[b]); 245 if (a == b) { 246 ERROR("Dangling parenthesis"); 247 return -EINVAL; 248 } 249 a += 1; 250 args = strndup(¶m[a], b - a); 251 if (args == NULL) { 252 ERROR("Not enough memory for parameter parsing"); 253 return -ENOMEM; 254 } 255 sdev = &priv->subs[head]; 256 if (strncmp(param, "dev", 3) == 0) { 257 ret = fs_parse_device(sdev, args); 258 if (ret) 259 goto free_args; 260 } else if (strncmp(param, "exec", 4) == 0) { 261 ret = fs_execute_cmd(sdev, args); 262 if (ret == -ENODEV) { 263 DEBUG("Reading device info from command line failed"); 264 ret = 0; 265 } 266 if (ret) 267 goto free_args; 268 } else if (strncmp(param, "fd(", 3) == 0) { 269 ret = fs_read_fd(sdev, args); 270 if (ret == -ENODEV) { 271 DEBUG("Reading device info from FD failed"); 272 ret = 0; 273 } 274 if (ret) 275 goto free_args; 276 } else { 277 ERROR("Unrecognized device type: %.*s", (int)b, param); 278 return -EINVAL; 279 } 280 free_args: 281 free(args); 282 return ret; 283 } 284 285 static int 286 fs_parse_sub_devices(parse_cb *cb, 287 struct rte_eth_dev *dev, const char *params) 288 { 289 size_t a, b; 290 uint8_t head; 291 int ret; 292 293 a = 0; 294 head = 0; 295 ret = 0; 296 while (params[a] != '\0') { 297 b = a; 298 while (params[b] != '(' && 299 params[b] != ',' && 300 params[b] != '\0') 301 b++; 302 if (b == a) { 303 ERROR("Invalid parameter"); 304 return -EINVAL; 305 } 306 if (params[b] == ',') { 307 a = b + 1; 308 continue; 309 } 310 if (params[b] == '(') { 311 size_t start = b; 312 313 b += closing_paren(¶ms[b]); 314 if (b == start) { 315 ERROR("Dangling parenthesis"); 316 return -EINVAL; 317 } 318 ret = (*cb)(dev, ¶ms[a], head); 319 if (ret) 320 return ret; 321 head += 1; 322 b += 1; 323 if (params[b] == '\0') 324 return 0; 325 } 326 a = b + 1; 327 } 328 return 0; 329 } 330 331 static int 332 fs_remove_sub_devices_definition(char params[DEVARGS_MAXLEN]) 333 { 334 char buffer[DEVARGS_MAXLEN] = {0}; 335 size_t a, b; 336 int i; 337 338 a = 0; 339 i = 0; 340 while (params[a] != '\0') { 341 b = a; 342 while (params[b] != '(' && 343 params[b] != ',' && 344 params[b] != '\0') 345 b++; 346 if (b == a) { 347 ERROR("Invalid parameter"); 348 return -EINVAL; 349 } 350 if (params[b] == ',' || params[b] == '\0') { 351 size_t len = b - a; 352 353 if (i > 0) 354 len += 1; 355 snprintf(&buffer[i], len + 1, "%s%s", 356 i ? "," : "", ¶ms[a]); 357 i += len; 358 } else if (params[b] == '(') { 359 size_t start = b; 360 361 b += closing_paren(¶ms[b]); 362 if (b == start) 363 return -EINVAL; 364 b += 1; 365 if (params[b] == '\0') 366 goto out; 367 } 368 a = b + 1; 369 } 370 out: 371 snprintf(params, DEVARGS_MAXLEN, "%s", buffer); 372 return 0; 373 } 374 375 static int 376 fs_get_u64_arg(const char *key __rte_unused, 377 const char *value, void *out) 378 { 379 uint64_t *u64 = out; 380 char *endptr = NULL; 381 382 if ((value == NULL) || (out == NULL)) 383 return -EINVAL; 384 errno = 0; 385 *u64 = strtoull(value, &endptr, 0); 386 if (errno != 0) 387 return -errno; 388 if (endptr == value) 389 return -1; 390 return 0; 391 } 392 393 static int 394 fs_get_mac_addr_arg(const char *key __rte_unused, 395 const char *value, void *out) 396 { 397 struct ether_addr *ea = out; 398 int ret; 399 400 if ((value == NULL) || (out == NULL)) 401 return -EINVAL; 402 ret = sscanf(value, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", 403 &ea->addr_bytes[0], &ea->addr_bytes[1], 404 &ea->addr_bytes[2], &ea->addr_bytes[3], 405 &ea->addr_bytes[4], &ea->addr_bytes[5]); 406 return ret != ETHER_ADDR_LEN; 407 } 408 409 int 410 failsafe_args_parse(struct rte_eth_dev *dev, const char *params) 411 { 412 struct fs_priv *priv; 413 char mut_params[DEVARGS_MAXLEN] = ""; 414 struct rte_kvargs *kvlist = NULL; 415 unsigned int arg_count; 416 size_t n; 417 int ret; 418 419 priv = PRIV(dev); 420 ret = 0; 421 priv->subs_tx = FAILSAFE_MAX_ETHPORTS; 422 /* default parameters */ 423 n = snprintf(mut_params, sizeof(mut_params), "%s", params); 424 if (n >= sizeof(mut_params)) { 425 ERROR("Parameter string too long (>=%zu)", 426 sizeof(mut_params)); 427 return -ENOMEM; 428 } 429 ret = fs_parse_sub_devices(fs_parse_device_param, 430 dev, params); 431 if (ret < 0) 432 return ret; 433 ret = fs_remove_sub_devices_definition(mut_params); 434 if (ret < 0) 435 return ret; 436 if (strnlen(mut_params, sizeof(mut_params)) > 0) { 437 kvlist = rte_kvargs_parse(mut_params, 438 pmd_failsafe_init_parameters); 439 if (kvlist == NULL) { 440 ERROR("Error parsing parameters, usage:\n" 441 PMD_FAILSAFE_PARAM_STRING); 442 return -1; 443 } 444 /* PLUG_IN event poll timer */ 445 arg_count = rte_kvargs_count(kvlist, 446 PMD_FAILSAFE_HOTPLUG_POLL_KVARG); 447 if (arg_count == 1) { 448 ret = rte_kvargs_process(kvlist, 449 PMD_FAILSAFE_HOTPLUG_POLL_KVARG, 450 &fs_get_u64_arg, &hotplug_poll); 451 if (ret < 0) 452 goto free_kvlist; 453 } 454 /* MAC addr */ 455 arg_count = rte_kvargs_count(kvlist, 456 PMD_FAILSAFE_MAC_KVARG); 457 if (arg_count > 0) { 458 ret = rte_kvargs_process(kvlist, 459 PMD_FAILSAFE_MAC_KVARG, 460 &fs_get_mac_addr_arg, 461 &dev->data->mac_addrs[0]); 462 if (ret < 0) 463 goto free_kvlist; 464 465 mac_from_arg = 1; 466 } 467 } 468 PRIV(dev)->state = DEV_PARSED; 469 free_kvlist: 470 rte_kvargs_free(kvlist); 471 return ret; 472 } 473 474 void 475 failsafe_args_free(struct rte_eth_dev *dev) 476 { 477 struct sub_device *sdev; 478 uint8_t i; 479 480 FOREACH_SUBDEV(sdev, i, dev) { 481 free(sdev->cmdline); 482 sdev->cmdline = NULL; 483 free(sdev->fd_str); 484 sdev->fd_str = NULL; 485 free(sdev->devargs.args); 486 sdev->devargs.args = NULL; 487 } 488 } 489 490 static int 491 fs_count_device(struct rte_eth_dev *dev, const char *param, 492 uint8_t head __rte_unused) 493 { 494 size_t b = 0; 495 496 while (param[b] != '(' && 497 param[b] != '\0') 498 b++; 499 if (strncmp(param, "dev", b) != 0 && 500 strncmp(param, "exec", b) != 0 && 501 strncmp(param, "fd(", b) != 0) { 502 ERROR("Unrecognized device type: %.*s", (int)b, param); 503 return -EINVAL; 504 } 505 PRIV(dev)->subs_tail += 1; 506 return 0; 507 } 508 509 int 510 failsafe_args_count_subdevice(struct rte_eth_dev *dev, 511 const char *params) 512 { 513 return fs_parse_sub_devices(fs_count_device, 514 dev, params); 515 } 516 517 static int 518 fs_parse_sub_device(struct sub_device *sdev) 519 { 520 struct rte_devargs *da; 521 char devstr[DEVARGS_MAXLEN] = ""; 522 523 da = &sdev->devargs; 524 snprintf(devstr, sizeof(devstr), "%s,%s", da->name, da->args); 525 return fs_parse_device(sdev, devstr); 526 } 527 528 int 529 failsafe_args_parse_subs(struct rte_eth_dev *dev) 530 { 531 struct sub_device *sdev; 532 uint8_t i; 533 int ret = 0; 534 535 FOREACH_SUBDEV(sdev, i, dev) { 536 if (sdev->state >= DEV_PARSED) 537 continue; 538 if (sdev->cmdline) 539 ret = fs_execute_cmd(sdev, sdev->cmdline); 540 else if (sdev->fd_str) 541 ret = fs_read_fd(sdev, sdev->fd_str); 542 else 543 ret = fs_parse_sub_device(sdev); 544 if (ret == 0) 545 sdev->state = DEV_PARSED; 546 } 547 return 0; 548 } 549