diff --git a/target/linux/generic/pending-6.1/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/target/linux/generic/pending-6.1/680-NET-skip-GRO-for-foreign-MAC-addresses.patch deleted file mode 100644 index 39dded18b..000000000 --- a/target/linux/generic/pending-6.1/680-NET-skip-GRO-for-foreign-MAC-addresses.patch +++ /dev/null @@ -1,151 +0,0 @@ -From: Felix Fietkau -Subject: net: replace GRO optimization patch with a new one that supports VLANs/bridges with different MAC addresses - -Signed-off-by: Felix Fietkau ---- - include/linux/netdevice.h | 2 ++ - include/linux/skbuff.h | 3 ++- - net/core/dev.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++ - net/ethernet/eth.c | 18 +++++++++++++++++- - 4 files changed, 69 insertions(+), 2 deletions(-) - ---- a/include/linux/netdevice.h -+++ b/include/linux/netdevice.h -@@ -2135,6 +2135,8 @@ struct net_device { - struct netdev_hw_addr_list mc; - struct netdev_hw_addr_list dev_addrs; - -+ unsigned char local_addr_mask[MAX_ADDR_LEN]; -+ - #ifdef CONFIG_SYSFS - struct kset *queues_kset; - #endif ---- a/include/linux/skbuff.h -+++ b/include/linux/skbuff.h -@@ -967,6 +967,7 @@ struct sk_buff { - #ifdef CONFIG_IPV6_NDISC_NODETYPE - __u8 ndisc_nodetype:2; - #endif -+ __u8 gro_skip:1; - - __u8 ipvs_property:1; - __u8 inner_protocol_type:1; ---- a/net/core/gro.c -+++ b/net/core/gro.c -@@ -492,6 +492,9 @@ static enum gro_result dev_gro_receive(s - int same_flow; - int grow; - -+ if (skb->gro_skip) -+ goto normal; -+ - if (netif_elide_gro(skb->dev)) - goto normal; - ---- a/net/core/dev.c -+++ b/net/core/dev.c -@@ -7643,6 +7643,48 @@ static void __netdev_adjacent_dev_unlink - &upper_dev->adj_list.lower); - } - -+static void __netdev_addr_mask(unsigned char *mask, const unsigned char *addr, -+ struct net_device *dev) -+{ -+ int i; -+ -+ for (i = 0; i < dev->addr_len; i++) -+ mask[i] |= addr[i] ^ dev->dev_addr[i]; -+} -+ -+static void __netdev_upper_mask(unsigned char *mask, struct net_device *dev, -+ struct net_device *lower) -+{ -+ struct net_device *cur; -+ struct list_head *iter; -+ -+ netdev_for_each_upper_dev_rcu(dev, cur, iter) { -+ __netdev_addr_mask(mask, cur->dev_addr, lower); -+ __netdev_upper_mask(mask, cur, lower); -+ } -+} -+ -+static void __netdev_update_addr_mask(struct net_device *dev) -+{ -+ unsigned char mask[MAX_ADDR_LEN]; -+ struct net_device *cur; -+ struct list_head *iter; -+ -+ memset(mask, 0, sizeof(mask)); -+ __netdev_upper_mask(mask, dev, dev); -+ memcpy(dev->local_addr_mask, mask, dev->addr_len); -+ -+ netdev_for_each_lower_dev(dev, cur, iter) -+ __netdev_update_addr_mask(cur); -+} -+ -+static void netdev_update_addr_mask(struct net_device *dev) -+{ -+ rcu_read_lock(); -+ __netdev_update_addr_mask(dev); -+ rcu_read_unlock(); -+} -+ - static int __netdev_upper_dev_link(struct net_device *dev, - struct net_device *upper_dev, bool master, - void *upper_priv, void *upper_info, -@@ -7694,6 +7736,7 @@ static int __netdev_upper_dev_link(struc - if (ret) - return ret; - -+ netdev_update_addr_mask(dev); - ret = call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, - &changeupper_info.info); - ret = notifier_to_errno(ret); -@@ -7790,6 +7833,7 @@ static void __netdev_upper_dev_unlink(st - - __netdev_adjacent_dev_unlink_neighbour(dev, upper_dev); - -+ netdev_update_addr_mask(dev); - call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, - &changeupper_info.info); - -@@ -8842,6 +8886,7 @@ int dev_set_mac_address(struct net_devic - if (err) - return err; - dev->addr_assign_type = NET_ADDR_SET; -+ netdev_update_addr_mask(dev); - call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); - add_device_randomness(dev->dev_addr, dev->addr_len); - return 0; ---- a/net/ethernet/eth.c -+++ b/net/ethernet/eth.c -@@ -143,6 +143,18 @@ u32 eth_get_headlen(const struct net_dev - } - EXPORT_SYMBOL(eth_get_headlen); - -+static inline bool -+eth_check_local_mask(const void *addr1, const void *addr2, const void *mask) -+{ -+ const u16 *a1 = addr1; -+ const u16 *a2 = addr2; -+ const u16 *m = mask; -+ -+ return (((a1[0] ^ a2[0]) & ~m[0]) | -+ ((a1[1] ^ a2[1]) & ~m[1]) | -+ ((a1[2] ^ a2[2]) & ~m[2])); -+} -+ - /** - * eth_type_trans - determine the packet's protocol ID. - * @skb: received socket data -@@ -419,6 +431,10 @@ struct sk_buff *eth_gro_receive(struct l - NAPI_GRO_CB(p)->same_flow = 0; - continue; - } -+ -+ if (eth_check_local_mask(eth->h_dest, dev->dev_addr, -+ dev->local_addr_mask)) -+ skb->gro_skip = 1; - } - - type = eh->h_proto; diff --git a/target/linux/generic/pending-6.1/680-net-add-TCP-fraglist-GRO-support.patch b/target/linux/generic/pending-6.1/680-net-add-TCP-fraglist-GRO-support.patch new file mode 100644 index 000000000..f52233fe9 --- /dev/null +++ b/target/linux/generic/pending-6.1/680-net-add-TCP-fraglist-GRO-support.patch @@ -0,0 +1,627 @@ +From: Felix Fietkau +Date: Tue, 23 Apr 2024 11:23:03 +0200 +Subject: [PATCH] net: add TCP fraglist GRO support + +When forwarding TCP after GRO, software segmentation is very expensive, +especially when the checksum needs to be recalculated. +One case where that's currently unavoidable is when routing packets over +PPPoE. Performance improves significantly when using fraglist GRO +implemented in the same way as for UDP. + +Here's a measurement of running 2 TCP streams through a MediaTek MT7622 +device (2-core Cortex-A53), which runs NAT with flow offload enabled from +one ethernet port to PPPoE on another ethernet port + cake qdisc set to +1Gbps. + +rx-gro-list off: 630 Mbit/s, CPU 35% idle +rx-gro-list on: 770 Mbit/s, CPU 40% idle + +Signe-off-by: Felix Fietkau +--- + +--- a/include/net/gro.h ++++ b/include/net/gro.h +@@ -424,6 +424,7 @@ static inline __wsum ip6_gro_compute_pse + } + + int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb); ++int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb); + + /* Pass the currently batched GRO_NORMAL SKBs up to the stack. */ + static inline void gro_normal_list(struct napi_struct *napi) +@@ -446,5 +447,48 @@ static inline void gro_normal_one(struct + gro_normal_list(napi); + } + ++/* This function is the alternative of 'inet_iif' and 'inet_sdif' ++ * functions in case we can not rely on fields of IPCB. ++ * ++ * The caller must verify skb_valid_dst(skb) is false and skb->dev is initialized. ++ * The caller must hold the RCU read lock. ++ */ ++static inline void inet_get_iif_sdif(const struct sk_buff *skb, int *iif, int *sdif) ++{ ++ *iif = inet_iif(skb) ?: skb->dev->ifindex; ++ *sdif = 0; ++ ++#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV) ++ if (netif_is_l3_slave(skb->dev)) { ++ struct net_device *master = netdev_master_upper_dev_get_rcu(skb->dev); ++ ++ *sdif = *iif; ++ *iif = master ? master->ifindex : 0; ++ } ++#endif ++} ++ ++/* This function is the alternative of 'inet6_iif' and 'inet6_sdif' ++ * functions in case we can not rely on fields of IP6CB. ++ * ++ * The caller must verify skb_valid_dst(skb) is false and skb->dev is initialized. ++ * The caller must hold the RCU read lock. ++ */ ++static inline void inet6_get_iif_sdif(const struct sk_buff *skb, int *iif, int *sdif) ++{ ++ /* using skb->dev->ifindex because skb_dst(skb) is not initialized */ ++ *iif = skb->dev->ifindex; ++ *sdif = 0; ++ ++#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV) ++ if (netif_is_l3_slave(skb->dev)) { ++ struct net_device *master = netdev_master_upper_dev_get_rcu(skb->dev); ++ ++ *sdif = *iif; ++ *iif = master ? master->ifindex : 0; ++ } ++#endif ++} ++ + + #endif /* _NET_IPV6_GRO_H */ +--- a/include/net/tcp.h ++++ b/include/net/tcp.h +@@ -2057,7 +2057,10 @@ void tcp_v4_destroy_sock(struct sock *sk + + struct sk_buff *tcp_gso_segment(struct sk_buff *skb, + netdev_features_t features); +-struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb); ++struct tcphdr *tcp_gro_pull_header(struct sk_buff *skb); ++struct sk_buff *tcp_gro_lookup(struct list_head *head, struct tcphdr *th); ++struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb, ++ struct tcphdr *th); + INDIRECT_CALLABLE_DECLARE(int tcp4_gro_complete(struct sk_buff *skb, int thoff)); + INDIRECT_CALLABLE_DECLARE(struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)); + INDIRECT_CALLABLE_DECLARE(int tcp6_gro_complete(struct sk_buff *skb, int thoff)); +--- a/net/core/gro.c ++++ b/net/core/gro.c +@@ -290,6 +290,33 @@ done: + return 0; + } + ++int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb) ++{ ++ if (unlikely(p->len + skb->len >= 65536)) ++ return -E2BIG; ++ ++ if (NAPI_GRO_CB(p)->last == p) ++ skb_shinfo(p)->frag_list = skb; ++ else ++ NAPI_GRO_CB(p)->last->next = skb; ++ ++ skb_pull(skb, skb_gro_offset(skb)); ++ ++ NAPI_GRO_CB(p)->last = skb; ++ NAPI_GRO_CB(p)->count++; ++ p->data_len += skb->len; ++ ++ /* sk ownership - if any - completely transferred to the aggregated packet */ ++ skb->destructor = NULL; ++ skb->sk = NULL; ++ p->truesize += skb->truesize; ++ p->len += skb->len; ++ ++ NAPI_GRO_CB(skb)->same_flow = 1; ++ ++ return 0; ++} ++ + + static void napi_gro_complete(struct napi_struct *napi, struct sk_buff *skb) + { +--- a/net/ipv4/tcp_offload.c ++++ b/net/ipv4/tcp_offload.c +@@ -27,6 +27,70 @@ static void tcp_gso_tstamp(struct sk_buf + } + } + ++static void __tcpv4_gso_segment_csum(struct sk_buff *seg, ++ __be32 *oldip, __be32 newip, ++ __be16 *oldport, __be16 newport) ++{ ++ struct tcphdr *th; ++ struct iphdr *iph; ++ ++ if (*oldip == newip && *oldport == newport) ++ return; ++ ++ th = tcp_hdr(seg); ++ iph = ip_hdr(seg); ++ ++ inet_proto_csum_replace4(&th->check, seg, *oldip, newip, true); ++ inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false); ++ *oldport = newport; ++ ++ csum_replace4(&iph->check, *oldip, newip); ++ *oldip = newip; ++} ++ ++static struct sk_buff *__tcpv4_gso_segment_list_csum(struct sk_buff *segs) ++{ ++ const struct tcphdr *th; ++ const struct iphdr *iph; ++ struct sk_buff *seg; ++ struct tcphdr *th2; ++ struct iphdr *iph2; ++ ++ seg = segs; ++ th = tcp_hdr(seg); ++ iph = ip_hdr(seg); ++ th2 = tcp_hdr(seg->next); ++ iph2 = ip_hdr(seg->next); ++ ++ if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) && ++ iph->daddr == iph2->daddr && iph->saddr == iph2->saddr) ++ return segs; ++ ++ while ((seg = seg->next)) { ++ th2 = tcp_hdr(seg); ++ iph2 = ip_hdr(seg); ++ ++ __tcpv4_gso_segment_csum(seg, ++ &iph2->saddr, iph->saddr, ++ &th2->source, th->source); ++ __tcpv4_gso_segment_csum(seg, ++ &iph2->daddr, iph->daddr, ++ &th2->dest, th->dest); ++ } ++ ++ return segs; ++} ++ ++static struct sk_buff *__tcp4_gso_segment_list(struct sk_buff *skb, ++ netdev_features_t features) ++{ ++ skb = skb_segment_list(skb, features, skb_mac_header_len(skb)); ++ if (IS_ERR(skb)) ++ return skb; ++ ++ return __tcpv4_gso_segment_list_csum(skb); ++} ++ + static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb, + netdev_features_t features) + { +@@ -36,6 +100,9 @@ static struct sk_buff *tcp4_gso_segment( + if (!pskb_may_pull(skb, sizeof(struct tcphdr))) + return ERR_PTR(-EINVAL); + ++ if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST) ++ return __tcp4_gso_segment_list(skb, features); ++ + if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { + const struct iphdr *iph = ip_hdr(skb); + struct tcphdr *th = tcp_hdr(skb); +@@ -177,61 +244,76 @@ out: + return segs; + } + +-struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb) ++struct sk_buff *tcp_gro_lookup(struct list_head *head, struct tcphdr *th) + { +- struct sk_buff *pp = NULL; ++ struct tcphdr *th2; + struct sk_buff *p; ++ ++ list_for_each_entry(p, head, list) { ++ if (!NAPI_GRO_CB(p)->same_flow) ++ continue; ++ ++ th2 = tcp_hdr(p); ++ if (*(u32 *)&th->source ^ *(u32 *)&th2->source) { ++ NAPI_GRO_CB(p)->same_flow = 0; ++ continue; ++ } ++ ++ return p; ++ } ++ ++ return NULL; ++} ++ ++struct tcphdr *tcp_gro_pull_header(struct sk_buff *skb) ++{ ++ unsigned int thlen, hlen, off; + struct tcphdr *th; +- struct tcphdr *th2; +- unsigned int len; +- unsigned int thlen; +- __be32 flags; +- unsigned int mss = 1; +- unsigned int hlen; +- unsigned int off; +- int flush = 1; +- int i; + + off = skb_gro_offset(skb); + hlen = off + sizeof(*th); + th = skb_gro_header(skb, hlen, off); + if (unlikely(!th)) +- goto out; ++ return NULL; + + thlen = th->doff * 4; + if (thlen < sizeof(*th)) +- goto out; ++ return NULL; + + hlen = off + thlen; + if (skb_gro_header_hard(skb, hlen)) { + th = skb_gro_header_slow(skb, hlen, off); + if (unlikely(!th)) +- goto out; ++ return NULL; + } + + skb_gro_pull(skb, thlen); + +- len = skb_gro_len(skb); +- flags = tcp_flag_word(th); +- +- list_for_each_entry(p, head, list) { +- if (!NAPI_GRO_CB(p)->same_flow) +- continue; ++ return th; ++} + +- th2 = tcp_hdr(p); ++struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb, ++ struct tcphdr *th) ++{ ++ unsigned int thlen = th->doff * 4; ++ struct sk_buff *pp = NULL; ++ struct sk_buff *p; ++ struct tcphdr *th2; ++ unsigned int len; ++ __be32 flags; ++ unsigned int mss = 1; ++ int flush = 1; ++ int i; + +- if (*(u32 *)&th->source ^ *(u32 *)&th2->source) { +- NAPI_GRO_CB(p)->same_flow = 0; +- continue; +- } ++ len = skb_gro_len(skb); ++ flags = tcp_flag_word(th); + +- goto found; +- } +- p = NULL; +- goto out_check_final; ++ p = tcp_gro_lookup(head, th); ++ if (!p) ++ goto out_check_final; + +-found: + /* Include the IP ID check below from the inner most IP hdr */ ++ th2 = tcp_hdr(p); + flush = NAPI_GRO_CB(p)->flush; + flush |= (__force int)(flags & TCP_FLAG_CWR); + flush |= (__force int)((flags ^ tcp_flag_word(th2)) & +@@ -268,6 +350,19 @@ found: + flush |= p->decrypted ^ skb->decrypted; + #endif + ++ if (unlikely(NAPI_GRO_CB(p)->is_flist)) { ++ flush |= (__force int)(flags ^ tcp_flag_word(th2)); ++ flush |= skb->ip_summed != p->ip_summed; ++ flush |= skb->csum_level != p->csum_level; ++ flush |= !pskb_may_pull(skb, skb_gro_offset(skb)); ++ flush |= NAPI_GRO_CB(p)->count >= 64; ++ ++ if (flush || skb_gro_receive_list(p, skb)) ++ mss = 1; ++ ++ goto out_check_final; ++ } ++ + if (flush || skb_gro_receive(p, skb)) { + mss = 1; + goto out_check_final; +@@ -289,7 +384,6 @@ out_check_final: + if (p && (!NAPI_GRO_CB(skb)->same_flow || flush)) + pp = p; + +-out: + NAPI_GRO_CB(skb)->flush |= (flush != 0); + + return pp; +@@ -315,18 +409,58 @@ int tcp_gro_complete(struct sk_buff *skb + } + EXPORT_SYMBOL(tcp_gro_complete); + ++static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb, ++ struct tcphdr *th) ++{ ++ const struct iphdr *iph; ++ struct sk_buff *p; ++ struct sock *sk; ++ struct net *net; ++ int iif, sdif; ++ ++ if (!(skb->dev->features & NETIF_F_GRO_FRAGLIST)) ++ return; ++ ++ p = tcp_gro_lookup(head, th); ++ if (p) { ++ NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist; ++ return; ++ } ++ ++ inet_get_iif_sdif(skb, &iif, &sdif); ++ iph = skb_gro_network_header(skb); ++ net = dev_net(skb->dev); ++ sk = __inet_lookup_established(net, net->ipv4.tcp_death_row.hashinfo, ++ iph->saddr, th->source, ++ iph->daddr, ntohs(th->dest), ++ iif, sdif); ++ NAPI_GRO_CB(skb)->is_flist = !sk; ++ if (sk) ++ sock_put(sk); ++} ++ + INDIRECT_CALLABLE_SCOPE + struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb) + { ++ struct tcphdr *th; ++ + /* Don't bother verifying checksum if we're going to flush anyway. */ + if (!NAPI_GRO_CB(skb)->flush && + skb_gro_checksum_validate(skb, IPPROTO_TCP, +- inet_gro_compute_pseudo)) { +- NAPI_GRO_CB(skb)->flush = 1; +- return NULL; +- } ++ inet_gro_compute_pseudo)) ++ goto flush; ++ ++ th = tcp_gro_pull_header(skb); ++ if (!th) ++ goto flush; + +- return tcp_gro_receive(head, skb); ++ tcp4_check_fraglist_gro(head, skb, th); ++ ++ return tcp_gro_receive(head, skb, th); ++ ++flush: ++ NAPI_GRO_CB(skb)->flush = 1; ++ return NULL; + } + + INDIRECT_CALLABLE_SCOPE int tcp4_gro_complete(struct sk_buff *skb, int thoff) +@@ -334,6 +468,15 @@ INDIRECT_CALLABLE_SCOPE int tcp4_gro_com + const struct iphdr *iph = ip_hdr(skb); + struct tcphdr *th = tcp_hdr(skb); + ++ if (unlikely(NAPI_GRO_CB(skb)->is_flist)) { ++ skb_shinfo(skb)->gso_type |= SKB_GSO_FRAGLIST | SKB_GSO_TCPV4; ++ skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count; ++ ++ __skb_incr_checksum_unnecessary(skb); ++ ++ return 0; ++ } ++ + th->check = ~tcp_v4_check(skb->len - thoff, iph->saddr, + iph->daddr, 0); + skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV4; +--- a/net/ipv4/udp_offload.c ++++ b/net/ipv4/udp_offload.c +@@ -425,33 +425,6 @@ out: + return segs; + } + +-static int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb) +-{ +- if (unlikely(p->len + skb->len >= 65536)) +- return -E2BIG; +- +- if (NAPI_GRO_CB(p)->last == p) +- skb_shinfo(p)->frag_list = skb; +- else +- NAPI_GRO_CB(p)->last->next = skb; +- +- skb_pull(skb, skb_gro_offset(skb)); +- +- NAPI_GRO_CB(p)->last = skb; +- NAPI_GRO_CB(p)->count++; +- p->data_len += skb->len; +- +- /* sk ownership - if any - completely transferred to the aggregated packet */ +- skb->destructor = NULL; +- skb->sk = NULL; +- p->truesize += skb->truesize; +- p->len += skb->len; +- +- NAPI_GRO_CB(skb)->same_flow = 1; +- +- return 0; +-} +- + + #define UDP_GRO_CNT_MAX 64 + static struct sk_buff *udp_gro_receive_segment(struct list_head *head, +--- a/net/ipv6/tcpv6_offload.c ++++ b/net/ipv6/tcpv6_offload.c +@@ -7,24 +7,67 @@ + */ + #include + #include ++#include + #include + #include + #include + #include + #include "ip6_offload.h" + ++static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb, ++ struct tcphdr *th) ++{ ++#if IS_ENABLED(CONFIG_IPV6) ++ const struct ipv6hdr *hdr; ++ struct sk_buff *p; ++ struct sock *sk; ++ struct net *net; ++ int iif, sdif; ++ ++ if (!(skb->dev->features & NETIF_F_GRO_FRAGLIST)) ++ return; ++ ++ p = tcp_gro_lookup(head, th); ++ if (p) { ++ NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist; ++ return; ++ } ++ ++ inet6_get_iif_sdif(skb, &iif, &sdif); ++ hdr = skb_gro_network_header(skb); ++ net = dev_net(skb->dev); ++ sk = __inet6_lookup_established(net, net->ipv4.tcp_death_row.hashinfo, ++ &hdr->saddr, th->source, ++ &hdr->daddr, ntohs(th->dest), ++ iif, sdif); ++ NAPI_GRO_CB(skb)->is_flist = !sk; ++ if (sk) ++ sock_put(sk); ++#endif /* IS_ENABLED(CONFIG_IPV6) */ ++} ++ + INDIRECT_CALLABLE_SCOPE + struct sk_buff *tcp6_gro_receive(struct list_head *head, struct sk_buff *skb) + { ++ struct tcphdr *th; ++ + /* Don't bother verifying checksum if we're going to flush anyway. */ + if (!NAPI_GRO_CB(skb)->flush && + skb_gro_checksum_validate(skb, IPPROTO_TCP, +- ip6_gro_compute_pseudo)) { +- NAPI_GRO_CB(skb)->flush = 1; +- return NULL; +- } ++ ip6_gro_compute_pseudo)) ++ goto flush; + +- return tcp_gro_receive(head, skb); ++ th = tcp_gro_pull_header(skb); ++ if (!th) ++ goto flush; ++ ++ tcp6_check_fraglist_gro(head, skb, th); ++ ++ return tcp_gro_receive(head, skb, th); ++ ++flush: ++ NAPI_GRO_CB(skb)->flush = 1; ++ return NULL; + } + + INDIRECT_CALLABLE_SCOPE int tcp6_gro_complete(struct sk_buff *skb, int thoff) +@@ -32,6 +75,15 @@ INDIRECT_CALLABLE_SCOPE int tcp6_gro_com + const struct ipv6hdr *iph = ipv6_hdr(skb); + struct tcphdr *th = tcp_hdr(skb); + ++ if (unlikely(NAPI_GRO_CB(skb)->is_flist)) { ++ skb_shinfo(skb)->gso_type |= SKB_GSO_FRAGLIST | SKB_GSO_TCPV6; ++ skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count; ++ ++ __skb_incr_checksum_unnecessary(skb); ++ ++ return 0; ++ } ++ + th->check = ~tcp_v6_check(skb->len - thoff, &iph->saddr, + &iph->daddr, 0); + skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV6; +@@ -39,6 +91,61 @@ INDIRECT_CALLABLE_SCOPE int tcp6_gro_com + return tcp_gro_complete(skb); + } + ++static void __tcpv6_gso_segment_csum(struct sk_buff *seg, ++ __be16 *oldport, __be16 newport) ++{ ++ struct tcphdr *th; ++ ++ if (*oldport == newport) ++ return; ++ ++ th = tcp_hdr(seg); ++ inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false); ++ *oldport = newport; ++} ++ ++static struct sk_buff *__tcpv6_gso_segment_list_csum(struct sk_buff *segs) ++{ ++ const struct tcphdr *th; ++ const struct ipv6hdr *iph; ++ struct sk_buff *seg; ++ struct tcphdr *th2; ++ struct ipv6hdr *iph2; ++ ++ seg = segs; ++ th = tcp_hdr(seg); ++ iph = ipv6_hdr(seg); ++ th2 = tcp_hdr(seg->next); ++ iph2 = ipv6_hdr(seg->next); ++ ++ if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) && ++ ipv6_addr_equal(&iph->saddr, &iph2->saddr) && ++ ipv6_addr_equal(&iph->daddr, &iph2->daddr)) ++ return segs; ++ ++ while ((seg = seg->next)) { ++ th2 = tcp_hdr(seg); ++ iph2 = ipv6_hdr(seg); ++ ++ iph2->saddr = iph->saddr; ++ iph2->daddr = iph->daddr; ++ __tcpv6_gso_segment_csum(seg, &th2->source, th->source); ++ __tcpv6_gso_segment_csum(seg, &th2->dest, th->dest); ++ } ++ ++ return segs; ++} ++ ++static struct sk_buff *__tcp6_gso_segment_list(struct sk_buff *skb, ++ netdev_features_t features) ++{ ++ skb = skb_segment_list(skb, features, skb_mac_header_len(skb)); ++ if (IS_ERR(skb)) ++ return skb; ++ ++ return __tcpv6_gso_segment_list_csum(skb); ++} ++ + static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb, + netdev_features_t features) + { +@@ -50,6 +157,9 @@ static struct sk_buff *tcp6_gso_segment( + if (!pskb_may_pull(skb, sizeof(*th))) + return ERR_PTR(-EINVAL); + ++ if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST) ++ return __tcp6_gso_segment_list(skb, features); ++ + if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { + const struct ipv6hdr *ipv6h = ipv6_hdr(skb); + struct tcphdr *th = tcp_hdr(skb); diff --git a/target/linux/generic/pending-6.1/681-net-bridge-fix-multicast-to-unicast-with-fraglist-GS.patch b/target/linux/generic/pending-6.1/681-net-bridge-fix-multicast-to-unicast-with-fraglist-GS.patch new file mode 100644 index 000000000..6a53a678d --- /dev/null +++ b/target/linux/generic/pending-6.1/681-net-bridge-fix-multicast-to-unicast-with-fraglist-GS.patch @@ -0,0 +1,23 @@ +From: Felix Fietkau +Date: Sat, 27 Apr 2024 18:54:25 +0200 +Subject: [PATCH] net: bridge: fix multicast-to-unicast with fraglist GSO + +Calling skb_copy on a SKB_GSO_FRAGLIST skb is not valid, since it returns +an invalid linearized skb. This code only needs to change the ethernet +header, so pskb_copy is the right function to call here. + +Fixes: 6db6f0eae605 ("bridge: multicast to unicast") +Signed-off-by: Felix Fietkau +--- + +--- a/net/bridge/br_forward.c ++++ b/net/bridge/br_forward.c +@@ -261,7 +261,7 @@ static void maybe_deliver_addr(struct ne + if (skb->dev == p->dev && ether_addr_equal(src, addr)) + return; + +- skb = skb_copy(skb, GFP_ATOMIC); ++ skb = pskb_copy(skb, GFP_ATOMIC); + if (!skb) { + DEV_STATS_INC(dev, tx_dropped); + return; diff --git a/target/linux/generic/pending-6.1/682-net-core-reject-skb_copy-_expand-for-fraglist-GSO-sk.patch b/target/linux/generic/pending-6.1/682-net-core-reject-skb_copy-_expand-for-fraglist-GSO-sk.patch new file mode 100644 index 000000000..719cac9bc --- /dev/null +++ b/target/linux/generic/pending-6.1/682-net-core-reject-skb_copy-_expand-for-fraglist-GSO-sk.patch @@ -0,0 +1,59 @@ +From: Felix Fietkau +Date: Sat, 27 Apr 2024 19:29:45 +0200 +Subject: [PATCH] net: core: reject skb_copy(_expand) for fraglist GSO skbs + +SKB_GSO_FRAGLIST skbs must not be linearized, otherwise they become +invalid. Return NULL if such an skb is passed to skb_copy or +skb_copy_expand, in order to prevent a crash on a potential later +call to skb_gso_segment. + +Fixes: 3a1296a38d0c ("net: Support GRO/GSO fraglist chaining.") +Signed-off-by: Felix Fietkau +--- + +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -1720,11 +1720,17 @@ static inline int skb_alloc_rx_flag(cons + + struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask) + { +- int headerlen = skb_headroom(skb); +- unsigned int size = skb_end_offset(skb) + skb->data_len; +- struct sk_buff *n = __alloc_skb(size, gfp_mask, +- skb_alloc_rx_flag(skb), NUMA_NO_NODE); ++ struct sk_buff *n; ++ unsigned int size; ++ int headerlen; + ++ if (WARN_ON_ONCE(skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)) ++ return NULL; ++ ++ headerlen = skb_headroom(skb); ++ size = skb_end_offset(skb) + skb->data_len; ++ n = __alloc_skb(size, gfp_mask, ++ skb_alloc_rx_flag(skb), NUMA_NO_NODE); + if (!n) + return NULL; + +@@ -2037,12 +2043,17 @@ struct sk_buff *skb_copy_expand(const st + /* + * Allocate the copy buffer + */ +- struct sk_buff *n = __alloc_skb(newheadroom + skb->len + newtailroom, +- gfp_mask, skb_alloc_rx_flag(skb), +- NUMA_NO_NODE); +- int oldheadroom = skb_headroom(skb); + int head_copy_len, head_copy_off; ++ struct sk_buff *n; ++ int oldheadroom; ++ ++ if (WARN_ON_ONCE(skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)) ++ return NULL; + ++ oldheadroom = skb_headroom(skb); ++ n = __alloc_skb(newheadroom + skb->len + newtailroom, ++ gfp_mask, skb_alloc_rx_flag(skb), ++ NUMA_NO_NODE); + if (!n) + return NULL; + diff --git a/target/linux/generic/pending-6.1/682-of_net-add-mac-address-increment-support.patch b/target/linux/generic/pending-6.1/682-of_net-add-mac-address-increment-support.patch deleted file mode 100644 index f6ae9f31f..000000000 --- a/target/linux/generic/pending-6.1/682-of_net-add-mac-address-increment-support.patch +++ /dev/null @@ -1,90 +0,0 @@ -From 844c273286f328acf0dab5fbd5d864366b4904dc Mon Sep 17 00:00:00 2001 -From: Ansuel Smith -Date: Tue, 30 Mar 2021 18:21:14 +0200 -Subject: [PATCH] of_net: add mac-address-increment support - -Lots of embedded devices use the mac-address of other interface -extracted from nvmem cells and increments it by one or two. Add two -bindings to integrate this and directly use the right mac-address for -the interface. Some example are some routers that use the gmac -mac-address stored in the art partition and increments it by one for the -wifi. mac-address-increment-byte bindings is used to tell what byte of -the mac-address has to be increased (if not defined the last byte is -increased) and mac-address-increment tells how much the byte decided -early has to be increased. - -Signed-off-by: Ansuel Smith ---- - net/core/of_net.c | 43 +++++++++++++++++++++++++++++++++++++++---- - 1 file changed, 39 insertions(+), 4 deletions(-) - ---- a/net/core/of_net.c -+++ b/net/core/of_net.c -@@ -119,28 +119,63 @@ static int of_get_mac_addr_nvmem(struct - * this case, the real MAC is in 'local-mac-address', and 'mac-address' exists - * but is all zeros. - * -+ * DT can tell the system to increment the mac-address after is extracted by -+ * using: -+ * - mac-address-increment-byte to decide what byte to increase -+ * (if not defined is increased the last byte) -+ * - mac-address-increment to decide how much to increase. The value WILL -+ * overflow to other bytes if the increment is over 255 or the total -+ * increment will exceed 255 of the current byte. -+ * (example 00:01:02:03:04:ff + 1 == 00:01:02:03:05:00) -+ * (example 00:01:02:03:04:fe + 5 == 00:01:02:03:05:03) -+ * - * Return: 0 on success and errno in case of error. - */ - int of_get_mac_address(struct device_node *np, u8 *addr) - { -+ u32 inc_idx, mac_inc, mac_val; - int ret; - -+ /* Check first if the increment byte is present and valid. -+ * If not set assume to increment the last byte if found. -+ */ -+ if (of_property_read_u32(np, "mac-address-increment-byte", &inc_idx)) -+ inc_idx = 5; -+ if (inc_idx < 3 || inc_idx > 5) -+ return -EINVAL; -+ - if (!np) - return -ENODEV; - - ret = of_get_mac_addr(np, "mac-address", addr); - if (!ret) -- return 0; -+ goto found; - - ret = of_get_mac_addr(np, "local-mac-address", addr); - if (!ret) -- return 0; -+ goto found; - - ret = of_get_mac_addr(np, "address", addr); - if (!ret) -- return 0; -+ goto found; -+ -+ ret = of_get_mac_addr_nvmem(np, addr); -+ if (ret) -+ return ret; -+ -+found: -+ if (!of_property_read_u32(np, "mac-address-increment", &mac_inc)) { -+ /* Convert to a contiguous value */ -+ mac_val = (addr[3] << 16) + (addr[4] << 8) + addr[5]; -+ mac_val += mac_inc << 8 * (5-inc_idx); -+ -+ /* Apply the incremented value handling overflow case */ -+ addr[3] = (mac_val >> 16) & 0xff; -+ addr[4] = (mac_val >> 8) & 0xff; -+ addr[5] = (mac_val >> 0) & 0xff; -+ } - -- return of_get_mac_addr_nvmem(np, addr); -+ return ret; - } - EXPORT_SYMBOL(of_get_mac_address); - diff --git a/target/linux/generic/pending-6.1/683-of_net-add-mac-address-to-of-tree.patch b/target/linux/generic/pending-6.1/683-of_net-add-mac-address-to-of-tree.patch index f7ef06a14..03ee537fb 100644 --- a/target/linux/generic/pending-6.1/683-of_net-add-mac-address-to-of-tree.patch +++ b/target/linux/generic/pending-6.1/683-of_net-add-mac-address-to-of-tree.patch @@ -45,11 +45,31 @@ property. This way, the MAC address can be accessed using procfs. /** * of_get_mac_address() * @np: Caller's Device Node -@@ -175,6 +196,7 @@ found: - addr[5] = (mac_val >> 0) & 0xff; - } +@@ -130,17 +151,23 @@ int of_get_mac_address(struct device_nod -+ of_add_mac_address(np, addr); - return ret; + ret = of_get_mac_addr(np, "mac-address", addr); + if (!ret) +- return 0; ++ goto found; + + ret = of_get_mac_addr(np, "local-mac-address", addr); + if (!ret) +- return 0; ++ goto found; + + ret = of_get_mac_addr(np, "address", addr); + if (!ret) +- return 0; ++ goto found; + +- return of_get_mac_addr_nvmem(np, addr); ++ ret = of_get_mac_addr_nvmem(np, addr); ++ if (ret) ++ return ret; ++ ++found: ++ ret = of_add_mac_address(np, addr); ++ return ret; } EXPORT_SYMBOL(of_get_mac_address); + diff --git a/target/linux/generic/pending-6.1/684-of_net-do-mac-address-increment-only-once.patch b/target/linux/generic/pending-6.1/684-of_net-do-mac-address-increment-only-once.patch deleted file mode 100644 index 44d88e31a..000000000 --- a/target/linux/generic/pending-6.1/684-of_net-do-mac-address-increment-only-once.patch +++ /dev/null @@ -1,31 +0,0 @@ -From dd07dd394d8bfdb5d527fab18ca54f20815ec4e4 Mon Sep 17 00:00:00 2001 -From: Will Moss -Date: Wed, 3 Aug 2022 13:48:55 +0000 -Subject: [PATCH] of_net: do mac-address-increment only once - -Remove mac-address-increment and mac-address-increment-byte -DT property after incrementing process to make sure MAC address -would not get incremented more if this function is stared again. -It could happen if device initialization is deferred after -unsuccessful attempt. - -Signed-off-by: Will Moss ---- - drivers/of/of_net.c | 6 ++++++ - 1 file changed, 6 insertions(+) - ---- a/net/core/of_net.c -+++ b/net/core/of_net.c -@@ -194,6 +194,12 @@ found: - addr[3] = (mac_val >> 16) & 0xff; - addr[4] = (mac_val >> 8) & 0xff; - addr[5] = (mac_val >> 0) & 0xff; -+ -+ /* Remove mac-address-increment and mac-address-increment-byte -+ * DT property to make sure MAC address would not get incremented -+ * more if this function is stared again. */ -+ of_remove_property(np, of_find_property(np, "mac-address-increment", NULL)); -+ of_remove_property(np, of_find_property(np, "mac-address-increment-byte", NULL)); - } - - of_add_mac_address(np, addr); diff --git a/target/linux/generic/pending-6.6/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/target/linux/generic/pending-6.6/680-NET-skip-GRO-for-foreign-MAC-addresses.patch deleted file mode 100644 index e11b45c5c..000000000 --- a/target/linux/generic/pending-6.6/680-NET-skip-GRO-for-foreign-MAC-addresses.patch +++ /dev/null @@ -1,151 +0,0 @@ -From: Felix Fietkau -Subject: net: replace GRO optimization patch with a new one that supports VLANs/bridges with different MAC addresses - -Signed-off-by: Felix Fietkau ---- - include/linux/netdevice.h | 2 ++ - include/linux/skbuff.h | 3 ++- - net/core/dev.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++ - net/ethernet/eth.c | 18 +++++++++++++++++- - 4 files changed, 69 insertions(+), 2 deletions(-) - ---- a/include/linux/netdevice.h -+++ b/include/linux/netdevice.h -@@ -2210,6 +2210,8 @@ struct net_device { - struct netdev_hw_addr_list mc; - struct netdev_hw_addr_list dev_addrs; - -+ unsigned char local_addr_mask[MAX_ADDR_LEN]; -+ - #ifdef CONFIG_SYSFS - struct kset *queues_kset; - #endif ---- a/include/linux/skbuff.h -+++ b/include/linux/skbuff.h -@@ -959,6 +959,7 @@ struct sk_buff { - #ifdef CONFIG_IPV6_NDISC_NODETYPE - __u8 ndisc_nodetype:2; - #endif -+ __u8 gro_skip:1; - - #if IS_ENABLED(CONFIG_IP_VS) - __u8 ipvs_property:1; ---- a/net/core/gro.c -+++ b/net/core/gro.c -@@ -446,6 +446,9 @@ static enum gro_result dev_gro_receive(s - enum gro_result ret; - int same_flow; - -+ if (skb->gro_skip) -+ goto normal; -+ - if (netif_elide_gro(skb->dev)) - goto normal; - ---- a/net/core/dev.c -+++ b/net/core/dev.c -@@ -7689,6 +7689,48 @@ static void __netdev_adjacent_dev_unlink - &upper_dev->adj_list.lower); - } - -+static void __netdev_addr_mask(unsigned char *mask, const unsigned char *addr, -+ struct net_device *dev) -+{ -+ int i; -+ -+ for (i = 0; i < dev->addr_len; i++) -+ mask[i] |= addr[i] ^ dev->dev_addr[i]; -+} -+ -+static void __netdev_upper_mask(unsigned char *mask, struct net_device *dev, -+ struct net_device *lower) -+{ -+ struct net_device *cur; -+ struct list_head *iter; -+ -+ netdev_for_each_upper_dev_rcu(dev, cur, iter) { -+ __netdev_addr_mask(mask, cur->dev_addr, lower); -+ __netdev_upper_mask(mask, cur, lower); -+ } -+} -+ -+static void __netdev_update_addr_mask(struct net_device *dev) -+{ -+ unsigned char mask[MAX_ADDR_LEN]; -+ struct net_device *cur; -+ struct list_head *iter; -+ -+ memset(mask, 0, sizeof(mask)); -+ __netdev_upper_mask(mask, dev, dev); -+ memcpy(dev->local_addr_mask, mask, dev->addr_len); -+ -+ netdev_for_each_lower_dev(dev, cur, iter) -+ __netdev_update_addr_mask(cur); -+} -+ -+static void netdev_update_addr_mask(struct net_device *dev) -+{ -+ rcu_read_lock(); -+ __netdev_update_addr_mask(dev); -+ rcu_read_unlock(); -+} -+ - static int __netdev_upper_dev_link(struct net_device *dev, - struct net_device *upper_dev, bool master, - void *upper_priv, void *upper_info, -@@ -7740,6 +7782,7 @@ static int __netdev_upper_dev_link(struc - if (ret) - return ret; - -+ netdev_update_addr_mask(dev); - ret = call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, - &changeupper_info.info); - ret = notifier_to_errno(ret); -@@ -7836,6 +7879,7 @@ static void __netdev_upper_dev_unlink(st - - __netdev_adjacent_dev_unlink_neighbour(dev, upper_dev); - -+ netdev_update_addr_mask(dev); - call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, - &changeupper_info.info); - -@@ -8892,6 +8936,7 @@ int dev_set_mac_address(struct net_devic - return err; - } - dev->addr_assign_type = NET_ADDR_SET; -+ netdev_update_addr_mask(dev); - call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); - add_device_randomness(dev->dev_addr, dev->addr_len); - return 0; ---- a/net/ethernet/eth.c -+++ b/net/ethernet/eth.c -@@ -143,6 +143,18 @@ u32 eth_get_headlen(const struct net_dev - } - EXPORT_SYMBOL(eth_get_headlen); - -+static inline bool -+eth_check_local_mask(const void *addr1, const void *addr2, const void *mask) -+{ -+ const u16 *a1 = addr1; -+ const u16 *a2 = addr2; -+ const u16 *m = mask; -+ -+ return (((a1[0] ^ a2[0]) & ~m[0]) | -+ ((a1[1] ^ a2[1]) & ~m[1]) | -+ ((a1[2] ^ a2[2]) & ~m[2])); -+} -+ - /** - * eth_type_trans - determine the packet's protocol ID. - * @skb: received socket data -@@ -419,6 +431,10 @@ struct sk_buff *eth_gro_receive(struct l - NAPI_GRO_CB(p)->same_flow = 0; - continue; - } -+ -+ if (eth_check_local_mask(eth->h_dest, dev->dev_addr, -+ dev->local_addr_mask)) -+ skb->gro_skip = 1; - } - - type = eh->h_proto; diff --git a/target/linux/generic/pending-6.6/680-net-add-TCP-fraglist-GRO-support.patch b/target/linux/generic/pending-6.6/680-net-add-TCP-fraglist-GRO-support.patch new file mode 100644 index 000000000..cd7762667 --- /dev/null +++ b/target/linux/generic/pending-6.6/680-net-add-TCP-fraglist-GRO-support.patch @@ -0,0 +1,578 @@ +From: Felix Fietkau +Date: Tue, 23 Apr 2024 11:23:03 +0200 +Subject: [PATCH] net: add TCP fraglist GRO support + +When forwarding TCP after GRO, software segmentation is very expensive, +especially when the checksum needs to be recalculated. +One case where that's currently unavoidable is when routing packets over +PPPoE. Performance improves significantly when using fraglist GRO +implemented in the same way as for UDP. + +Here's a measurement of running 2 TCP streams through a MediaTek MT7622 +device (2-core Cortex-A53), which runs NAT with flow offload enabled from +one ethernet port to PPPoE on another ethernet port + cake qdisc set to +1Gbps. + +rx-gro-list off: 630 Mbit/s, CPU 35% idle +rx-gro-list on: 770 Mbit/s, CPU 40% idle + +Signe-off-by: Felix Fietkau +--- + +--- a/include/net/gro.h ++++ b/include/net/gro.h +@@ -439,6 +439,7 @@ static inline __wsum ip6_gro_compute_pse + } + + int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb); ++int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb); + + /* Pass the currently batched GRO_NORMAL SKBs up to the stack. */ + static inline void gro_normal_list(struct napi_struct *napi) +--- a/include/net/tcp.h ++++ b/include/net/tcp.h +@@ -2082,7 +2082,10 @@ void tcp_v4_destroy_sock(struct sock *sk + + struct sk_buff *tcp_gso_segment(struct sk_buff *skb, + netdev_features_t features); +-struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb); ++struct tcphdr *tcp_gro_pull_header(struct sk_buff *skb); ++struct sk_buff *tcp_gro_lookup(struct list_head *head, struct tcphdr *th); ++struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb, ++ struct tcphdr *th); + INDIRECT_CALLABLE_DECLARE(int tcp4_gro_complete(struct sk_buff *skb, int thoff)); + INDIRECT_CALLABLE_DECLARE(struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)); + INDIRECT_CALLABLE_DECLARE(int tcp6_gro_complete(struct sk_buff *skb, int thoff)); +--- a/net/core/gro.c ++++ b/net/core/gro.c +@@ -233,6 +233,33 @@ done: + return 0; + } + ++int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb) ++{ ++ if (unlikely(p->len + skb->len >= 65536)) ++ return -E2BIG; ++ ++ if (NAPI_GRO_CB(p)->last == p) ++ skb_shinfo(p)->frag_list = skb; ++ else ++ NAPI_GRO_CB(p)->last->next = skb; ++ ++ skb_pull(skb, skb_gro_offset(skb)); ++ ++ NAPI_GRO_CB(p)->last = skb; ++ NAPI_GRO_CB(p)->count++; ++ p->data_len += skb->len; ++ ++ /* sk ownership - if any - completely transferred to the aggregated packet */ ++ skb->destructor = NULL; ++ skb->sk = NULL; ++ p->truesize += skb->truesize; ++ p->len += skb->len; ++ ++ NAPI_GRO_CB(skb)->same_flow = 1; ++ ++ return 0; ++} ++ + + static void napi_gro_complete(struct napi_struct *napi, struct sk_buff *skb) + { +--- a/net/ipv4/tcp_offload.c ++++ b/net/ipv4/tcp_offload.c +@@ -28,6 +28,70 @@ static void tcp_gso_tstamp(struct sk_buf + } + } + ++static void __tcpv4_gso_segment_csum(struct sk_buff *seg, ++ __be32 *oldip, __be32 newip, ++ __be16 *oldport, __be16 newport) ++{ ++ struct tcphdr *th; ++ struct iphdr *iph; ++ ++ if (*oldip == newip && *oldport == newport) ++ return; ++ ++ th = tcp_hdr(seg); ++ iph = ip_hdr(seg); ++ ++ inet_proto_csum_replace4(&th->check, seg, *oldip, newip, true); ++ inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false); ++ *oldport = newport; ++ ++ csum_replace4(&iph->check, *oldip, newip); ++ *oldip = newip; ++} ++ ++static struct sk_buff *__tcpv4_gso_segment_list_csum(struct sk_buff *segs) ++{ ++ const struct tcphdr *th; ++ const struct iphdr *iph; ++ struct sk_buff *seg; ++ struct tcphdr *th2; ++ struct iphdr *iph2; ++ ++ seg = segs; ++ th = tcp_hdr(seg); ++ iph = ip_hdr(seg); ++ th2 = tcp_hdr(seg->next); ++ iph2 = ip_hdr(seg->next); ++ ++ if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) && ++ iph->daddr == iph2->daddr && iph->saddr == iph2->saddr) ++ return segs; ++ ++ while ((seg = seg->next)) { ++ th2 = tcp_hdr(seg); ++ iph2 = ip_hdr(seg); ++ ++ __tcpv4_gso_segment_csum(seg, ++ &iph2->saddr, iph->saddr, ++ &th2->source, th->source); ++ __tcpv4_gso_segment_csum(seg, ++ &iph2->daddr, iph->daddr, ++ &th2->dest, th->dest); ++ } ++ ++ return segs; ++} ++ ++static struct sk_buff *__tcp4_gso_segment_list(struct sk_buff *skb, ++ netdev_features_t features) ++{ ++ skb = skb_segment_list(skb, features, skb_mac_header_len(skb)); ++ if (IS_ERR(skb)) ++ return skb; ++ ++ return __tcpv4_gso_segment_list_csum(skb); ++} ++ + static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb, + netdev_features_t features) + { +@@ -37,6 +101,9 @@ static struct sk_buff *tcp4_gso_segment( + if (!pskb_may_pull(skb, sizeof(struct tcphdr))) + return ERR_PTR(-EINVAL); + ++ if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST) ++ return __tcp4_gso_segment_list(skb, features); ++ + if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { + const struct iphdr *iph = ip_hdr(skb); + struct tcphdr *th = tcp_hdr(skb); +@@ -178,61 +245,76 @@ out: + return segs; + } + +-struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb) ++struct sk_buff *tcp_gro_lookup(struct list_head *head, struct tcphdr *th) + { +- struct sk_buff *pp = NULL; ++ struct tcphdr *th2; + struct sk_buff *p; ++ ++ list_for_each_entry(p, head, list) { ++ if (!NAPI_GRO_CB(p)->same_flow) ++ continue; ++ ++ th2 = tcp_hdr(p); ++ if (*(u32 *)&th->source ^ *(u32 *)&th2->source) { ++ NAPI_GRO_CB(p)->same_flow = 0; ++ continue; ++ } ++ ++ return p; ++ } ++ ++ return NULL; ++} ++ ++struct tcphdr *tcp_gro_pull_header(struct sk_buff *skb) ++{ ++ unsigned int thlen, hlen, off; + struct tcphdr *th; +- struct tcphdr *th2; +- unsigned int len; +- unsigned int thlen; +- __be32 flags; +- unsigned int mss = 1; +- unsigned int hlen; +- unsigned int off; +- int flush = 1; +- int i; + + off = skb_gro_offset(skb); + hlen = off + sizeof(*th); + th = skb_gro_header(skb, hlen, off); + if (unlikely(!th)) +- goto out; ++ return NULL; + + thlen = th->doff * 4; + if (thlen < sizeof(*th)) +- goto out; ++ return NULL; + + hlen = off + thlen; + if (skb_gro_header_hard(skb, hlen)) { + th = skb_gro_header_slow(skb, hlen, off); + if (unlikely(!th)) +- goto out; ++ return NULL; + } + + skb_gro_pull(skb, thlen); + +- len = skb_gro_len(skb); +- flags = tcp_flag_word(th); +- +- list_for_each_entry(p, head, list) { +- if (!NAPI_GRO_CB(p)->same_flow) +- continue; ++ return th; ++} + +- th2 = tcp_hdr(p); ++struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb, ++ struct tcphdr *th) ++{ ++ unsigned int thlen = th->doff * 4; ++ struct sk_buff *pp = NULL; ++ struct sk_buff *p; ++ struct tcphdr *th2; ++ unsigned int len; ++ __be32 flags; ++ unsigned int mss = 1; ++ int flush = 1; ++ int i; + +- if (*(u32 *)&th->source ^ *(u32 *)&th2->source) { +- NAPI_GRO_CB(p)->same_flow = 0; +- continue; +- } ++ len = skb_gro_len(skb); ++ flags = tcp_flag_word(th); + +- goto found; +- } +- p = NULL; +- goto out_check_final; ++ p = tcp_gro_lookup(head, th); ++ if (!p) ++ goto out_check_final; + +-found: + /* Include the IP ID check below from the inner most IP hdr */ ++ th2 = tcp_hdr(p); + flush = NAPI_GRO_CB(p)->flush; + flush |= (__force int)(flags & TCP_FLAG_CWR); + flush |= (__force int)((flags ^ tcp_flag_word(th2)) & +@@ -269,6 +351,19 @@ found: + flush |= p->decrypted ^ skb->decrypted; + #endif + ++ if (unlikely(NAPI_GRO_CB(p)->is_flist)) { ++ flush |= (__force int)(flags ^ tcp_flag_word(th2)); ++ flush |= skb->ip_summed != p->ip_summed; ++ flush |= skb->csum_level != p->csum_level; ++ flush |= !pskb_may_pull(skb, skb_gro_offset(skb)); ++ flush |= NAPI_GRO_CB(p)->count >= 64; ++ ++ if (flush || skb_gro_receive_list(p, skb)) ++ mss = 1; ++ ++ goto out_check_final; ++ } ++ + if (flush || skb_gro_receive(p, skb)) { + mss = 1; + goto out_check_final; +@@ -290,7 +385,6 @@ out_check_final: + if (p && (!NAPI_GRO_CB(skb)->same_flow || flush)) + pp = p; + +-out: + NAPI_GRO_CB(skb)->flush |= (flush != 0); + + return pp; +@@ -314,18 +408,58 @@ void tcp_gro_complete(struct sk_buff *sk + } + EXPORT_SYMBOL(tcp_gro_complete); + ++static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb, ++ struct tcphdr *th) ++{ ++ const struct iphdr *iph; ++ struct sk_buff *p; ++ struct sock *sk; ++ struct net *net; ++ int iif, sdif; ++ ++ if (!(skb->dev->features & NETIF_F_GRO_FRAGLIST)) ++ return; ++ ++ p = tcp_gro_lookup(head, th); ++ if (p) { ++ NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist; ++ return; ++ } ++ ++ inet_get_iif_sdif(skb, &iif, &sdif); ++ iph = skb_gro_network_header(skb); ++ net = dev_net(skb->dev); ++ sk = __inet_lookup_established(net, net->ipv4.tcp_death_row.hashinfo, ++ iph->saddr, th->source, ++ iph->daddr, ntohs(th->dest), ++ iif, sdif); ++ NAPI_GRO_CB(skb)->is_flist = !sk; ++ if (sk) ++ sock_put(sk); ++} ++ + INDIRECT_CALLABLE_SCOPE + struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb) + { ++ struct tcphdr *th; ++ + /* Don't bother verifying checksum if we're going to flush anyway. */ + if (!NAPI_GRO_CB(skb)->flush && + skb_gro_checksum_validate(skb, IPPROTO_TCP, +- inet_gro_compute_pseudo)) { +- NAPI_GRO_CB(skb)->flush = 1; +- return NULL; +- } ++ inet_gro_compute_pseudo)) ++ goto flush; ++ ++ th = tcp_gro_pull_header(skb); ++ if (!th) ++ goto flush; + +- return tcp_gro_receive(head, skb); ++ tcp4_check_fraglist_gro(head, skb, th); ++ ++ return tcp_gro_receive(head, skb, th); ++ ++flush: ++ NAPI_GRO_CB(skb)->flush = 1; ++ return NULL; + } + + INDIRECT_CALLABLE_SCOPE int tcp4_gro_complete(struct sk_buff *skb, int thoff) +@@ -333,6 +467,15 @@ INDIRECT_CALLABLE_SCOPE int tcp4_gro_com + const struct iphdr *iph = ip_hdr(skb); + struct tcphdr *th = tcp_hdr(skb); + ++ if (unlikely(NAPI_GRO_CB(skb)->is_flist)) { ++ skb_shinfo(skb)->gso_type |= SKB_GSO_FRAGLIST | SKB_GSO_TCPV4; ++ skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count; ++ ++ __skb_incr_checksum_unnecessary(skb); ++ ++ return 0; ++ } ++ + th->check = ~tcp_v4_check(skb->len - thoff, iph->saddr, + iph->daddr, 0); + skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV4; +--- a/net/ipv4/udp_offload.c ++++ b/net/ipv4/udp_offload.c +@@ -433,33 +433,6 @@ out: + return segs; + } + +-static int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb) +-{ +- if (unlikely(p->len + skb->len >= 65536)) +- return -E2BIG; +- +- if (NAPI_GRO_CB(p)->last == p) +- skb_shinfo(p)->frag_list = skb; +- else +- NAPI_GRO_CB(p)->last->next = skb; +- +- skb_pull(skb, skb_gro_offset(skb)); +- +- NAPI_GRO_CB(p)->last = skb; +- NAPI_GRO_CB(p)->count++; +- p->data_len += skb->len; +- +- /* sk ownership - if any - completely transferred to the aggregated packet */ +- skb->destructor = NULL; +- skb->sk = NULL; +- p->truesize += skb->truesize; +- p->len += skb->len; +- +- NAPI_GRO_CB(skb)->same_flow = 1; +- +- return 0; +-} +- + + #define UDP_GRO_CNT_MAX 64 + static struct sk_buff *udp_gro_receive_segment(struct list_head *head, +--- a/net/ipv6/tcpv6_offload.c ++++ b/net/ipv6/tcpv6_offload.c +@@ -7,24 +7,67 @@ + */ + #include + #include ++#include + #include + #include + #include + #include + #include "ip6_offload.h" + ++static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb, ++ struct tcphdr *th) ++{ ++#if IS_ENABLED(CONFIG_IPV6) ++ const struct ipv6hdr *hdr; ++ struct sk_buff *p; ++ struct sock *sk; ++ struct net *net; ++ int iif, sdif; ++ ++ if (!(skb->dev->features & NETIF_F_GRO_FRAGLIST)) ++ return; ++ ++ p = tcp_gro_lookup(head, th); ++ if (p) { ++ NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist; ++ return; ++ } ++ ++ inet6_get_iif_sdif(skb, &iif, &sdif); ++ hdr = skb_gro_network_header(skb); ++ net = dev_net(skb->dev); ++ sk = __inet6_lookup_established(net, net->ipv4.tcp_death_row.hashinfo, ++ &hdr->saddr, th->source, ++ &hdr->daddr, ntohs(th->dest), ++ iif, sdif); ++ NAPI_GRO_CB(skb)->is_flist = !sk; ++ if (sk) ++ sock_put(sk); ++#endif /* IS_ENABLED(CONFIG_IPV6) */ ++} ++ + INDIRECT_CALLABLE_SCOPE + struct sk_buff *tcp6_gro_receive(struct list_head *head, struct sk_buff *skb) + { ++ struct tcphdr *th; ++ + /* Don't bother verifying checksum if we're going to flush anyway. */ + if (!NAPI_GRO_CB(skb)->flush && + skb_gro_checksum_validate(skb, IPPROTO_TCP, +- ip6_gro_compute_pseudo)) { +- NAPI_GRO_CB(skb)->flush = 1; +- return NULL; +- } ++ ip6_gro_compute_pseudo)) ++ goto flush; + +- return tcp_gro_receive(head, skb); ++ th = tcp_gro_pull_header(skb); ++ if (!th) ++ goto flush; ++ ++ tcp6_check_fraglist_gro(head, skb, th); ++ ++ return tcp_gro_receive(head, skb, th); ++ ++flush: ++ NAPI_GRO_CB(skb)->flush = 1; ++ return NULL; + } + + INDIRECT_CALLABLE_SCOPE int tcp6_gro_complete(struct sk_buff *skb, int thoff) +@@ -32,6 +75,15 @@ INDIRECT_CALLABLE_SCOPE int tcp6_gro_com + const struct ipv6hdr *iph = ipv6_hdr(skb); + struct tcphdr *th = tcp_hdr(skb); + ++ if (unlikely(NAPI_GRO_CB(skb)->is_flist)) { ++ skb_shinfo(skb)->gso_type |= SKB_GSO_FRAGLIST | SKB_GSO_TCPV6; ++ skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count; ++ ++ __skb_incr_checksum_unnecessary(skb); ++ ++ return 0; ++ } ++ + th->check = ~tcp_v6_check(skb->len - thoff, &iph->saddr, + &iph->daddr, 0); + skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV6; +@@ -40,6 +92,61 @@ INDIRECT_CALLABLE_SCOPE int tcp6_gro_com + return 0; + } + ++static void __tcpv6_gso_segment_csum(struct sk_buff *seg, ++ __be16 *oldport, __be16 newport) ++{ ++ struct tcphdr *th; ++ ++ if (*oldport == newport) ++ return; ++ ++ th = tcp_hdr(seg); ++ inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false); ++ *oldport = newport; ++} ++ ++static struct sk_buff *__tcpv6_gso_segment_list_csum(struct sk_buff *segs) ++{ ++ const struct tcphdr *th; ++ const struct ipv6hdr *iph; ++ struct sk_buff *seg; ++ struct tcphdr *th2; ++ struct ipv6hdr *iph2; ++ ++ seg = segs; ++ th = tcp_hdr(seg); ++ iph = ipv6_hdr(seg); ++ th2 = tcp_hdr(seg->next); ++ iph2 = ipv6_hdr(seg->next); ++ ++ if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) && ++ ipv6_addr_equal(&iph->saddr, &iph2->saddr) && ++ ipv6_addr_equal(&iph->daddr, &iph2->daddr)) ++ return segs; ++ ++ while ((seg = seg->next)) { ++ th2 = tcp_hdr(seg); ++ iph2 = ipv6_hdr(seg); ++ ++ iph2->saddr = iph->saddr; ++ iph2->daddr = iph->daddr; ++ __tcpv6_gso_segment_csum(seg, &th2->source, th->source); ++ __tcpv6_gso_segment_csum(seg, &th2->dest, th->dest); ++ } ++ ++ return segs; ++} ++ ++static struct sk_buff *__tcp6_gso_segment_list(struct sk_buff *skb, ++ netdev_features_t features) ++{ ++ skb = skb_segment_list(skb, features, skb_mac_header_len(skb)); ++ if (IS_ERR(skb)) ++ return skb; ++ ++ return __tcpv6_gso_segment_list_csum(skb); ++} ++ + static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb, + netdev_features_t features) + { +@@ -51,6 +158,9 @@ static struct sk_buff *tcp6_gso_segment( + if (!pskb_may_pull(skb, sizeof(*th))) + return ERR_PTR(-EINVAL); + ++ if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST) ++ return __tcp6_gso_segment_list(skb, features); ++ + if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { + const struct ipv6hdr *ipv6h = ipv6_hdr(skb); + struct tcphdr *th = tcp_hdr(skb); diff --git a/target/linux/generic/pending-6.6/681-net-bridge-fix-multicast-to-unicast-with-fraglist-GS.patch b/target/linux/generic/pending-6.6/681-net-bridge-fix-multicast-to-unicast-with-fraglist-GS.patch new file mode 100644 index 000000000..8361bb16a --- /dev/null +++ b/target/linux/generic/pending-6.6/681-net-bridge-fix-multicast-to-unicast-with-fraglist-GS.patch @@ -0,0 +1,23 @@ +From: Felix Fietkau +Date: Sat, 27 Apr 2024 18:54:25 +0200 +Subject: [PATCH] net: bridge: fix multicast-to-unicast with fraglist GSO + +Calling skb_copy on a SKB_GSO_FRAGLIST skb is not valid, since it returns +an invalid linearized skb. This code only needs to change the ethernet +header, so pskb_copy is the right function to call here. + +Fixes: 6db6f0eae605 ("bridge: multicast to unicast") +Signed-off-by: Felix Fietkau +--- + +--- a/net/bridge/br_forward.c ++++ b/net/bridge/br_forward.c +@@ -266,7 +266,7 @@ static void maybe_deliver_addr(struct ne + if (skb->dev == p->dev && ether_addr_equal(src, addr)) + return; + +- skb = skb_copy(skb, GFP_ATOMIC); ++ skb = pskb_copy(skb, GFP_ATOMIC); + if (!skb) { + DEV_STATS_INC(dev, tx_dropped); + return; diff --git a/target/linux/generic/pending-6.6/682-net-core-reject-skb_copy-_expand-for-fraglist-GSO-sk.patch b/target/linux/generic/pending-6.6/682-net-core-reject-skb_copy-_expand-for-fraglist-GSO-sk.patch new file mode 100644 index 000000000..215b4756a --- /dev/null +++ b/target/linux/generic/pending-6.6/682-net-core-reject-skb_copy-_expand-for-fraglist-GSO-sk.patch @@ -0,0 +1,59 @@ +From: Felix Fietkau +Date: Sat, 27 Apr 2024 19:29:45 +0200 +Subject: [PATCH] net: core: reject skb_copy(_expand) for fraglist GSO skbs + +SKB_GSO_FRAGLIST skbs must not be linearized, otherwise they become +invalid. Return NULL if such an skb is passed to skb_copy or +skb_copy_expand, in order to prevent a crash on a potential later +call to skb_gso_segment. + +Fixes: 3a1296a38d0c ("net: Support GRO/GSO fraglist chaining.") +Signed-off-by: Felix Fietkau +--- + +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -1971,11 +1971,17 @@ static inline int skb_alloc_rx_flag(cons + + struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask) + { +- int headerlen = skb_headroom(skb); +- unsigned int size = skb_end_offset(skb) + skb->data_len; +- struct sk_buff *n = __alloc_skb(size, gfp_mask, +- skb_alloc_rx_flag(skb), NUMA_NO_NODE); ++ struct sk_buff *n; ++ unsigned int size; ++ int headerlen; + ++ if (WARN_ON_ONCE(skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)) ++ return NULL; ++ ++ headerlen = skb_headroom(skb); ++ size = skb_end_offset(skb) + skb->data_len; ++ n = __alloc_skb(size, gfp_mask, ++ skb_alloc_rx_flag(skb), NUMA_NO_NODE); + if (!n) + return NULL; + +@@ -2303,12 +2309,17 @@ struct sk_buff *skb_copy_expand(const st + /* + * Allocate the copy buffer + */ +- struct sk_buff *n = __alloc_skb(newheadroom + skb->len + newtailroom, +- gfp_mask, skb_alloc_rx_flag(skb), +- NUMA_NO_NODE); +- int oldheadroom = skb_headroom(skb); + int head_copy_len, head_copy_off; ++ struct sk_buff *n; ++ int oldheadroom; ++ ++ if (WARN_ON_ONCE(skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)) ++ return NULL; + ++ oldheadroom = skb_headroom(skb); ++ n = __alloc_skb(newheadroom + skb->len + newtailroom, ++ gfp_mask, skb_alloc_rx_flag(skb), ++ NUMA_NO_NODE); + if (!n) + return NULL; + diff --git a/target/linux/generic/pending-6.6/682-of_net-add-mac-address-increment-support.patch b/target/linux/generic/pending-6.6/682-of_net-add-mac-address-increment-support.patch deleted file mode 100644 index 24c04a19b..000000000 --- a/target/linux/generic/pending-6.6/682-of_net-add-mac-address-increment-support.patch +++ /dev/null @@ -1,90 +0,0 @@ -From 844c273286f328acf0dab5fbd5d864366b4904dc Mon Sep 17 00:00:00 2001 -From: Ansuel Smith -Date: Tue, 30 Mar 2021 18:21:14 +0200 -Subject: [PATCH] of_net: add mac-address-increment support - -Lots of embedded devices use the mac-address of other interface -extracted from nvmem cells and increments it by one or two. Add two -bindings to integrate this and directly use the right mac-address for -the interface. Some example are some routers that use the gmac -mac-address stored in the art partition and increments it by one for the -wifi. mac-address-increment-byte bindings is used to tell what byte of -the mac-address has to be increased (if not defined the last byte is -increased) and mac-address-increment tells how much the byte decided -early has to be increased. - -Signed-off-by: Ansuel Smith ---- - net/core/of_net.c | 43 +++++++++++++++++++++++++++++++++++++++---- - 1 file changed, 39 insertions(+), 4 deletions(-) - ---- a/net/core/of_net.c -+++ b/net/core/of_net.c -@@ -121,28 +121,63 @@ EXPORT_SYMBOL(of_get_mac_address_nvmem); - * this case, the real MAC is in 'local-mac-address', and 'mac-address' exists - * but is all zeros. - * -+ * DT can tell the system to increment the mac-address after is extracted by -+ * using: -+ * - mac-address-increment-byte to decide what byte to increase -+ * (if not defined is increased the last byte) -+ * - mac-address-increment to decide how much to increase. The value WILL -+ * overflow to other bytes if the increment is over 255 or the total -+ * increment will exceed 255 of the current byte. -+ * (example 00:01:02:03:04:ff + 1 == 00:01:02:03:05:00) -+ * (example 00:01:02:03:04:fe + 5 == 00:01:02:03:05:03) -+ * - * Return: 0 on success and errno in case of error. - */ - int of_get_mac_address(struct device_node *np, u8 *addr) - { -+ u32 inc_idx, mac_inc, mac_val; - int ret; - -+ /* Check first if the increment byte is present and valid. -+ * If not set assume to increment the last byte if found. -+ */ -+ if (of_property_read_u32(np, "mac-address-increment-byte", &inc_idx)) -+ inc_idx = 5; -+ if (inc_idx < 3 || inc_idx > 5) -+ return -EINVAL; -+ - if (!np) - return -ENODEV; - - ret = of_get_mac_addr(np, "mac-address", addr); - if (!ret) -- return 0; -+ goto found; - - ret = of_get_mac_addr(np, "local-mac-address", addr); - if (!ret) -- return 0; -+ goto found; - - ret = of_get_mac_addr(np, "address", addr); - if (!ret) -- return 0; -+ goto found; -+ -+ ret = of_get_mac_address_nvmem(np, addr); -+ if (ret) -+ return ret; -+ -+found: -+ if (!of_property_read_u32(np, "mac-address-increment", &mac_inc)) { -+ /* Convert to a contiguous value */ -+ mac_val = (addr[3] << 16) + (addr[4] << 8) + addr[5]; -+ mac_val += mac_inc << 8 * (5-inc_idx); -+ -+ /* Apply the incremented value handling overflow case */ -+ addr[3] = (mac_val >> 16) & 0xff; -+ addr[4] = (mac_val >> 8) & 0xff; -+ addr[5] = (mac_val >> 0) & 0xff; -+ } - -- return of_get_mac_address_nvmem(np, addr); -+ return ret; - } - EXPORT_SYMBOL(of_get_mac_address); - diff --git a/target/linux/generic/pending-6.6/683-of_net-add-mac-address-to-of-tree.patch b/target/linux/generic/pending-6.6/683-of_net-add-mac-address-to-of-tree.patch index 89b4d70c1..0fb02dbb6 100644 --- a/target/linux/generic/pending-6.6/683-of_net-add-mac-address-to-of-tree.patch +++ b/target/linux/generic/pending-6.6/683-of_net-add-mac-address-to-of-tree.patch @@ -45,11 +45,31 @@ property. This way, the MAC address can be accessed using procfs. /** * of_get_mac_address() * @np: Caller's Device Node -@@ -177,6 +198,7 @@ found: - addr[5] = (mac_val >> 0) & 0xff; - } +@@ -132,17 +153,23 @@ int of_get_mac_address(struct device_nod -+ of_add_mac_address(np, addr); - return ret; + ret = of_get_mac_addr(np, "mac-address", addr); + if (!ret) +- return 0; ++ goto found; + + ret = of_get_mac_addr(np, "local-mac-address", addr); + if (!ret) +- return 0; ++ goto found; + + ret = of_get_mac_addr(np, "address", addr); + if (!ret) +- return 0; ++ goto found; + +- return of_get_mac_address_nvmem(np, addr); ++ ret = of_get_mac_address_nvmem(np, addr); ++ if (ret) ++ return ret; ++ ++found: ++ ret = of_add_mac_address(np, addr); ++ return ret; } EXPORT_SYMBOL(of_get_mac_address); +