linux/drivers/net/ethernet/netronome/nfp/abm/main.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
   2/* Copyright (C) 2018 Netronome Systems, Inc. */
   3
   4#include <linux/bitfield.h>
   5#include <linux/etherdevice.h>
   6#include <linux/lockdep.h>
   7#include <linux/netdevice.h>
   8#include <linux/rcupdate.h>
   9#include <linux/slab.h>
  10#include <net/pkt_cls.h>
  11#include <net/pkt_sched.h>
  12#include <net/red.h>
  13
  14#include "../nfpcore/nfp.h"
  15#include "../nfpcore/nfp_cpp.h"
  16#include "../nfpcore/nfp_nsp.h"
  17#include "../nfp_app.h"
  18#include "../nfp_main.h"
  19#include "../nfp_net.h"
  20#include "../nfp_net_repr.h"
  21#include "../nfp_port.h"
  22#include "main.h"
  23
  24static u32 nfp_abm_portid(enum nfp_repr_type rtype, unsigned int id)
  25{
  26        return FIELD_PREP(NFP_ABM_PORTID_TYPE, rtype) |
  27               FIELD_PREP(NFP_ABM_PORTID_ID, id);
  28}
  29
  30static int nfp_abm_reset_stats(struct nfp_abm_link *alink)
  31{
  32        int err;
  33
  34        err = nfp_abm_ctrl_read_stats(alink, &alink->qdiscs[0].stats);
  35        if (err)
  36                return err;
  37        alink->qdiscs[0].stats.backlog_pkts = 0;
  38        alink->qdiscs[0].stats.backlog_bytes = 0;
  39
  40        err = nfp_abm_ctrl_read_xstats(alink, &alink->qdiscs[0].xstats);
  41        if (err)
  42                return err;
  43
  44        return 0;
  45}
  46
  47static void
  48nfp_abm_red_destroy(struct net_device *netdev, struct nfp_abm_link *alink,
  49                    u32 handle)
  50{
  51        struct nfp_port *port = nfp_port_from_netdev(netdev);
  52
  53        if (handle != alink->qdiscs[0].handle)
  54                return;
  55
  56        alink->qdiscs[0].handle = TC_H_UNSPEC;
  57        port->tc_offload_cnt = 0;
  58        nfp_abm_ctrl_set_all_q_lvls(alink, ~0);
  59}
  60
  61static int
  62nfp_abm_red_replace(struct net_device *netdev, struct nfp_abm_link *alink,
  63                    struct tc_red_qopt_offload *opt)
  64{
  65        struct nfp_port *port = nfp_port_from_netdev(netdev);
  66        int err;
  67
  68        if (opt->set.min != opt->set.max || !opt->set.is_ecn) {
  69                nfp_warn(alink->abm->app->cpp,
  70                         "RED offload failed - unsupported parameters\n");
  71                err = -EINVAL;
  72                goto err_destroy;
  73        }
  74        err = nfp_abm_ctrl_set_all_q_lvls(alink, opt->set.min);
  75        if (err)
  76                goto err_destroy;
  77
  78        /* Reset stats only on new qdisc */
  79        if (alink->qdiscs[0].handle != opt->handle) {
  80                err = nfp_abm_reset_stats(alink);
  81                if (err)
  82                        goto err_destroy;
  83        }
  84
  85        alink->qdiscs[0].handle = opt->handle;
  86        port->tc_offload_cnt = 1;
  87
  88        return 0;
  89err_destroy:
  90        /* If the qdisc keeps on living, but we can't offload undo changes */
  91        if (alink->qdiscs[0].handle == opt->handle) {
  92                opt->set.qstats->qlen -= alink->qdiscs[0].stats.backlog_pkts;
  93                opt->set.qstats->backlog -=
  94                        alink->qdiscs[0].stats.backlog_bytes;
  95        }
  96        if (alink->qdiscs[0].handle != TC_H_UNSPEC)
  97                nfp_abm_red_destroy(netdev, alink, alink->qdiscs[0].handle);
  98        return err;
  99}
 100
 101static void
 102nfp_abm_update_stats(struct nfp_alink_stats *new, struct nfp_alink_stats *old,
 103                     struct tc_qopt_offload_stats *stats)
 104{
 105        _bstats_update(stats->bstats, new->tx_bytes - old->tx_bytes,
 106                       new->tx_pkts - old->tx_pkts);
 107        stats->qstats->qlen += new->backlog_pkts - old->backlog_pkts;
 108        stats->qstats->backlog += new->backlog_bytes - old->backlog_bytes;
 109        stats->qstats->overlimits += new->overlimits - old->overlimits;
 110        stats->qstats->drops += new->drops - old->drops;
 111}
 112
 113static int
 114nfp_abm_red_stats(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt)
 115{
 116        struct nfp_alink_stats *prev_stats;
 117        struct nfp_alink_stats stats;
 118        int err;
 119
 120        if (alink->qdiscs[0].handle != opt->handle)
 121                return -EOPNOTSUPP;
 122        prev_stats = &alink->qdiscs[0].stats;
 123
 124        err = nfp_abm_ctrl_read_stats(alink, &stats);
 125        if (err)
 126                return err;
 127
 128        nfp_abm_update_stats(&stats, prev_stats, &opt->stats);
 129
 130        *prev_stats = stats;
 131
 132        return 0;
 133}
 134
 135static int
 136nfp_abm_red_xstats(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt)
 137{
 138        struct nfp_alink_xstats *prev_xstats;
 139        struct nfp_alink_xstats xstats;
 140        int err;
 141
 142        if (alink->qdiscs[0].handle != opt->handle)
 143                return -EOPNOTSUPP;
 144        prev_xstats = &alink->qdiscs[0].xstats;
 145
 146        err = nfp_abm_ctrl_read_xstats(alink, &xstats);
 147        if (err)
 148                return err;
 149
 150        opt->xstats->forced_mark += xstats.ecn_marked - prev_xstats->ecn_marked;
 151        opt->xstats->pdrop += xstats.pdrop - prev_xstats->pdrop;
 152
 153        *prev_xstats = xstats;
 154
 155        return 0;
 156}
 157
 158static int
 159nfp_abm_setup_tc_red(struct net_device *netdev, struct nfp_abm_link *alink,
 160                     struct tc_red_qopt_offload *opt)
 161{
 162        if (opt->parent != TC_H_ROOT)
 163                return -EOPNOTSUPP;
 164
 165        switch (opt->command) {
 166        case TC_RED_REPLACE:
 167                return nfp_abm_red_replace(netdev, alink, opt);
 168        case TC_RED_DESTROY:
 169                nfp_abm_red_destroy(netdev, alink, opt->handle);
 170                return 0;
 171        case TC_RED_STATS:
 172                return nfp_abm_red_stats(alink, opt);
 173        case TC_RED_XSTATS:
 174                return nfp_abm_red_xstats(alink, opt);
 175        default:
 176                return -EOPNOTSUPP;
 177        }
 178}
 179
 180static int
 181nfp_abm_setup_tc(struct nfp_app *app, struct net_device *netdev,
 182                 enum tc_setup_type type, void *type_data)
 183{
 184        struct nfp_repr *repr = netdev_priv(netdev);
 185        struct nfp_port *port;
 186
 187        port = nfp_port_from_netdev(netdev);
 188        if (!port || port->type != NFP_PORT_PF_PORT)
 189                return -EOPNOTSUPP;
 190
 191        switch (type) {
 192        case TC_SETUP_QDISC_RED:
 193                return nfp_abm_setup_tc_red(netdev, repr->app_priv, type_data);
 194        default:
 195                return -EOPNOTSUPP;
 196        }
 197}
 198
 199static struct net_device *
 200nfp_abm_repr_get(struct nfp_app *app, u32 port_id, bool *redir_egress)
 201{
 202        enum nfp_repr_type rtype;
 203        struct nfp_reprs *reprs;
 204        u8 port;
 205
 206        rtype = FIELD_GET(NFP_ABM_PORTID_TYPE, port_id);
 207        port = FIELD_GET(NFP_ABM_PORTID_ID, port_id);
 208
 209        reprs = rcu_dereference(app->reprs[rtype]);
 210        if (!reprs)
 211                return NULL;
 212
 213        if (port >= reprs->num_reprs)
 214                return NULL;
 215
 216        return rcu_dereference(reprs->reprs[port]);
 217}
 218
 219static int
 220nfp_abm_spawn_repr(struct nfp_app *app, struct nfp_abm_link *alink,
 221                   enum nfp_port_type ptype)
 222{
 223        struct net_device *netdev;
 224        enum nfp_repr_type rtype;
 225        struct nfp_reprs *reprs;
 226        struct nfp_repr *repr;
 227        struct nfp_port *port;
 228        int err;
 229
 230        if (ptype == NFP_PORT_PHYS_PORT)
 231                rtype = NFP_REPR_TYPE_PHYS_PORT;
 232        else
 233                rtype = NFP_REPR_TYPE_PF;
 234
 235        netdev = nfp_repr_alloc(app);
 236        if (!netdev)
 237                return -ENOMEM;
 238        repr = netdev_priv(netdev);
 239        repr->app_priv = alink;
 240
 241        port = nfp_port_alloc(app, ptype, netdev);
 242        if (IS_ERR(port)) {
 243                err = PTR_ERR(port);
 244                goto err_free_repr;
 245        }
 246
 247        if (ptype == NFP_PORT_PHYS_PORT) {
 248                port->eth_forced = true;
 249                err = nfp_port_init_phy_port(app->pf, app, port, alink->id);
 250                if (err)
 251                        goto err_free_port;
 252        } else {
 253                port->pf_id = alink->abm->pf_id;
 254                port->vnic = alink->vnic->dp.ctrl_bar;
 255        }
 256
 257        SET_NETDEV_DEV(netdev, &alink->vnic->pdev->dev);
 258        eth_hw_addr_random(netdev);
 259
 260        err = nfp_repr_init(app, netdev, nfp_abm_portid(rtype, alink->id),
 261                            port, alink->vnic->dp.netdev);
 262        if (err)
 263                goto err_free_port;
 264
 265        reprs = nfp_reprs_get_locked(app, rtype);
 266        WARN(nfp_repr_get_locked(app, reprs, alink->id), "duplicate repr");
 267        rtnl_lock();
 268        rcu_assign_pointer(reprs->reprs[alink->id], netdev);
 269        rtnl_unlock();
 270
 271        nfp_info(app->cpp, "%s Port %d Representor(%s) created\n",
 272                 ptype == NFP_PORT_PF_PORT ? "PCIe" : "Phys",
 273                 alink->id, netdev->name);
 274
 275        return 0;
 276
 277err_free_port:
 278        nfp_port_free(port);
 279err_free_repr:
 280        nfp_repr_free(netdev);
 281        return err;
 282}
 283
 284static void
 285nfp_abm_kill_repr(struct nfp_app *app, struct nfp_abm_link *alink,
 286                  enum nfp_repr_type rtype)
 287{
 288        struct net_device *netdev;
 289        struct nfp_reprs *reprs;
 290
 291        reprs = nfp_reprs_get_locked(app, rtype);
 292        netdev = nfp_repr_get_locked(app, reprs, alink->id);
 293        if (!netdev)
 294                return;
 295        rtnl_lock();
 296        rcu_assign_pointer(reprs->reprs[alink->id], NULL);
 297        rtnl_unlock();
 298        synchronize_rcu();
 299        /* Cast to make sure nfp_repr_clean_and_free() takes a nfp_repr */
 300        nfp_repr_clean_and_free((struct nfp_repr *)netdev_priv(netdev));
 301}
 302
 303static void
 304nfp_abm_kill_reprs(struct nfp_abm *abm, struct nfp_abm_link *alink)
 305{
 306        nfp_abm_kill_repr(abm->app, alink, NFP_REPR_TYPE_PF);
 307        nfp_abm_kill_repr(abm->app, alink, NFP_REPR_TYPE_PHYS_PORT);
 308}
 309
 310static void nfp_abm_kill_reprs_all(struct nfp_abm *abm)
 311{
 312        struct nfp_pf *pf = abm->app->pf;
 313        struct nfp_net *nn;
 314
 315        list_for_each_entry(nn, &pf->vnics, vnic_list)
 316                nfp_abm_kill_reprs(abm, (struct nfp_abm_link *)nn->app_priv);
 317}
 318
 319static enum devlink_eswitch_mode nfp_abm_eswitch_mode_get(struct nfp_app *app)
 320{
 321        struct nfp_abm *abm = app->priv;
 322
 323        return abm->eswitch_mode;
 324}
 325
 326static int nfp_abm_eswitch_set_legacy(struct nfp_abm *abm)
 327{
 328        nfp_abm_kill_reprs_all(abm);
 329        nfp_abm_ctrl_qm_disable(abm);
 330
 331        abm->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY;
 332        return 0;
 333}
 334
 335static void nfp_abm_eswitch_clean_up(struct nfp_abm *abm)
 336{
 337        if (abm->eswitch_mode != DEVLINK_ESWITCH_MODE_LEGACY)
 338                WARN_ON(nfp_abm_eswitch_set_legacy(abm));
 339}
 340
 341static int nfp_abm_eswitch_set_switchdev(struct nfp_abm *abm)
 342{
 343        struct nfp_app *app = abm->app;
 344        struct nfp_pf *pf = app->pf;
 345        struct nfp_net *nn;
 346        int err;
 347
 348        err = nfp_abm_ctrl_qm_enable(abm);
 349        if (err)
 350                return err;
 351
 352        list_for_each_entry(nn, &pf->vnics, vnic_list) {
 353                struct nfp_abm_link *alink = nn->app_priv;
 354
 355                err = nfp_abm_spawn_repr(app, alink, NFP_PORT_PHYS_PORT);
 356                if (err)
 357                        goto err_kill_all_reprs;
 358
 359                err = nfp_abm_spawn_repr(app, alink, NFP_PORT_PF_PORT);
 360                if (err)
 361                        goto err_kill_all_reprs;
 362        }
 363
 364        abm->eswitch_mode = DEVLINK_ESWITCH_MODE_SWITCHDEV;
 365        return 0;
 366
 367err_kill_all_reprs:
 368        nfp_abm_kill_reprs_all(abm);
 369        nfp_abm_ctrl_qm_disable(abm);
 370        return err;
 371}
 372
 373static int nfp_abm_eswitch_mode_set(struct nfp_app *app, u16 mode)
 374{
 375        struct nfp_abm *abm = app->priv;
 376
 377        if (abm->eswitch_mode == mode)
 378                return 0;
 379
 380        switch (mode) {
 381        case DEVLINK_ESWITCH_MODE_LEGACY:
 382                return nfp_abm_eswitch_set_legacy(abm);
 383        case DEVLINK_ESWITCH_MODE_SWITCHDEV:
 384                return nfp_abm_eswitch_set_switchdev(abm);
 385        default:
 386                return -EINVAL;
 387        }
 388}
 389
 390static void
 391nfp_abm_vnic_set_mac(struct nfp_pf *pf, struct nfp_abm *abm, struct nfp_net *nn,
 392                     unsigned int id)
 393{
 394        struct nfp_eth_table_port *eth_port = &pf->eth_tbl->ports[id];
 395        u8 mac_addr[ETH_ALEN];
 396        const char *mac_str;
 397        char name[32];
 398
 399        if (id > pf->eth_tbl->count) {
 400                nfp_warn(pf->cpp, "No entry for persistent MAC address\n");
 401                eth_hw_addr_random(nn->dp.netdev);
 402                return;
 403        }
 404
 405        snprintf(name, sizeof(name), "eth%u.mac.pf%u",
 406                 eth_port->eth_index, abm->pf_id);
 407
 408        mac_str = nfp_hwinfo_lookup(pf->hwinfo, name);
 409        if (!mac_str) {
 410                nfp_warn(pf->cpp, "Can't lookup persistent MAC address (%s)\n",
 411                         name);
 412                eth_hw_addr_random(nn->dp.netdev);
 413                return;
 414        }
 415
 416        if (sscanf(mac_str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
 417                   &mac_addr[0], &mac_addr[1], &mac_addr[2],
 418                   &mac_addr[3], &mac_addr[4], &mac_addr[5]) != 6) {
 419                nfp_warn(pf->cpp, "Can't parse persistent MAC address (%s)\n",
 420                         mac_str);
 421                eth_hw_addr_random(nn->dp.netdev);
 422                return;
 423        }
 424
 425        ether_addr_copy(nn->dp.netdev->dev_addr, mac_addr);
 426        ether_addr_copy(nn->dp.netdev->perm_addr, mac_addr);
 427}
 428
 429static int
 430nfp_abm_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, unsigned int id)
 431{
 432        struct nfp_eth_table_port *eth_port = &app->pf->eth_tbl->ports[id];
 433        struct nfp_abm *abm = app->priv;
 434        struct nfp_abm_link *alink;
 435        int err;
 436
 437        alink = kzalloc(sizeof(*alink), GFP_KERNEL);
 438        if (!alink)
 439                return -ENOMEM;
 440        nn->app_priv = alink;
 441        alink->abm = abm;
 442        alink->vnic = nn;
 443        alink->id = id;
 444
 445        /* This is a multi-host app, make sure MAC/PHY is up, but don't
 446         * make the MAC/PHY state follow the state of any of the ports.
 447         */
 448        err = nfp_eth_set_configured(app->cpp, eth_port->index, true);
 449        if (err < 0)
 450                goto err_free_alink;
 451
 452        netif_keep_dst(nn->dp.netdev);
 453
 454        nfp_abm_vnic_set_mac(app->pf, abm, nn, id);
 455        nfp_abm_ctrl_read_params(alink);
 456
 457        return 0;
 458
 459err_free_alink:
 460        kfree(alink);
 461        return err;
 462}
 463
 464static void nfp_abm_vnic_free(struct nfp_app *app, struct nfp_net *nn)
 465{
 466        struct nfp_abm_link *alink = nn->app_priv;
 467
 468        nfp_abm_kill_reprs(alink->abm, alink);
 469        kfree(alink);
 470}
 471
 472static int nfp_abm_init(struct nfp_app *app)
 473{
 474        struct nfp_pf *pf = app->pf;
 475        struct nfp_reprs *reprs;
 476        struct nfp_abm *abm;
 477        int err;
 478
 479        if (!pf->eth_tbl) {
 480                nfp_err(pf->cpp, "ABM NIC requires ETH table\n");
 481                return -EINVAL;
 482        }
 483        if (pf->max_data_vnics != pf->eth_tbl->count) {
 484                nfp_err(pf->cpp, "ETH entries don't match vNICs (%d vs %d)\n",
 485                        pf->max_data_vnics, pf->eth_tbl->count);
 486                return -EINVAL;
 487        }
 488        if (!pf->mac_stats_bar) {
 489                nfp_warn(app->cpp, "ABM NIC requires mac_stats symbol\n");
 490                return -EINVAL;
 491        }
 492
 493        abm = kzalloc(sizeof(*abm), GFP_KERNEL);
 494        if (!abm)
 495                return -ENOMEM;
 496        app->priv = abm;
 497        abm->app = app;
 498
 499        err = nfp_abm_ctrl_find_addrs(abm);
 500        if (err)
 501                goto err_free_abm;
 502
 503        /* We start in legacy mode, make sure advanced queuing is disabled */
 504        err = nfp_abm_ctrl_qm_disable(abm);
 505        if (err)
 506                goto err_free_abm;
 507
 508        err = -ENOMEM;
 509        reprs = nfp_reprs_alloc(pf->max_data_vnics);
 510        if (!reprs)
 511                goto err_free_abm;
 512        RCU_INIT_POINTER(app->reprs[NFP_REPR_TYPE_PHYS_PORT], reprs);
 513
 514        reprs = nfp_reprs_alloc(pf->max_data_vnics);
 515        if (!reprs)
 516                goto err_free_phys;
 517        RCU_INIT_POINTER(app->reprs[NFP_REPR_TYPE_PF], reprs);
 518
 519        return 0;
 520
 521err_free_phys:
 522        nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PHYS_PORT);
 523err_free_abm:
 524        kfree(abm);
 525        app->priv = NULL;
 526        return err;
 527}
 528
 529static void nfp_abm_clean(struct nfp_app *app)
 530{
 531        struct nfp_abm *abm = app->priv;
 532
 533        nfp_abm_eswitch_clean_up(abm);
 534        nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PF);
 535        nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PHYS_PORT);
 536        kfree(abm);
 537        app->priv = NULL;
 538}
 539
 540const struct nfp_app_type app_abm = {
 541        .id             = NFP_APP_ACTIVE_BUFFER_MGMT_NIC,
 542        .name           = "abm",
 543
 544        .init           = nfp_abm_init,
 545        .clean          = nfp_abm_clean,
 546
 547        .vnic_alloc     = nfp_abm_vnic_alloc,
 548        .vnic_free      = nfp_abm_vnic_free,
 549
 550        .setup_tc       = nfp_abm_setup_tc,
 551
 552        .eswitch_mode_get       = nfp_abm_eswitch_mode_get,
 553        .eswitch_mode_set       = nfp_abm_eswitch_mode_set,
 554
 555        .dev_get        = nfp_abm_repr_get,
 556};
 557