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