| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545 |
- // SPDX-License-Identifier: GPL-2.0
- /* Copyright 2020 NXP
- */
- #include "sja1105.h"
- #include "sja1105_vl.h"
- struct sja1105_rule *sja1105_rule_find(struct sja1105_private *priv,
- unsigned long cookie)
- {
- struct sja1105_rule *rule;
- list_for_each_entry(rule, &priv->flow_block.rules, list)
- if (rule->cookie == cookie)
- return rule;
- return NULL;
- }
- static int sja1105_find_free_l2_policer(struct sja1105_private *priv)
- {
- int i;
- for (i = 0; i < SJA1105_NUM_L2_POLICERS; i++)
- if (!priv->flow_block.l2_policer_used[i])
- return i;
- return -1;
- }
- static int sja1105_setup_bcast_policer(struct sja1105_private *priv,
- struct netlink_ext_ack *extack,
- unsigned long cookie, int port,
- u64 rate_bytes_per_sec,
- u32 burst)
- {
- struct sja1105_rule *rule = sja1105_rule_find(priv, cookie);
- struct sja1105_l2_policing_entry *policing;
- struct dsa_switch *ds = priv->ds;
- bool new_rule = false;
- unsigned long p;
- int rc;
- if (!rule) {
- rule = kzalloc(sizeof(*rule), GFP_KERNEL);
- if (!rule)
- return -ENOMEM;
- rule->cookie = cookie;
- rule->type = SJA1105_RULE_BCAST_POLICER;
- rule->bcast_pol.sharindx = sja1105_find_free_l2_policer(priv);
- rule->key.type = SJA1105_KEY_BCAST;
- new_rule = true;
- }
- if (rule->bcast_pol.sharindx == -1) {
- NL_SET_ERR_MSG_MOD(extack, "No more L2 policers free");
- rc = -ENOSPC;
- goto out;
- }
- policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries;
- if (policing[(ds->num_ports * SJA1105_NUM_TC) + port].sharindx != port) {
- NL_SET_ERR_MSG_MOD(extack,
- "Port already has a broadcast policer");
- rc = -EEXIST;
- goto out;
- }
- rule->port_mask |= BIT(port);
- /* Make the broadcast policers of all ports attached to this block
- * point to the newly allocated policer
- */
- for_each_set_bit(p, &rule->port_mask, SJA1105_MAX_NUM_PORTS) {
- int bcast = (ds->num_ports * SJA1105_NUM_TC) + p;
- policing[bcast].sharindx = rule->bcast_pol.sharindx;
- }
- policing[rule->bcast_pol.sharindx].rate = div_u64(rate_bytes_per_sec *
- 512, 1000000);
- policing[rule->bcast_pol.sharindx].smax = burst;
- /* TODO: support per-flow MTU */
- policing[rule->bcast_pol.sharindx].maxlen = VLAN_ETH_FRAME_LEN +
- ETH_FCS_LEN;
- rc = sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING);
- out:
- if (rc == 0 && new_rule) {
- priv->flow_block.l2_policer_used[rule->bcast_pol.sharindx] = true;
- list_add(&rule->list, &priv->flow_block.rules);
- } else if (new_rule) {
- kfree(rule);
- }
- return rc;
- }
- static int sja1105_setup_tc_policer(struct sja1105_private *priv,
- struct netlink_ext_ack *extack,
- unsigned long cookie, int port, int tc,
- u64 rate_bytes_per_sec,
- u32 burst)
- {
- struct sja1105_rule *rule = sja1105_rule_find(priv, cookie);
- struct sja1105_l2_policing_entry *policing;
- bool new_rule = false;
- unsigned long p;
- int rc;
- if (!rule) {
- rule = kzalloc(sizeof(*rule), GFP_KERNEL);
- if (!rule)
- return -ENOMEM;
- rule->cookie = cookie;
- rule->type = SJA1105_RULE_TC_POLICER;
- rule->tc_pol.sharindx = sja1105_find_free_l2_policer(priv);
- rule->key.type = SJA1105_KEY_TC;
- rule->key.tc.pcp = tc;
- new_rule = true;
- }
- if (rule->tc_pol.sharindx == -1) {
- NL_SET_ERR_MSG_MOD(extack, "No more L2 policers free");
- rc = -ENOSPC;
- goto out;
- }
- policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries;
- if (policing[(port * SJA1105_NUM_TC) + tc].sharindx != port) {
- NL_SET_ERR_MSG_MOD(extack,
- "Port-TC pair already has an L2 policer");
- rc = -EEXIST;
- goto out;
- }
- rule->port_mask |= BIT(port);
- /* Make the policers for traffic class @tc of all ports attached to
- * this block point to the newly allocated policer
- */
- for_each_set_bit(p, &rule->port_mask, SJA1105_MAX_NUM_PORTS) {
- int index = (p * SJA1105_NUM_TC) + tc;
- policing[index].sharindx = rule->tc_pol.sharindx;
- }
- policing[rule->tc_pol.sharindx].rate = div_u64(rate_bytes_per_sec *
- 512, 1000000);
- policing[rule->tc_pol.sharindx].smax = burst;
- /* TODO: support per-flow MTU */
- policing[rule->tc_pol.sharindx].maxlen = VLAN_ETH_FRAME_LEN +
- ETH_FCS_LEN;
- rc = sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING);
- out:
- if (rc == 0 && new_rule) {
- priv->flow_block.l2_policer_used[rule->tc_pol.sharindx] = true;
- list_add(&rule->list, &priv->flow_block.rules);
- } else if (new_rule) {
- kfree(rule);
- }
- return rc;
- }
- static int sja1105_flower_policer(struct sja1105_private *priv, int port,
- struct netlink_ext_ack *extack,
- unsigned long cookie,
- struct sja1105_key *key,
- u64 rate_bytes_per_sec,
- u32 burst)
- {
- switch (key->type) {
- case SJA1105_KEY_BCAST:
- return sja1105_setup_bcast_policer(priv, extack, cookie, port,
- rate_bytes_per_sec, burst);
- case SJA1105_KEY_TC:
- return sja1105_setup_tc_policer(priv, extack, cookie, port,
- key->tc.pcp, rate_bytes_per_sec,
- burst);
- default:
- NL_SET_ERR_MSG_MOD(extack, "Unknown keys for policing");
- return -EOPNOTSUPP;
- }
- }
- static int sja1105_flower_parse_key(struct sja1105_private *priv,
- struct netlink_ext_ack *extack,
- struct flow_cls_offload *cls,
- struct sja1105_key *key)
- {
- struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
- struct flow_dissector *dissector = rule->match.dissector;
- bool is_bcast_dmac = false;
- u64 dmac = U64_MAX;
- u16 vid = U16_MAX;
- u16 pcp = U16_MAX;
- if (dissector->used_keys &
- ~(BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
- BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |
- BIT_ULL(FLOW_DISSECTOR_KEY_VLAN) |
- BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS))) {
- NL_SET_ERR_MSG_MOD(extack,
- "Unsupported keys used");
- return -EOPNOTSUPP;
- }
- if (flow_rule_match_has_control_flags(rule, extack))
- return -EOPNOTSUPP;
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
- struct flow_match_basic match;
- flow_rule_match_basic(rule, &match);
- if (match.key->n_proto) {
- NL_SET_ERR_MSG_MOD(extack,
- "Matching on protocol not supported");
- return -EOPNOTSUPP;
- }
- }
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
- u8 bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
- u8 null[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
- struct flow_match_eth_addrs match;
- flow_rule_match_eth_addrs(rule, &match);
- if (!ether_addr_equal_masked(match.key->src, null,
- match.mask->src)) {
- NL_SET_ERR_MSG_MOD(extack,
- "Matching on source MAC not supported");
- return -EOPNOTSUPP;
- }
- if (!ether_addr_equal(match.mask->dst, bcast)) {
- NL_SET_ERR_MSG_MOD(extack,
- "Masked matching on MAC not supported");
- return -EOPNOTSUPP;
- }
- dmac = ether_addr_to_u64(match.key->dst);
- is_bcast_dmac = ether_addr_equal(match.key->dst, bcast);
- }
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
- struct flow_match_vlan match;
- flow_rule_match_vlan(rule, &match);
- if (match.mask->vlan_id &&
- match.mask->vlan_id != VLAN_VID_MASK) {
- NL_SET_ERR_MSG_MOD(extack,
- "Masked matching on VID is not supported");
- return -EOPNOTSUPP;
- }
- if (match.mask->vlan_priority &&
- match.mask->vlan_priority != 0x7) {
- NL_SET_ERR_MSG_MOD(extack,
- "Masked matching on PCP is not supported");
- return -EOPNOTSUPP;
- }
- if (match.mask->vlan_id)
- vid = match.key->vlan_id;
- if (match.mask->vlan_priority)
- pcp = match.key->vlan_priority;
- }
- if (is_bcast_dmac && vid == U16_MAX && pcp == U16_MAX) {
- key->type = SJA1105_KEY_BCAST;
- return 0;
- }
- if (dmac == U64_MAX && vid == U16_MAX && pcp != U16_MAX) {
- key->type = SJA1105_KEY_TC;
- key->tc.pcp = pcp;
- return 0;
- }
- if (dmac != U64_MAX && vid != U16_MAX && pcp != U16_MAX) {
- key->type = SJA1105_KEY_VLAN_AWARE_VL;
- key->vl.dmac = dmac;
- key->vl.vid = vid;
- key->vl.pcp = pcp;
- return 0;
- }
- if (dmac != U64_MAX) {
- key->type = SJA1105_KEY_VLAN_UNAWARE_VL;
- key->vl.dmac = dmac;
- return 0;
- }
- NL_SET_ERR_MSG_MOD(extack, "Not matching on any known key");
- return -EOPNOTSUPP;
- }
- static int sja1105_policer_validate(const struct flow_action *action,
- const struct flow_action_entry *act,
- struct netlink_ext_ack *extack)
- {
- if (act->police.exceed.act_id != FLOW_ACTION_DROP) {
- NL_SET_ERR_MSG_MOD(extack,
- "Offload not supported when exceed action is not drop");
- return -EOPNOTSUPP;
- }
- if (act->police.notexceed.act_id != FLOW_ACTION_PIPE &&
- act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) {
- NL_SET_ERR_MSG_MOD(extack,
- "Offload not supported when conform action is not pipe or ok");
- return -EOPNOTSUPP;
- }
- if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT &&
- !flow_action_is_last_entry(action, act)) {
- NL_SET_ERR_MSG_MOD(extack,
- "Offload not supported when conform action is ok, but action is not last");
- return -EOPNOTSUPP;
- }
- if (act->police.peakrate_bytes_ps ||
- act->police.avrate || act->police.overhead) {
- NL_SET_ERR_MSG_MOD(extack,
- "Offload not supported when peakrate/avrate/overhead is configured");
- return -EOPNOTSUPP;
- }
- if (act->police.rate_pkt_ps) {
- NL_SET_ERR_MSG_MOD(extack,
- "QoS offload not support packets per second");
- return -EOPNOTSUPP;
- }
- return 0;
- }
- int sja1105_cls_flower_add(struct dsa_switch *ds, int port,
- struct flow_cls_offload *cls, bool ingress)
- {
- struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
- struct netlink_ext_ack *extack = cls->common.extack;
- struct sja1105_private *priv = ds->priv;
- const struct flow_action_entry *act;
- unsigned long cookie = cls->cookie;
- bool routing_rule = false;
- struct sja1105_key key;
- bool gate_rule = false;
- bool vl_rule = false;
- int rc, i;
- rc = sja1105_flower_parse_key(priv, extack, cls, &key);
- if (rc)
- return rc;
- flow_action_for_each(i, act, &rule->action) {
- switch (act->id) {
- case FLOW_ACTION_POLICE:
- rc = sja1105_policer_validate(&rule->action, act, extack);
- if (rc)
- goto out;
- rc = sja1105_flower_policer(priv, port, extack, cookie,
- &key,
- act->police.rate_bytes_ps,
- act->police.burst);
- if (rc)
- goto out;
- break;
- case FLOW_ACTION_TRAP: {
- int cpu = dsa_upstream_port(ds, port);
- routing_rule = true;
- vl_rule = true;
- rc = sja1105_vl_redirect(priv, port, extack, cookie,
- &key, BIT(cpu), true);
- if (rc)
- goto out;
- break;
- }
- case FLOW_ACTION_REDIRECT: {
- struct dsa_port *to_dp;
- to_dp = dsa_port_from_netdev(act->dev);
- if (IS_ERR(to_dp)) {
- NL_SET_ERR_MSG_MOD(extack,
- "Destination not a switch port");
- return -EOPNOTSUPP;
- }
- routing_rule = true;
- vl_rule = true;
- rc = sja1105_vl_redirect(priv, port, extack, cookie,
- &key, BIT(to_dp->index), true);
- if (rc)
- goto out;
- break;
- }
- case FLOW_ACTION_DROP:
- vl_rule = true;
- rc = sja1105_vl_redirect(priv, port, extack, cookie,
- &key, 0, false);
- if (rc)
- goto out;
- break;
- case FLOW_ACTION_GATE:
- gate_rule = true;
- vl_rule = true;
- rc = sja1105_vl_gate(priv, port, extack, cookie,
- &key, act->hw_index,
- act->gate.prio,
- act->gate.basetime,
- act->gate.cycletime,
- act->gate.cycletimeext,
- act->gate.num_entries,
- act->gate.entries);
- if (rc)
- goto out;
- break;
- default:
- NL_SET_ERR_MSG_MOD(extack,
- "Action not supported");
- rc = -EOPNOTSUPP;
- goto out;
- }
- }
- if (vl_rule && !rc) {
- /* Delay scheduling configuration until DESTPORTS has been
- * populated by all other actions.
- */
- if (gate_rule) {
- if (!routing_rule) {
- NL_SET_ERR_MSG_MOD(extack,
- "Can only offload gate action together with redirect or trap");
- return -EOPNOTSUPP;
- }
- rc = sja1105_init_scheduling(priv);
- if (rc)
- goto out;
- }
- rc = sja1105_static_config_reload(priv, SJA1105_VIRTUAL_LINKS);
- }
- out:
- return rc;
- }
- int sja1105_cls_flower_del(struct dsa_switch *ds, int port,
- struct flow_cls_offload *cls, bool ingress)
- {
- struct sja1105_private *priv = ds->priv;
- struct sja1105_rule *rule = sja1105_rule_find(priv, cls->cookie);
- struct sja1105_l2_policing_entry *policing;
- int old_sharindx;
- if (!rule)
- return 0;
- if (rule->type == SJA1105_RULE_VL)
- return sja1105_vl_delete(priv, port, rule, cls->common.extack);
- policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries;
- if (rule->type == SJA1105_RULE_BCAST_POLICER) {
- int bcast = (ds->num_ports * SJA1105_NUM_TC) + port;
- old_sharindx = policing[bcast].sharindx;
- policing[bcast].sharindx = port;
- } else if (rule->type == SJA1105_RULE_TC_POLICER) {
- int index = (port * SJA1105_NUM_TC) + rule->key.tc.pcp;
- old_sharindx = policing[index].sharindx;
- policing[index].sharindx = port;
- } else {
- return -EINVAL;
- }
- rule->port_mask &= ~BIT(port);
- if (!rule->port_mask) {
- priv->flow_block.l2_policer_used[old_sharindx] = false;
- list_del(&rule->list);
- kfree(rule);
- }
- return sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING);
- }
- int sja1105_cls_flower_stats(struct dsa_switch *ds, int port,
- struct flow_cls_offload *cls, bool ingress)
- {
- struct sja1105_private *priv = ds->priv;
- struct sja1105_rule *rule = sja1105_rule_find(priv, cls->cookie);
- int rc;
- if (!rule)
- return 0;
- if (rule->type != SJA1105_RULE_VL)
- return 0;
- rc = sja1105_vl_stats(priv, port, rule, &cls->stats,
- cls->common.extack);
- if (rc)
- return rc;
- return 0;
- }
- void sja1105_flower_setup(struct dsa_switch *ds)
- {
- struct sja1105_private *priv = ds->priv;
- int port;
- INIT_LIST_HEAD(&priv->flow_block.rules);
- for (port = 0; port < ds->num_ports; port++)
- priv->flow_block.l2_policer_used[port] = true;
- }
- void sja1105_flower_teardown(struct dsa_switch *ds)
- {
- struct sja1105_private *priv = ds->priv;
- struct sja1105_rule *rule;
- struct list_head *pos, *n;
- list_for_each_safe(pos, n, &priv->flow_block.rules) {
- rule = list_entry(pos, struct sja1105_rule, list);
- list_del(&rule->list);
- kfree(rule);
- }
- }
|