650-net-pppoe-implement-GRO-support.patch 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. From: Felix Fietkau <[email protected]>
  2. Date: Tue, 15 Jul 2025 12:37:45 +0200
  3. Subject: [PATCH] net: pppoe: implement GRO support
  4. Only handles packets where the pppoe header length field matches the exact
  5. packet length. Significantly improves rx throughput.
  6. When running NAT traffic through a MediaTek MT7621 devices from a host
  7. behind PPPoE to a host directly connected via ethernet, the TCP throughput
  8. that the device is able to handle improves from ~130 Mbit/s to ~630 Mbit/s,
  9. using fraglist GRO.
  10. Signed-off-by: Felix Fietkau <[email protected]>
  11. ---
  12. --- a/drivers/net/ppp/pppoe.c
  13. +++ b/drivers/net/ppp/pppoe.c
  14. @@ -77,6 +77,7 @@
  15. #include <net/net_namespace.h>
  16. #include <net/netns/generic.h>
  17. #include <net/sock.h>
  18. +#include <net/gro.h>
  19. #include <linux/uaccess.h>
  20. @@ -435,7 +436,7 @@ static int pppoe_rcv(struct sk_buff *skb
  21. if (skb->len < len)
  22. goto drop;
  23. - if (pskb_trim_rcsum(skb, len))
  24. + if (!skb_is_gso(skb) && pskb_trim_rcsum(skb, len))
  25. goto drop;
  26. ph = pppoe_hdr(skb);
  27. @@ -1173,6 +1174,161 @@ static struct pernet_operations pppoe_ne
  28. .size = sizeof(struct pppoe_net),
  29. };
  30. +static u16
  31. +compare_pppoe_header(struct pppoe_hdr *phdr, struct pppoe_hdr *phdr2)
  32. +{
  33. + return (__force __u16)((phdr->sid ^ phdr2->sid) |
  34. + (phdr->tag[0].tag_type ^ phdr2->tag[0].tag_type));
  35. +}
  36. +
  37. +static __be16 pppoe_hdr_proto(struct pppoe_hdr *phdr)
  38. +{
  39. + switch (phdr->tag[0].tag_type) {
  40. + case cpu_to_be16(PPP_IP):
  41. + return cpu_to_be16(ETH_P_IP);
  42. + case cpu_to_be16(PPP_IPV6):
  43. + return cpu_to_be16(ETH_P_IPV6);
  44. + default:
  45. + return 0;
  46. + }
  47. +
  48. +}
  49. +
  50. +static struct sk_buff *pppoe_gro_receive(struct list_head *head,
  51. + struct sk_buff *skb)
  52. +{
  53. + const struct packet_offload *ptype;
  54. + unsigned int hlen, off_pppoe;
  55. + struct sk_buff *pp = NULL;
  56. + struct pppoe_hdr *phdr;
  57. + struct sk_buff *p;
  58. + int flush = 1;
  59. + __be16 type;
  60. +
  61. + off_pppoe = skb_gro_offset(skb);
  62. + hlen = off_pppoe + sizeof(*phdr);
  63. + phdr = skb_gro_header(skb, hlen + 2, off_pppoe);
  64. + if (unlikely(!phdr))
  65. + goto out;
  66. +
  67. + /* ignore packets with padding or invalid length */
  68. + if (skb_gro_len(skb) != be16_to_cpu(phdr->length) + hlen)
  69. + goto out;
  70. +
  71. + type = pppoe_hdr_proto(phdr);
  72. + if (!type)
  73. + goto out;
  74. +
  75. + ptype = gro_find_receive_by_type(type);
  76. + if (!ptype)
  77. + goto out;
  78. +
  79. + flush = 0;
  80. +
  81. + list_for_each_entry(p, head, list) {
  82. + struct pppoe_hdr *phdr2;
  83. +
  84. + if (!NAPI_GRO_CB(p)->same_flow)
  85. + continue;
  86. +
  87. + phdr2 = (struct pppoe_hdr *)(p->data + off_pppoe);
  88. + if (compare_pppoe_header(phdr, phdr2))
  89. + NAPI_GRO_CB(p)->same_flow = 0;
  90. + }
  91. +
  92. + skb_gro_pull(skb, sizeof(*phdr) + 2);
  93. + skb_gro_postpull_rcsum(skb, phdr, sizeof(*phdr) + 2);
  94. +
  95. + pp = indirect_call_gro_receive_inet(ptype->callbacks.gro_receive,
  96. + ipv6_gro_receive, inet_gro_receive,
  97. + head, skb);
  98. +
  99. +out:
  100. + skb_gro_flush_final(skb, pp, flush);
  101. +
  102. + return pp;
  103. +}
  104. +
  105. +static int pppoe_gro_complete(struct sk_buff *skb, int nhoff)
  106. +{
  107. + struct pppoe_hdr *phdr = (struct pppoe_hdr *)(skb->data + nhoff);
  108. + __be16 type = pppoe_hdr_proto(phdr);
  109. + struct packet_offload *ptype;
  110. + int len, err;
  111. +
  112. + ptype = gro_find_complete_by_type(type);
  113. + if (!ptype)
  114. + return -ENOENT;
  115. +
  116. + err = INDIRECT_CALL_INET(ptype->callbacks.gro_complete,
  117. + ipv6_gro_complete, inet_gro_complete,
  118. + skb, nhoff + sizeof(*phdr) + 2);
  119. + if (err)
  120. + return err;
  121. +
  122. + len = skb->len - (nhoff + sizeof(*phdr));
  123. + phdr->length = cpu_to_be16(len);
  124. +
  125. + return 0;
  126. +}
  127. +
  128. +static struct sk_buff *pppoe_gso_segment(struct sk_buff *skb,
  129. + netdev_features_t features)
  130. +{
  131. + unsigned int pppoe_hlen = sizeof(struct pppoe_hdr) + 2;
  132. + struct sk_buff *segs = ERR_PTR(-EINVAL);
  133. + u16 mac_offset = skb->mac_header;
  134. + struct packet_offload *ptype;
  135. + u16 mac_len = skb->mac_len;
  136. + struct pppoe_hdr *phdr;
  137. + __be16 orig_type, type;
  138. + int len, nhoff;
  139. +
  140. + skb_reset_network_header(skb);
  141. + nhoff = skb_network_header(skb) - skb_mac_header(skb);
  142. +
  143. + if (unlikely(!pskb_may_pull(skb, pppoe_hlen)))
  144. + goto out;
  145. +
  146. + phdr = (struct pppoe_hdr *)skb_network_header(skb);
  147. + type = pppoe_hdr_proto(phdr);
  148. + ptype = gro_find_complete_by_type(type);
  149. + if (!ptype)
  150. + goto out;
  151. +
  152. + orig_type = skb->protocol;
  153. + __skb_pull(skb, pppoe_hlen);
  154. + segs = ptype->callbacks.gso_segment(skb, features);
  155. + if (IS_ERR_OR_NULL(segs)) {
  156. + skb_gso_error_unwind(skb, orig_type, pppoe_hlen, mac_offset,
  157. + mac_len);
  158. + goto out;
  159. + }
  160. +
  161. + skb = segs;
  162. + do {
  163. + phdr = (struct pppoe_hdr *)(skb_mac_header(skb) + nhoff);
  164. + len = skb->len - (nhoff + sizeof(*phdr));
  165. + phdr->length = cpu_to_be16(len);
  166. + skb->network_header = (u8 *)phdr - skb->head;
  167. + skb->protocol = orig_type;
  168. + skb_reset_mac_len(skb);
  169. + } while ((skb = skb->next));
  170. +
  171. +out:
  172. + return segs;
  173. +}
  174. +
  175. +static struct packet_offload pppoe_packet_offload __read_mostly = {
  176. + .type = cpu_to_be16(ETH_P_PPP_SES),
  177. + .priority = 20,
  178. + .callbacks = {
  179. + .gro_receive = pppoe_gro_receive,
  180. + .gro_complete = pppoe_gro_complete,
  181. + .gso_segment = pppoe_gso_segment,
  182. + },
  183. +};
  184. +
  185. static int __init pppoe_init(void)
  186. {
  187. int err;
  188. @@ -1189,6 +1345,7 @@ static int __init pppoe_init(void)
  189. if (err)
  190. goto out_unregister_pppoe_proto;
  191. + dev_add_offload(&pppoe_packet_offload);
  192. dev_add_pack(&pppoes_ptype);
  193. dev_add_pack(&pppoed_ptype);
  194. register_netdevice_notifier(&pppoe_notifier);
  195. @@ -1208,6 +1365,7 @@ static void __exit pppoe_exit(void)
  196. unregister_netdevice_notifier(&pppoe_notifier);
  197. dev_remove_pack(&pppoed_ptype);
  198. dev_remove_pack(&pppoes_ptype);
  199. + dev_remove_offload(&pppoe_packet_offload);
  200. unregister_pppox_proto(PX_PROTO_OE);
  201. proto_unregister(&pppoe_sk_proto);
  202. unregister_pernet_device(&pppoe_net_ops);
  203. --- a/net/ipv4/af_inet.c
  204. +++ b/net/ipv4/af_inet.c
  205. @@ -1587,6 +1587,7 @@ out:
  206. return pp;
  207. }
  208. +EXPORT_INDIRECT_CALLABLE(inet_gro_receive);
  209. static struct sk_buff *ipip_gro_receive(struct list_head *head,
  210. struct sk_buff *skb)
  211. @@ -1672,6 +1673,7 @@ int inet_gro_complete(struct sk_buff *sk
  212. out:
  213. return err;
  214. }
  215. +EXPORT_INDIRECT_CALLABLE(inet_gro_complete);
  216. static int ipip_gro_complete(struct sk_buff *skb, int nhoff)
  217. {
  218. --- a/net/ipv6/ip6_offload.c
  219. +++ b/net/ipv6/ip6_offload.c
  220. @@ -319,6 +319,7 @@ out:
  221. return pp;
  222. }
  223. +EXPORT_INDIRECT_CALLABLE(ipv6_gro_receive);
  224. static struct sk_buff *sit_ip6ip6_gro_receive(struct list_head *head,
  225. struct sk_buff *skb)
  226. @@ -401,6 +402,7 @@ INDIRECT_CALLABLE_SCOPE int ipv6_gro_com
  227. out:
  228. return err;
  229. }
  230. +EXPORT_INDIRECT_CALLABLE(ipv6_gro_complete);
  231. static int sit_gro_complete(struct sk_buff *skb, int nhoff)
  232. {