diff options
-rw-r--r-- | include/net/dsa.h | 13 | ||||
-rw-r--r-- | net/dsa/slave.c | 79 |
2 files changed, 85 insertions, 7 deletions
diff --git a/include/net/dsa.h b/include/net/dsa.h index aeb411e77b9a..fb3f9222f2a1 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -130,9 +130,10 @@ struct dsa_switch_tree { struct list_head rtable; }; -/* TC matchall action types, only mirroring for now */ +/* TC matchall action types */ enum dsa_port_mall_action_type { DSA_PORT_MALL_MIRROR, + DSA_PORT_MALL_POLICER, }; /* TC mirroring entry */ @@ -141,6 +142,12 @@ struct dsa_mall_mirror_tc_entry { bool ingress; }; +/* TC port policer entry */ +struct dsa_mall_policer_tc_entry { + s64 burst; + u64 rate_bytes_per_sec; +}; + /* TC matchall entry */ struct dsa_mall_tc_entry { struct list_head list; @@ -148,6 +155,7 @@ struct dsa_mall_tc_entry { enum dsa_port_mall_action_type type; union { struct dsa_mall_mirror_tc_entry mirror; + struct dsa_mall_policer_tc_entry policer; }; }; @@ -557,6 +565,9 @@ struct dsa_switch_ops { bool ingress); void (*port_mirror_del)(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror); + int (*port_policer_add)(struct dsa_switch *ds, int port, + struct dsa_mall_policer_tc_entry *policer); + void (*port_policer_del)(struct dsa_switch *ds, int port); int (*port_setup_tc)(struct dsa_switch *ds, int port, enum tc_setup_type type, void *type_data); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index e6040a11bd83..9692a726f2ed 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -859,14 +859,14 @@ dsa_slave_add_cls_matchall_mirred(struct net_device *dev, act = &cls->rule->action.entries[0]; if (!ds->ops->port_mirror_add) - return err; + return -EOPNOTSUPP; if (!act->dev) return -EINVAL; if (!flow_action_basic_hw_stats_check(&cls->rule->action, cls->common.extack)) - return err; + return -EOPNOTSUPP; act = &cls->rule->action.entries[0]; @@ -897,6 +897,67 @@ dsa_slave_add_cls_matchall_mirred(struct net_device *dev, return err; } +static int +dsa_slave_add_cls_matchall_police(struct net_device *dev, + struct tc_cls_matchall_offload *cls, + bool ingress) +{ + struct netlink_ext_ack *extack = cls->common.extack; + struct dsa_port *dp = dsa_slave_to_port(dev); + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_mall_policer_tc_entry *policer; + struct dsa_mall_tc_entry *mall_tc_entry; + struct dsa_switch *ds = dp->ds; + struct flow_action_entry *act; + int err; + + if (!ds->ops->port_policer_add) { + NL_SET_ERR_MSG_MOD(extack, + "Policing offload not implemented\n"); + return -EOPNOTSUPP; + } + + if (!ingress) { + NL_SET_ERR_MSG_MOD(extack, + "Only supported on ingress qdisc\n"); + return -EOPNOTSUPP; + } + + if (!flow_action_basic_hw_stats_check(&cls->rule->action, + cls->common.extack)) + return -EOPNOTSUPP; + + list_for_each_entry(mall_tc_entry, &p->mall_tc_list, list) { + if (mall_tc_entry->type == DSA_PORT_MALL_POLICER) { + NL_SET_ERR_MSG_MOD(extack, + "Only one port policer allowed\n"); + return -EEXIST; + } + } + + act = &cls->rule->action.entries[0]; + + mall_tc_entry = kzalloc(sizeof(*mall_tc_entry), GFP_KERNEL); + if (!mall_tc_entry) + return -ENOMEM; + + mall_tc_entry->cookie = cls->cookie; + mall_tc_entry->type = DSA_PORT_MALL_POLICER; + policer = &mall_tc_entry->policer; + policer->rate_bytes_per_sec = act->police.rate_bytes_ps; + policer->burst = act->police.burst; + + err = ds->ops->port_policer_add(ds, dp->index, policer); + if (err) { + kfree(mall_tc_entry); + return err; + } + + list_add_tail(&mall_tc_entry->list, &p->mall_tc_list); + + return err; +} + static int dsa_slave_add_cls_matchall(struct net_device *dev, struct tc_cls_matchall_offload *cls, bool ingress) @@ -907,6 +968,9 @@ static int dsa_slave_add_cls_matchall(struct net_device *dev, flow_offload_has_one_action(&cls->rule->action) && cls->rule->action.entries[0].id == FLOW_ACTION_MIRRED) err = dsa_slave_add_cls_matchall_mirred(dev, cls, ingress); + else if (flow_offload_has_one_action(&cls->rule->action) && + cls->rule->action.entries[0].id == FLOW_ACTION_POLICE) + err = dsa_slave_add_cls_matchall_police(dev, cls, ingress); return err; } @@ -918,9 +982,6 @@ static void dsa_slave_del_cls_matchall(struct net_device *dev, struct dsa_mall_tc_entry *mall_tc_entry; struct dsa_switch *ds = dp->ds; - if (!ds->ops->port_mirror_del) - return; - mall_tc_entry = dsa_slave_mall_tc_entry_find(dev, cls->cookie); if (!mall_tc_entry) return; @@ -929,7 +990,13 @@ static void dsa_slave_del_cls_matchall(struct net_device *dev, switch (mall_tc_entry->type) { case DSA_PORT_MALL_MIRROR: - ds->ops->port_mirror_del(ds, dp->index, &mall_tc_entry->mirror); + if (ds->ops->port_mirror_del) + ds->ops->port_mirror_del(ds, dp->index, + &mall_tc_entry->mirror); + break; + case DSA_PORT_MALL_POLICER: + if (ds->ops->port_policer_del) + ds->ops->port_policer_del(ds, dp->index); break; default: WARN_ON(1); |