kernel: improve mtk ppe flow accounting
Properly track L2 flows, and ensure that stale data gets cleared Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
		| @@ -0,0 +1,314 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Thu, 23 Mar 2023 10:24:11 +0100 | ||||
| Subject: [PATCH] net: ethernet: mtk_eth_soc: improve keeping track of | ||||
|  offloaded flows | ||||
|  | ||||
| Unify tracking of L2 and L3 flows. Use the generic list field in struct | ||||
| mtk_foe_entry for tracking L2 subflows. Preparation for improving | ||||
| flow accounting support. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| @@ -466,26 +466,30 @@ int mtk_foe_entry_set_queue(struct mtk_e | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static int | ||||
| +mtk_flow_entry_match_len(struct mtk_eth *eth, struct mtk_foe_entry *entry) | ||||
| +{ | ||||
| +	int type = mtk_get_ib1_pkt_type(eth, entry->ib1); | ||||
| + | ||||
| +	if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE) | ||||
| +		return offsetof(struct mtk_foe_entry, ipv6._rsv); | ||||
| +	else | ||||
| +		return offsetof(struct mtk_foe_entry, ipv4.ib2); | ||||
| +} | ||||
| + | ||||
|  static bool | ||||
|  mtk_flow_entry_match(struct mtk_eth *eth, struct mtk_flow_entry *entry, | ||||
| -		     struct mtk_foe_entry *data) | ||||
| +		     struct mtk_foe_entry *data, int len) | ||||
|  { | ||||
| -	int type, len; | ||||
| - | ||||
|  	if ((data->ib1 ^ entry->data.ib1) & MTK_FOE_IB1_UDP) | ||||
|  		return false; | ||||
|   | ||||
| -	type = mtk_get_ib1_pkt_type(eth, entry->data.ib1); | ||||
| -	if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE) | ||||
| -		len = offsetof(struct mtk_foe_entry, ipv6._rsv); | ||||
| -	else | ||||
| -		len = offsetof(struct mtk_foe_entry, ipv4.ib2); | ||||
| - | ||||
|  	return !memcmp(&entry->data.data, &data->data, len - 4); | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| -__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) | ||||
| +__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, | ||||
| +		      bool set_state) | ||||
|  { | ||||
|  	struct hlist_head *head; | ||||
|  	struct hlist_node *tmp; | ||||
| @@ -495,13 +499,12 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp | ||||
|  				       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); | ||||
| +		hlist_for_each_entry_safe(entry, tmp, head, list) | ||||
| +			__mtk_foe_entry_clear(ppe, entry, set_state); | ||||
|  		return; | ||||
|  	} | ||||
|   | ||||
| -	hlist_del_init(&entry->list); | ||||
| -	if (entry->hash != 0xffff) { | ||||
| +	if (entry->hash != 0xffff && set_state) { | ||||
|  		struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash); | ||||
|   | ||||
|  		hwe->ib1 &= ~MTK_FOE_IB1_STATE; | ||||
| @@ -520,7 +523,7 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp | ||||
|  	if (entry->type != MTK_FLOW_TYPE_L2_SUBFLOW) | ||||
|  		return; | ||||
|   | ||||
| -	hlist_del_init(&entry->l2_data.list); | ||||
| +	hlist_del_init(&entry->list); | ||||
|  	kfree(entry); | ||||
|  } | ||||
|   | ||||
| @@ -536,66 +539,55 @@ static int __mtk_foe_entry_idle_time(str | ||||
|  		return now - timestamp; | ||||
|  } | ||||
|   | ||||
| +static bool | ||||
| +mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) | ||||
| +{ | ||||
| +	struct mtk_foe_entry foe = {}; | ||||
| +	struct mtk_foe_entry *hwe; | ||||
| +	u16 hash = entry->hash; | ||||
| +	int len; | ||||
| + | ||||
| +	if (hash == 0xffff) | ||||
| +		return false; | ||||
| + | ||||
| +	hwe = mtk_foe_get_entry(ppe, hash); | ||||
| +	len = mtk_flow_entry_match_len(ppe->eth, &entry->data); | ||||
| +	memcpy(&foe, hwe, len); | ||||
| + | ||||
| +	if (!mtk_flow_entry_match(ppe->eth, entry, &foe, len) || | ||||
| +	    FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND) | ||||
| +		return false; | ||||
| + | ||||
| +	entry->data.ib1 = foe.ib1; | ||||
| + | ||||
| +	return true; | ||||
| +} | ||||
| + | ||||
|  static void | ||||
|  mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) | ||||
|  { | ||||
|  	u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth); | ||||
|  	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) { | ||||
| +	hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, list) { | ||||
|  		int cur_idle; | ||||
| -		u32 ib1; | ||||
| - | ||||
| -		hwe = mtk_foe_get_entry(ppe, 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); | ||||
| +		if (!mtk_flow_entry_update(ppe, cur)) { | ||||
| +			__mtk_foe_entry_clear(ppe, entry, false); | ||||
|  			continue; | ||||
|  		} | ||||
|   | ||||
| -		cur_idle = __mtk_foe_entry_idle_time(ppe, ib1); | ||||
| +		cur_idle = __mtk_foe_entry_idle_time(ppe, cur->data.ib1); | ||||
|  		if (cur_idle >= idle) | ||||
|  			continue; | ||||
|   | ||||
|  		idle = cur_idle; | ||||
|  		entry->data.ib1 &= ~ib1_ts_mask; | ||||
| -		entry->data.ib1 |= hwe->ib1 & ib1_ts_mask; | ||||
| -	} | ||||
| -} | ||||
| - | ||||
| -static void | ||||
| -mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) | ||||
| -{ | ||||
| -	struct mtk_foe_entry foe = {}; | ||||
| -	struct mtk_foe_entry *hwe; | ||||
| - | ||||
| -	spin_lock_bh(&ppe_lock); | ||||
| - | ||||
| -	if (entry->type == MTK_FLOW_TYPE_L2) { | ||||
| -		mtk_flow_entry_update_l2(ppe, entry); | ||||
| -		goto out; | ||||
| +		entry->data.ib1 |= cur->data.ib1 & ib1_ts_mask; | ||||
|  	} | ||||
| - | ||||
| -	if (entry->hash == 0xffff) | ||||
| -		goto out; | ||||
| - | ||||
| -	hwe = mtk_foe_get_entry(ppe, entry->hash); | ||||
| -	memcpy(&foe, hwe, ppe->eth->soc->foe_entry_size); | ||||
| -	if (!mtk_flow_entry_match(ppe->eth, entry, &foe)) { | ||||
| -		entry->hash = 0xffff; | ||||
| -		goto out; | ||||
| -	} | ||||
| - | ||||
| -	entry->data.ib1 = foe.ib1; | ||||
| - | ||||
| -out: | ||||
| -	spin_unlock_bh(&ppe_lock); | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| @@ -632,7 +624,8 @@ __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); | ||||
| -	__mtk_foe_entry_clear(ppe, entry); | ||||
| +	__mtk_foe_entry_clear(ppe, entry, true); | ||||
| +	hlist_del_init(&entry->list); | ||||
|  	spin_unlock_bh(&ppe_lock); | ||||
|  } | ||||
|   | ||||
| @@ -679,8 +672,8 @@ mtk_foe_entry_commit_subflow(struct mtk_ | ||||
|  { | ||||
|  	const struct mtk_soc_data *soc = ppe->eth->soc; | ||||
|  	struct mtk_flow_entry *flow_info; | ||||
| -	struct mtk_foe_entry foe = {}, *hwe; | ||||
|  	struct mtk_foe_mac_info *l2; | ||||
| +	struct mtk_foe_entry *hwe; | ||||
|  	u32 ib1_mask = mtk_get_ib1_pkt_type_mask(ppe->eth) | MTK_FOE_IB1_UDP; | ||||
|  	int type; | ||||
|   | ||||
| @@ -688,30 +681,30 @@ mtk_foe_entry_commit_subflow(struct mtk_ | ||||
|  	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 / soc->hash_offset]); | ||||
| -	hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows); | ||||
| +	hlist_add_head(&flow_info->list, &entry->l2_flows); | ||||
|   | ||||
|  	hwe = mtk_foe_get_entry(ppe, hash); | ||||
| -	memcpy(&foe, hwe, soc->foe_entry_size); | ||||
| -	foe.ib1 &= ib1_mask; | ||||
| -	foe.ib1 |= entry->data.ib1 & ~ib1_mask; | ||||
| +	memcpy(&flow_info->data, hwe, soc->foe_entry_size); | ||||
| +	flow_info->data.ib1 &= ib1_mask; | ||||
| +	flow_info->data.ib1 |= entry->data.ib1 & ~ib1_mask; | ||||
|   | ||||
| -	l2 = mtk_foe_entry_l2(ppe->eth, &foe); | ||||
| +	l2 = mtk_foe_entry_l2(ppe->eth, &flow_info->data); | ||||
|  	memcpy(l2, &entry->data.bridge.l2, sizeof(*l2)); | ||||
|   | ||||
| -	type = mtk_get_ib1_pkt_type(ppe->eth, foe.ib1); | ||||
| +	type = mtk_get_ib1_pkt_type(ppe->eth, flow_info->data.ib1); | ||||
|  	if (type == MTK_PPE_PKT_TYPE_IPV4_HNAPT) | ||||
| -		memcpy(&foe.ipv4.new, &foe.ipv4.orig, sizeof(foe.ipv4.new)); | ||||
| +		memcpy(&flow_info->data.ipv4.new, &flow_info->data.ipv4.orig, | ||||
| +		       sizeof(flow_info->data.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(ppe->eth, &foe) = entry->data.bridge.ib2; | ||||
| +	*mtk_foe_entry_ib2(ppe->eth, &flow_info->data) = entry->data.bridge.ib2; | ||||
|   | ||||
| -	__mtk_foe_entry_commit(ppe, &foe, hash); | ||||
| +	__mtk_foe_entry_commit(ppe, &flow_info->data, hash); | ||||
|  } | ||||
|   | ||||
|  void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) | ||||
| @@ -721,9 +714,11 @@ void __mtk_ppe_check_skb(struct mtk_ppe | ||||
|  	struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, hash); | ||||
|  	struct mtk_flow_entry *entry; | ||||
|  	struct mtk_foe_bridge key = {}; | ||||
| +	struct mtk_foe_entry foe = {}; | ||||
|  	struct hlist_node *n; | ||||
|  	struct ethhdr *eh; | ||||
|  	bool found = false; | ||||
| +	int entry_len; | ||||
|  	u8 *tag; | ||||
|   | ||||
|  	spin_lock_bh(&ppe_lock); | ||||
| @@ -731,20 +726,14 @@ void __mtk_ppe_check_skb(struct mtk_ppe | ||||
|  	if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND) | ||||
|  		goto out; | ||||
|   | ||||
| -	hlist_for_each_entry_safe(entry, n, 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; | ||||
| -		} | ||||
| +	entry_len = mtk_flow_entry_match_len(ppe->eth, hwe); | ||||
| +	memcpy(&foe, hwe, entry_len); | ||||
|   | ||||
| -		if (found || !mtk_flow_entry_match(ppe->eth, entry, hwe)) { | ||||
| +	hlist_for_each_entry_safe(entry, n, head, list) { | ||||
| +		if (found || | ||||
| +		    !mtk_flow_entry_match(ppe->eth, entry, &foe, entry_len)) { | ||||
|  			if (entry->hash != 0xffff) | ||||
| -				entry->hash = 0xffff; | ||||
| +				__mtk_foe_entry_clear(ppe, entry, false); | ||||
|  			continue; | ||||
|  		} | ||||
|   | ||||
| @@ -795,9 +784,17 @@ out: | ||||
|   | ||||
|  int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) | ||||
|  { | ||||
| -	mtk_flow_entry_update(ppe, entry); | ||||
| +	int idle; | ||||
| + | ||||
| +	spin_lock_bh(&ppe_lock); | ||||
| +	if (entry->type == MTK_FLOW_TYPE_L2) | ||||
| +		mtk_flow_entry_update_l2(ppe, entry); | ||||
| +	else | ||||
| +		mtk_flow_entry_update(ppe, entry); | ||||
| +	idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1); | ||||
| +	spin_unlock_bh(&ppe_lock); | ||||
|   | ||||
| -	return __mtk_foe_entry_idle_time(ppe, entry->data.ib1); | ||||
| +	return idle; | ||||
|  } | ||||
|   | ||||
|  int mtk_ppe_prepare_reset(struct mtk_ppe *ppe) | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| @@ -275,13 +275,7 @@ struct mtk_flow_entry { | ||||
|  	s8 wed_index; | ||||
|  	u8 ppe_index; | ||||
|  	u16 hash; | ||||
| -	union { | ||||
| -		struct mtk_foe_entry data; | ||||
| -		struct { | ||||
| -			struct mtk_flow_entry *base_flow; | ||||
| -			struct hlist_node list; | ||||
| -		} l2_data; | ||||
| -	}; | ||||
| +	struct mtk_foe_entry data; | ||||
|  	struct rhash_head node; | ||||
|  	unsigned long cookie; | ||||
|  }; | ||||
| @@ -0,0 +1,320 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Thu, 23 Mar 2023 11:05:22 +0100 | ||||
| Subject: [PATCH] net: ethernet: mediatek: fix ppe flow accounting for L2 | ||||
|  flows | ||||
|  | ||||
| For L2 flows, the packet/byte counters should report the sum of the | ||||
| counters of their subflows, both current and expired. | ||||
| In order to make this work, change the way that accounting data is tracked. | ||||
| Reset counters when a flow enters bind. Once it expires (or enters unbind), | ||||
| store the last counter value in struct mtk_flow_entry. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| @@ -80,9 +80,9 @@ static int mtk_ppe_mib_wait_busy(struct | ||||
|  	int ret; | ||||
|  	u32 val; | ||||
|   | ||||
| -	ret = readl_poll_timeout(ppe->base + MTK_PPE_MIB_SER_CR, val, | ||||
| -				 !(val & MTK_PPE_MIB_SER_CR_ST), | ||||
| -				 20, MTK_PPE_WAIT_TIMEOUT_US); | ||||
| +	ret = readl_poll_timeout_atomic(ppe->base + MTK_PPE_MIB_SER_CR, val, | ||||
| +					!(val & MTK_PPE_MIB_SER_CR_ST), | ||||
| +					20, MTK_PPE_WAIT_TIMEOUT_US); | ||||
|   | ||||
|  	if (ret) | ||||
|  		dev_err(ppe->dev, "MIB table busy"); | ||||
| @@ -90,18 +90,32 @@ static int mtk_ppe_mib_wait_busy(struct | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| -static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets) | ||||
| +static inline struct mtk_foe_accounting * | ||||
| +mtk_ppe_acct_data(struct mtk_ppe *ppe, u16 index) | ||||
| +{ | ||||
| +	if (!ppe->acct_table) | ||||
| +		return NULL; | ||||
| + | ||||
| +	return ppe->acct_table + index * sizeof(struct mtk_foe_accounting); | ||||
| +} | ||||
| + | ||||
| +struct mtk_foe_accounting *mtk_ppe_mib_entry_read(struct mtk_ppe *ppe, u16 index) | ||||
|  { | ||||
|  	u32 byte_cnt_low, byte_cnt_high, pkt_cnt_low, pkt_cnt_high; | ||||
|  	u32 val, cnt_r0, cnt_r1, cnt_r2; | ||||
| +	struct mtk_foe_accounting *acct; | ||||
|  	int ret; | ||||
|   | ||||
|  	val = FIELD_PREP(MTK_PPE_MIB_SER_CR_ADDR, index) | MTK_PPE_MIB_SER_CR_ST; | ||||
|  	ppe_w32(ppe, MTK_PPE_MIB_SER_CR, val); | ||||
|   | ||||
| +	acct = mtk_ppe_acct_data(ppe, index); | ||||
| +	if (!acct) | ||||
| +		return NULL; | ||||
| + | ||||
|  	ret = mtk_ppe_mib_wait_busy(ppe); | ||||
|  	if (ret) | ||||
| -		return ret; | ||||
| +		return acct; | ||||
|   | ||||
|  	cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0); | ||||
|  	cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1); | ||||
| @@ -111,10 +125,11 @@ static int mtk_mib_entry_read(struct mtk | ||||
|  	byte_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH, cnt_r1); | ||||
|  	pkt_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R1_PKT_CNT_LOW, cnt_r1); | ||||
|  	pkt_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH, cnt_r2); | ||||
| -	*bytes = ((u64)byte_cnt_high << 32) | byte_cnt_low; | ||||
| -	*packets = (pkt_cnt_high << 16) | pkt_cnt_low; | ||||
|   | ||||
| -	return 0; | ||||
| +	acct->bytes += ((u64)byte_cnt_high << 32) | byte_cnt_low; | ||||
| +	acct->packets += (pkt_cnt_high << 16) | pkt_cnt_low; | ||||
| + | ||||
| +	return acct; | ||||
|  } | ||||
|   | ||||
|  static void mtk_ppe_cache_clear(struct mtk_ppe *ppe) | ||||
| @@ -510,13 +525,6 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp | ||||
|  		hwe->ib1 &= ~MTK_FOE_IB1_STATE; | ||||
|  		hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID); | ||||
|  		dma_wmb(); | ||||
| -		if (ppe->accounting) { | ||||
| -			struct mtk_foe_accounting *acct; | ||||
| - | ||||
| -			acct = ppe->acct_table + entry->hash * sizeof(*acct); | ||||
| -			acct->packets = 0; | ||||
| -			acct->bytes = 0; | ||||
| -		} | ||||
|  	} | ||||
|  	entry->hash = 0xffff; | ||||
|   | ||||
| @@ -540,8 +548,10 @@ static int __mtk_foe_entry_idle_time(str | ||||
|  } | ||||
|   | ||||
|  static bool | ||||
| -mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) | ||||
| +mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, | ||||
| +		      u64 *packets, u64 *bytes) | ||||
|  { | ||||
| +	struct mtk_foe_accounting *acct; | ||||
|  	struct mtk_foe_entry foe = {}; | ||||
|  	struct mtk_foe_entry *hwe; | ||||
|  	u16 hash = entry->hash; | ||||
| @@ -555,16 +565,29 @@ mtk_flow_entry_update(struct mtk_ppe *pp | ||||
|  	memcpy(&foe, hwe, len); | ||||
|   | ||||
|  	if (!mtk_flow_entry_match(ppe->eth, entry, &foe, len) || | ||||
| -	    FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND) | ||||
| +	    FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND) { | ||||
| +		acct = mtk_ppe_acct_data(ppe, hash); | ||||
| +		if (acct) { | ||||
| +			entry->packets += acct->packets; | ||||
| +			entry->bytes += acct->bytes; | ||||
| +		} | ||||
| + | ||||
|  		return false; | ||||
| +	} | ||||
|   | ||||
|  	entry->data.ib1 = foe.ib1; | ||||
| +	acct = mtk_ppe_mib_entry_read(ppe, hash); | ||||
| +	if (acct) { | ||||
| +		*packets += acct->packets; | ||||
| +		*bytes += acct->bytes; | ||||
| +	} | ||||
|   | ||||
|  	return true; | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| -mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) | ||||
| +mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, | ||||
| +			 u64 *packets, u64 *bytes) | ||||
|  { | ||||
|  	u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth); | ||||
|  	struct mtk_flow_entry *cur; | ||||
| @@ -575,7 +598,9 @@ mtk_flow_entry_update_l2(struct mtk_ppe | ||||
|  	hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, list) { | ||||
|  		int cur_idle; | ||||
|   | ||||
| -		if (!mtk_flow_entry_update(ppe, cur)) { | ||||
| +		if (!mtk_flow_entry_update(ppe, cur, packets, bytes)) { | ||||
| +			entry->packets += cur->packets; | ||||
| +			entry->bytes += cur->bytes; | ||||
|  			__mtk_foe_entry_clear(ppe, entry, false); | ||||
|  			continue; | ||||
|  		} | ||||
| @@ -590,10 +615,31 @@ mtk_flow_entry_update_l2(struct mtk_ppe | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| +void mtk_foe_entry_get_stats(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, | ||||
| +			     int *idle, u64 *packets, u64 *bytes) | ||||
| +{ | ||||
| +	*packets = 0; | ||||
| +	*bytes = 0; | ||||
| + | ||||
| +	spin_lock_bh(&ppe_lock); | ||||
| + | ||||
| +	if (entry->type == MTK_FLOW_TYPE_L2) | ||||
| +		mtk_flow_entry_update_l2(ppe, entry, packets, bytes); | ||||
| +	else | ||||
| +		mtk_flow_entry_update(ppe, entry, packets, bytes); | ||||
| + | ||||
| +	*packets += entry->packets; | ||||
| +	*bytes += entry->bytes; | ||||
| +	*idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1); | ||||
| + | ||||
| +	spin_unlock_bh(&ppe_lock); | ||||
| +} | ||||
| + | ||||
|  static void | ||||
|  __mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, | ||||
|  		       u16 hash) | ||||
|  { | ||||
| +	struct mtk_foe_accounting *acct; | ||||
|  	struct mtk_eth *eth = ppe->eth; | ||||
|  	u16 timestamp = mtk_eth_timestamp(eth); | ||||
|  	struct mtk_foe_entry *hwe; | ||||
| @@ -618,6 +664,12 @@ __mtk_foe_entry_commit(struct mtk_ppe *p | ||||
|   | ||||
|  	dma_wmb(); | ||||
|   | ||||
| +	acct = mtk_ppe_mib_entry_read(ppe, hash); | ||||
| +	if (acct) { | ||||
| +		acct->packets = 0; | ||||
| +		acct->bytes = 0; | ||||
| +	} | ||||
| + | ||||
|  	mtk_ppe_cache_clear(ppe); | ||||
|  } | ||||
|   | ||||
| @@ -782,21 +834,6 @@ out: | ||||
|  	spin_unlock_bh(&ppe_lock); | ||||
|  } | ||||
|   | ||||
| -int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) | ||||
| -{ | ||||
| -	int idle; | ||||
| - | ||||
| -	spin_lock_bh(&ppe_lock); | ||||
| -	if (entry->type == MTK_FLOW_TYPE_L2) | ||||
| -		mtk_flow_entry_update_l2(ppe, entry); | ||||
| -	else | ||||
| -		mtk_flow_entry_update(ppe, entry); | ||||
| -	idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1); | ||||
| -	spin_unlock_bh(&ppe_lock); | ||||
| - | ||||
| -	return idle; | ||||
| -} | ||||
| - | ||||
|  int mtk_ppe_prepare_reset(struct mtk_ppe *ppe) | ||||
|  { | ||||
|  	if (!ppe) | ||||
| @@ -824,32 +861,6 @@ int mtk_ppe_prepare_reset(struct mtk_ppe | ||||
|  	return mtk_ppe_wait_busy(ppe); | ||||
|  } | ||||
|   | ||||
| -struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index, | ||||
| -						 struct mtk_foe_accounting *diff) | ||||
| -{ | ||||
| -	struct mtk_foe_accounting *acct; | ||||
| -	int size = sizeof(struct mtk_foe_accounting); | ||||
| -	u64 bytes, packets; | ||||
| - | ||||
| -	if (!ppe->accounting) | ||||
| -		return NULL; | ||||
| - | ||||
| -	if (mtk_mib_entry_read(ppe, index, &bytes, &packets)) | ||||
| -		return NULL; | ||||
| - | ||||
| -	acct = ppe->acct_table + index * size; | ||||
| - | ||||
| -	acct->bytes += bytes; | ||||
| -	acct->packets += packets; | ||||
| - | ||||
| -	if (diff) { | ||||
| -		diff->bytes = bytes; | ||||
| -		diff->packets = packets; | ||||
| -	} | ||||
| - | ||||
| -	return acct; | ||||
| -} | ||||
| - | ||||
|  struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index) | ||||
|  { | ||||
|  	bool accounting = eth->soc->has_accounting; | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h | ||||
| @@ -278,6 +278,8 @@ struct mtk_flow_entry { | ||||
|  	struct mtk_foe_entry data; | ||||
|  	struct rhash_head node; | ||||
|  	unsigned long cookie; | ||||
| +	u64 packets; | ||||
| +	u64 bytes; | ||||
|  }; | ||||
|   | ||||
|  struct mtk_mib_entry { | ||||
| @@ -320,6 +322,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_ | ||||
|  void mtk_ppe_start(struct mtk_ppe *ppe); | ||||
|  int mtk_ppe_stop(struct mtk_ppe *ppe); | ||||
|  int mtk_ppe_prepare_reset(struct mtk_ppe *ppe); | ||||
| +struct mtk_foe_accounting *mtk_ppe_mib_entry_read(struct mtk_ppe *ppe, u16 index); | ||||
|   | ||||
|  void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash); | ||||
|   | ||||
| @@ -368,9 +371,8 @@ int mtk_foe_entry_set_queue(struct mtk_e | ||||
|  			    unsigned int queue); | ||||
|  int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); | ||||
|  void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); | ||||
| -int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); | ||||
|  int mtk_ppe_debugfs_init(struct mtk_ppe *ppe, int index); | ||||
| -struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index, | ||||
| -						 struct mtk_foe_accounting *diff); | ||||
| +void mtk_foe_entry_get_stats(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, | ||||
| +			     int *idle, u64 *packets, u64 *bytes); | ||||
|   | ||||
|  #endif | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c | ||||
| @@ -96,7 +96,7 @@ mtk_ppe_debugfs_foe_show(struct seq_file | ||||
|  		if (bind && state != MTK_FOE_STATE_BIND) | ||||
|  			continue; | ||||
|   | ||||
| -		acct = mtk_foe_entry_get_mib(ppe, i, NULL); | ||||
| +		acct = mtk_ppe_mib_entry_read(ppe, i); | ||||
|   | ||||
|  		type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); | ||||
|  		seq_printf(m, "%05x %s %7s", i, | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c | ||||
| @@ -499,24 +499,17 @@ static int | ||||
|  mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f) | ||||
|  { | ||||
|  	struct mtk_flow_entry *entry; | ||||
| -	struct mtk_foe_accounting diff; | ||||
| -	u32 idle; | ||||
| +	int idle; | ||||
|   | ||||
|  	entry = rhashtable_lookup(ð->flow_table, &f->cookie, | ||||
|  				  mtk_flow_ht_params); | ||||
|  	if (!entry) | ||||
|  		return -ENOENT; | ||||
|   | ||||
| -	idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry); | ||||
| +	mtk_foe_entry_get_stats(eth->ppe[entry->ppe_index], entry, &idle, | ||||
| +				&f->stats.pkts, &f->stats.bytes); | ||||
|  	f->stats.lastused = jiffies - idle * HZ; | ||||
|   | ||||
| -	if (entry->hash != 0xFFFF && | ||||
| -	    mtk_foe_entry_get_mib(eth->ppe[entry->ppe_index], entry->hash, | ||||
| -				  &diff)) { | ||||
| -		f->stats.pkts += diff.packets; | ||||
| -		f->stats.bytes += diff.bytes; | ||||
| -	} | ||||
| - | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| @@ -0,0 +1,21 @@ | ||||
| From: Felix Fietkau <nbd@nbd.name> | ||||
| Date: Thu, 23 Mar 2023 11:19:14 +0100 | ||||
| Subject: [PATCH] net: ethernet: mtk_eth_soc: add missing ppe cache flush when | ||||
|  deleting a flow | ||||
|  | ||||
| The cache needs to be flushed to ensure that the hardware stops offloading | ||||
| the flow immediately. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c | ||||
| @@ -525,6 +525,7 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp | ||||
|  		hwe->ib1 &= ~MTK_FOE_IB1_STATE; | ||||
|  		hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID); | ||||
|  		dma_wmb(); | ||||
| +		mtk_ppe_cache_clear(ppe); | ||||
|  	} | ||||
|  	entry->hash = 0xffff; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Felix Fietkau
					Felix Fietkau