From nobody Tue Jun 9 00:50:27 2026 Received: from mail-pl1-f175.google.com (mail-pl1-f175.google.com [209.85.214.175]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 427BD37C102 for ; Sun, 24 May 2026 22:43:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779662634; cv=none; b=Lu56pPpRrm9Z76pDCEHMwPw5Ss4E7SsZ2tcJnnV/L3X2L9IGzW3u9etCtU2XO+FGc4GIfuiMHK+MwIa+4IL8Ju5W8tO//Wvmhu9fPuLCf384GjUVm/pWcFHuM9t71s+QriPaMCkRxT4g4rbjKJjyb/Udgp/XxiuaiDDjOORaGys= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779662634; c=relaxed/simple; bh=/9KHSrwMadfcWICdaY+M9KNwrMq0JFUQrrZ2RWOYLqU=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=bYkO8McEOmn6+TXjj0vgmCrTjAiRts1VqURsk3kVLx4AB++bBQCdrOMvQpJROo4uXLkt3lU6YV5puzR5r24HrgsABR3DhDCVtQnG7mVIaH8ns3ZtRmvzeTz0n0Ks+5Eozokk1I+6JYejE55z7+j9OwY/GR8D07L/I2G/F21LgtM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=edAjYzN9; arc=none smtp.client-ip=209.85.214.175 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="edAjYzN9" Received: by mail-pl1-f175.google.com with SMTP id d9443c01a7336-2bd80b3aa13so59242635ad.0 for ; Sun, 24 May 2026 15:43:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779662631; x=1780267431; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=vQjZaq2V359LxFj/1Py1W2spC//5ryaGbLK3jja2HKs=; b=edAjYzN9TYRsN9jotN3hVI9hUgl45ZTLxAD5jqZw+lAj5tQ9t69/qggIo6HIvDLHAL f0UvoH3NOH+00hMltVGNpoEBRwgZjgfGVKKOo2xkTFNW3FrIf95Da2Q2gShhZASCnDiP FGhcGNXwKqP9KCNw82O1pIUG6Vood9C8mUk1RM8h45OKMZWVxAqjZwzn54GQHEF5HzGu xSs/9eBNeL3oMQ2B03GjdDWxgIJXhcBES2J74lXT2VzWwpgLop669dzesQQ/KUFI3Vhu ZiHWajM2e9wZlazHAGKYpqPwjyqYWGqSoVdqfWUNLpoZrkelIJikBRdaWBdqfrzxRwNw Dm7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779662631; x=1780267431; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=vQjZaq2V359LxFj/1Py1W2spC//5ryaGbLK3jja2HKs=; b=EXkJ/Fdfy1AzBxAS/l44W417W/lAtlgK0q9vFGHJO/tkFWKRaUHOIlAUU86zmcCwdP 70E3Yr9mKVYjP+yNiN2uVucJhHs1jZi8N3AgYbNL5FK2RJGS9XnQvaCswGkQxmiOwieR W99ACEbv+MVAXKa/ZOnTH0ul3RDT1CSFHiLHk+vkWf19vwPFdc/DcK+2tu+LjOLBU3V7 3kXjLHI6yBSKm6UOFzuCveJKygIwMNI5RF3Mp3CW3iXn6EFAvn7ssdFNEFEN65IodWW8 ZE6DMQ8FnHOFAtQxREvRXZFMG5S9E0UCO8iVZbo26S2L5TmIdw3Yxu+oNK427QBKVnuS 5a/w== X-Forwarded-Encrypted: i=1; AFNElJ84HdMmMKk70hTt3F798oJi53JI+NNPcrNj9Pdi1LsuNYWEk0mRp2JmnO/2mBVDJmZDT4mjL8C24k7bYuk=@vger.kernel.org X-Gm-Message-State: AOJu0YzEznuCbjw5o8x+HF5O2qgYVKbvYxGPR/bygz7mNPNaaUc8spJY 6scXTMiVRU0Iojl1H/rlpMR8Miw8DuSJVfZoDw3mFRFirsb+Q0BL3lYjwcXETxq2 X-Gm-Gg: Acq92OHXOciEzZCoRjdKqEgGnYlN4eecdLhFj6T4AuPI9utGpm0ApeDnXWKKT4cERRZ YujeFRrgVTmH82dzUuUQTib7actZfKkbvP20kK3rgkImDPI7CdED052muMsONWwLySInfHQ/cA8 X5d35EGNd7dJVrAJMuf+tcqD+P7EOnk0KhJ24O4rxYvBa53FK9uFxc5y8/bPq/J8z0/XwsPAEql 5V5Lz7ClDvfvjFxeJ6O7VO9idCPlI+pCihe7wSTjWYymJ/bOJjfJSvGM+NmFLVGd/tGETTLdWpw LrBwMWy9tyG0aJ0NP7MOUfG4nilG8ML6+oYWINUxVReWPyNzlmuynsmVOkqSHcNN1JeM1zfijDH nXQfH7GBLkGQB51/4xjma539NSj1pHhp/1BMb8ymo5BDC5GXWX5EslXN6sl7kCH822HXOJy8pDA mtZX5970kZL8TOsMDG6NeeX8+m X-Received: by 2002:a17:903:2f8d:b0:2b2:be01:5532 with SMTP id d9443c01a7336-2beb0841de5mr126883415ad.35.1779662631489; Sun, 24 May 2026 15:43:51 -0700 (PDT) Received: from mincom1 ([125.149.177.227]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2beb569592dsm75643475ad.16.2026.05.24.15.43.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 24 May 2026 15:43:51 -0700 (PDT) From: Jihong Min To: netdev@vger.kernel.org Cc: Lorenzo Bianconi , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, linux-kernel@vger.kernel.org, Jihong Min Subject: [PATCH net-next] net: airoha: bind WLAN-bound flows on PPE driver L2 cache miss Date: Mon, 25 May 2026 07:43:30 +0900 Message-ID: <20260524224330.3995807-1-hurryman2212@gmail.com> X-Mailer: git-send-email 2.53.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The Linux bridge FDB can resolve a destination station to WDMA even when the Airoha PPE driver's L2 offload cache has no entry for that MAC pair. The normal bind path only checks the PPE driver's L2 offload cache, so an unbound PPE hit for WLAN egress can stay unbound even though the bridge already knows the right output path, unless a later offload event fills that PPE driver cache. This matters for bridge-visible WLAN egress, such as wired-to-WLAN forwarding or WLAN peer forwarding across another BSS, radio or MLO link. Same-link or same-radio intra-BSS forwarding can stay inside the WLAN datapath and is not covered. Before touching the PPE table, resolve the destination MAC through the bridge device above the ingress netdev. If the PPE driver's L2 offload cache lookup misses, bind the hardware flow to the resolved CDM4/WDMA path. Assisted-by: Codex:gpt-5.5 Signed-off-by: Jihong Min --- drivers/net/ethernet/airoha/airoha_ppe.c | 138 +++++++++++++++++++---- 1 file changed, 119 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/etherne= t/airoha/airoha_ppe.c index 26da519236bf..ea932e6d87f6 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe.c +++ b/drivers/net/ethernet/airoha/airoha_ppe.c @@ -803,65 +803,163 @@ static void airoha_ppe_foe_flow_remove_entry(struct = airoha_ppe *ppe, } =20 static int -airoha_ppe_foe_commit_subflow_entry(struct airoha_ppe *ppe, - struct airoha_flow_table_entry *e, - u32 hash, bool rx_wlan) +airoha_ppe_foe_commit_subflow(struct airoha_ppe *ppe, + const struct airoha_foe_entry *bridge, + u32 hash, bool rx_wlan) { u32 mask =3D AIROHA_FOE_IB1_BIND_PACKET_TYPE | AIROHA_FOE_IB1_BIND_UDP; struct airoha_foe_entry *hwe_p, hwe; - struct airoha_flow_table_entry *f; int type; =20 hwe_p =3D airoha_ppe_foe_get_entry_locked(ppe, hash); if (!hwe_p) return -EINVAL; =20 - f =3D kzalloc_obj(*f, GFP_ATOMIC); - if (!f) - return -ENOMEM; - - hlist_add_head(&f->l2_subflow_node, &e->l2_flows); - f->type =3D FLOW_TYPE_L2_SUBFLOW; - f->hash =3D hash; - memcpy(&hwe, hwe_p, sizeof(*hwe_p)); - hwe.ib1 =3D (hwe.ib1 & mask) | (e->data.ib1 & ~mask); + hwe.ib1 =3D (hwe.ib1 & mask) | (bridge->ib1 & ~mask); =20 type =3D FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe.ib1); if (type >=3D PPE_PKT_TYPE_IPV6_ROUTE_3T) { - memcpy(&hwe.ipv6.l2, &e->data.bridge.l2, sizeof(hwe.ipv6.l2)); - hwe.ipv6.ib2 =3D e->data.bridge.ib2; + memcpy(&hwe.ipv6.l2, &bridge->bridge.l2, + sizeof(hwe.ipv6.l2)); + hwe.ipv6.ib2 =3D bridge->bridge.ib2; /* setting smac_id to 0xf instruct the hw to keep original * source mac address */ hwe.ipv6.l2.src_mac_hi =3D FIELD_PREP(AIROHA_FOE_MAC_SMAC_ID, 0xf); } else { - memcpy(&hwe.bridge.l2, &e->data.bridge.l2, + memcpy(&hwe.bridge.l2, &bridge->bridge.l2, sizeof(hwe.bridge.l2)); - hwe.bridge.ib2 =3D e->data.bridge.ib2; + hwe.bridge.ib2 =3D bridge->bridge.ib2; if (type =3D=3D PPE_PKT_TYPE_IPV4_HNAPT) memcpy(&hwe.ipv4.new_tuple, &hwe.ipv4.orig_tuple, sizeof(hwe.ipv4.new_tuple)); } =20 - hwe.bridge.data =3D e->data.bridge.data; - airoha_ppe_foe_commit_entry(ppe, &hwe, hash, rx_wlan); + hwe.bridge.data =3D bridge->bridge.data; + + return airoha_ppe_foe_commit_entry(ppe, &hwe, hash, rx_wlan); +} + +static int +airoha_ppe_foe_commit_subflow_entry(struct airoha_ppe *ppe, + struct airoha_flow_table_entry *e, + u32 hash, bool rx_wlan) +{ + struct airoha_flow_table_entry *f; + int err; + + f =3D kzalloc_obj(*f, GFP_ATOMIC); + if (!f) + return -ENOMEM; + + err =3D airoha_ppe_foe_commit_subflow(ppe, &e->data, hash, rx_wlan); + if (err) { + kfree(f); + return err; + } + + hlist_add_head(&f->l2_subflow_node, &e->l2_flows); + f->type =3D FLOW_TYPE_L2_SUBFLOW; + f->hash =3D hash; =20 return 0; } =20 +static bool +airoha_ppe_foe_prepare_wdma_subflow_dev(struct airoha_ppe *ppe, + struct net_device *dev, + struct airoha_flow_data *data, + struct airoha_foe_entry *hwe) +{ + u32 pse_port; + int err; + + err =3D airoha_ppe_foe_entry_prepare(ppe->eth, hwe, dev, + PPE_PKT_TYPE_BRIDGE, data, 0); + if (err) + return false; + + pse_port =3D FIELD_GET(AIROHA_FOE_IB2_PSE_PORT, hwe->bridge.ib2); + if (pse_port !=3D FE_PSE_PORT_CDM4) + return false; + + return true; +} + +static struct net_device * +airoha_ppe_foe_get_bridge_master(struct net_device *dev) +{ + struct net_device *master =3D NULL; + + rcu_read_lock(); + master =3D netdev_master_upper_dev_get_rcu(dev); + if (master && netif_is_bridge_master(master)) + dev_hold(master); + else + master =3D NULL; + rcu_read_unlock(); + + return master; +} + +static bool +airoha_ppe_foe_prepare_wdma_subflow(struct airoha_ppe *ppe, + struct sk_buff *skb, + struct airoha_foe_entry *hwe) +{ + struct ethhdr *eh =3D eth_hdr(skb); + struct airoha_flow_data data =3D {}; + struct net_device *master; + + if (!is_valid_ether_addr(eh->h_source) || + !is_valid_ether_addr(eh->h_dest)) + return false; + + ether_addr_copy(data.eth.h_dest, eh->h_dest); + ether_addr_copy(data.eth.h_source, eh->h_source); + + if (!skb->dev) + return false; + + /* WLAN egress unbound hits can arrive before flowtable creates the + * L2 master flow normally used for subflow binding. Resolve only + * through the bridge master so dev_fill_forward_path() must use the + * bridge FDB for the destination MAC. Calling the ingress AP netdev + * directly can describe the source station's WDMA path and would + * corrupt Wi-Fi-to-wired flows whose real egress is not WDMA. + */ + master =3D airoha_ppe_foe_get_bridge_master(skb->dev); + if (!master) + return false; + + if (airoha_ppe_foe_prepare_wdma_subflow_dev(ppe, master, &data, + hwe)) { + dev_put(master); + return true; + } + + dev_put(master); + return false; +} + static void airoha_ppe_foe_insert_entry(struct airoha_ppe *ppe, struct sk_buff *skb, u32 hash, bool rx_wlan) { + struct airoha_foe_entry wdma_hwe =3D {}; struct airoha_flow_table_entry *e; struct airoha_foe_bridge br =3D {}; struct airoha_foe_entry *hwe; bool commit_done =3D false; + bool wdma_ready =3D false; struct hlist_node *n; u32 index, state; =20 + wdma_ready =3D airoha_ppe_foe_prepare_wdma_subflow(ppe, skb, + &wdma_hwe); + spin_lock_bh(&ppe_lock); =20 hwe =3D airoha_ppe_foe_get_entry_locked(ppe, hash); @@ -899,6 +997,8 @@ static void airoha_ppe_foe_insert_entry(struct airoha_p= pe *ppe, airoha_l2_flow_table_params); if (e) airoha_ppe_foe_commit_subflow_entry(ppe, e, hash, rx_wlan); + else if (wdma_ready) + airoha_ppe_foe_commit_subflow(ppe, &wdma_hwe, hash, rx_wlan); unlock: spin_unlock_bh(&ppe_lock); } --=20 2.53.0