ramips: fix two-way hash and auto ageout on MT7621

Current code directly writes the FOE entry to hash_val+1 position
when hash collision occurs. However, it is found that this behavior
will cause the cache and the hardware FOE table to be inconsistent.

For example, there are three flows, and their hashed values are all
equal to 100. The first flow is written to the position of 100. The
second flow is written to the position of 100+1. Then, the logic of
the current code will also write the third flow to 100+1.

At this time, the cache has flow 1 and 2; and the hardware FOE table
has flow 1 and 3, where these two parts store different contents.
So it is necessary to check whether the hash_val+1 is also occupied
before writing. If hash_val+1 is also occupied, we won’t bind th
third flow to the FOE table.

Addition to that, we also cancel the processing of foe_entry removal
because the hardware has auto age-out ability. The hardware will
periodically iterate through the FOE table to find out the time-out
entry and set it as INVALID.

Signed-off-by: HsiuWen Yen <y.hsiuwen@gmail.com>
master
HsiuWen Yen 6 years ago committed by John Crispin
parent 45a2771953
commit fe7d965ea9
  1. 33
      target/linux/ramips/files-4.14/drivers/net/ethernet/mediatek/mtk_offload.c

@ -119,11 +119,11 @@ mtk_foe_set_mac(struct mtk_foe_entry *entry, u8 *smac, u8 *dmac)
} }
static int static int
mtk_check_hashcollision(struct mtk_eth *eth, u32 hash) mtk_check_entry_available(struct mtk_eth *eth, u32 hash)
{ {
struct mtk_foe_entry entry = ((struct mtk_foe_entry *)eth->foe_table)[hash]; struct mtk_foe_entry entry = ((struct mtk_foe_entry *)eth->foe_table)[hash];
return (entry.bfib1.state != BIND)? 0:1; return (entry.bfib1.state == BIND)? 0:1;
} }
static void static void
@ -157,6 +157,12 @@ int mtk_flow_offload(struct mtk_eth *eth,
if (otuple->l4proto != IPPROTO_TCP && otuple->l4proto != IPPROTO_UDP) if (otuple->l4proto != IPPROTO_TCP && otuple->l4proto != IPPROTO_UDP)
return -EINVAL; return -EINVAL;
if (type == FLOW_OFFLOAD_DEL) {
flow = NULL;
synchronize_rcu();
return 0;
}
switch (otuple->l3proto) { switch (otuple->l3proto) {
case AF_INET: case AF_INET:
if (mtk_foe_prepare_v4(&orig, otuple, rtuple, src, dest) || if (mtk_foe_prepare_v4(&orig, otuple, rtuple, src, dest) ||
@ -174,30 +180,25 @@ int mtk_flow_offload(struct mtk_eth *eth,
return -EINVAL; return -EINVAL;
} }
if (type == FLOW_OFFLOAD_DEL) {
orig.bfib1.state = INVALID;
reply.bfib1.state = INVALID;
flow = NULL;
goto write;
}
/* Two-way hash: when hash collision occurs, the hash value will be shifted to the next position. */ /* Two-way hash: when hash collision occurs, the hash value will be shifted to the next position. */
if(mtk_check_hashcollision(eth, ohash)) if (!mtk_check_entry_available(eth, ohash)){
if (!mtk_check_entry_available(eth, ohash + 1))
return -EINVAL;
ohash += 1; ohash += 1;
if(mtk_check_hashcollision(eth, rhash)) }
if (!mtk_check_entry_available(eth, rhash)){
if (!mtk_check_entry_available(eth, rhash + 1))
return -EINVAL;
rhash += 1; rhash += 1;
}
mtk_foe_set_mac(&orig, dest->eth_src, dest->eth_dest); mtk_foe_set_mac(&orig, dest->eth_src, dest->eth_dest);
mtk_foe_set_mac(&reply, src->eth_src, src->eth_dest); mtk_foe_set_mac(&reply, src->eth_src, src->eth_dest);
write:
mtk_foe_write(eth, ohash, &orig); mtk_foe_write(eth, ohash, &orig);
mtk_foe_write(eth, rhash, &reply); mtk_foe_write(eth, rhash, &reply);
rcu_assign_pointer(eth->foe_flow_table[ohash], flow); rcu_assign_pointer(eth->foe_flow_table[ohash], flow);
rcu_assign_pointer(eth->foe_flow_table[rhash], flow); rcu_assign_pointer(eth->foe_flow_table[rhash], flow);
if (type == FLOW_OFFLOAD_DEL)
synchronize_rcu();
return 0; return 0;
} }

Loading…
Cancel
Save