net: mscc: ocelot: offload egress VLAN rewriting to VCAP ES0
authorXiaoliang Yang <xiaoliang.yang_1@nxp.com>
Fri, 2 Oct 2020 12:02:24 +0000 (15:02 +0300)
committerDavid S. Miller <davem@davemloft.net>
Fri, 2 Oct 2020 22:40:30 +0000 (15:40 -0700)
VCAP ES0 is an egress VCAP operating on all outgoing frames.
This patch added ES0 driver to support vlan push action of tc filter.
Usage:

tc filter add dev swp1 egress protocol 802.1Q flower indev swp0 skip_sw \
        vlan_id 1 vlan_prio 1 action vlan push id 2 priority 2

Signed-off-by: Xiaoliang Yang <xiaoliang.yang_1@nxp.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mscc/ocelot.c
drivers/net/ethernet/mscc/ocelot_flower.c
drivers/net/ethernet/mscc/ocelot_vcap.c
drivers/net/ethernet/mscc/ocelot_vcap.h

index ba47359c26c750a22f75b39d688f89d051059211..e026617d61337e7fa613a7b32051b383e4d1e728 100644 (file)
@@ -111,6 +111,10 @@ static void ocelot_vcap_enable(struct ocelot *ocelot, int port)
 
        ocelot_write_gix(ocelot, ANA_PORT_VCAP_CFG_S1_ENA,
                         ANA_PORT_VCAP_CFG, port);
+
+       ocelot_rmw_gix(ocelot, REW_PORT_CFG_ES0_EN,
+                      REW_PORT_CFG_ES0_EN,
+                      REW_PORT_CFG, port);
 }
 
 static inline u32 ocelot_vlant_read_vlanaccess(struct ocelot *ocelot)
index b8a588e65929ac644f948355cb6e4c5ea5b6dc07..feeaf016f8cafb6865a792365e166ea90db195ff 100644 (file)
@@ -148,6 +148,7 @@ static int ocelot_flower_parse_action(struct flow_cls_offload *f, bool ingress,
        struct netlink_ext_ack *extack = f->common.extack;
        bool allow_missing_goto_target = false;
        const struct flow_action_entry *a;
+       enum ocelot_tag_tpid_sel tpid;
        int i, chain;
        u64 rate;
 
@@ -267,6 +268,31 @@ static int ocelot_flower_parse_action(struct flow_cls_offload *f, bool ingress,
                                filter->type = OCELOT_VCAP_FILTER_PAG;
                        }
                        break;
+               case FLOW_ACTION_VLAN_PUSH:
+                       if (filter->block_id != VCAP_ES0) {
+                               NL_SET_ERR_MSG_MOD(extack,
+                                                  "VLAN push action can only be offloaded to VCAP ES0");
+                               return -EOPNOTSUPP;
+                       }
+                       switch (ntohs(a->vlan.proto)) {
+                       case ETH_P_8021Q:
+                               tpid = OCELOT_TAG_TPID_SEL_8021Q;
+                               break;
+                       case ETH_P_8021AD:
+                               tpid = OCELOT_TAG_TPID_SEL_8021AD;
+                               break;
+                       default:
+                               NL_SET_ERR_MSG_MOD(extack,
+                                                  "Cannot push custom TPID");
+                               return -EOPNOTSUPP;
+                       }
+                       filter->action.tag_a_tpid_sel = tpid;
+                       filter->action.push_outer_tag = OCELOT_ES0_TAG;
+                       filter->action.tag_a_vid_sel = 1;
+                       filter->action.vid_a_val = a->vlan.vid;
+                       filter->action.pcp_a_val = a->vlan.prio;
+                       filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
+                       break;
                default:
                        NL_SET_ERR_MSG_MOD(extack, "Cannot offload action");
                        return -EOPNOTSUPP;
@@ -292,18 +318,73 @@ static int ocelot_flower_parse_action(struct flow_cls_offload *f, bool ingress,
        return 0;
 }
 
-static int ocelot_flower_parse_key(struct flow_cls_offload *f, bool ingress,
-                                  struct ocelot_vcap_filter *filter)
+static int ocelot_flower_parse_indev(struct ocelot *ocelot, int port,
+                                    struct flow_cls_offload *f,
+                                    struct ocelot_vcap_filter *filter)
+{
+       struct flow_rule *rule = flow_cls_offload_flow_rule(f);
+       const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];
+       int key_length = vcap->keys[VCAP_ES0_IGR_PORT].length;
+       struct netlink_ext_ack *extack = f->common.extack;
+       struct net_device *dev, *indev;
+       struct flow_match_meta match;
+       int ingress_port;
+
+       flow_rule_match_meta(rule, &match);
+
+       if (!match.mask->ingress_ifindex)
+               return 0;
+
+       if (match.mask->ingress_ifindex != 0xFFFFFFFF) {
+               NL_SET_ERR_MSG_MOD(extack, "Unsupported ingress ifindex mask");
+               return -EOPNOTSUPP;
+       }
+
+       dev = ocelot->ops->port_to_netdev(ocelot, port);
+       if (!dev)
+               return -EINVAL;
+
+       indev = __dev_get_by_index(dev_net(dev), match.key->ingress_ifindex);
+       if (!indev) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Can't find the ingress port to match on");
+               return -ENOENT;
+       }
+
+       ingress_port = ocelot->ops->netdev_to_port(indev);
+       if (ingress_port < 0) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Can only offload an ocelot ingress port");
+               return -EOPNOTSUPP;
+       }
+       if (ingress_port == port) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Ingress port is equal to the egress port");
+               return -EINVAL;
+       }
+
+       filter->ingress_port.value = ingress_port;
+       filter->ingress_port.mask = GENMASK(key_length - 1, 0);
+
+       return 0;
+}
+
+static int
+ocelot_flower_parse_key(struct ocelot *ocelot, int port, bool ingress,
+                       struct flow_cls_offload *f,
+                       struct ocelot_vcap_filter *filter)
 {
        struct flow_rule *rule = flow_cls_offload_flow_rule(f);
        struct flow_dissector *dissector = rule->match.dissector;
        struct netlink_ext_ack *extack = f->common.extack;
        u16 proto = ntohs(f->common.protocol);
        bool match_protocol = true;
+       int ret;
 
        if (dissector->used_keys &
            ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
              BIT(FLOW_DISSECTOR_KEY_BASIC) |
+             BIT(FLOW_DISSECTOR_KEY_META) |
              BIT(FLOW_DISSECTOR_KEY_PORTS) |
              BIT(FLOW_DISSECTOR_KEY_VLAN) |
              BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
@@ -312,6 +393,13 @@ static int ocelot_flower_parse_key(struct flow_cls_offload *f, bool ingress,
                return -EOPNOTSUPP;
        }
 
+       /* For VCAP ES0 (egress rewriter) we can match on the ingress port */
+       if (!ingress) {
+               ret = ocelot_flower_parse_indev(ocelot, port, f, filter);
+               if (ret)
+                       return ret;
+       }
+
        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
                struct flow_match_control match;
 
@@ -321,6 +409,12 @@ static int ocelot_flower_parse_key(struct flow_cls_offload *f, bool ingress,
        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
                struct flow_match_eth_addrs match;
 
+               if (filter->block_id == VCAP_ES0) {
+                       NL_SET_ERR_MSG_MOD(extack,
+                                          "VCAP ES0 cannot match on MAC address");
+                       return -EOPNOTSUPP;
+               }
+
                if (filter->block_id == VCAP_IS1 &&
                    !is_zero_ether_addr(match.mask->dst)) {
                        NL_SET_ERR_MSG_MOD(extack,
@@ -359,6 +453,12 @@ static int ocelot_flower_parse_key(struct flow_cls_offload *f, bool ingress,
 
                flow_rule_match_basic(rule, &match);
                if (ntohs(match.key->n_proto) == ETH_P_IP) {
+                       if (filter->block_id == VCAP_ES0) {
+                               NL_SET_ERR_MSG_MOD(extack,
+                                                  "VCAP ES0 cannot match on IP protocol");
+                               return -EOPNOTSUPP;
+                       }
+
                        filter->key_type = OCELOT_VCAP_KEY_IPV4;
                        filter->key.ipv4.proto.value[0] =
                                match.key->ip_proto;
@@ -367,6 +467,12 @@ static int ocelot_flower_parse_key(struct flow_cls_offload *f, bool ingress,
                        match_protocol = false;
                }
                if (ntohs(match.key->n_proto) == ETH_P_IPV6) {
+                       if (filter->block_id == VCAP_ES0) {
+                               NL_SET_ERR_MSG_MOD(extack,
+                                                  "VCAP ES0 cannot match on IP protocol");
+                               return -EOPNOTSUPP;
+                       }
+
                        filter->key_type = OCELOT_VCAP_KEY_IPV6;
                        filter->key.ipv6.proto.value[0] =
                                match.key->ip_proto;
@@ -381,6 +487,12 @@ static int ocelot_flower_parse_key(struct flow_cls_offload *f, bool ingress,
                struct flow_match_ipv4_addrs match;
                u8 *tmp;
 
+               if (filter->block_id == VCAP_ES0) {
+                       NL_SET_ERR_MSG_MOD(extack,
+                                          "VCAP ES0 cannot match on IP address");
+                       return -EOPNOTSUPP;
+               }
+
                if (filter->block_id == VCAP_IS1 && *(u32 *)&match.mask->dst) {
                        NL_SET_ERR_MSG_MOD(extack,
                                           "Key type S1_NORMAL cannot match on destination IP");
@@ -410,6 +522,12 @@ static int ocelot_flower_parse_key(struct flow_cls_offload *f, bool ingress,
        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
                struct flow_match_ports match;
 
+               if (filter->block_id == VCAP_ES0) {
+                       NL_SET_ERR_MSG_MOD(extack,
+                                          "VCAP ES0 cannot match on L4 ports");
+                       return -EOPNOTSUPP;
+               }
+
                flow_rule_match_ports(rule, &match);
                filter->key.ipv4.sport.value = ntohs(match.key->src);
                filter->key.ipv4.sport.mask = ntohs(match.mask->src);
@@ -432,6 +550,12 @@ static int ocelot_flower_parse_key(struct flow_cls_offload *f, bool ingress,
 
 finished_key_parsing:
        if (match_protocol && proto != ETH_P_ALL) {
+               if (filter->block_id == VCAP_ES0) {
+                       NL_SET_ERR_MSG_MOD(extack,
+                                          "VCAP ES0 cannot match on L2 proto");
+                       return -EOPNOTSUPP;
+               }
+
                /* TODO: support SNAP, LLC etc */
                if (proto < ETH_P_802_3_MIN)
                        return -EOPNOTSUPP;
@@ -444,7 +568,8 @@ finished_key_parsing:
        return 0;
 }
 
-static int ocelot_flower_parse(struct flow_cls_offload *f, bool ingress,
+static int ocelot_flower_parse(struct ocelot *ocelot, int port, bool ingress,
+                              struct flow_cls_offload *f,
                               struct ocelot_vcap_filter *filter)
 {
        int ret;
@@ -456,12 +581,12 @@ static int ocelot_flower_parse(struct flow_cls_offload *f, bool ingress,
        if (ret)
                return ret;
 
-       return ocelot_flower_parse_key(f, ingress, filter);
+       return ocelot_flower_parse_key(ocelot, port, ingress, f, filter);
 }
 
 static struct ocelot_vcap_filter
-*ocelot_vcap_filter_create(struct ocelot *ocelot, int port,
-                        struct flow_cls_offload *f)
+*ocelot_vcap_filter_create(struct ocelot *ocelot, int port, bool ingress,
+                          struct flow_cls_offload *f)
 {
        struct ocelot_vcap_filter *filter;
 
@@ -469,7 +594,16 @@ static struct ocelot_vcap_filter
        if (!filter)
                return NULL;
 
-       filter->ingress_port_mask = BIT(port);
+       if (ingress) {
+               filter->ingress_port_mask = BIT(port);
+       } else {
+               const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];
+               int key_length = vcap->keys[VCAP_ES0_EGR_PORT].length;
+
+               filter->egress_port.value = port;
+               filter->egress_port.mask = GENMASK(key_length - 1, 0);
+       }
+
        return filter;
 }
 
@@ -503,11 +637,11 @@ int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
                return -EOPNOTSUPP;
        }
 
-       filter = ocelot_vcap_filter_create(ocelot, port, f);
+       filter = ocelot_vcap_filter_create(ocelot, port, ingress, f);
        if (!filter)
                return -ENOMEM;
 
-       ret = ocelot_flower_parse(f, ingress, filter);
+       ret = ocelot_flower_parse(ocelot, port, ingress, f, filter);
        if (ret) {
                kfree(filter);
                return ret;
index be3293e7c8928fedeb6ac90468cdb3694019c4e7..d0e5c5bbdbf844c76b8d798808e4c208d48592bb 100644 (file)
@@ -774,6 +774,79 @@ static void is1_entry_set(struct ocelot *ocelot, int ix,
        vcap_row_cmd(ocelot, vcap, row, VCAP_CMD_WRITE, VCAP_SEL_ALL);
 }
 
+static void es0_action_set(struct ocelot *ocelot, struct vcap_data *data,
+                          const struct ocelot_vcap_filter *filter)
+{
+       const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];
+       const struct ocelot_vcap_action *a = &filter->action;
+
+       vcap_action_set(vcap, data, VCAP_ES0_ACT_PUSH_OUTER_TAG,
+                       a->push_outer_tag);
+       vcap_action_set(vcap, data, VCAP_ES0_ACT_PUSH_INNER_TAG,
+                       a->push_inner_tag);
+       vcap_action_set(vcap, data, VCAP_ES0_ACT_TAG_A_TPID_SEL,
+                       a->tag_a_tpid_sel);
+       vcap_action_set(vcap, data, VCAP_ES0_ACT_TAG_A_VID_SEL,
+                       a->tag_a_vid_sel);
+       vcap_action_set(vcap, data, VCAP_ES0_ACT_TAG_A_PCP_SEL,
+                       a->tag_a_pcp_sel);
+       vcap_action_set(vcap, data, VCAP_ES0_ACT_VID_A_VAL, a->vid_a_val);
+       vcap_action_set(vcap, data, VCAP_ES0_ACT_PCP_A_VAL, a->pcp_a_val);
+       vcap_action_set(vcap, data, VCAP_ES0_ACT_TAG_B_TPID_SEL,
+                       a->tag_b_tpid_sel);
+       vcap_action_set(vcap, data, VCAP_ES0_ACT_TAG_B_VID_SEL,
+                       a->tag_b_vid_sel);
+       vcap_action_set(vcap, data, VCAP_ES0_ACT_TAG_B_PCP_SEL,
+                       a->tag_b_pcp_sel);
+       vcap_action_set(vcap, data, VCAP_ES0_ACT_VID_B_VAL, a->vid_b_val);
+       vcap_action_set(vcap, data, VCAP_ES0_ACT_PCP_B_VAL, a->pcp_b_val);
+}
+
+static void es0_entry_set(struct ocelot *ocelot, int ix,
+                         struct ocelot_vcap_filter *filter)
+{
+       const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];
+       struct ocelot_vcap_key_vlan *tag = &filter->vlan;
+       struct ocelot_vcap_u64 payload;
+       struct vcap_data data;
+       int row = ix;
+
+       memset(&payload, 0, sizeof(payload));
+       memset(&data, 0, sizeof(data));
+
+       /* Read row */
+       vcap_row_cmd(ocelot, vcap, row, VCAP_CMD_READ, VCAP_SEL_ALL);
+       vcap_cache2entry(ocelot, vcap, &data);
+       vcap_cache2action(ocelot, vcap, &data);
+
+       data.tg_sw = VCAP_TG_FULL;
+       data.type = ES0_ACTION_TYPE_NORMAL;
+       vcap_data_offset_get(vcap, &data, ix);
+       data.tg = (data.tg & ~data.tg_mask);
+       if (filter->prio != 0)
+               data.tg |= data.tg_value;
+
+       vcap_key_set(vcap, &data, VCAP_ES0_IGR_PORT, filter->ingress_port.value,
+                    filter->ingress_port.mask);
+       vcap_key_set(vcap, &data, VCAP_ES0_EGR_PORT, filter->egress_port.value,
+                    filter->egress_port.mask);
+       vcap_key_bit_set(vcap, &data, VCAP_ES0_L2_MC, filter->dmac_mc);
+       vcap_key_bit_set(vcap, &data, VCAP_ES0_L2_BC, filter->dmac_bc);
+       vcap_key_set(vcap, &data, VCAP_ES0_VID,
+                    tag->vid.value, tag->vid.mask);
+       vcap_key_set(vcap, &data, VCAP_ES0_PCP,
+                    tag->pcp.value[0], tag->pcp.mask[0]);
+
+       es0_action_set(ocelot, &data, filter);
+       vcap_data_set(data.counter, data.counter_offset,
+                     vcap->counter_width, filter->stats.pkts);
+
+       /* Write row */
+       vcap_entry2cache(ocelot, vcap, &data);
+       vcap_action2cache(ocelot, vcap, &data);
+       vcap_row_cmd(ocelot, vcap, row, VCAP_CMD_WRITE, VCAP_SEL_ALL);
+}
+
 static void vcap_entry_get(struct ocelot *ocelot, int ix,
                           struct ocelot_vcap_filter *filter)
 {
@@ -782,7 +855,11 @@ static void vcap_entry_get(struct ocelot *ocelot, int ix,
        int row, count;
        u32 cnt;
 
-       data.tg_sw = VCAP_TG_HALF;
+       if (filter->block_id == VCAP_ES0)
+               data.tg_sw = VCAP_TG_FULL;
+       else
+               data.tg_sw = VCAP_TG_HALF;
+
        count = (1 << (data.tg_sw - 1));
        row = (ix / count);
        vcap_row_cmd(ocelot, vcap, row, VCAP_CMD_READ, VCAP_SEL_COUNTER);
@@ -801,6 +878,8 @@ static void vcap_entry_set(struct ocelot *ocelot, int ix,
                return is1_entry_set(ocelot, ix, filter);
        if (filter->block_id == VCAP_IS2)
                return is2_entry_set(ocelot, ix, filter);
+       if (filter->block_id == VCAP_ES0)
+               return es0_entry_set(ocelot, ix, filter);
 }
 
 static int ocelot_vcap_policer_add(struct ocelot *ocelot, u32 pol_ix,
index a71bb44476481e84628b708f1065b55031810e2b..82fd10581a1494409d38b312ee4362e22a043ae4 100644 (file)
@@ -78,6 +78,11 @@ struct ocelot_vcap_udp_tcp {
        u16 mask;
 };
 
+struct ocelot_vcap_port {
+       u8 value;
+       u8 mask;
+};
+
 enum ocelot_vcap_key_type {
        OCELOT_VCAP_KEY_ANY,
        OCELOT_VCAP_KEY_ETYPE,
@@ -184,8 +189,38 @@ enum ocelot_mask_mode {
        OCELOT_MASK_MODE_REDIRECT,
 };
 
+enum ocelot_es0_tag {
+       OCELOT_NO_ES0_TAG,
+       OCELOT_ES0_TAG,
+       OCELOT_FORCE_PORT_TAG,
+       OCELOT_FORCE_UNTAG,
+};
+
+enum ocelot_tag_tpid_sel {
+       OCELOT_TAG_TPID_SEL_8021Q,
+       OCELOT_TAG_TPID_SEL_8021AD,
+};
+
 struct ocelot_vcap_action {
        union {
+               /* VCAP ES0 */
+               struct {
+                       enum ocelot_es0_tag push_outer_tag;
+                       enum ocelot_es0_tag push_inner_tag;
+                       enum ocelot_tag_tpid_sel tag_a_tpid_sel;
+                       int tag_a_vid_sel;
+                       int tag_a_pcp_sel;
+                       u16 vid_a_val;
+                       u8 pcp_a_val;
+                       u8 dei_a_val;
+                       enum ocelot_tag_tpid_sel tag_b_tpid_sel;
+                       int tag_b_vid_sel;
+                       int tag_b_pcp_sel;
+                       u16 vid_b_val;
+                       u8 pcp_b_val;
+                       u8 dei_b_val;
+               };
+
                /* VCAP IS1 */
                struct {
                        bool vid_replace_ena;
@@ -239,7 +274,11 @@ struct ocelot_vcap_filter {
 
        struct ocelot_vcap_action action;
        struct ocelot_vcap_stats stats;
+       /* For VCAP IS1 and IS2 */
        unsigned long ingress_port_mask;
+       /* For VCAP ES0 */
+       struct ocelot_vcap_port ingress_port;
+       struct ocelot_vcap_port egress_port;
 
        enum ocelot_vcap_bit dmac_mc;
        enum ocelot_vcap_bit dmac_bc;