dpdk/drivers/net/failsafe/failsafe_args.c
<<
>>
Prefs
   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 */
  22typedef int (parse_cb)(struct rte_eth_dev *dev, const char *params,
  23                uint8_t head);
  24
  25uint64_t failsafe_hotplug_poll = FAILSAFE_HOTPLUG_DEFAULT_TIMEOUT_MS;
  26int failsafe_mac_from_arg;
  27
  28static 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 */
  40static size_t
  41closing_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
  58static int
  59fs_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
  76static void
  77fs_sanitize_cmdline(char *args)
  78{
  79        char *nl;
  80
  81        nl = strrchr(args, '\n');
  82        if (nl)
  83                nl[0] = '\0';
  84}
  85
  86static int
  87fs_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);
 132ret_pclose:
 133        if (pclose(fp) == -1)
 134                ERROR("pclose: %s", strerror(errno));
 135        return ret;
 136}
 137
 138static int
 139fs_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);
 191error:
 192        if (fp)
 193                fclose(fp);
 194        if (fd != -1)
 195                close(fd);
 196        return err;
 197}
 198
 199static int
 200fs_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(&param[b]);
 218        if (a == b) {
 219                ERROR("Dangling parenthesis");
 220                return -EINVAL;
 221        }
 222        a += 1;
 223        args = strndup(&param[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                return -EINVAL;
 252        }
 253free_args:
 254        free(args);
 255        return ret;
 256}
 257
 258static int
 259fs_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(&params[b]);
 287                        if (b == start) {
 288                                ERROR("Dangling parenthesis");
 289                                return -EINVAL;
 290                        }
 291                        ret = (*cb)(dev, &params[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
 304static int
 305fs_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 ? "," : "", &params[a]);
 330                        i += len;
 331                } else if (params[b] == '(') {
 332                        size_t start = b;
 333
 334                        b += closing_paren(&params[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        }
 343out:
 344        strlcpy(params, buffer, DEVARGS_MAXLEN);
 345        return 0;
 346}
 347
 348static int
 349fs_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
 366static int
 367fs_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
 378int
 379failsafe_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:\n"
 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;
 438free_kvlist:
 439        rte_kvargs_free(kvlist);
 440        return ret;
 441}
 442
 443void
 444failsafe_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
 458static int
 459fs_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
 477int
 478failsafe_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
 485static int
 486fs_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
 496int
 497failsafe_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