diff --git a/target/linux/generic/pending-6.12/650-net-pppoe-implement-GRO-support.patch b/target/linux/generic/backport-6.12/650-v7.2-net-pppoe-implement-GRO-GSO-support.patch similarity index 69% rename from target/linux/generic/pending-6.12/650-net-pppoe-implement-GRO-support.patch rename to target/linux/generic/backport-6.12/650-v7.2-net-pppoe-implement-GRO-GSO-support.patch index 11d0f03fb..9aa883a8e 100644 --- a/target/linux/generic/pending-6.12/650-net-pppoe-implement-GRO-support.patch +++ b/target/linux/generic/backport-6.12/650-v7.2-net-pppoe-implement-GRO-GSO-support.patch @@ -1,6 +1,7 @@ +From 55a5d8fca8365312ed53bc93ae1af67ee35c2915 Mon Sep 17 00:00:00 2001 From: Felix Fietkau -Date: Tue, 15 Jul 2025 12:37:45 +0200 -Subject: [PATCH] net: pppoe: implement GRO support +Date: Wed, 13 May 2026 09:33:48 +0800 +Subject: [PATCH] net: pppoe: implement GRO/GSO support Only handles packets where the pppoe header length field matches the exact packet length. Significantly improves rx throughput. @@ -11,7 +12,15 @@ that the device is able to handle improves from ~130 Mbit/s to ~630 Mbit/s, using fraglist GRO. Signed-off-by: Felix Fietkau +Signed-off-by: Qingfang Deng +Tested-by: Pablo Neira Ayuso +Link: https://patch.msgid.link/20260513013400.7467-1-qingfang.deng@linux.dev +Signed-off-by: Paolo Abeni --- + drivers/net/ppp/pppoe.c | 161 +++++++++++++++++++++++++++++++++++++++- + net/ipv4/af_inet.c | 2 + + net/ipv6/ip6_offload.c | 2 + + 3 files changed, 164 insertions(+), 1 deletion(-) --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -32,28 +41,34 @@ Signed-off-by: Felix Fietkau goto drop; ph = pppoe_hdr(skb); -@@ -1173,6 +1174,161 @@ static struct pernet_operations pppoe_ne +@@ -1176,6 +1177,160 @@ static struct pernet_operations pppoe_ne .size = sizeof(struct pppoe_net), }; +static u16 -+compare_pppoe_header(struct pppoe_hdr *phdr, struct pppoe_hdr *phdr2) ++compare_pppoe_header(const struct pppoe_hdr *phdr, ++ const struct pppoe_hdr *phdr2) +{ -+ return (__force __u16)((phdr->sid ^ phdr2->sid) | -+ (phdr->tag[0].tag_type ^ phdr2->tag[0].tag_type)); ++ __be16 proto = *(const __be16 *)(phdr + 1); ++ __be16 proto2 = *(const __be16 *)(phdr2 + 1); ++ ++ return (__force u16)((phdr->sid ^ phdr2->sid) | (proto ^ proto2)); +} + -+static __be16 pppoe_hdr_proto(struct pppoe_hdr *phdr) ++static __be16 pppoe_hdr_proto(const struct pppoe_hdr *phdr) +{ -+ switch (phdr->tag[0].tag_type) { ++ __be16 proto = *(const __be16 *)(phdr + 1); ++ ++ switch (proto) { + case cpu_to_be16(PPP_IP): + return cpu_to_be16(ETH_P_IP); ++#if IS_ENABLED(CONFIG_IPV6) + case cpu_to_be16(PPP_IPV6): + return cpu_to_be16(ETH_P_IPV6); ++#endif + default: + return 0; + } -+ +} + +static struct sk_buff *pppoe_gro_receive(struct list_head *head, @@ -61,26 +76,27 @@ Signed-off-by: Felix Fietkau +{ + const struct packet_offload *ptype; + unsigned int hlen, off_pppoe; ++ const struct pppoe_hdr *phdr; + struct sk_buff *pp = NULL; -+ struct pppoe_hdr *phdr; + struct sk_buff *p; + int flush = 1; + __be16 type; + + off_pppoe = skb_gro_offset(skb); -+ hlen = off_pppoe + sizeof(*phdr); -+ phdr = skb_gro_header(skb, hlen + 2, off_pppoe); ++ hlen = off_pppoe + PPPOE_SES_HLEN; ++ phdr = skb_gro_header(skb, hlen, off_pppoe); + if (unlikely(!phdr)) + goto out; + ++ /* filter for session packets (type:1, ver:1, code:0) */ ++ if (*(const __be16 *)phdr != cpu_to_be16(0x1100)) ++ goto out; ++ + /* ignore packets with padding or invalid length */ -+ if (skb_gro_len(skb) != be16_to_cpu(phdr->length) + hlen) ++ if (skb_gro_len(skb) != be16_to_cpu(phdr->length) + sizeof(*phdr)) + goto out; + + type = pppoe_hdr_proto(phdr); -+ if (!type) -+ goto out; -+ + ptype = gro_find_receive_by_type(type); + if (!ptype) + goto out; @@ -88,18 +104,18 @@ Signed-off-by: Felix Fietkau + flush = 0; + + list_for_each_entry(p, head, list) { -+ struct pppoe_hdr *phdr2; ++ const struct pppoe_hdr *phdr2; + + if (!NAPI_GRO_CB(p)->same_flow) + continue; + -+ phdr2 = (struct pppoe_hdr *)(p->data + off_pppoe); ++ phdr2 = (const struct pppoe_hdr *)(p->data + off_pppoe); + if (compare_pppoe_header(phdr, phdr2)) + NAPI_GRO_CB(p)->same_flow = 0; + } + -+ skb_gro_pull(skb, sizeof(*phdr) + 2); -+ skb_gro_postpull_rcsum(skb, phdr, sizeof(*phdr) + 2); ++ skb_gro_pull(skb, PPPOE_SES_HLEN); ++ skb_gro_postpull_rcsum(skb, phdr, PPPOE_SES_HLEN); + + pp = indirect_call_gro_receive_inet(ptype->callbacks.gro_receive, + ipv6_gro_receive, inet_gro_receive, @@ -116,32 +132,26 @@ Signed-off-by: Felix Fietkau + struct pppoe_hdr *phdr = (struct pppoe_hdr *)(skb->data + nhoff); + __be16 type = pppoe_hdr_proto(phdr); + struct packet_offload *ptype; -+ int len, err; ++ unsigned int len; + + ptype = gro_find_complete_by_type(type); + if (!ptype) + return -ENOENT; + -+ err = INDIRECT_CALL_INET(ptype->callbacks.gro_complete, -+ ipv6_gro_complete, inet_gro_complete, -+ skb, nhoff + sizeof(*phdr) + 2); -+ if (err) -+ return err; -+ + len = skb->len - (nhoff + sizeof(*phdr)); ++ len = min(len, 0xFFFFU); + phdr->length = cpu_to_be16(len); + -+ return 0; ++ return INDIRECT_CALL_INET(ptype->callbacks.gro_complete, ++ ipv6_gro_complete, inet_gro_complete, ++ skb, nhoff + PPPOE_SES_HLEN); +} + +static struct sk_buff *pppoe_gso_segment(struct sk_buff *skb, + netdev_features_t features) +{ -+ unsigned int pppoe_hlen = sizeof(struct pppoe_hdr) + 2; + struct sk_buff *segs = ERR_PTR(-EINVAL); -+ u16 mac_offset = skb->mac_header; + struct packet_offload *ptype; -+ u16 mac_len = skb->mac_len; + struct pppoe_hdr *phdr; + __be16 orig_type, type; + int len, nhoff; @@ -149,7 +159,7 @@ Signed-off-by: Felix Fietkau + skb_reset_network_header(skb); + nhoff = skb_network_header(skb) - skb_mac_header(skb); + -+ if (unlikely(!pskb_may_pull(skb, pppoe_hlen))) ++ if (unlikely(!pskb_may_pull(skb, PPPOE_SES_HLEN))) + goto out; + + phdr = (struct pppoe_hdr *)skb_network_header(skb); @@ -159,13 +169,11 @@ Signed-off-by: Felix Fietkau + goto out; + + orig_type = skb->protocol; -+ __skb_pull(skb, pppoe_hlen); ++ __skb_pull(skb, PPPOE_SES_HLEN); ++ features &= ~NETIF_F_GSO_SOFTWARE; + segs = ptype->callbacks.gso_segment(skb, features); -+ if (IS_ERR_OR_NULL(segs)) { -+ skb_gso_error_unwind(skb, orig_type, pppoe_hlen, mac_offset, -+ mac_len); ++ if (IS_ERR_OR_NULL(segs)) + goto out; -+ } + + skb = segs; + do { @@ -194,19 +202,21 @@ Signed-off-by: Felix Fietkau static int __init pppoe_init(void) { int err; -@@ -1189,6 +1345,7 @@ static int __init pppoe_init(void) +@@ -1192,6 +1347,8 @@ static int __init pppoe_init(void) if (err) goto out_unregister_pppoe_proto; -+ dev_add_offload(&pppoe_packet_offload); ++ if (IS_ENABLED(CONFIG_INET)) ++ dev_add_offload(&pppoe_packet_offload); dev_add_pack(&pppoes_ptype); dev_add_pack(&pppoed_ptype); register_netdevice_notifier(&pppoe_notifier); -@@ -1208,6 +1365,7 @@ static void __exit pppoe_exit(void) +@@ -1211,6 +1368,8 @@ static void __exit pppoe_exit(void) unregister_netdevice_notifier(&pppoe_notifier); dev_remove_pack(&pppoed_ptype); dev_remove_pack(&pppoes_ptype); -+ dev_remove_offload(&pppoe_packet_offload); ++ if (IS_ENABLED(CONFIG_INET)) ++ dev_remove_offload(&pppoe_packet_offload); unregister_pppox_proto(PX_PROTO_OE); proto_unregister(&pppoe_sk_proto); unregister_pernet_device(&pppoe_net_ops); diff --git a/target/linux/generic/pending-6.18/650-net-pppoe-implement-GRO-support.patch b/target/linux/generic/backport-6.18/650-v7.2-net-pppoe-implement-GRO-GSO-support.patch similarity index 69% rename from target/linux/generic/pending-6.18/650-net-pppoe-implement-GRO-support.patch rename to target/linux/generic/backport-6.18/650-v7.2-net-pppoe-implement-GRO-GSO-support.patch index 9c50b0b1f..01ccd4a9b 100644 --- a/target/linux/generic/pending-6.18/650-net-pppoe-implement-GRO-support.patch +++ b/target/linux/generic/backport-6.18/650-v7.2-net-pppoe-implement-GRO-GSO-support.patch @@ -1,6 +1,7 @@ +From 55a5d8fca8365312ed53bc93ae1af67ee35c2915 Mon Sep 17 00:00:00 2001 From: Felix Fietkau -Date: Tue, 15 Jul 2025 12:37:45 +0200 -Subject: [PATCH] net: pppoe: implement GRO support +Date: Wed, 13 May 2026 09:33:48 +0800 +Subject: [PATCH] net: pppoe: implement GRO/GSO support Only handles packets where the pppoe header length field matches the exact packet length. Significantly improves rx throughput. @@ -11,7 +12,15 @@ that the device is able to handle improves from ~130 Mbit/s to ~630 Mbit/s, using fraglist GRO. Signed-off-by: Felix Fietkau +Signed-off-by: Qingfang Deng +Tested-by: Pablo Neira Ayuso +Link: https://patch.msgid.link/20260513013400.7467-1-qingfang.deng@linux.dev +Signed-off-by: Paolo Abeni --- + drivers/net/ppp/pppoe.c | 161 +++++++++++++++++++++++++++++++++++++++- + net/ipv4/af_inet.c | 2 + + net/ipv6/ip6_offload.c | 2 + + 3 files changed, 164 insertions(+), 1 deletion(-) --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -32,28 +41,34 @@ Signed-off-by: Felix Fietkau goto drop; ph = pppoe_hdr(skb); -@@ -1176,6 +1177,161 @@ static struct pernet_operations pppoe_ne +@@ -1176,6 +1177,160 @@ static struct pernet_operations pppoe_ne .size = sizeof(struct pppoe_net), }; +static u16 -+compare_pppoe_header(struct pppoe_hdr *phdr, struct pppoe_hdr *phdr2) ++compare_pppoe_header(const struct pppoe_hdr *phdr, ++ const struct pppoe_hdr *phdr2) +{ -+ return (__force __u16)((phdr->sid ^ phdr2->sid) | -+ (phdr->tag[0].tag_type ^ phdr2->tag[0].tag_type)); ++ __be16 proto = *(const __be16 *)(phdr + 1); ++ __be16 proto2 = *(const __be16 *)(phdr2 + 1); ++ ++ return (__force u16)((phdr->sid ^ phdr2->sid) | (proto ^ proto2)); +} + -+static __be16 pppoe_hdr_proto(struct pppoe_hdr *phdr) ++static __be16 pppoe_hdr_proto(const struct pppoe_hdr *phdr) +{ -+ switch (phdr->tag[0].tag_type) { ++ __be16 proto = *(const __be16 *)(phdr + 1); ++ ++ switch (proto) { + case cpu_to_be16(PPP_IP): + return cpu_to_be16(ETH_P_IP); ++#if IS_ENABLED(CONFIG_IPV6) + case cpu_to_be16(PPP_IPV6): + return cpu_to_be16(ETH_P_IPV6); ++#endif + default: + return 0; + } -+ +} + +static struct sk_buff *pppoe_gro_receive(struct list_head *head, @@ -61,26 +76,27 @@ Signed-off-by: Felix Fietkau +{ + const struct packet_offload *ptype; + unsigned int hlen, off_pppoe; ++ const struct pppoe_hdr *phdr; + struct sk_buff *pp = NULL; -+ struct pppoe_hdr *phdr; + struct sk_buff *p; + int flush = 1; + __be16 type; + + off_pppoe = skb_gro_offset(skb); -+ hlen = off_pppoe + sizeof(*phdr); -+ phdr = skb_gro_header(skb, hlen + 2, off_pppoe); ++ hlen = off_pppoe + PPPOE_SES_HLEN; ++ phdr = skb_gro_header(skb, hlen, off_pppoe); + if (unlikely(!phdr)) + goto out; + ++ /* filter for session packets (type:1, ver:1, code:0) */ ++ if (*(const __be16 *)phdr != cpu_to_be16(0x1100)) ++ goto out; ++ + /* ignore packets with padding or invalid length */ -+ if (skb_gro_len(skb) != be16_to_cpu(phdr->length) + hlen) ++ if (skb_gro_len(skb) != be16_to_cpu(phdr->length) + sizeof(*phdr)) + goto out; + + type = pppoe_hdr_proto(phdr); -+ if (!type) -+ goto out; -+ + ptype = gro_find_receive_by_type(type); + if (!ptype) + goto out; @@ -88,18 +104,18 @@ Signed-off-by: Felix Fietkau + flush = 0; + + list_for_each_entry(p, head, list) { -+ struct pppoe_hdr *phdr2; ++ const struct pppoe_hdr *phdr2; + + if (!NAPI_GRO_CB(p)->same_flow) + continue; + -+ phdr2 = (struct pppoe_hdr *)(p->data + off_pppoe); ++ phdr2 = (const struct pppoe_hdr *)(p->data + off_pppoe); + if (compare_pppoe_header(phdr, phdr2)) + NAPI_GRO_CB(p)->same_flow = 0; + } + -+ skb_gro_pull(skb, sizeof(*phdr) + 2); -+ skb_gro_postpull_rcsum(skb, phdr, sizeof(*phdr) + 2); ++ skb_gro_pull(skb, PPPOE_SES_HLEN); ++ skb_gro_postpull_rcsum(skb, phdr, PPPOE_SES_HLEN); + + pp = indirect_call_gro_receive_inet(ptype->callbacks.gro_receive, + ipv6_gro_receive, inet_gro_receive, @@ -116,32 +132,26 @@ Signed-off-by: Felix Fietkau + struct pppoe_hdr *phdr = (struct pppoe_hdr *)(skb->data + nhoff); + __be16 type = pppoe_hdr_proto(phdr); + struct packet_offload *ptype; -+ int len, err; ++ unsigned int len; + + ptype = gro_find_complete_by_type(type); + if (!ptype) + return -ENOENT; + -+ err = INDIRECT_CALL_INET(ptype->callbacks.gro_complete, -+ ipv6_gro_complete, inet_gro_complete, -+ skb, nhoff + sizeof(*phdr) + 2); -+ if (err) -+ return err; -+ + len = skb->len - (nhoff + sizeof(*phdr)); ++ len = min(len, 0xFFFFU); + phdr->length = cpu_to_be16(len); + -+ return 0; ++ return INDIRECT_CALL_INET(ptype->callbacks.gro_complete, ++ ipv6_gro_complete, inet_gro_complete, ++ skb, nhoff + PPPOE_SES_HLEN); +} + +static struct sk_buff *pppoe_gso_segment(struct sk_buff *skb, + netdev_features_t features) +{ -+ unsigned int pppoe_hlen = sizeof(struct pppoe_hdr) + 2; + struct sk_buff *segs = ERR_PTR(-EINVAL); -+ u16 mac_offset = skb->mac_header; + struct packet_offload *ptype; -+ u16 mac_len = skb->mac_len; + struct pppoe_hdr *phdr; + __be16 orig_type, type; + int len, nhoff; @@ -149,7 +159,7 @@ Signed-off-by: Felix Fietkau + skb_reset_network_header(skb); + nhoff = skb_network_header(skb) - skb_mac_header(skb); + -+ if (unlikely(!pskb_may_pull(skb, pppoe_hlen))) ++ if (unlikely(!pskb_may_pull(skb, PPPOE_SES_HLEN))) + goto out; + + phdr = (struct pppoe_hdr *)skb_network_header(skb); @@ -159,13 +169,11 @@ Signed-off-by: Felix Fietkau + goto out; + + orig_type = skb->protocol; -+ __skb_pull(skb, pppoe_hlen); ++ __skb_pull(skb, PPPOE_SES_HLEN); ++ features &= ~NETIF_F_GSO_SOFTWARE; + segs = ptype->callbacks.gso_segment(skb, features); -+ if (IS_ERR_OR_NULL(segs)) { -+ skb_gso_error_unwind(skb, orig_type, pppoe_hlen, mac_offset, -+ mac_len); ++ if (IS_ERR_OR_NULL(segs)) + goto out; -+ } + + skb = segs; + do { @@ -194,19 +202,21 @@ Signed-off-by: Felix Fietkau static int __init pppoe_init(void) { int err; -@@ -1192,6 +1348,7 @@ static int __init pppoe_init(void) +@@ -1192,6 +1347,8 @@ static int __init pppoe_init(void) if (err) goto out_unregister_pppoe_proto; -+ dev_add_offload(&pppoe_packet_offload); ++ if (IS_ENABLED(CONFIG_INET)) ++ dev_add_offload(&pppoe_packet_offload); dev_add_pack(&pppoes_ptype); dev_add_pack(&pppoed_ptype); register_netdevice_notifier(&pppoe_notifier); -@@ -1211,6 +1368,7 @@ static void __exit pppoe_exit(void) +@@ -1211,6 +1368,8 @@ static void __exit pppoe_exit(void) unregister_netdevice_notifier(&pppoe_notifier); dev_remove_pack(&pppoed_ptype); dev_remove_pack(&pppoes_ptype); -+ dev_remove_offload(&pppoe_packet_offload); ++ if (IS_ENABLED(CONFIG_INET)) ++ dev_remove_offload(&pppoe_packet_offload); unregister_pppox_proto(PX_PROTO_OE); proto_unregister(&pppoe_sk_proto); unregister_pernet_device(&pppoe_net_ops);