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