Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'ocelot-vlan'

Vladimir Oltean says:

====================
Egress VLAN modification using VCAP ES0 on Ocelot switches

This patch set adds support for modifying a VLAN ID at the egress stage
of Ocelot/Felix switch ports. It is useful for replicating a packet on
multiple ports, and each egress port sends it using a different VLAN ID.

Tested by rewriting the VLAN ID of both
(a) packets injected from the CPU port
(b) packets received from an external station on a front-facing port

Adding a selftest to make sure it doesn't bit-rot, and if it does, that
it can be traced back easily.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>

+204 -26
+1 -1
drivers/net/ethernet/mscc/ocelot.c
··· 916 916 ocelot_ifh_set_bypass(ifh, 1); 917 917 ocelot_ifh_set_dest(ifh, BIT_ULL(port)); 918 918 ocelot_ifh_set_tag_type(ifh, IFH_TAG_TYPE_C); 919 - ocelot_ifh_set_vid(ifh, skb_vlan_tag_get(skb)); 919 + ocelot_ifh_set_vlan_tci(ifh, skb_vlan_tag_get(skb)); 920 920 ocelot_ifh_set_rew_op(ifh, rew_op); 921 921 922 922 for (i = 0; i < OCELOT_TAG_LEN / 4; i++)
+105 -20
drivers/net/ethernet/mscc/ocelot_flower.c
··· 142 142 return NULL; 143 143 } 144 144 145 + static int 146 + ocelot_flower_parse_ingress_vlan_modify(struct ocelot *ocelot, int port, 147 + struct ocelot_vcap_filter *filter, 148 + const struct flow_action_entry *a, 149 + struct netlink_ext_ack *extack) 150 + { 151 + struct ocelot_port *ocelot_port = ocelot->ports[port]; 152 + 153 + if (filter->goto_target != -1) { 154 + NL_SET_ERR_MSG_MOD(extack, 155 + "Last action must be GOTO"); 156 + return -EOPNOTSUPP; 157 + } 158 + 159 + if (!ocelot_port->vlan_aware) { 160 + NL_SET_ERR_MSG_MOD(extack, 161 + "Can only modify VLAN under VLAN aware bridge"); 162 + return -EOPNOTSUPP; 163 + } 164 + 165 + filter->action.vid_replace_ena = true; 166 + filter->action.pcp_dei_ena = true; 167 + filter->action.vid = a->vlan.vid; 168 + filter->action.pcp = a->vlan.prio; 169 + filter->type = OCELOT_VCAP_FILTER_OFFLOAD; 170 + 171 + return 0; 172 + } 173 + 174 + static int 175 + ocelot_flower_parse_egress_vlan_modify(struct ocelot_vcap_filter *filter, 176 + const struct flow_action_entry *a, 177 + struct netlink_ext_ack *extack) 178 + { 179 + enum ocelot_tag_tpid_sel tpid; 180 + 181 + switch (ntohs(a->vlan.proto)) { 182 + case ETH_P_8021Q: 183 + tpid = OCELOT_TAG_TPID_SEL_8021Q; 184 + break; 185 + case ETH_P_8021AD: 186 + tpid = OCELOT_TAG_TPID_SEL_8021AD; 187 + break; 188 + default: 189 + NL_SET_ERR_MSG_MOD(extack, 190 + "Cannot modify custom TPID"); 191 + return -EOPNOTSUPP; 192 + } 193 + 194 + filter->action.tag_a_tpid_sel = tpid; 195 + filter->action.push_outer_tag = OCELOT_ES0_TAG; 196 + filter->action.tag_a_vid_sel = OCELOT_ES0_VID_PLUS_CLASSIFIED_VID; 197 + filter->action.vid_a_val = a->vlan.vid; 198 + filter->action.pcp_a_val = a->vlan.prio; 199 + filter->action.tag_a_pcp_sel = OCELOT_ES0_PCP; 200 + filter->type = OCELOT_VCAP_FILTER_OFFLOAD; 201 + 202 + return 0; 203 + } 204 + 145 205 static int ocelot_flower_parse_action(struct ocelot *ocelot, int port, 146 206 bool ingress, struct flow_cls_offload *f, 147 207 struct ocelot_vcap_filter *filter) 148 208 { 149 - struct ocelot_port *ocelot_port = ocelot->ports[port]; 150 209 struct netlink_ext_ack *extack = f->common.extack; 151 210 bool allow_missing_goto_target = false; 152 211 const struct flow_action_entry *a; 153 212 enum ocelot_tag_tpid_sel tpid; 154 213 int i, chain, egress_port; 155 214 u64 rate; 215 + int err; 156 216 157 217 if (!flow_action_basic_hw_stats_check(&f->rule->action, 158 218 f->common.extack)) ··· 333 273 filter->type = OCELOT_VCAP_FILTER_OFFLOAD; 334 274 break; 335 275 case FLOW_ACTION_VLAN_MANGLE: 336 - if (filter->block_id != VCAP_IS1) { 276 + if (filter->block_id == VCAP_IS1) { 277 + err = ocelot_flower_parse_ingress_vlan_modify(ocelot, port, 278 + filter, a, 279 + extack); 280 + } else if (filter->block_id == VCAP_ES0) { 281 + err = ocelot_flower_parse_egress_vlan_modify(filter, a, 282 + extack); 283 + } else { 337 284 NL_SET_ERR_MSG_MOD(extack, 338 - "VLAN modify action can only be offloaded to VCAP IS1"); 339 - return -EOPNOTSUPP; 285 + "VLAN modify action can only be offloaded to VCAP IS1 or ES0"); 286 + err = -EOPNOTSUPP; 340 287 } 341 - if (filter->goto_target != -1) { 342 - NL_SET_ERR_MSG_MOD(extack, 343 - "Last action must be GOTO"); 344 - return -EOPNOTSUPP; 345 - } 346 - if (!ocelot_port->vlan_aware) { 347 - NL_SET_ERR_MSG_MOD(extack, 348 - "Can only modify VLAN under VLAN aware bridge"); 349 - return -EOPNOTSUPP; 350 - } 351 - filter->action.vid_replace_ena = true; 352 - filter->action.pcp_dei_ena = true; 353 - filter->action.vid = a->vlan.vid; 354 - filter->action.pcp = a->vlan.prio; 355 - filter->type = OCELOT_VCAP_FILTER_OFFLOAD; 288 + if (err) 289 + return err; 356 290 break; 357 291 case FLOW_ACTION_PRIORITY: 358 292 if (filter->block_id != VCAP_IS1) { ··· 394 340 } 395 341 filter->action.tag_a_tpid_sel = tpid; 396 342 filter->action.push_outer_tag = OCELOT_ES0_TAG; 397 - filter->action.tag_a_vid_sel = 1; 343 + filter->action.tag_a_vid_sel = OCELOT_ES0_VID; 398 344 filter->action.vid_a_val = a->vlan.vid; 399 345 filter->action.pcp_a_val = a->vlan.prio; 400 346 filter->type = OCELOT_VCAP_FILTER_OFFLOAD; ··· 732 678 return 0; 733 679 } 734 680 681 + /* If we have an egress VLAN modification rule, we need to actually write the 682 + * delta between the input VLAN (from the key) and the output VLAN (from the 683 + * action), but the action was parsed first. So we need to patch the delta into 684 + * the action here. 685 + */ 686 + static int 687 + ocelot_flower_patch_es0_vlan_modify(struct ocelot_vcap_filter *filter, 688 + struct netlink_ext_ack *extack) 689 + { 690 + if (filter->block_id != VCAP_ES0 || 691 + filter->action.tag_a_vid_sel != OCELOT_ES0_VID_PLUS_CLASSIFIED_VID) 692 + return 0; 693 + 694 + if (filter->vlan.vid.mask != VLAN_VID_MASK) { 695 + NL_SET_ERR_MSG_MOD(extack, 696 + "VCAP ES0 VLAN rewriting needs a full VLAN in the key"); 697 + return -EOPNOTSUPP; 698 + } 699 + 700 + filter->action.vid_a_val -= filter->vlan.vid.value; 701 + filter->action.vid_a_val &= VLAN_VID_MASK; 702 + 703 + return 0; 704 + } 705 + 735 706 int ocelot_cls_flower_replace(struct ocelot *ocelot, int port, 736 707 struct flow_cls_offload *f, bool ingress) 737 708 { ··· 775 696 return -ENOMEM; 776 697 777 698 ret = ocelot_flower_parse(ocelot, port, ingress, f, filter); 699 + if (ret) { 700 + kfree(filter); 701 + return ret; 702 + } 703 + 704 + ret = ocelot_flower_patch_es0_vlan_modify(filter, extack); 778 705 if (ret) { 779 706 kfree(filter); 780 707 return ret;
+2 -2
include/linux/dsa/ocelot.h
··· 210 210 packing(injection, &tag_type, 16, 16, OCELOT_TAG_LEN, PACK, 0); 211 211 } 212 212 213 - static inline void ocelot_ifh_set_vid(void *injection, u64 vid) 213 + static inline void ocelot_ifh_set_vlan_tci(void *injection, u64 vlan_tci) 214 214 { 215 - packing(injection, &vid, 11, 0, OCELOT_TAG_LEN, PACK, 0); 215 + packing(injection, &vlan_tci, 15, 0, OCELOT_TAG_LEN, PACK, 0); 216 216 } 217 217 218 218 #endif
+10
include/soc/mscc/ocelot_vcap.h
··· 576 576 OCELOT_MASK_MODE_REDIRECT, 577 577 }; 578 578 579 + enum ocelot_es0_vid_sel { 580 + OCELOT_ES0_VID_PLUS_CLASSIFIED_VID = 0, 581 + OCELOT_ES0_VID = 1, 582 + }; 583 + 584 + enum ocelot_es0_pcp_sel { 585 + OCELOT_CLASSIFIED_PCP = 0, 586 + OCELOT_ES0_PCP = 1, 587 + }; 588 + 579 589 enum ocelot_es0_tag { 580 590 OCELOT_NO_ES0_TAG, 581 591 OCELOT_ES0_TAG,
+39
net/dsa/tag_ocelot.c
··· 5 5 #include <soc/mscc/ocelot.h> 6 6 #include "dsa_priv.h" 7 7 8 + /* If the port is under a VLAN-aware bridge, remove the VLAN header from the 9 + * payload and move it into the DSA tag, which will make the switch classify 10 + * the packet to the bridge VLAN. Otherwise, leave the classified VLAN at zero, 11 + * which is the pvid of standalone and VLAN-unaware bridge ports. 12 + */ 13 + static void ocelot_xmit_get_vlan_info(struct sk_buff *skb, struct dsa_port *dp, 14 + u64 *vlan_tci, u64 *tag_type) 15 + { 16 + struct net_device *br = READ_ONCE(dp->bridge_dev); 17 + struct vlan_ethhdr *hdr; 18 + u16 proto, tci; 19 + 20 + if (!br || !br_vlan_enabled(br)) { 21 + *vlan_tci = 0; 22 + *tag_type = IFH_TAG_TYPE_C; 23 + return; 24 + } 25 + 26 + hdr = (struct vlan_ethhdr *)skb_mac_header(skb); 27 + br_vlan_get_proto(br, &proto); 28 + 29 + if (ntohs(hdr->h_vlan_proto) == proto) { 30 + __skb_vlan_pop(skb, &tci); 31 + *vlan_tci = tci; 32 + } else { 33 + rcu_read_lock(); 34 + br_vlan_get_pvid_rcu(br, &tci); 35 + rcu_read_unlock(); 36 + *vlan_tci = tci; 37 + } 38 + 39 + *tag_type = (proto != ETH_P_8021Q) ? IFH_TAG_TYPE_S : IFH_TAG_TYPE_C; 40 + } 41 + 8 42 static void ocelot_xmit_common(struct sk_buff *skb, struct net_device *netdev, 9 43 __be32 ifh_prefix, void **ifh) 10 44 { 11 45 struct dsa_port *dp = dsa_slave_to_port(netdev); 12 46 struct dsa_switch *ds = dp->ds; 47 + u64 vlan_tci, tag_type; 13 48 void *injection; 14 49 __be32 *prefix; 15 50 u32 rew_op = 0; 51 + 52 + ocelot_xmit_get_vlan_info(skb, dp, &vlan_tci, &tag_type); 16 53 17 54 injection = skb_push(skb, OCELOT_TAG_LEN); 18 55 prefix = skb_push(skb, OCELOT_SHORT_PREFIX_LEN); ··· 59 22 ocelot_ifh_set_bypass(injection, 1); 60 23 ocelot_ifh_set_src(injection, ds->num_ports); 61 24 ocelot_ifh_set_qos_class(injection, skb->priority); 25 + ocelot_ifh_set_vlan_tci(injection, vlan_tci); 26 + ocelot_ifh_set_tag_type(injection, tag_type); 62 27 63 28 rew_op = ocelot_ptp_rew_op(skb); 64 29 if (rew_op)
+47 -3
tools/testing/selftests/drivers/net/ocelot/tc_flower_chains.sh
··· 156 156 157 157 setup_prepare() 158 158 { 159 + ip link set $eth0 up 160 + ip link set $eth1 up 161 + ip link set $eth2 up 162 + ip link set $eth3 up 163 + 159 164 create_tcam_skeleton $eth0 160 165 161 166 ip link add br0 type bridge ··· 247 242 tcpdump_cleanup 248 243 } 249 244 250 - test_vlan_modify() 245 + test_vlan_ingress_modify() 251 246 { 252 - printf "Testing VLAN modification.. " 247 + printf "Testing ingress VLAN modification.. " 253 248 254 249 ip link set br0 type bridge vlan_filtering 1 255 250 bridge vlan add dev $eth0 vid 200 ··· 285 280 ip link set br0 type bridge vlan_filtering 0 286 281 } 287 282 283 + test_vlan_egress_modify() 284 + { 285 + printf "Testing egress VLAN modification.. " 286 + 287 + tc qdisc add dev $eth1 clsact 288 + 289 + ip link set br0 type bridge vlan_filtering 1 290 + bridge vlan add dev $eth0 vid 200 291 + bridge vlan add dev $eth1 vid 200 292 + 293 + tc filter add dev $eth1 egress chain $(ES0) pref 3 \ 294 + protocol 802.1Q flower skip_sw vlan_id 200 vlan_prio 0 \ 295 + action vlan modify id 300 priority 7 296 + 297 + tcpdump_start $eth2 298 + 299 + $MZ $eth3.200 -q -c 1 -p 64 -a $eth3_mac -b $eth2_mac -t ip 300 + 301 + sleep 1 302 + 303 + tcpdump_stop 304 + 305 + if tcpdump_show | grep -q "$eth3_mac > $eth2_mac, .* vlan 300"; then 306 + echo "OK" 307 + else 308 + echo "FAIL" 309 + fi 310 + 311 + tcpdump_cleanup 312 + 313 + tc filter del dev $eth1 egress chain $(ES0) pref 3 314 + tc qdisc del dev $eth1 clsact 315 + 316 + bridge vlan del dev $eth0 vid 200 317 + bridge vlan del dev $eth1 vid 200 318 + ip link set br0 type bridge vlan_filtering 0 319 + } 320 + 288 321 test_skbedit_priority() 289 322 { 290 323 local num_pkts=100 ··· 347 304 ALL_TESTS=" 348 305 test_vlan_pop 349 306 test_vlan_push 350 - test_vlan_modify 307 + test_vlan_ingress_modify 308 + test_vlan_egress_modify 351 309 test_skbedit_priority 352 310 " 353 311