linux/drivers/net/ethernet/mscc/ocelot_tc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0 OR MIT)
   2/* Microsemi Ocelot Switch TC driver
   3 *
   4 * Copyright (c) 2019 Microsemi Corporation
   5 */
   6
   7#include "ocelot_tc.h"
   8#include "ocelot_police.h"
   9#include "ocelot_ace.h"
  10#include <net/pkt_cls.h>
  11
  12static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv,
  13                                        struct tc_cls_matchall_offload *f,
  14                                        bool ingress)
  15{
  16        struct netlink_ext_ack *extack = f->common.extack;
  17        struct ocelot *ocelot = priv->port.ocelot;
  18        struct ocelot_policer pol = { 0 };
  19        struct flow_action_entry *action;
  20        int port = priv->chip_port;
  21        int err;
  22
  23        netdev_dbg(priv->dev, "%s: port %u command %d cookie %lu\n",
  24                   __func__, port, f->command, f->cookie);
  25
  26        if (!ingress) {
  27                NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported");
  28                return -EOPNOTSUPP;
  29        }
  30
  31        switch (f->command) {
  32        case TC_CLSMATCHALL_REPLACE:
  33                if (!flow_offload_has_one_action(&f->rule->action)) {
  34                        NL_SET_ERR_MSG_MOD(extack,
  35                                           "Only one action is supported");
  36                        return -EOPNOTSUPP;
  37                }
  38
  39                if (priv->tc.block_shared) {
  40                        NL_SET_ERR_MSG_MOD(extack,
  41                                           "Rate limit is not supported on shared blocks");
  42                        return -EOPNOTSUPP;
  43                }
  44
  45                action = &f->rule->action.entries[0];
  46
  47                if (action->id != FLOW_ACTION_POLICE) {
  48                        NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
  49                        return -EOPNOTSUPP;
  50                }
  51
  52                if (priv->tc.police_id && priv->tc.police_id != f->cookie) {
  53                        NL_SET_ERR_MSG_MOD(extack,
  54                                           "Only one policer per port is supported\n");
  55                        return -EEXIST;
  56                }
  57
  58                pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8;
  59                pol.burst = (u32)div_u64(action->police.rate_bytes_ps *
  60                                         PSCHED_NS2TICKS(action->police.burst),
  61                                         PSCHED_TICKS_PER_SEC);
  62
  63                err = ocelot_port_policer_add(ocelot, port, &pol);
  64                if (err) {
  65                        NL_SET_ERR_MSG_MOD(extack, "Could not add policer\n");
  66                        return err;
  67                }
  68
  69                priv->tc.police_id = f->cookie;
  70                priv->tc.offload_cnt++;
  71                return 0;
  72        case TC_CLSMATCHALL_DESTROY:
  73                if (priv->tc.police_id != f->cookie)
  74                        return -ENOENT;
  75
  76                err = ocelot_port_policer_del(ocelot, port);
  77                if (err) {
  78                        NL_SET_ERR_MSG_MOD(extack,
  79                                           "Could not delete policer\n");
  80                        return err;
  81                }
  82                priv->tc.police_id = 0;
  83                priv->tc.offload_cnt--;
  84                return 0;
  85        case TC_CLSMATCHALL_STATS: /* fall through */
  86        default:
  87                return -EOPNOTSUPP;
  88        }
  89}
  90
  91static int ocelot_setup_tc_block_cb(enum tc_setup_type type,
  92                                    void *type_data,
  93                                    void *cb_priv, bool ingress)
  94{
  95        struct ocelot_port_private *priv = cb_priv;
  96
  97        if (!tc_cls_can_offload_and_chain0(priv->dev, type_data))
  98                return -EOPNOTSUPP;
  99
 100        switch (type) {
 101        case TC_SETUP_CLSMATCHALL:
 102                netdev_dbg(priv->dev, "tc_block_cb: TC_SETUP_CLSMATCHALL %s\n",
 103                           ingress ? "ingress" : "egress");
 104
 105                return ocelot_setup_tc_cls_matchall(priv, type_data, ingress);
 106        case TC_SETUP_CLSFLOWER:
 107                return 0;
 108        default:
 109                netdev_dbg(priv->dev, "tc_block_cb: type %d %s\n",
 110                           type,
 111                           ingress ? "ingress" : "egress");
 112
 113                return -EOPNOTSUPP;
 114        }
 115}
 116
 117static int ocelot_setup_tc_block_cb_ig(enum tc_setup_type type,
 118                                       void *type_data,
 119                                       void *cb_priv)
 120{
 121        return ocelot_setup_tc_block_cb(type, type_data,
 122                                        cb_priv, true);
 123}
 124
 125static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type,
 126                                       void *type_data,
 127                                       void *cb_priv)
 128{
 129        return ocelot_setup_tc_block_cb(type, type_data,
 130                                        cb_priv, false);
 131}
 132
 133static LIST_HEAD(ocelot_block_cb_list);
 134
 135static int ocelot_setup_tc_block(struct ocelot_port_private *priv,
 136                                 struct flow_block_offload *f)
 137{
 138        struct flow_block_cb *block_cb;
 139        flow_setup_cb_t *cb;
 140        int err;
 141
 142        netdev_dbg(priv->dev, "tc_block command %d, binder_type %d\n",
 143                   f->command, f->binder_type);
 144
 145        if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) {
 146                cb = ocelot_setup_tc_block_cb_ig;
 147                priv->tc.block_shared = f->block_shared;
 148        } else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) {
 149                cb = ocelot_setup_tc_block_cb_eg;
 150        } else {
 151                return -EOPNOTSUPP;
 152        }
 153
 154        f->driver_block_list = &ocelot_block_cb_list;
 155
 156        switch (f->command) {
 157        case FLOW_BLOCK_BIND:
 158                if (flow_block_cb_is_busy(cb, priv, &ocelot_block_cb_list))
 159                        return -EBUSY;
 160
 161                block_cb = flow_block_cb_alloc(cb, priv, priv, NULL);
 162                if (IS_ERR(block_cb))
 163                        return PTR_ERR(block_cb);
 164
 165                err = ocelot_setup_tc_block_flower_bind(priv, f);
 166                if (err < 0) {
 167                        flow_block_cb_free(block_cb);
 168                        return err;
 169                }
 170                flow_block_cb_add(block_cb, f);
 171                list_add_tail(&block_cb->driver_list, f->driver_block_list);
 172                return 0;
 173        case FLOW_BLOCK_UNBIND:
 174                block_cb = flow_block_cb_lookup(f->block, cb, priv);
 175                if (!block_cb)
 176                        return -ENOENT;
 177
 178                ocelot_setup_tc_block_flower_unbind(priv, f);
 179                flow_block_cb_remove(block_cb, f);
 180                list_del(&block_cb->driver_list);
 181                return 0;
 182        default:
 183                return -EOPNOTSUPP;
 184        }
 185}
 186
 187int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
 188                    void *type_data)
 189{
 190        struct ocelot_port_private *priv = netdev_priv(dev);
 191
 192        switch (type) {
 193        case TC_SETUP_BLOCK:
 194                return ocelot_setup_tc_block(priv, type_data);
 195        default:
 196                return -EOPNOTSUPP;
 197        }
 198        return 0;
 199}
 200