mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-30 13:34:27 -04:00 
			
		
		
		
	This series also contains other improvement for hardware flow offload support Signed-off-by: Felix Fietkau <nbd@nbd.name>
		
			
				
	
	
		
			554 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			554 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From: Felix Fietkau <nbd@nbd.name>
 | |
| Date: Wed, 23 Feb 2022 10:56:34 +0100
 | |
| Subject: [PATCH] net: ethernet: mtk_eth_soc: support creating mac
 | |
|  address based offload entries
 | |
| 
 | |
| This will be used to implement a limited form of bridge offloading.
 | |
| Since the hardware does not support flow table entries with just source
 | |
| and destination MAC address, the driver has to emulate it.
 | |
| 
 | |
| The hardware automatically creates entries entries for incoming flows, even
 | |
| when they are bridged instead of routed, and reports when packets for these
 | |
| flows have reached the minimum PPS rate for offloading.
 | |
| 
 | |
| After this happens, we look up the L2 flow offload entry based on the MAC
 | |
| header and fill in the output routing information in the flow table.
 | |
| The dynamically created per-flow entries are automatically removed when
 | |
| either the hardware flowtable entry expires, is replaced, or if the offload
 | |
| rule they belong to is removed
 | |
| 
 | |
| Signed-off-by: Felix Fietkau <nbd@nbd.name>
 | |
| ---
 | |
| 
 | |
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.c
 | |
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
 | |
| @@ -6,12 +6,22 @@
 | |
|  #include <linux/iopoll.h>
 | |
|  #include <linux/etherdevice.h>
 | |
|  #include <linux/platform_device.h>
 | |
| +#include <linux/if_ether.h>
 | |
| +#include <linux/if_vlan.h>
 | |
| +#include <net/dsa.h>
 | |
|  #include "mtk_eth_soc.h"
 | |
|  #include "mtk_ppe.h"
 | |
|  #include "mtk_ppe_regs.h"
 | |
|  
 | |
|  static DEFINE_SPINLOCK(ppe_lock);
 | |
|  
 | |
| +static const struct rhashtable_params mtk_flow_l2_ht_params = {
 | |
| +	.head_offset = offsetof(struct mtk_flow_entry, l2_node),
 | |
| +	.key_offset = offsetof(struct mtk_flow_entry, data.bridge),
 | |
| +	.key_len = offsetof(struct mtk_foe_bridge, key_end),
 | |
| +	.automatic_shrinking = true,
 | |
| +};
 | |
| +
 | |
|  static void ppe_w32(struct mtk_ppe *ppe, u32 reg, u32 val)
 | |
|  {
 | |
|  	writel(val, ppe->base + reg);
 | |
| @@ -123,6 +133,9 @@ mtk_foe_entry_l2(struct mtk_foe_entry *e
 | |
|  {
 | |
|  	int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
 | |
|  
 | |
| +	if (type == MTK_PPE_PKT_TYPE_BRIDGE)
 | |
| +		return &entry->bridge.l2;
 | |
| +
 | |
|  	if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
 | |
|  		return &entry->ipv6.l2;
 | |
|  
 | |
| @@ -134,6 +147,9 @@ mtk_foe_entry_ib2(struct mtk_foe_entry *
 | |
|  {
 | |
|  	int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
 | |
|  
 | |
| +	if (type == MTK_PPE_PKT_TYPE_BRIDGE)
 | |
| +		return &entry->bridge.ib2;
 | |
| +
 | |
|  	if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
 | |
|  		return &entry->ipv6.ib2;
 | |
|  
 | |
| @@ -168,7 +184,12 @@ int mtk_foe_entry_prepare(struct mtk_foe
 | |
|  	if (type == MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T)
 | |
|  		entry->ipv6.ports = ports_pad;
 | |
|  
 | |
| -	if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
 | |
| +	if (type == MTK_PPE_PKT_TYPE_BRIDGE) {
 | |
| +		ether_addr_copy(entry->bridge.src_mac, src_mac);
 | |
| +		ether_addr_copy(entry->bridge.dest_mac, dest_mac);
 | |
| +		entry->bridge.ib2 = val;
 | |
| +		l2 = &entry->bridge.l2;
 | |
| +	} else if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
 | |
|  		entry->ipv6.ib2 = val;
 | |
|  		l2 = &entry->ipv6.l2;
 | |
|  	} else {
 | |
| @@ -372,12 +393,96 @@ mtk_flow_entry_match(struct mtk_flow_ent
 | |
|  }
 | |
|  
 | |
|  static void
 | |
| +__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
 | |
| +{
 | |
| +	struct hlist_head *head;
 | |
| +	struct hlist_node *tmp;
 | |
| +
 | |
| +	if (entry->type == MTK_FLOW_TYPE_L2) {
 | |
| +		rhashtable_remove_fast(&ppe->l2_flows, &entry->l2_node,
 | |
| +				       mtk_flow_l2_ht_params);
 | |
| +
 | |
| +		head = &entry->l2_flows;
 | |
| +		hlist_for_each_entry_safe(entry, tmp, head, l2_data.list)
 | |
| +			__mtk_foe_entry_clear(ppe, entry);
 | |
| +		return;
 | |
| +	}
 | |
| +
 | |
| +	hlist_del_init(&entry->list);
 | |
| +	if (entry->hash != 0xffff) {
 | |
| +		ppe->foe_table[entry->hash].ib1 &= ~MTK_FOE_IB1_STATE;
 | |
| +		ppe->foe_table[entry->hash].ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE,
 | |
| +							      MTK_FOE_STATE_BIND);
 | |
| +		dma_wmb();
 | |
| +	}
 | |
| +	entry->hash = 0xffff;
 | |
| +
 | |
| +	if (entry->type != MTK_FLOW_TYPE_L2_SUBFLOW)
 | |
| +		return;
 | |
| +
 | |
| +	hlist_del_init(&entry->l2_data.list);
 | |
| +	kfree(entry);
 | |
| +}
 | |
| +
 | |
| +static int __mtk_foe_entry_idle_time(struct mtk_ppe *ppe, u32 ib1)
 | |
| +{
 | |
| +	u16 timestamp;
 | |
| +	u16 now;
 | |
| +
 | |
| +	now = mtk_eth_timestamp(ppe->eth) & MTK_FOE_IB1_BIND_TIMESTAMP;
 | |
| +	timestamp = ib1 & MTK_FOE_IB1_BIND_TIMESTAMP;
 | |
| +
 | |
| +	if (timestamp > now)
 | |
| +		return MTK_FOE_IB1_BIND_TIMESTAMP + 1 - timestamp + now;
 | |
| +	else
 | |
| +		return now - timestamp;
 | |
| +}
 | |
| +
 | |
| +static void
 | |
| +mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
 | |
| +{
 | |
| +	struct mtk_flow_entry *cur;
 | |
| +	struct mtk_foe_entry *hwe;
 | |
| +	struct hlist_node *tmp;
 | |
| +	int idle;
 | |
| +
 | |
| +	idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
 | |
| +	hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_data.list) {
 | |
| +		int cur_idle;
 | |
| +		u32 ib1;
 | |
| +
 | |
| +		hwe = &ppe->foe_table[cur->hash];
 | |
| +		ib1 = READ_ONCE(hwe->ib1);
 | |
| +
 | |
| +		if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND) {
 | |
| +			cur->hash = 0xffff;
 | |
| +			__mtk_foe_entry_clear(ppe, cur);
 | |
| +			continue;
 | |
| +		}
 | |
| +
 | |
| +		cur_idle = __mtk_foe_entry_idle_time(ppe, ib1);
 | |
| +		if (cur_idle >= idle)
 | |
| +			continue;
 | |
| +
 | |
| +		idle = cur_idle;
 | |
| +		entry->data.ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP;
 | |
| +		entry->data.ib1 |= hwe->ib1 & MTK_FOE_IB1_BIND_TIMESTAMP;
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +static void
 | |
|  mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
 | |
|  {
 | |
|  	struct mtk_foe_entry *hwe;
 | |
|  	struct mtk_foe_entry foe;
 | |
|  
 | |
|  	spin_lock_bh(&ppe_lock);
 | |
| +
 | |
| +	if (entry->type == MTK_FLOW_TYPE_L2) {
 | |
| +		mtk_flow_entry_update_l2(ppe, entry);
 | |
| +		goto out;
 | |
| +	}
 | |
| +
 | |
|  	if (entry->hash == 0xffff)
 | |
|  		goto out;
 | |
|  
 | |
| @@ -419,21 +524,28 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
 | |
|  void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
 | |
|  {
 | |
|  	spin_lock_bh(&ppe_lock);
 | |
| -	hlist_del_init(&entry->list);
 | |
| -	if (entry->hash != 0xffff) {
 | |
| -		ppe->foe_table[entry->hash].ib1 &= ~MTK_FOE_IB1_STATE;
 | |
| -		ppe->foe_table[entry->hash].ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE,
 | |
| -							      MTK_FOE_STATE_BIND);
 | |
| -		dma_wmb();
 | |
| -	}
 | |
| -	entry->hash = 0xffff;
 | |
| +	__mtk_foe_entry_clear(ppe, entry);
 | |
|  	spin_unlock_bh(&ppe_lock);
 | |
|  }
 | |
|  
 | |
| +static int
 | |
| +mtk_foe_entry_commit_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
 | |
| +{
 | |
| +	entry->type = MTK_FLOW_TYPE_L2;
 | |
| +
 | |
| +	return rhashtable_insert_fast(&ppe->l2_flows, &entry->l2_node,
 | |
| +				      mtk_flow_l2_ht_params);
 | |
| +}
 | |
| +
 | |
|  int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
 | |
|  {
 | |
| -	u32 hash = mtk_ppe_hash_entry(&entry->data);
 | |
| +	int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->data.ib1);
 | |
| +	u32 hash;
 | |
| +
 | |
| +	if (type == MTK_PPE_PKT_TYPE_BRIDGE)
 | |
| +		return mtk_foe_entry_commit_l2(ppe, entry);
 | |
|  
 | |
| +	hash = mtk_ppe_hash_entry(&entry->data);
 | |
|  	entry->hash = 0xffff;
 | |
|  	spin_lock_bh(&ppe_lock);
 | |
|  	hlist_add_head(&entry->list, &ppe->foe_flow[hash / 2]);
 | |
| @@ -442,18 +554,72 @@ int mtk_foe_entry_commit(struct mtk_ppe
 | |
|  	return 0;
 | |
|  }
 | |
|  
 | |
| +static void
 | |
| +mtk_foe_entry_commit_subflow(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
 | |
| +			     u16 hash)
 | |
| +{
 | |
| +	struct mtk_flow_entry *flow_info;
 | |
| +	struct mtk_foe_entry foe, *hwe;
 | |
| +	struct mtk_foe_mac_info *l2;
 | |
| +	u32 ib1_mask = MTK_FOE_IB1_PACKET_TYPE | MTK_FOE_IB1_UDP;
 | |
| +	int type;
 | |
| +
 | |
| +	flow_info = kzalloc(offsetof(struct mtk_flow_entry, l2_data.end),
 | |
| +			    GFP_ATOMIC);
 | |
| +	if (!flow_info)
 | |
| +		return;
 | |
| +
 | |
| +	flow_info->l2_data.base_flow = entry;
 | |
| +	flow_info->type = MTK_FLOW_TYPE_L2_SUBFLOW;
 | |
| +	flow_info->hash = hash;
 | |
| +	hlist_add_head(&flow_info->list, &ppe->foe_flow[hash / 2]);
 | |
| +	hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows);
 | |
| +
 | |
| +	hwe = &ppe->foe_table[hash];
 | |
| +	memcpy(&foe, hwe, sizeof(foe));
 | |
| +	foe.ib1 &= ib1_mask;
 | |
| +	foe.ib1 |= entry->data.ib1 & ~ib1_mask;
 | |
| +
 | |
| +	l2 = mtk_foe_entry_l2(&foe);
 | |
| +	memcpy(l2, &entry->data.bridge.l2, sizeof(*l2));
 | |
| +
 | |
| +	type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, foe.ib1);
 | |
| +	if (type == MTK_PPE_PKT_TYPE_IPV4_HNAPT)
 | |
| +		memcpy(&foe.ipv4.new, &foe.ipv4.orig, sizeof(foe.ipv4.new));
 | |
| +	else if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T && l2->etype == ETH_P_IP)
 | |
| +		l2->etype = ETH_P_IPV6;
 | |
| +
 | |
| +	*mtk_foe_entry_ib2(&foe) = entry->data.bridge.ib2;
 | |
| +
 | |
| +	__mtk_foe_entry_commit(ppe, &foe, hash);
 | |
| +}
 | |
| +
 | |
|  void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash)
 | |
|  {
 | |
|  	struct hlist_head *head = &ppe->foe_flow[hash / 2];
 | |
| -	struct mtk_flow_entry *entry;
 | |
|  	struct mtk_foe_entry *hwe = &ppe->foe_table[hash];
 | |
| +	struct mtk_flow_entry *entry;
 | |
| +	struct mtk_foe_bridge key = {};
 | |
| +	struct ethhdr *eh;
 | |
|  	bool found = false;
 | |
| -
 | |
| -	if (hlist_empty(head))
 | |
| -		return;
 | |
| +	u8 *tag;
 | |
|  
 | |
|  	spin_lock_bh(&ppe_lock);
 | |
| +
 | |
| +	if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND)
 | |
| +		goto out;
 | |
| +
 | |
|  	hlist_for_each_entry(entry, head, list) {
 | |
| +		if (entry->type == MTK_FLOW_TYPE_L2_SUBFLOW) {
 | |
| +			if (unlikely(FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) ==
 | |
| +				     MTK_FOE_STATE_BIND))
 | |
| +				continue;
 | |
| +
 | |
| +			entry->hash = 0xffff;
 | |
| +			__mtk_foe_entry_clear(ppe, entry);
 | |
| +			continue;
 | |
| +		}
 | |
| +
 | |
|  		if (found || !mtk_flow_entry_match(entry, hwe)) {
 | |
|  			if (entry->hash != 0xffff)
 | |
|  				entry->hash = 0xffff;
 | |
| @@ -464,21 +630,50 @@ void __mtk_ppe_check_skb(struct mtk_ppe
 | |
|  		__mtk_foe_entry_commit(ppe, &entry->data, hash);
 | |
|  		found = true;
 | |
|  	}
 | |
| +
 | |
| +	if (found)
 | |
| +		goto out;
 | |
| +
 | |
| +	eh = eth_hdr(skb);
 | |
| +	ether_addr_copy(key.dest_mac, eh->h_dest);
 | |
| +	ether_addr_copy(key.src_mac, eh->h_source);
 | |
| +	tag = skb->data - 2;
 | |
| +	key.vlan = 0;
 | |
| +	switch (skb->protocol) {
 | |
| +#if IS_ENABLED(CONFIG_NET_DSA)
 | |
| +	case htons(ETH_P_XDSA):
 | |
| +		if (!netdev_uses_dsa(skb->dev) ||
 | |
| +		    skb->dev->dsa_ptr->tag_ops->proto != DSA_TAG_PROTO_MTK)
 | |
| +			goto out;
 | |
| +
 | |
| +		tag += 4;
 | |
| +		if (get_unaligned_be16(tag) != ETH_P_8021Q)
 | |
| +			break;
 | |
| +
 | |
| +		fallthrough;
 | |
| +#endif
 | |
| +	case htons(ETH_P_8021Q):
 | |
| +		key.vlan = get_unaligned_be16(tag + 2) & VLAN_VID_MASK;
 | |
| +		break;
 | |
| +	default:
 | |
| +		break;
 | |
| +	}
 | |
| +
 | |
| +	entry = rhashtable_lookup_fast(&ppe->l2_flows, &key, mtk_flow_l2_ht_params);
 | |
| +	if (!entry)
 | |
| +		goto out;
 | |
| +
 | |
| +	mtk_foe_entry_commit_subflow(ppe, entry, hash);
 | |
| +
 | |
| +out:
 | |
|  	spin_unlock_bh(&ppe_lock);
 | |
|  }
 | |
|  
 | |
|  int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
 | |
|  {
 | |
| -	u16 now = mtk_eth_timestamp(ppe->eth) & MTK_FOE_IB1_BIND_TIMESTAMP;
 | |
| -	u16 timestamp;
 | |
| -
 | |
|  	mtk_flow_entry_update(ppe, entry);
 | |
| -	timestamp = entry->data.ib1 & MTK_FOE_IB1_BIND_TIMESTAMP;
 | |
|  
 | |
| -	if (timestamp > now)
 | |
| -		return MTK_FOE_IB1_BIND_TIMESTAMP + 1 - timestamp + now;
 | |
| -	else
 | |
| -		return now - timestamp;
 | |
| +	return __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
 | |
|  }
 | |
|  
 | |
|  struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
 | |
| @@ -492,6 +687,8 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
 | |
|  	if (!ppe)
 | |
|  		return NULL;
 | |
|  
 | |
| +	rhashtable_init(&ppe->l2_flows, &mtk_flow_l2_ht_params);
 | |
| +
 | |
|  	/* need to allocate a separate device, since it PPE DMA access is
 | |
|  	 * not coherent.
 | |
|  	 */
 | |
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.h
 | |
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
 | |
| @@ -6,6 +6,7 @@
 | |
|  
 | |
|  #include <linux/kernel.h>
 | |
|  #include <linux/bitfield.h>
 | |
| +#include <linux/rhashtable.h>
 | |
|  
 | |
|  #define MTK_ETH_PPE_BASE		0xc00
 | |
|  
 | |
| @@ -84,19 +85,16 @@ struct mtk_foe_mac_info {
 | |
|  	u16 src_mac_lo;
 | |
|  };
 | |
|  
 | |
| +/* software-only entry type */
 | |
|  struct mtk_foe_bridge {
 | |
| -	u32 dest_mac_hi;
 | |
| -
 | |
| -	u16 src_mac_lo;
 | |
| -	u16 dest_mac_lo;
 | |
| +	u8 dest_mac[ETH_ALEN];
 | |
| +	u8 src_mac[ETH_ALEN];
 | |
| +	u16 vlan;
 | |
|  
 | |
| -	u32 src_mac_hi;
 | |
| +	struct {} key_end;
 | |
|  
 | |
|  	u32 ib2;
 | |
|  
 | |
| -	u32 _rsv[5];
 | |
| -
 | |
| -	u32 udf_tsid;
 | |
|  	struct mtk_foe_mac_info l2;
 | |
|  };
 | |
|  
 | |
| @@ -235,13 +233,33 @@ enum {
 | |
|  	MTK_PPE_CPU_REASON_INVALID			= 0x1f,
 | |
|  };
 | |
|  
 | |
| +enum {
 | |
| +	MTK_FLOW_TYPE_L4,
 | |
| +	MTK_FLOW_TYPE_L2,
 | |
| +	MTK_FLOW_TYPE_L2_SUBFLOW,
 | |
| +};
 | |
| +
 | |
|  struct mtk_flow_entry {
 | |
| +	union {
 | |
| +		struct hlist_node list;
 | |
| +		struct {
 | |
| +			struct rhash_head l2_node;
 | |
| +			struct hlist_head l2_flows;
 | |
| +		};
 | |
| +	};
 | |
| +	u8 type;
 | |
| +	s8 wed_index;
 | |
| +	u16 hash;
 | |
| +	union {
 | |
| +		struct mtk_foe_entry data;
 | |
| +		struct {
 | |
| +			struct mtk_flow_entry *base_flow;
 | |
| +			struct hlist_node list;
 | |
| +			struct {} end;
 | |
| +		} l2_data;
 | |
| +	};
 | |
|  	struct rhash_head node;
 | |
| -	struct hlist_node list;
 | |
|  	unsigned long cookie;
 | |
| -	struct mtk_foe_entry data;
 | |
| -	u16 hash;
 | |
| -	s8 wed_index;
 | |
|  };
 | |
|  
 | |
|  struct mtk_ppe {
 | |
| @@ -256,6 +274,8 @@ struct mtk_ppe {
 | |
|  	u16 foe_check_time[MTK_PPE_ENTRIES];
 | |
|  	struct hlist_head foe_flow[MTK_PPE_ENTRIES / 2];
 | |
|  
 | |
| +	struct rhashtable l2_flows;
 | |
| +
 | |
|  	void *acct_table;
 | |
|  };
 | |
|  
 | |
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 | |
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 | |
| @@ -32,6 +32,8 @@ struct mtk_flow_data {
 | |
|  	__be16 src_port;
 | |
|  	__be16 dst_port;
 | |
|  
 | |
| +	u16 vlan_in;
 | |
| +
 | |
|  	struct {
 | |
|  		u16 id;
 | |
|  		__be16 proto;
 | |
| @@ -258,9 +260,45 @@ mtk_flow_offload_replace(struct mtk_eth
 | |
|  		return -EOPNOTSUPP;
 | |
|  	}
 | |
|  
 | |
| +	switch (addr_type) {
 | |
| +	case 0:
 | |
| +		offload_type = MTK_PPE_PKT_TYPE_BRIDGE;
 | |
| +		if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
 | |
| +			struct flow_match_eth_addrs match;
 | |
| +
 | |
| +			flow_rule_match_eth_addrs(rule, &match);
 | |
| +			memcpy(data.eth.h_dest, match.key->dst, ETH_ALEN);
 | |
| +			memcpy(data.eth.h_source, match.key->src, ETH_ALEN);
 | |
| +		} else {
 | |
| +			return -EOPNOTSUPP;
 | |
| +		}
 | |
| +
 | |
| +		if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
 | |
| +			struct flow_match_vlan match;
 | |
| +
 | |
| +			flow_rule_match_vlan(rule, &match);
 | |
| +
 | |
| +			if (match.key->vlan_tpid != cpu_to_be16(ETH_P_8021Q))
 | |
| +				return -EOPNOTSUPP;
 | |
| +
 | |
| +			data.vlan_in = match.key->vlan_id;
 | |
| +		}
 | |
| +		break;
 | |
| +	case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
 | |
| +		offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
 | |
| +		break;
 | |
| +	case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
 | |
| +		offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T;
 | |
| +		break;
 | |
| +	default:
 | |
| +		return -EOPNOTSUPP;
 | |
| +	}
 | |
| +
 | |
|  	flow_action_for_each(i, act, &rule->action) {
 | |
|  		switch (act->id) {
 | |
|  		case FLOW_ACTION_MANGLE:
 | |
| +			if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
 | |
| +				return -EOPNOTSUPP;
 | |
|  			if (act->mangle.htype == FLOW_ACT_MANGLE_HDR_TYPE_ETH)
 | |
|  				mtk_flow_offload_mangle_eth(act, &data.eth);
 | |
|  			break;
 | |
| @@ -292,17 +330,6 @@ mtk_flow_offload_replace(struct mtk_eth
 | |
|  		}
 | |
|  	}
 | |
|  
 | |
| -	switch (addr_type) {
 | |
| -	case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
 | |
| -		offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
 | |
| -		break;
 | |
| -	case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
 | |
| -		offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T;
 | |
| -		break;
 | |
| -	default:
 | |
| -		return -EOPNOTSUPP;
 | |
| -	}
 | |
| -
 | |
|  	if (!is_valid_ether_addr(data.eth.h_source) ||
 | |
|  	    !is_valid_ether_addr(data.eth.h_dest))
 | |
|  		return -EINVAL;
 | |
| @@ -316,10 +343,13 @@ mtk_flow_offload_replace(struct mtk_eth
 | |
|  	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
 | |
|  		struct flow_match_ports ports;
 | |
|  
 | |
| +		if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
 | |
| +			return -EOPNOTSUPP;
 | |
| +
 | |
|  		flow_rule_match_ports(rule, &ports);
 | |
|  		data.src_port = ports.key->src;
 | |
|  		data.dst_port = ports.key->dst;
 | |
| -	} else {
 | |
| +	} else if (offload_type != MTK_PPE_PKT_TYPE_BRIDGE) {
 | |
|  		return -EOPNOTSUPP;
 | |
|  	}
 | |
|  
 | |
| @@ -349,6 +379,9 @@ mtk_flow_offload_replace(struct mtk_eth
 | |
|  		if (act->id != FLOW_ACTION_MANGLE)
 | |
|  			continue;
 | |
|  
 | |
| +		if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
 | |
| +			return -EOPNOTSUPP;
 | |
| +
 | |
|  		switch (act->mangle.htype) {
 | |
|  		case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
 | |
|  		case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
 | |
| @@ -374,6 +407,9 @@ mtk_flow_offload_replace(struct mtk_eth
 | |
|  			return err;
 | |
|  	}
 | |
|  
 | |
| +	if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
 | |
| +		foe.bridge.vlan = data.vlan_in;
 | |
| +
 | |
|  	if (data.vlan.num == 1) {
 | |
|  		if (data.vlan.proto != htons(ETH_P_8021Q))
 | |
|  			return -EOPNOTSUPP;
 |