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

Merge branch 'Offload-tc-vlan-mangle-to-mscc_ocelot-switch'

Vladimir Oltean says:

====================
Offload tc-vlan mangle to mscc_ocelot switch

This series offloads one more action to the VCAP IS1 ingress TCAM, which
is to change the classified VLAN for packets, according to the VCAP IS1
keys (VLAN, source MAC, source IP, EtherType, etc).
====================

Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+119 -6
+14 -1
drivers/net/ethernet/mscc/ocelot.c
··· 205 205 struct ocelot_port *ocelot_port = ocelot->ports[port]; 206 206 u32 val; 207 207 208 - if (switchdev_trans_ph_prepare(trans)) 208 + if (switchdev_trans_ph_prepare(trans)) { 209 + struct ocelot_vcap_block *block = &ocelot->block[VCAP_IS1]; 210 + struct ocelot_vcap_filter *filter; 211 + 212 + list_for_each_entry(filter, &block->rules, list) { 213 + if (filter->ingress_port_mask & BIT(port) && 214 + filter->action.vid_replace_ena) { 215 + dev_err(ocelot->dev, 216 + "Cannot change VLAN state with vlan modify rules active\n"); 217 + return -EBUSY; 218 + } 219 + } 220 + 209 221 return 0; 222 + } 210 223 211 224 ocelot_port->vlan_aware = vlan_aware; 212 225
+26 -3
drivers/net/ethernet/mscc/ocelot_flower.c
··· 142 142 return NULL; 143 143 } 144 144 145 - static int ocelot_flower_parse_action(struct ocelot *ocelot, bool ingress, 146 - struct flow_cls_offload *f, 145 + static int ocelot_flower_parse_action(struct ocelot *ocelot, int port, 146 + bool ingress, struct flow_cls_offload *f, 147 147 struct ocelot_vcap_filter *filter) 148 148 { 149 + struct ocelot_port *ocelot_port = ocelot->ports[port]; 149 150 struct netlink_ext_ack *extack = f->common.extack; 150 151 bool allow_missing_goto_target = false; 151 152 const struct flow_action_entry *a; ··· 265 264 "Cannot pop more than 2 VLAN headers"); 266 265 return -EOPNOTSUPP; 267 266 } 267 + filter->type = OCELOT_VCAP_FILTER_OFFLOAD; 268 + break; 269 + case FLOW_ACTION_VLAN_MANGLE: 270 + if (filter->block_id != VCAP_IS1) { 271 + NL_SET_ERR_MSG_MOD(extack, 272 + "VLAN modify action can only be offloaded to VCAP IS1"); 273 + return -EOPNOTSUPP; 274 + } 275 + if (filter->goto_target != -1) { 276 + NL_SET_ERR_MSG_MOD(extack, 277 + "Last action must be GOTO"); 278 + return -EOPNOTSUPP; 279 + } 280 + if (!ocelot_port->vlan_aware) { 281 + NL_SET_ERR_MSG_MOD(extack, 282 + "Can only modify VLAN under VLAN aware bridge"); 283 + return -EOPNOTSUPP; 284 + } 285 + filter->action.vid_replace_ena = true; 286 + filter->action.pcp_dei_ena = true; 287 + filter->action.vid = a->vlan.vid; 288 + filter->action.pcp = a->vlan.prio; 268 289 filter->type = OCELOT_VCAP_FILTER_OFFLOAD; 269 290 break; 270 291 case FLOW_ACTION_PRIORITY: ··· 624 601 filter->prio = f->common.prio; 625 602 filter->id = f->cookie; 626 603 627 - ret = ocelot_flower_parse_action(ocelot, ingress, f, filter); 604 + ret = ocelot_flower_parse_action(ocelot, port, ingress, f, filter); 628 605 if (ret) 629 606 return ret; 630 607
+34
net/dsa/tag_ocelot.c
··· 184 184 struct net_device *netdev, 185 185 struct packet_type *pt) 186 186 { 187 + struct dsa_port *cpu_dp = netdev->dsa_ptr; 188 + struct dsa_switch *ds = cpu_dp->ds; 189 + struct ocelot *ocelot = ds->priv; 187 190 u64 src_port, qos_class; 191 + u64 vlan_tci, tag_type; 188 192 u8 *start = skb->data; 189 193 u8 *extraction; 194 + u16 vlan_tpid; 190 195 191 196 /* Revert skb->data by the amount consumed by the DSA master, 192 197 * so it points to the beginning of the frame. ··· 219 214 220 215 packing(extraction, &src_port, 46, 43, OCELOT_TAG_LEN, UNPACK, 0); 221 216 packing(extraction, &qos_class, 19, 17, OCELOT_TAG_LEN, UNPACK, 0); 217 + packing(extraction, &tag_type, 16, 16, OCELOT_TAG_LEN, UNPACK, 0); 218 + packing(extraction, &vlan_tci, 15, 0, OCELOT_TAG_LEN, UNPACK, 0); 222 219 223 220 skb->dev = dsa_master_find_slave(netdev, 0, src_port); 224 221 if (!skb->dev) ··· 234 227 235 228 skb->offload_fwd_mark = 1; 236 229 skb->priority = qos_class; 230 + 231 + /* Ocelot switches copy frames unmodified to the CPU. However, it is 232 + * possible for the user to request a VLAN modification through 233 + * VCAP_IS1_ACT_VID_REPLACE_ENA. In this case, what will happen is that 234 + * the VLAN ID field from the Extraction Header gets updated, but the 235 + * 802.1Q header does not (the classified VLAN only becomes visible on 236 + * egress through the "port tag" of front-panel ports). 237 + * So, for traffic extracted by the CPU, we want to pick up the 238 + * classified VLAN and manually replace the existing 802.1Q header from 239 + * the packet with it, so that the operating system is always up to 240 + * date with the result of tc-vlan actions. 241 + * NOTE: In VLAN-unaware mode, we don't want to do that, we want the 242 + * frame to remain unmodified, because the classified VLAN is always 243 + * equal to the pvid of the ingress port and should not be used for 244 + * processing. 245 + */ 246 + vlan_tpid = tag_type ? ETH_P_8021AD : ETH_P_8021Q; 247 + 248 + if (ocelot->ports[src_port]->vlan_aware && 249 + eth_hdr(skb)->h_proto == htons(vlan_tpid)) { 250 + u16 dummy_vlan_tci; 251 + 252 + skb_push_rcsum(skb, ETH_HLEN); 253 + __skb_vlan_pop(skb, &dummy_vlan_tci); 254 + skb_pull_rcsum(skb, ETH_HLEN); 255 + __vlan_hwaccel_put_tag(skb, htons(vlan_tpid), vlan_tci); 256 + } 237 257 238 258 return skb; 239 259 }
+45 -2
tools/testing/selftests/drivers/net/ocelot/tc_flower_chains.sh
··· 166 166 ip link add link $eth3 name $eth3.100 type vlan id 100 167 167 ip link set $eth3.100 up 168 168 169 + ip link add link $eth3 name $eth3.200 type vlan id 200 170 + ip link set $eth3.200 up 171 + 169 172 tc filter add dev $eth0 ingress chain $(IS1 1) pref 1 \ 170 173 protocol 802.1Q flower skip_sw vlan_id 100 \ 171 174 action vlan pop \ ··· 178 175 flower skip_sw indev $eth1 \ 179 176 action vlan push protocol 802.1Q id 100 180 177 181 - tc filter add dev $eth0 ingress chain $(IS1 0) \ 178 + tc filter add dev $eth0 ingress chain $(IS1 0) pref 2 \ 182 179 protocol ipv4 flower skip_sw src_ip 10.1.1.2 \ 183 180 action skbedit priority 7 \ 184 181 action goto chain $(IS1 1) 185 182 186 - tc filter add dev $eth0 ingress chain $(IS2 0 0) \ 183 + tc filter add dev $eth0 ingress chain $(IS2 0 0) pref 1 \ 187 184 protocol ipv4 flower skip_sw ip_proto udp dst_port 5201 \ 188 185 action police rate 50mbit burst 64k \ 189 186 action goto chain $(IS2 1 0) ··· 191 188 192 189 cleanup() 193 190 { 191 + ip link del $eth3.200 194 192 ip link del $eth3.100 195 193 tc qdisc del dev $eth0 clsact 196 194 ip link del br0 ··· 242 238 tcpdump_cleanup 243 239 } 244 240 241 + test_vlan_modify() 242 + { 243 + printf "Testing VLAN modification.. " 244 + 245 + ip link set br0 type bridge vlan_filtering 1 246 + bridge vlan add dev $eth0 vid 200 247 + bridge vlan add dev $eth0 vid 300 248 + bridge vlan add dev $eth1 vid 300 249 + 250 + tc filter add dev $eth0 ingress chain $(IS1 2) pref 3 \ 251 + protocol 802.1Q flower skip_sw vlan_id 200 \ 252 + action vlan modify id 300 \ 253 + action goto chain $(IS2 0 0) 254 + 255 + tcpdump_start $eth2 256 + 257 + $MZ $eth3.200 -q -c 1 -p 64 -a $eth3_mac -b $eth2_mac -t ip 258 + 259 + sleep 1 260 + 261 + tcpdump_stop 262 + 263 + if tcpdump_show | grep -q "$eth3_mac > $eth2_mac, .* vlan 300"; then 264 + echo "OK" 265 + else 266 + echo "FAIL" 267 + fi 268 + 269 + tcpdump_cleanup 270 + 271 + tc filter del dev $eth0 ingress chain $(IS1 2) pref 3 272 + 273 + bridge vlan del dev $eth0 vid 200 274 + bridge vlan del dev $eth0 vid 300 275 + bridge vlan del dev $eth1 vid 300 276 + ip link set br0 type bridge vlan_filtering 0 277 + } 278 + 245 279 test_skbedit_priority() 246 280 { 247 281 local num_pkts=100 ··· 304 262 ALL_TESTS=" 305 263 test_vlan_pop 306 264 test_vlan_push 265 + test_vlan_modify 307 266 test_skbedit_priority 308 267 " 309 268