mirror of
https://github.com/coolsnowwolf/lede.git
synced 2025-04-16 04:13:31 +00:00
kernel: add missing check for TCP GRO
This commit is contained in:
parent
8c6cc8f468
commit
542de0b0ac
@ -1,151 +0,0 @@
|
|||||||
From: Felix Fietkau <nbd@nbd.name>
|
|
||||||
Subject: net: replace GRO optimization patch with a new one that supports VLANs/bridges with different MAC addresses
|
|
||||||
|
|
||||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|
||||||
---
|
|
||||||
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;
|
|
@ -0,0 +1,627 @@
|
|||||||
|
From: Felix Fietkau <nbd@nbd.name>
|
||||||
|
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 <nbd@nbd.name>
|
||||||
|
---
|
||||||
|
|
||||||
|
--- 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 <linux/indirect_call_wrapper.h>
|
||||||
|
#include <linux/skbuff.h>
|
||||||
|
+#include <net/inet6_hashtables.h>
|
||||||
|
#include <net/gro.h>
|
||||||
|
#include <net/protocol.h>
|
||||||
|
#include <net/tcp.h>
|
||||||
|
#include <net/ip6_checksum.h>
|
||||||
|
#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);
|
@ -0,0 +1,23 @@
|
|||||||
|
From: Felix Fietkau <nbd@nbd.name>
|
||||||
|
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 <nbd@nbd.name>
|
||||||
|
---
|
||||||
|
|
||||||
|
--- 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;
|
@ -0,0 +1,59 @@
|
|||||||
|
From: Felix Fietkau <nbd@nbd.name>
|
||||||
|
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 <nbd@nbd.name>
|
||||||
|
---
|
||||||
|
|
||||||
|
--- 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;
|
||||||
|
|
@ -1,90 +0,0 @@
|
|||||||
From 844c273286f328acf0dab5fbd5d864366b4904dc Mon Sep 17 00:00:00 2001
|
|
||||||
From: Ansuel Smith <ansuelsmth@gmail.com>
|
|
||||||
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 <ansuelsmth@gmail.com>
|
|
||||||
---
|
|
||||||
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);
|
|
||||||
|
|
@ -45,11 +45,31 @@ property. This way, the MAC address can be accessed using procfs.
|
|||||||
/**
|
/**
|
||||||
* of_get_mac_address()
|
* of_get_mac_address()
|
||||||
* @np: Caller's Device Node
|
* @np: Caller's Device Node
|
||||||
@@ -175,6 +196,7 @@ found:
|
@@ -130,17 +151,23 @@ int of_get_mac_address(struct device_nod
|
||||||
addr[5] = (mac_val >> 0) & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ of_add_mac_address(np, addr);
|
ret = of_get_mac_addr(np, "mac-address", addr);
|
||||||
return ret;
|
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);
|
EXPORT_SYMBOL(of_get_mac_address);
|
||||||
|
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
From dd07dd394d8bfdb5d527fab18ca54f20815ec4e4 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Will Moss <willormos@gmail.com>
|
|
||||||
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 <willormos@gmail.com>
|
|
||||||
---
|
|
||||||
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);
|
|
@ -1,151 +0,0 @@
|
|||||||
From: Felix Fietkau <nbd@nbd.name>
|
|
||||||
Subject: net: replace GRO optimization patch with a new one that supports VLANs/bridges with different MAC addresses
|
|
||||||
|
|
||||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|
||||||
---
|
|
||||||
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;
|
|
@ -0,0 +1,578 @@
|
|||||||
|
From: Felix Fietkau <nbd@nbd.name>
|
||||||
|
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 <nbd@nbd.name>
|
||||||
|
---
|
||||||
|
|
||||||
|
--- 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 <linux/indirect_call_wrapper.h>
|
||||||
|
#include <linux/skbuff.h>
|
||||||
|
+#include <net/inet6_hashtables.h>
|
||||||
|
#include <net/gro.h>
|
||||||
|
#include <net/protocol.h>
|
||||||
|
#include <net/tcp.h>
|
||||||
|
#include <net/ip6_checksum.h>
|
||||||
|
#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);
|
@ -0,0 +1,23 @@
|
|||||||
|
From: Felix Fietkau <nbd@nbd.name>
|
||||||
|
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 <nbd@nbd.name>
|
||||||
|
---
|
||||||
|
|
||||||
|
--- 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;
|
@ -0,0 +1,59 @@
|
|||||||
|
From: Felix Fietkau <nbd@nbd.name>
|
||||||
|
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 <nbd@nbd.name>
|
||||||
|
---
|
||||||
|
|
||||||
|
--- 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;
|
||||||
|
|
@ -1,90 +0,0 @@
|
|||||||
From 844c273286f328acf0dab5fbd5d864366b4904dc Mon Sep 17 00:00:00 2001
|
|
||||||
From: Ansuel Smith <ansuelsmth@gmail.com>
|
|
||||||
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 <ansuelsmth@gmail.com>
|
|
||||||
---
|
|
||||||
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);
|
|
||||||
|
|
@ -45,11 +45,31 @@ property. This way, the MAC address can be accessed using procfs.
|
|||||||
/**
|
/**
|
||||||
* of_get_mac_address()
|
* of_get_mac_address()
|
||||||
* @np: Caller's Device Node
|
* @np: Caller's Device Node
|
||||||
@@ -177,6 +198,7 @@ found:
|
@@ -132,17 +153,23 @@ int of_get_mac_address(struct device_nod
|
||||||
addr[5] = (mac_val >> 0) & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ of_add_mac_address(np, addr);
|
ret = of_get_mac_addr(np, "mac-address", addr);
|
||||||
return ret;
|
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);
|
EXPORT_SYMBOL(of_get_mac_address);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user