Skip to content

Commit

Permalink
Merge branch 'Offload-tc-vlan-mangle-to-mscc_ocelot-switch'
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jakub Kicinski committed Oct 11, 2020
2 parents bea4b30 + 82c200b commit bc081a6
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 6 deletions.
15 changes: 14 additions & 1 deletion drivers/net/ethernet/mscc/ocelot.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,21 @@ int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
struct ocelot_port *ocelot_port = ocelot->ports[port];
u32 val;

if (switchdev_trans_ph_prepare(trans))
if (switchdev_trans_ph_prepare(trans)) {
struct ocelot_vcap_block *block = &ocelot->block[VCAP_IS1];
struct ocelot_vcap_filter *filter;

list_for_each_entry(filter, &block->rules, list) {
if (filter->ingress_port_mask & BIT(port) &&
filter->action.vid_replace_ena) {
dev_err(ocelot->dev,
"Cannot change VLAN state with vlan modify rules active\n");
return -EBUSY;
}
}

return 0;
}

ocelot_port->vlan_aware = vlan_aware;

Expand Down
29 changes: 26 additions & 3 deletions drivers/net/ethernet/mscc/ocelot_flower.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,11 @@ ocelot_find_vcap_filter_that_points_at(struct ocelot *ocelot, int chain)
return NULL;
}

static int ocelot_flower_parse_action(struct ocelot *ocelot, bool ingress,
struct flow_cls_offload *f,
static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,
bool ingress, struct flow_cls_offload *f,
struct ocelot_vcap_filter *filter)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct netlink_ext_ack *extack = f->common.extack;
bool allow_missing_goto_target = false;
const struct flow_action_entry *a;
Expand Down Expand Up @@ -266,6 +267,28 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, bool ingress,
}
filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
break;
case FLOW_ACTION_VLAN_MANGLE:
if (filter->block_id != VCAP_IS1) {
NL_SET_ERR_MSG_MOD(extack,
"VLAN modify action can only be offloaded to VCAP IS1");
return -EOPNOTSUPP;
}
if (filter->goto_target != -1) {
NL_SET_ERR_MSG_MOD(extack,
"Last action must be GOTO");
return -EOPNOTSUPP;
}
if (!ocelot_port->vlan_aware) {
NL_SET_ERR_MSG_MOD(extack,
"Can only modify VLAN under VLAN aware bridge");
return -EOPNOTSUPP;
}
filter->action.vid_replace_ena = true;
filter->action.pcp_dei_ena = true;
filter->action.vid = a->vlan.vid;
filter->action.pcp = a->vlan.prio;
filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
break;
case FLOW_ACTION_PRIORITY:
if (filter->block_id != VCAP_IS1) {
NL_SET_ERR_MSG_MOD(extack,
Expand Down Expand Up @@ -601,7 +624,7 @@ static int ocelot_flower_parse(struct ocelot *ocelot, int port, bool ingress,
filter->prio = f->common.prio;
filter->id = f->cookie;

ret = ocelot_flower_parse_action(ocelot, ingress, f, filter);
ret = ocelot_flower_parse_action(ocelot, port, ingress, f, filter);
if (ret)
return ret;

Expand Down
34 changes: 34 additions & 0 deletions net/dsa/tag_ocelot.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,14 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
struct net_device *netdev,
struct packet_type *pt)
{
struct dsa_port *cpu_dp = netdev->dsa_ptr;
struct dsa_switch *ds = cpu_dp->ds;
struct ocelot *ocelot = ds->priv;
u64 src_port, qos_class;
u64 vlan_tci, tag_type;
u8 *start = skb->data;
u8 *extraction;
u16 vlan_tpid;

/* Revert skb->data by the amount consumed by the DSA master,
* so it points to the beginning of the frame.
Expand Down Expand Up @@ -214,6 +219,8 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb,

packing(extraction, &src_port, 46, 43, OCELOT_TAG_LEN, UNPACK, 0);
packing(extraction, &qos_class, 19, 17, OCELOT_TAG_LEN, UNPACK, 0);
packing(extraction, &tag_type, 16, 16, OCELOT_TAG_LEN, UNPACK, 0);
packing(extraction, &vlan_tci, 15, 0, OCELOT_TAG_LEN, UNPACK, 0);

skb->dev = dsa_master_find_slave(netdev, 0, src_port);
if (!skb->dev)
Expand All @@ -228,6 +235,33 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
skb->offload_fwd_mark = 1;
skb->priority = qos_class;

/* Ocelot switches copy frames unmodified to the CPU. However, it is
* possible for the user to request a VLAN modification through
* VCAP_IS1_ACT_VID_REPLACE_ENA. In this case, what will happen is that
* the VLAN ID field from the Extraction Header gets updated, but the
* 802.1Q header does not (the classified VLAN only becomes visible on
* egress through the "port tag" of front-panel ports).
* So, for traffic extracted by the CPU, we want to pick up the
* classified VLAN and manually replace the existing 802.1Q header from
* the packet with it, so that the operating system is always up to
* date with the result of tc-vlan actions.
* NOTE: In VLAN-unaware mode, we don't want to do that, we want the
* frame to remain unmodified, because the classified VLAN is always
* equal to the pvid of the ingress port and should not be used for
* processing.
*/
vlan_tpid = tag_type ? ETH_P_8021AD : ETH_P_8021Q;

if (ocelot->ports[src_port]->vlan_aware &&
eth_hdr(skb)->h_proto == htons(vlan_tpid)) {
u16 dummy_vlan_tci;

skb_push_rcsum(skb, ETH_HLEN);
__skb_vlan_pop(skb, &dummy_vlan_tci);
skb_pull_rcsum(skb, ETH_HLEN);
__vlan_hwaccel_put_tag(skb, htons(vlan_tpid), vlan_tci);
}

return skb;
}

Expand Down
47 changes: 45 additions & 2 deletions tools/testing/selftests/drivers/net/ocelot/tc_flower_chains.sh
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ setup_prepare()
ip link add link $eth3 name $eth3.100 type vlan id 100
ip link set $eth3.100 up

ip link add link $eth3 name $eth3.200 type vlan id 200
ip link set $eth3.200 up

tc filter add dev $eth0 ingress chain $(IS1 1) pref 1 \
protocol 802.1Q flower skip_sw vlan_id 100 \
action vlan pop \
Expand All @@ -175,19 +178,20 @@ setup_prepare()
flower skip_sw indev $eth1 \
action vlan push protocol 802.1Q id 100

tc filter add dev $eth0 ingress chain $(IS1 0) \
tc filter add dev $eth0 ingress chain $(IS1 0) pref 2 \
protocol ipv4 flower skip_sw src_ip 10.1.1.2 \
action skbedit priority 7 \
action goto chain $(IS1 1)

tc filter add dev $eth0 ingress chain $(IS2 0 0) \
tc filter add dev $eth0 ingress chain $(IS2 0 0) pref 1 \
protocol ipv4 flower skip_sw ip_proto udp dst_port 5201 \
action police rate 50mbit burst 64k \
action goto chain $(IS2 1 0)
}

cleanup()
{
ip link del $eth3.200
ip link del $eth3.100
tc qdisc del dev $eth0 clsact
ip link del br0
Expand Down Expand Up @@ -238,6 +242,44 @@ test_vlan_push()
tcpdump_cleanup
}

test_vlan_modify()
{
printf "Testing VLAN modification.. "

ip link set br0 type bridge vlan_filtering 1
bridge vlan add dev $eth0 vid 200
bridge vlan add dev $eth0 vid 300
bridge vlan add dev $eth1 vid 300

tc filter add dev $eth0 ingress chain $(IS1 2) pref 3 \
protocol 802.1Q flower skip_sw vlan_id 200 \
action vlan modify id 300 \
action goto chain $(IS2 0 0)

tcpdump_start $eth2

$MZ $eth3.200 -q -c 1 -p 64 -a $eth3_mac -b $eth2_mac -t ip

sleep 1

tcpdump_stop

if tcpdump_show | grep -q "$eth3_mac > $eth2_mac, .* vlan 300"; then
echo "OK"
else
echo "FAIL"
fi

tcpdump_cleanup

tc filter del dev $eth0 ingress chain $(IS1 2) pref 3

bridge vlan del dev $eth0 vid 200
bridge vlan del dev $eth0 vid 300
bridge vlan del dev $eth1 vid 300
ip link set br0 type bridge vlan_filtering 0
}

test_skbedit_priority()
{
local num_pkts=100
Expand All @@ -262,6 +304,7 @@ trap cleanup EXIT
ALL_TESTS="
test_vlan_pop
test_vlan_push
test_vlan_modify
test_skbedit_priority
"

Expand Down

0 comments on commit bc081a6

Please sign in to comment.