* [PATCH net-next v2 0/4] net: convert UDP getsockopt to sockopt_t
From: Breno Leitao @ 2026-06-30 14:01 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Willem de Bruijn, Shuah Khan, sdf.kernel
Cc: netdev, linux-kernel, linux-kselftest, Breno Leitao, kernel-team
The leaf proto_ops getsockopt callbacks have been moving to the new
getsockopt_iter()/sockopt_t interface.
I was trying to get SMC into getsockop and retire .getsockopt, but,
I found the best approach is to keep converting other protocols.
This series starts the same conversion one layer down, at the struct proto
getsockopt path, beginning with UDP.
Example of the current code.
static int udp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
if (level == SOL_UDP)
return udp_lib_getsockopt(sk, level, optname, optval, optlen);
return ip_getsockopt(sk, level, optname, optval, optlen);
}
We want udp_getsockopt to go to .getsockopt_iter, and there are two
approaches in this case:
1) Create a patchset that moves both of them to getsockopt_iter, which
is will be a huge change (ip_getsockopt() is used in many places)
2) Break this down, and transform from bottoms up. First
udp_lib_getsockopt() up to the point we can easily convert
others, such as ip_getsockopt().
I am taking the approach 2), so, the intermediate code will be something
like:
static int udp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
sockopt_t opt;
int err;
if (level != SOL_UDP)
return ip_getsockopt(sk, level, optname, optval, optlen);
// Convert optlen/optval in sockopt // (first patch)
err = udp_lib_getsockopt(sk, level, optname, &opt);
}
The work is bottom-up and mergeable in small steps: a protocol's inner
getsockopt helper is switched to sockopt_t behind its existing thin
__user wrapper, one patch at a time.
Once every inner helper speaks sockopt_t, a later series flips the shared
struct proto.getsockopt and inet_connection_sock_af_ops.getsockopt signatures
and drops the transitional wrappers.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
Changes in v2:
- Check for invalid `len` at call site (Stanlislav)
- Do not rewrite `optlen` on error path (Sashiko)
- Converted ipv4 raw
- Expanded the selftest to test this new helper.
- Link to v1: https://lore.kernel.org/r/20260612-getsockopt_phase2-v1-0-7b01f1f5d106@debian.org
---
Breno Leitao (4):
net: add sockopt_init_user() for getsockopt conversion
udp: convert udp_lib_getsockopt to sockopt_t
ipv4: raw: convert do_raw_getsockopt to sockopt_t
selftests: net: getsockopt_iter: add raw ICMP_FILTER coverage
include/linux/net.h | 23 +++++++
include/net/udp.h | 2 +-
net/ipv4/raw.c | 41 ++++++-----
net/ipv4/udp.c | 39 ++++++++---
net/ipv6/udp.c | 19 +++++-
tools/testing/selftests/net/getsockopt_iter.c | 97 +++++++++++++++++++++++++++
6 files changed, 191 insertions(+), 30 deletions(-)
---
base-commit: c8459ee2fef502d6ef6c063751c33d9ac7943eab
change-id: 20260611-getsockopt_phase2-cd495a0115ca
Best regards,
--
Breno Leitao <leitao@debian.org>
^ permalink raw reply
* [PATCH net-next v2 1/4] net: add sockopt_init_user() for getsockopt conversion
From: Breno Leitao @ 2026-06-30 14:01 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Willem de Bruijn, Shuah Khan, sdf.kernel
Cc: netdev, linux-kernel, linux-kselftest, Breno Leitao, kernel-team
In-Reply-To: <20260630-getsockopt_phase2-v2-0-193335f3d4d1@debian.org>
Add a helper that initializes a user-backed sockopt_t from the (optval,
optlen) __user pair passed to a getsockopt() callback.
It is used by transitional __user getsockopt wrappers while the
proto-layer getsockopt callbacks are converted to take a sockopt_t, and
is removed once the conversion is complete.
The goal is to help to convert leafs. Example:
sock_common_getsockopt(... char __user *optval, int __user *optlen)
→ udp_getsockopt(sk, level, optname, optval__user, optlen__user)
→ udp_lib_getsockopt(sk, level, optname, &opt) /* needs a sockopt_t */
Signed-off-by: Breno Leitao <leitao@debian.org>
---
include/linux/net.h | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/include/linux/net.h b/include/linux/net.h
index f268f395ce473..277188a40c72e 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -47,6 +47,29 @@ typedef struct sockopt {
int optlen;
} sockopt_t;
+/*
+ * Initialize a user-backed sockopt_t from the (optval, optlen) __user pair of
+ * a getsockopt() callback. Used by transitional __user getsockopt wrappers
+ * while the proto-layer callbacks are converted to take a sockopt_t; the
+ * caller writes opt->optlen back to the user optlen after the callback.
+ */
+static inline int sockopt_init_user(sockopt_t *opt, char __user *optval,
+ int __user *optlen)
+{
+ int len;
+
+ if (get_user(len, optlen))
+ return -EFAULT;
+ if (len < 0)
+ return -EINVAL;
+
+ iov_iter_ubuf(&opt->iter_out, ITER_DEST, optval, len);
+ iov_iter_ubuf(&opt->iter_in, ITER_SOURCE, optval, len);
+ opt->optlen = len;
+
+ return 0;
+}
+
struct poll_table_struct;
struct pipe_inode_info;
struct inode;
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v2 2/4] udp: convert udp_lib_getsockopt to sockopt_t
From: Breno Leitao @ 2026-06-30 14:01 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Willem de Bruijn, Shuah Khan, sdf.kernel
Cc: netdev, linux-kernel, linux-kselftest, Breno Leitao, kernel-team
In-Reply-To: <20260630-getsockopt_phase2-v2-0-193335f3d4d1@debian.org>
In preparation for converting the proto-layer getsockopt callbacks to the
sockopt_t interface, switch udp_lib_getsockopt() to take a sockopt_t.
The thin udp_getsockopt()/udpv6_getsockopt() wrappers keep their __user
signature for now: they build a user-backed sockopt_t with
sockopt_init_user(), call the helper, and write the returned length back
to optlen. The helper uses copy_to_iter() instead of copy_to_user().
No functional change.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
include/net/udp.h | 2 +-
net/ipv4/udp.c | 39 +++++++++++++++++++++++++++++----------
net/ipv6/udp.c | 19 ++++++++++++++++---
3 files changed, 46 insertions(+), 14 deletions(-)
diff --git a/include/net/udp.h b/include/net/udp.h
index 8262e2b215b4e..1fee17274745f 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -430,7 +430,7 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
netdev_features_t features,
bool is_ipv6);
int udp_lib_getsockopt(struct sock *sk, int level, int optname,
- char __user *optval, int __user *optlen);
+ sockopt_t *opt);
int udp_lib_setsockopt(struct sock *sk, int level, int optname,
sockptr_t optval, unsigned int optlen,
int (*push_pending_frames)(struct sock *));
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 70f6cbd4ef73b..59248a59358ca 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -76,6 +76,7 @@
#include <linux/bpf-cgroup.h>
#include <linux/uaccess.h>
+#include <linux/uio.h>
#include <asm/ioctls.h>
#include <linux/memblock.h>
#include <linux/highmem.h>
@@ -2995,14 +2996,13 @@ static int udp_setsockopt(struct sock *sk, int level, int optname, sockptr_t opt
}
int udp_lib_getsockopt(struct sock *sk, int level, int optname,
- char __user *optval, int __user *optlen)
+ sockopt_t *opt)
{
struct udp_sock *up = udp_sk(sk);
int val, len;
- if (get_user(len, optlen))
- return -EFAULT;
-
+ len = opt->optlen;
+ /* keep the check so direct sockopt_t callers stay covered. */
if (len < 0)
return -EINVAL;
@@ -3037,9 +3037,8 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
return -ENOPROTOOPT;
}
- if (put_user(len, optlen))
- return -EFAULT;
- if (copy_to_user(optval, &val, len))
+ opt->optlen = len;
+ if (copy_to_iter(&val, len, &opt->iter_out) != len)
return -EFAULT;
return 0;
}
@@ -3047,9 +3046,29 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
static int udp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
- if (level == SOL_UDP)
- return udp_lib_getsockopt(sk, level, optname, optval, optlen);
- return ip_getsockopt(sk, level, optname, optval, optlen);
+ sockopt_t opt;
+ int err;
+
+ /*
+ * keep the old __user pointers, until ip_getsockopt() moves
+ * to sockopt_t
+ */
+ if (level != SOL_UDP)
+ return ip_getsockopt(sk, level, optname, optval, optlen);
+
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = udp_lib_getsockopt(sk, level, optname, &opt);
+ if (err)
+ return err;
+
+ /* optval was written by copy_to_iter() in udp_lib_getsockopt() */
+ if (put_user(opt.optlen, optlen))
+ return -EFAULT;
+
+ return 0;
}
/**
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 15e032194eccc..392e18b970454 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1826,9 +1826,22 @@ static int udpv6_setsockopt(struct sock *sk, int level, int optname,
static int udpv6_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
- if (level == SOL_UDP)
- return udp_lib_getsockopt(sk, level, optname, optval, optlen);
- return ipv6_getsockopt(sk, level, optname, optval, optlen);
+ sockopt_t opt;
+ int err;
+
+ if (level != SOL_UDP)
+ return ipv6_getsockopt(sk, level, optname, optval, optlen);
+
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = udp_lib_getsockopt(sk, level, optname, &opt);
+ if (err)
+ return err;
+ if (put_user(opt.optlen, optlen))
+ return -EFAULT;
+ return 0;
}
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v2 3/4] ipv4: raw: convert do_raw_getsockopt to sockopt_t
From: Breno Leitao @ 2026-06-30 14:01 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Willem de Bruijn, Shuah Khan, sdf.kernel
Cc: netdev, linux-kernel, linux-kselftest, Breno Leitao, kernel-team
In-Reply-To: <20260630-getsockopt_phase2-v2-0-193335f3d4d1@debian.org>
Continue converting the proto-layer getsockopt callbacks to the sockopt_t
interface, switching do_raw_getsockopt() and its raw_geticmpfilter()
helper to take a sockopt_t.
The thin raw_getsockopt() wrapper keeps its __user signature for now: it
builds a user-backed sockopt_t with sockopt_init_user(), calls the helper,
and writes the returned length back to optlen. The helper uses
copy_to_iter() instead of copy_to_user(). No functional change.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/ipv4/raw.c | 41 +++++++++++++++++++++++++----------------
1 file changed, 25 insertions(+), 16 deletions(-)
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index e9fbab6ad9146..2aebaf8297e04 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -809,23 +809,18 @@ static int raw_seticmpfilter(struct sock *sk, sockptr_t optval, int optlen)
return 0;
}
-static int raw_geticmpfilter(struct sock *sk, char __user *optval, int __user *optlen)
+static int raw_geticmpfilter(struct sock *sk, sockopt_t *opt)
{
- int len, ret = -EFAULT;
+ int len = opt->optlen;
- if (get_user(len, optlen))
- goto out;
- ret = -EINVAL;
if (len < 0)
- goto out;
+ return -EINVAL;
if (len > sizeof(struct icmp_filter))
len = sizeof(struct icmp_filter);
- ret = -EFAULT;
- if (put_user(len, optlen) ||
- copy_to_user(optval, &raw_sk(sk)->filter, len))
- goto out;
- ret = 0;
-out: return ret;
+ opt->optlen = len;
+ if (copy_to_iter(&raw_sk(sk)->filter, len, &opt->iter_out) != len)
+ return -EFAULT;
+ return 0;
}
static int do_raw_setsockopt(struct sock *sk, int optname,
@@ -848,14 +843,13 @@ static int raw_setsockopt(struct sock *sk, int level, int optname,
return do_raw_setsockopt(sk, optname, optval, optlen);
}
-static int do_raw_getsockopt(struct sock *sk, int optname,
- char __user *optval, int __user *optlen)
+static int do_raw_getsockopt(struct sock *sk, int optname, sockopt_t *opt)
{
if (optname == ICMP_FILTER) {
if (inet_sk(sk)->inet_num != IPPROTO_ICMP)
return -EOPNOTSUPP;
else
- return raw_geticmpfilter(sk, optval, optlen);
+ return raw_geticmpfilter(sk, opt);
}
return -ENOPROTOOPT;
}
@@ -863,9 +857,24 @@ static int do_raw_getsockopt(struct sock *sk, int optname,
static int raw_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
+ sockopt_t opt;
+ int err;
+
if (level != SOL_RAW)
return ip_getsockopt(sk, level, optname, optval, optlen);
- return do_raw_getsockopt(sk, optname, optval, optlen);
+
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = do_raw_getsockopt(sk, optname, &opt);
+ if (err)
+ return err;
+
+ if (put_user(opt.optlen, optlen))
+ return -EFAULT;
+
+ return 0;
}
static int raw_ioctl(struct sock *sk, int cmd, int *karg)
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v2 4/4] selftests: net: getsockopt_iter: add raw ICMP_FILTER coverage
From: Breno Leitao @ 2026-06-30 14:01 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Willem de Bruijn, Shuah Khan, sdf.kernel
Cc: netdev, linux-kernel, linux-kselftest, Breno Leitao, kernel-team
In-Reply-To: <20260630-getsockopt_phase2-v2-0-193335f3d4d1@debian.org>
Exercise the raw getsockopt path now backed by sockopt_t. ICMP_FILTER
returns a fixed-size struct and, unlike the int/u64 options already
covered, clamps the length down to the user buffer on a short read
instead of failing, so check that semantic explicitly along with the
exact and oversized cases, the -EOPNOTSUPP path on a non-ICMP raw
socket, and an unknown optname.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
tools/testing/selftests/net/getsockopt_iter.c | 97 +++++++++++++++++++++++++++
1 file changed, 97 insertions(+)
diff --git a/tools/testing/selftests/net/getsockopt_iter.c b/tools/testing/selftests/net/getsockopt_iter.c
index 209569354d0e3..fe5a5268bc34e 100644
--- a/tools/testing/selftests/net/getsockopt_iter.c
+++ b/tools/testing/selftests/net/getsockopt_iter.c
@@ -11,6 +11,8 @@
* that always reports the required buffer length back via optlen,
* even when the user buffer is too small to receive any group bits.
* - vsock: SO_VM_SOCKETS_BUFFER_SIZE covers the u64 path.
+ * - raw: ICMP_FILTER covers a fixed-size struct payload that clamps
+ * the length down on a short buffer instead of failing.
*
* Author: Breno Leitao <leitao@debian.org>
*/
@@ -24,12 +26,20 @@
#include <linux/rtnetlink.h>
#include <linux/time_types.h>
#include <linux/vm_sockets.h>
+#include <linux/icmp.h>
+#include <netinet/in.h>
#include <sys/socket.h>
#include "kselftest_harness.h"
#ifndef AF_VSOCK
#define AF_VSOCK 40
#endif
+#ifndef SOL_RAW
+#define SOL_RAW 255
+#endif
+#ifndef ICMP_FILTER
+#define ICMP_FILTER 1
+#endif
/* ---------- netlink ---------- */
@@ -297,4 +307,91 @@ TEST_F(vsock, connect_timeout_old_exact)
ASSERT_EQ(sizeof(tv), optlen);
}
+/* ---------- raw (ipv4) ---------- */
+
+FIXTURE(raw)
+{
+ int fd;
+};
+
+FIXTURE_SETUP(raw)
+{
+ struct icmp_filter filt = { .data = 0xdeadbeef };
+
+ self->fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ if (self->fd < 0)
+ SKIP(return, "SOCK_RAW/ICMP socket: %s", strerror(errno));
+
+ if (setsockopt(self->fd, SOL_RAW, ICMP_FILTER, &filt, sizeof(filt)) < 0)
+ SKIP(return, "set ICMP_FILTER: %s", strerror(errno));
+}
+
+FIXTURE_TEARDOWN(raw)
+{
+ if (self->fd >= 0)
+ close(self->fd);
+}
+
+TEST_F(raw, icmpfilter_exact)
+{
+ struct icmp_filter filt = {};
+ socklen_t optlen = sizeof(filt);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_RAW, ICMP_FILTER,
+ &filt, &optlen));
+ ASSERT_EQ(sizeof(filt), optlen);
+ ASSERT_EQ(0xdeadbeef, filt.data);
+}
+
+TEST_F(raw, icmpfilter_oversize_clamped)
+{
+ char buf[16] = {};
+ socklen_t optlen = sizeof(buf);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_RAW, ICMP_FILTER,
+ buf, &optlen));
+ ASSERT_EQ(sizeof(struct icmp_filter), optlen);
+}
+
+/* Unlike the int/u64 options above, ICMP_FILTER clamps the length down
+ * to the user buffer instead of returning EINVAL: a short buffer
+ * succeeds and reports the truncated length back via optlen.
+ */
+TEST_F(raw, icmpfilter_undersize_clamped)
+{
+ char buf[2] = {};
+ socklen_t optlen = sizeof(buf);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_RAW, ICMP_FILTER,
+ buf, &optlen));
+ ASSERT_EQ(sizeof(buf), optlen);
+}
+
+TEST_F(raw, icmpfilter_wrong_proto)
+{
+ struct icmp_filter filt;
+ socklen_t optlen = sizeof(filt);
+ int fd;
+
+ fd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
+ if (fd < 0)
+ SKIP(return, "SOCK_RAW/UDP socket: %s", strerror(errno));
+
+ ASSERT_EQ(-1, getsockopt(fd, SOL_RAW, ICMP_FILTER, &filt, &optlen));
+ ASSERT_EQ(EOPNOTSUPP, errno);
+ close(fd);
+}
+
+TEST_F(raw, bad_optname)
+{
+ socklen_t optlen;
+ int val;
+
+ optlen = sizeof(val);
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_RAW, 0x7fff, &val, &optlen));
+ ASSERT_EQ(ENOPROTOOPT, errno);
+ ASSERT_EQ(sizeof(val), optlen);
+}
+
TEST_HARNESS_MAIN
--
2.53.0-Meta
^ permalink raw reply related
* Re: [PATCH net] selftests: net: bump default cmd() timeout to 20 seconds
From: Breno Leitao @ 2026-06-30 14:02 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, shuah,
petrm, dw, noren, gal, linux-kselftest
In-Reply-To: <20260629233348.2145841-1-kuba@kernel.org>
On Mon, Jun 29, 2026 at 04:33:48PM -0700, Jakub Kicinski wrote:
> We always used 5 sec as the default command timeout. But soon after
> it was introduced, David effectively made us ignore the timeout
> (it was passed to process.communicate() as the wrong argument).
> Gal recently fixed that, but turns out the 5 sec is not enough
> for a lot of tests and setups. The fix regressed regressions.
>
> In particular running reconfig commands (e.g. XDP attach) on mlx5
> with 32 rings and 9k MTU, on a heavily-debug-enabled kernel takes
> more than 5 sec. The XDP installation command will time out after
> 5 sec but since the sleeps in the kernel are non interruptible
> the command finishes anyway, leaving the XDP program attached,
> but with non-zero exit code. defer()ed cleanups are not installed,
> breaking the environment for subsequent tests.
>
> Since "install XDP" is a pretty normal command a "point fix"
> does not seem appropriate. 32 rings is a fairly reasonable
> config, too, so we should just increase the timeout to 20 sec.
>
> There's no real reason behind the value of 20.
>
> Fixes: 1cf270424218 ("net: selftest: add test for netdev netlink queue-get API")
> Fixes: f0bd19316663 ("selftests: net: fix timeout passed as positional argument to communicate()")
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Acked-by: Breno Leitao <leitao@debian.org>
^ permalink raw reply
* Re: [PATCH 3/3] net: stmmac: dwmac-socfpga: Add mac-mode DT property support
From: Andrew Lunn @ 2026-06-30 14:02 UTC (permalink / raw)
To: muhammad.nazim.amirul.nazle.asmade
Cc: dinguyen, maxime.chevallier, rmk+kernel, krzk+dt, conor+dt, robh,
davem, edumazet, kuba, pabeni, andrew+netdev, devicetree,
linux-arm-kernel, netdev, linux-kernel
In-Reply-To: <20260630133108.27244-4-muhammad.nazim.amirul.nazle.asmade@altera.com>
On Tue, Jun 30, 2026 at 06:31:08AM -0700, muhammad.nazim.amirul.nazle.asmade@altera.com wrote:
> From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
>
> Russell King's commit de696c63c1dc ("net: stmmac: socfpga: convert to
> use phy_interface") replaced mac_interface with phy_interface in
> socfpga_get_plat_phymode(), noting that no upstream DTS files set the
> "mac-mode" property, making the two values identical.
>
> The Agilex5 SoCDK TSN Config2 board is an exception: its gmac1 TSN
> port uses GMII internally in the MAC while the PHY-side interface is
> RGMII, so mac-mode and phy-mode differ.
Maybe you need to represent the hardware block which magically
converts GMII to RGMII in DT?
Andrew
^ permalink raw reply
* Re: [PATCH net-next v7 5/5] veth: time-based BQL completion coalescing via ethtool tx-usecs
From: Jonas Köppeler @ 2026-06-30 14:00 UTC (permalink / raw)
To: Simon Schippers, hawk, netdev
Cc: kernel-team, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Alexei Starovoitov, Daniel Borkmann,
John Fastabend, Stanislav Fomichev, linux-kernel, bpf
In-Reply-To: <f120fd22-1ee5-4d5b-9cdd-71804353e6a7@tu-dortmund.de>
On 6/13/26 4:14 PM, Simon Schippers wrote:
> On 6/12/26 10:35, hawk@kernel.org wrote:
>> From: Simon Schippers <simon.schippers@tu-dortmund.de>
>>
>> Per-packet BQL completion forces DQL to converge on limit=2, causing
>> excessive NAPI scheduling overhead and qdisc requeues.
>>
>> Accumulate BQL completions and flush them when a configurable time
>> threshold (tx-usecs) is exceeded, letting DQL discover a limit that
>> bounds actual queuing delay to the configured interval. Coalescing
>> state persists across NAPI polls in struct veth_rq so completions can
>> accumulate beyond a single budget=64 cycle.
>>
>> The flush condition is:
>>
>> state->time + bql_flush_ns <= current_time || state->n_bql > dql.limit
>>
>> Flushing when n_bql exceeds dql.limit handles BQL starvation.
>>
>> The comparison is strictly greater-than because netdev_tx_sent_queue()
>> always lets the producer exceed the limit by one before it stops, so
>> n_bql == dql.limit is a normal in-flight state. dql.limit lives in
>> the same cacheline as the completion path, so the check is cheap.
>>
>> Add ethtool tx-usecs support for runtime tuning. Default is 100 us;
>> setting tx-usecs to 0 disables coalescing and falls back to per-packet
>> completion.
>>
>> ethtool -C <veth-dev> tx-usecs 500 # 500us coalescing
>> ethtool -C <veth-dev> tx-usecs 0 # per-packet (no coalescing)
>>
>> Co-developed-by: Jesper Dangaard Brouer <hawk@kernel.org>
>> Signed-off-by: Jesper Dangaard Brouer <hawk@kernel.org>
>> Co-developed-by: Jonas Köppeler <j.koeppeler@tu-berlin.de>
>> Signed-off-by: Jonas Köppeler <j.koeppeler@tu-berlin.de>
>> Signed-off-by: Simon Schippers <simon.schippers@tu-dortmund.de>
>> ---
>> drivers/net/veth.c | 123 ++++++++++++++++++++++++++++++++++++++++++---
>> 1 file changed, 117 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/net/veth.c b/drivers/net/veth.c
>> index 2473f730734b..c62d87a8402c 100644
>> --- a/drivers/net/veth.c
>> +++ b/drivers/net/veth.c
>> @@ -28,6 +28,7 @@
>> #include <linux/bpf_trace.h>
>> #include <linux/net_tstamp.h>
>> #include <linux/skbuff_ref.h>
>> +#include <linux/sched/clock.h>
>> #include <net/page_pool/helpers.h>
>>
>> #define DRV_NAME "veth"
>> @@ -50,6 +51,7 @@
>> * delay => 64 * 250 ms = 16 s.
>> */
>> #define VETH_WATCHDOG_TIMEOUT_MS (64 * 250)
>> +#define VETH_BQL_COAL_TX_USECS 100 /* default tx-usecs for BQL batching*/
>>
>> struct veth_stats {
>> u64 rx_drops;
>> @@ -69,6 +71,11 @@ struct veth_rq_stats {
>> struct u64_stats_sync syncp;
>> };
>>
>> +struct veth_bql_state {
>> + u64 time; /* sched_clock() when current coalescing window started */
>> + uint n_bql; /* BQL completions batched in the current window */
>> +};
>> +
>> struct veth_rq {
>> struct napi_struct xdp_napi;
>> struct napi_struct __rcu *napi; /* points to xdp_napi when the latteris initialized */
>> @@ -76,6 +83,7 @@ struct veth_rq {
>> struct bpf_prog __rcu *xdp_prog;
>> struct xdp_mem_info xdp_mem;
>> struct veth_rq_stats stats;
>> + struct veth_bql_state bql_state;
>> bool rx_notify_masked;
>> struct ptr_ring xdp_ring;
>> struct xdp_rxq_info xdp_rxq;
>> @@ -88,6 +96,7 @@ struct veth_priv {
>> struct bpf_prog *_xdp_prog;
>> struct veth_rq *rq;
>> unsigned int requested_headroom;
>> + unsigned int tx_coal_usecs; /* BQL completion coalescing */
>> };
>>
>> struct veth_xdp_tx_bq {
>> @@ -272,7 +281,56 @@ static void veth_get_channels(struct net_device *dev,
>> static int veth_set_channels(struct net_device *dev,
>> struct ethtool_channels *ch);
>>
>> +static int veth_get_coalesce(struct net_device *dev,
>> + struct ethtool_coalesce *ec,
>> + struct kernel_ethtool_coalesce *kernel_coal,
>> + struct netlink_ext_ack *extack)
>> +{
>> + struct veth_priv *priv = netdev_priv(dev);
>> +
>> + ec->tx_coalesce_usecs = priv->tx_coal_usecs;
>> + return 0;
>> +}
>> +
>> +static int veth_set_coalesce(struct net_device *dev,
>> + struct ethtool_coalesce *ec,
>> + struct kernel_ethtool_coalesce *kernel_coal,
>> + struct netlink_ext_ack *extack)
>> +{
>> + struct veth_priv *priv = netdev_priv(dev);
>> + struct net_device *peer;
>> +
>> + /* The coalescing window delays BQL completions, so keep tx-usecs well
>> + * below the tx_timeout watchdog; otherwise a large value could stall a
>> + * stopped queue long enough to trip a false watchdog timeout. Cap at
>> + * half the watchdog to leave a generous safety margin. tx-usecs is
>> + * microseconds, the watchdog is milliseconds.
>> + */
>> + if (ec->tx_coalesce_usecs > VETH_WATCHDOG_TIMEOUT_MS / 2 * USEC_PER_MSEC) {
>> + NL_SET_ERR_MSG_MOD(extack,
>> + "tx-usecs must stay below half the tx_timeout watchdog");
>> + return -ERANGE;
>> + }
>> +
>> + /* Paired with READ_ONCE in veth_xdp_rcv(). */
>> + WRITE_ONCE(priv->tx_coal_usecs, ec->tx_coalesce_usecs);
>> +
>> + /* veth_xdp_rcv() reads each device's own value, so mirror it onto
>> + * the peer to keep the pair symmetric: both directions coalesce
>> + * with the same tx-usecs. Called under RTNL, rtnl_dereference() is safe.
>> + */
>> + peer = rtnl_dereference(priv->peer);
>> + if (peer) {
>> + struct veth_priv *peer_priv = netdev_priv(peer);
>> +
>> + WRITE_ONCE(peer_priv->tx_coal_usecs, ec->tx_coalesce_usecs);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> static const struct ethtool_ops veth_ethtool_ops = {
>> + .supported_coalesce_params = ETHTOOL_COALESCE_TX_USECS,
>> .get_drvinfo = veth_get_drvinfo,
>> .get_link = ethtool_op_get_link,
>> .get_strings = veth_get_strings,
>> @@ -282,6 +340,8 @@ static const struct ethtool_ops veth_ethtool_ops ={
>> .get_ts_info = ethtool_op_get_ts_info,
>> .get_channels = veth_get_channels,
>> .set_channels = veth_set_channels,
>> + .get_coalesce = veth_get_coalesce,
>> + .set_coalesce = veth_set_coalesce,
>> };
>>
>> /* general routines */
>> @@ -969,13 +1029,54 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq,
>> return NULL;
>> }
>>
>> +static void veth_bql_maybe_complete(struct veth_bql_state *state,
>> + struct netdev_queue *peer_txq,
>> + u64 bql_flush_ns)
>> +{
>> + u64 current_time;
>> +
>> + /* There is no reason to complete with 0 and
>> + * peer_txq could go away.
>> + */
>> + if (!state->n_bql || !peer_txq)
>> + return;
>> +
>> + current_time = sched_clock();
>> +
>> + /* We complete if:
>> + * 1. We reach bql_flush_ns.
>> + * 2. We potentially have BQL starvation.
>> + */
>> + if (state->time + bql_flush_ns <= current_time ||
>> + state->n_bql > peer_txq->dql.limit) {
>
Indeed, this does not compile when CONFIG_BQL is not set. I think we
should just bring back the 'queue is empty + queue is stopped' check
from v6 back at the end of the poll and remove the n_bql > dql.limit
check. It also feels not obvious why this is handling the starvation
case. This only works, because the producer has went overlimit
previously and was stopped. So more than 'limit' packets have been
enqueued to the ring, and they are eventually drained when this check is
true. By removing this we can also avoid accessing dql internal members,
but if you don't think that's a problem we can leave as is.
Further, this is only works if VETH_BQL_UNIT stays 1, otherwise it will
never fire. Anyway, still its necessary to check for CONFIG_BQL. But we
could solve this by adding VETH_BQL_UNIT to n_bql instead of 1. This is
also safe from any overflows, since limit is bound to limit_max,
inflight is always less than limit + 1*VETH_BQL_UNIT and n_bql <= inflight.
In a version of bringing back the 'queue-empty' check and keeping most
of the current logic (so a mixture of v6 and v7) resulted in the same
performance on an x86_64 architecture.
> Both Sashiko-Nipa and Sashiko-Gemini are right, this is missing a
> #ifdef CONFIG_BQL. Not sure what is the best way to add them.
> And for the struct we could maybe do:
>
> #ifdef CONFIG_BQL
> struct veth_bql_state {
> u64 time; /* sched_clock() when current coalescing window started */
> uint n_bql; /* BQL completions batched in the current window */
> };
> #else
> struct veth_bql_state {};
> #endif
Regarding the configs: we can just do something along those lines.
struct veth_rq {
...
#ifdef CONFIG_BQL
struct veth_bql_state dql;
#endif
...
}
and we put the rest of the code that accesses or performs an action
regarding bql in some functions and do it like in netdev_* functions with
Function-Signature()
{
#ifdef CONFIG_BQL
// Code
#endif
}
Wdyt?
- Jonas
>
>> + netdev_tx_completed_queue(peer_txq, state->n_bql,
>> + state->n_bql * VETH_BQL_UNIT);
>> + state->time = current_time;
>> + state->n_bql = 0;
>> + }
>> +}
>> +
>> static int veth_xdp_rcv(struct veth_rq *rq, int budget,
>> struct veth_xdp_tx_bq *bq,
>> struct veth_stats *stats,
>> struct netdev_queue *peer_txq)
>> {
>> + struct veth_priv *priv = netdev_priv(rq->dev);
>> + struct veth_bql_state *state = &rq->bql_state;
>> int i, done = 0, n_xdpf = 0;
>> void *xdpf[VETH_XDP_BATCH];
>> + u64 bql_flush_ns;
>> +
>> + /* Mirrored to both peers; paired with WRITE_ONCE() in veth_set_coalesce */
>> + bql_flush_ns = (u64)READ_ONCE(priv->tx_coal_usecs) * 1000;
>> +
>> + /* Clamp stored timestamp in case we migrated to a CPU with a behind
>> + * sched_clock(); tries to reduce late BQL flushes.
>> + */
>> + state->time = min(state->time, sched_clock());
>> +
>> + /* Flush completions that timed out since the previous NAPI poll. */
>> + veth_bql_maybe_complete(state, peer_txq, bql_flush_ns);>>
>> for (i = 0; i < budget; i++) {
>> void *ptr = __ptr_ring_consume(&rq->xdp_ring);
>> @@ -1000,12 +1101,11 @@ static int veth_xdp_rcv(struct veth_rq *rq, int budget,
>> }
>> } else {
>> /* ndo_start_xmit */
>> - bool bql_charged = veth_ptr_is_bql(ptr);
>> struct sk_buff *skb = veth_ptr_to_skb(ptr);
>>
>> + if (veth_ptr_is_bql(ptr))
>> + state->n_bql++;
>> stats->xdp_bytes += skb->len;
>> - if (peer_txq && bql_charged)
>> - netdev_tx_completed_queue(peer_txq, 1, VETH_BQL_UNIT);
>>
>> skb = veth_xdp_rcv_skb(rq, skb, bq, stats);
>> if (skb) {
>> @@ -1015,6 +1115,7 @@ static int veth_xdp_rcv(struct veth_rq *rq, int budget,
>> napi_gro_receive(&rq->xdp_napi, skb);
>> }
>> }
>> + veth_bql_maybe_complete(state, peer_txq, bql_flush_ns);
>> done++;
>
> Sashiko-Nipa reports:
>
> "If veth_xdp_rcv() finishes and returns a done count less than the budget,
> NAPI will go to sleep in veth_poll(). Do we need to unconditionally flush
> any stranded BQL completions in veth_poll() before sleeping?
> If completions are left in rq->bql_state indefinitely across NAPI idle
> periods, it might present an artificially massive delay to DQL. This could
> cause DQL to mistakenly conclude the hardware is extremely slow and
> aggressively shrink dql.limit to its minimum, crippling throughput on
> subsequent bursts."
>
> Again the issue that I found to be non-problematic in [1] and can be
> seen by an BQL inflight > 0 when for example pktgen suddenly stops.
>
> If we would "unconditionally flush any stranded BQL completions in
> veth_poll() before sleeping" we would *not* accumulate BQL completions
> across NAPI polls but we want to do that.
>
> Do you agree?
>
> [1] https://lore.kernel.org/netdev/c8650d3a-e488-4279-b28f-549d766c23a1@tu-dortmund.de/
^ permalink raw reply
* Re: [PATCH v2 1/1] xfrm: nat_keepalive: avoid double free on send error
From: Steffen Klassert @ 2026-06-30 14:03 UTC (permalink / raw)
To: Eyal Birger; +Cc: Ren Wei, netdev, herbert, davem, yuantan098, bird, qianyuluo3
In-Reply-To: <CAHsH6Gupn_PVjZjOWPRLWL6LCL=-p8Gde6_BruGdetn+eP3NMQ@mail.gmail.com>
On Sun, Jun 28, 2026 at 06:42:44AM -0700, Eyal Birger wrote:
> On Wed, Jun 24, 2026 at 10:55 PM Ren Wei <n05ec@lzu.edu.cn> wrote:
> >
> > From: Qianyu Luo <qianyuluo3@gmail.com>
> >
> > nat_keepalive_send() frees the keepalive skb whenever the IPv4 or IPv6
> > send helper reports an error.
> >
> > That cleanup is only correct before the skb is handed to the output
> > path. Once ip_build_and_send_pkt() or ip6_xmit() takes ownership, the
> > networking stack may already have consumed the skb before returning an
> > error, so freeing it again is unsafe.
> >
> > Handle the pre-handoff failure cases inside nat_keepalive_send_ipv4()
> > and nat_keepalive_send_ipv6(), where the caller still owns the skb, and
> > keep nat_keepalive_send() responsible only for family dispatch and the
> > unsupported-family cleanup path.
> >
> > Fixes: f531d13bdfe3 ("xfrm: support sending NAT keepalives in ESP in UDP states")
>
> Thanks for the fix!
>
> Reviewed-by: Eyal Birger <eyal.birger@gmail.com>
Applied, thanks everyone!
^ permalink raw reply
* Re: [PATCH 3/3] net: stmmac: dwmac-socfpga: Add mac-mode DT property support
From: Maxime Chevallier @ 2026-06-30 14:04 UTC (permalink / raw)
To: Andrew Lunn, muhammad.nazim.amirul.nazle.asmade
Cc: dinguyen, rmk+kernel, krzk+dt, conor+dt, robh, davem, edumazet,
kuba, pabeni, andrew+netdev, devicetree, linux-arm-kernel, netdev,
linux-kernel
In-Reply-To: <4c285993-978c-4d9e-a8c5-c3b36baa6840@lunn.ch>
On 6/30/26 16:02, Andrew Lunn wrote:
> On Tue, Jun 30, 2026 at 06:31:08AM -0700, muhammad.nazim.amirul.nazle.asmade@altera.com wrote:
>> From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
>>
>> Russell King's commit de696c63c1dc ("net: stmmac: socfpga: convert to
>> use phy_interface") replaced mac_interface with phy_interface in
>> socfpga_get_plat_phymode(), noting that no upstream DTS files set the
>> "mac-mode" property, making the two values identical.
>>
>> The Agilex5 SoCDK TSN Config2 board is an exception: its gmac1 TSN
>> port uses GMII internally in the MAC while the PHY-side interface is
>> RGMII, so mac-mode and phy-mode differ.
>
> Maybe you need to represent the hardware block which magically
> converts GMII to RGMII in DT?
Yeah that's what we have on CycloneV, and we force the INTF_SEL to GMII if that
HW block is present. I wonder if there's the same on agileX5 ?
>
> Andrew
^ permalink raw reply
* [PATCH net-next 00/15] pull request for net-next: batman-adv 2026-06-30
From: Simon Wunderlich @ 2026-06-30 14:06 UTC (permalink / raw)
To: netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, b.a.t.m.a.n, Simon Wunderlich
Dear net maintainers,
here is cleanup pull request of batman-adv to go into net-next.
Please pull or let me know of any problem!
Thank you,
Simon
The following changes since commit 805185b7c7a1069e407b6f7b3bc98e44d415f484:
Merge tag 'net-7.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net (2026-06-25 12:25:36 -0700)
are available in the Git repository at:
https://git.open-mesh.org/batadv.git tags/batadv-next-pullrequest-20260630
for you to fetch changes up to 247691642fd4de7a029de253e47dba936542ce9f:
batman-adv: tp_meter: delay allocation of unacked entry (2026-06-28 22:09:04 +0200)
----------------------------------------------------------------
This cleanup patchset includes the following patches:
- drop hardif global list, by Nora Schiffer (2 patches)
- make hard_iface->mesh_iface immutable, by Sven Eckelmann
- further post-hardif global list cleanups,
by Nora Schiffer (3 patches)
- cleanups and simplifications depending on the hardif->mesh_iface
immutability guarantee, by Sven Eckelmann (3 patches)
- tvlv: extract tvlv header iterator, by Sven Eckelmann
- tp_meter: improve unacked list handling,
by Sven Eckelmann (5 patches)
----------------------------------------------------------------
Nora Schiffer (5):
batman-adv: create hardif only for netdevs that are part of a mesh
batman-adv: remove global hardif list
batman-adv: remove BATADV_IF_NOT_IN_USE hardif state
batman-adv: move hardif generation counter into batadv_priv
batman-adv: drop unneeded goto and initialization from batadv_hardif_disable_interface()
Sven Eckelmann (10):
batman-adv: make hard_iface->mesh_iface immutable
batman-adv: drop NULL check for immutable hardif->mesh_iface
Revert "batman-adv: v: stop OGMv2 on disabled interface"
batman-adv: iv: drop migration check for batadv_hard_iface
batman-adv: tvlv: extract tvlv header iterator
batman-adv: tp_meter: simplify unordered ack calculation
batman-adv: tp_meter: combine adjacent/overlapping unacked entries
batman-adv: tp_meter: keep unacked list for receivers
batman-adv: tp_meter: adjust name of receiver lock
batman-adv: tp_meter: delay allocation of unacked entry
net/batman-adv/bat_iv_ogm.c | 12 +-
net/batman-adv/bat_v_elp.c | 9 +-
net/batman-adv/bat_v_ogm.c | 33 ++---
net/batman-adv/bridge_loop_avoidance.c | 9 +-
net/batman-adv/hard-interface.c | 165 ++++++++-----------------
net/batman-adv/hard-interface.h | 10 +-
net/batman-adv/main.c | 9 --
net/batman-adv/main.h | 3 -
net/batman-adv/mesh-interface.c | 13 +-
net/batman-adv/netlink.c | 4 +-
net/batman-adv/originator.c | 4 -
net/batman-adv/tp_meter.c | 217 ++++++++++++++++++++-------------
net/batman-adv/tvlv.c | 86 +++++++------
net/batman-adv/types.h | 28 ++---
14 files changed, 273 insertions(+), 329 deletions(-)
^ permalink raw reply
* [PATCH net-next 01/15] batman-adv: create hardif only for netdevs that are part of a mesh
From: Simon Wunderlich @ 2026-06-30 14:06 UTC (permalink / raw)
To: netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, b.a.t.m.a.n, Nora Schiffer, Sven Eckelmann,
Simon Wunderlich
In-Reply-To: <20260630140623.88431-1-sw@simonwunderlich.de>
From: Nora Schiffer <neocturne@universe-factory.net>
batman-adv is using netdev notifiers to create a hard_iface struct for
every Ethernet-like netdev in the system. These hardifs are tracked in a
global linked list, which results in a few performance issues:
Lookups in this list are O(n) in the total number of netdevs. As a
hardif is looked up when a netdev is removed, this also takes O(n) in
the number of netdevs, and removing n netdevs may take O(n^2). This
slowdown will always happen when the batman-adv module is loaded, no
mesh needs to be active.
With the hardif being referenced as iflink private data, the global list
is only needed for hardifs that are *not* part of a mesh (that is, the
hardif is unused). To prepare for removing the global list, only create
a hardif struct when an interface is added to a mesh and destroy it on
removal.
As adding/removing and enabling/disabling a hardif become one and the
same, batadv_hardif_add_interface() is merged into
batadv_hardif_enable_interface(), and batadv_hardif_remove_interface()
can be dropped altogether.
Signed-off-by: Nora Schiffer <neocturne@universe-factory.net>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
net/batman-adv/hard-interface.c | 120 +++++++++++---------------------
net/batman-adv/hard-interface.h | 2 +-
net/batman-adv/mesh-interface.c | 13 +---
3 files changed, 44 insertions(+), 91 deletions(-)
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index 03d01c20a9548..9c9a892d22c6b 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -723,33 +723,58 @@ batadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface)
}
/**
- * batadv_hardif_enable_interface() - Enslave hard interface to mesh interface
- * @hard_iface: hard interface to add to mesh interface
+ * batadv_hardif_enable_interface() - Enslave interface to mesh interface
+ * @net_dev: netdev struct of the interface to add to mesh interface
* @mesh_iface: netdev struct of the mesh interface
*
* Return: 0 on success or negative error number in case of failure
*/
-int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
+int batadv_hardif_enable_interface(struct net_device *net_dev,
struct net_device *mesh_iface)
{
struct batadv_priv *bat_priv;
__be16 ethertype = htons(ETH_P_BATMAN);
int max_header_len = batadv_max_header_len();
+ struct batadv_hard_iface *hard_iface;
unsigned int required_mtu;
unsigned int hardif_mtu;
bool fragmentation;
int ret;
- hardif_mtu = READ_ONCE(hard_iface->net_dev->mtu);
+ ASSERT_RTNL();
+
+ if (!batadv_is_valid_iface(net_dev))
+ return -EINVAL;
+
+ hardif_mtu = READ_ONCE(net_dev->mtu);
required_mtu = READ_ONCE(mesh_iface->mtu) + max_header_len;
if (hardif_mtu < ETH_MIN_MTU + max_header_len)
return -EINVAL;
- if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
- goto out;
+ hard_iface = kzalloc_obj(*hard_iface, GFP_ATOMIC);
+ if (!hard_iface)
+ return -ENOMEM;
+
+ netdev_hold(net_dev, &hard_iface->dev_tracker, GFP_ATOMIC);
+ hard_iface->net_dev = net_dev;
+
+ hard_iface->if_status = BATADV_IF_INACTIVE;
+
+ INIT_LIST_HEAD(&hard_iface->list);
+ INIT_HLIST_HEAD(&hard_iface->neigh_list);
- kref_get(&hard_iface->refcount);
+ mutex_init(&hard_iface->bat_iv.ogm_buff_mutex);
+ spin_lock_init(&hard_iface->neigh_list_lock);
+ kref_init(&hard_iface->refcount);
+
+ hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
+ if (batadv_is_wifi_hardif(hard_iface))
+ hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
+
+ WRITE_ONCE(hard_iface->hop_penalty, 0);
+
+ batadv_v_hardif_init(hard_iface);
netdev_hold(mesh_iface, &hard_iface->meshif_dev_tracker, GFP_ATOMIC);
hard_iface->mesh_iface = mesh_iface;
@@ -764,9 +789,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
if (ret < 0)
goto err_upper;
- hard_iface->if_status = BATADV_IF_INACTIVE;
-
- kref_get(&hard_iface->refcount);
hard_iface->batman_adv_ptype.type = ethertype;
hard_iface->batman_adv_ptype.func = batadv_batman_skb_recv;
hard_iface->batman_adv_ptype.dev = hard_iface->net_dev;
@@ -802,7 +824,9 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
if (bat_priv->algo_ops->iface.enabled)
bat_priv->algo_ops->iface.enabled(hard_iface);
-out:
+ list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
+ batadv_hardif_generation++;
+
return 0;
err_upper:
@@ -823,15 +847,19 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
struct batadv_hard_iface *primary_if = NULL;
+ ASSERT_RTNL();
+
batadv_hardif_deactivate_interface(hard_iface);
if (hard_iface->if_status != BATADV_IF_INACTIVE)
goto out;
+ list_del_rcu(&hard_iface->list);
+ batadv_hardif_generation++;
+
batadv_info(hard_iface->mesh_iface, "Removing interface: %s\n",
hard_iface->net_dev->name);
dev_remove_pack(&hard_iface->batman_adv_ptype);
- batadv_hardif_put(hard_iface);
primary_if = batadv_primary_if_get_selected(bat_priv);
if (hard_iface == primary_if) {
@@ -844,7 +872,7 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
}
bat_priv->algo_ops->iface.disable(hard_iface);
- hard_iface->if_status = BATADV_IF_NOT_IN_USE;
+ hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
/* delete all references to this hard_iface */
batadv_purge_orig_ref(bat_priv);
@@ -865,63 +893,6 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
batadv_hardif_put(primary_if);
}
-static struct batadv_hard_iface *
-batadv_hardif_add_interface(struct net_device *net_dev)
-{
- struct batadv_hard_iface *hard_iface;
-
- ASSERT_RTNL();
-
- if (!batadv_is_valid_iface(net_dev))
- return NULL;
-
- hard_iface = kzalloc_obj(*hard_iface, GFP_ATOMIC);
- if (!hard_iface)
- return NULL;
-
- netdev_hold(net_dev, &hard_iface->dev_tracker, GFP_ATOMIC);
- hard_iface->net_dev = net_dev;
-
- hard_iface->mesh_iface = NULL;
- hard_iface->if_status = BATADV_IF_NOT_IN_USE;
-
- INIT_LIST_HEAD(&hard_iface->list);
- INIT_HLIST_HEAD(&hard_iface->neigh_list);
-
- mutex_init(&hard_iface->bat_iv.ogm_buff_mutex);
- spin_lock_init(&hard_iface->neigh_list_lock);
- kref_init(&hard_iface->refcount);
-
- hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
- if (batadv_is_wifi_hardif(hard_iface))
- hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
-
- WRITE_ONCE(hard_iface->hop_penalty, 0);
-
- batadv_v_hardif_init(hard_iface);
-
- kref_get(&hard_iface->refcount);
- list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
- batadv_hardif_generation++;
-
- return hard_iface;
-}
-
-static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface)
-{
- ASSERT_RTNL();
-
- /* first deactivate interface */
- if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
- batadv_hardif_disable_interface(hard_iface);
-
- if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
- return;
-
- hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
- batadv_hardif_put(hard_iface);
-}
-
/**
* batadv_hard_if_event_meshif() - Handle events for mesh interfaces
* @event: NETDEV_* event to handle
@@ -1082,10 +1053,6 @@ static int batadv_hard_if_event(struct notifier_block *this,
batadv_wifi_net_device_event(event, net_dev);
hard_iface = batadv_hardif_get_by_netdev(net_dev);
- if (!hard_iface && (event == NETDEV_REGISTER ||
- event == NETDEV_POST_TYPE_CHANGE))
- hard_iface = batadv_hardif_add_interface(net_dev);
-
if (!hard_iface)
goto out;
@@ -1099,10 +1066,7 @@ static int batadv_hard_if_event(struct notifier_block *this,
break;
case NETDEV_UNREGISTER:
case NETDEV_PRE_TYPE_CHANGE:
- list_del_rcu(&hard_iface->list);
- batadv_hardif_generation++;
-
- batadv_hardif_remove_interface(hard_iface);
+ batadv_hardif_disable_interface(hard_iface);
break;
case NETDEV_CHANGEMTU:
if (hard_iface->mesh_iface)
diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h
index af31696c39780..6d72dbdd5c203 100644
--- a/net/batman-adv/hard-interface.h
+++ b/net/batman-adv/hard-interface.h
@@ -75,7 +75,7 @@ u32 batadv_hardif_get_wifi_flags(struct batadv_hard_iface *hard_iface);
bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface);
struct batadv_hard_iface*
batadv_hardif_get_by_netdev(const struct net_device *net_dev);
-int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
+int batadv_hardif_enable_interface(struct net_device *net_dev,
struct net_device *mesh_iface);
void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface);
int batadv_hardif_min_mtu(struct net_device *mesh_iface);
diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c
index 44026810b99ce..a37368c1f5b55 100644
--- a/net/batman-adv/mesh-interface.c
+++ b/net/batman-adv/mesh-interface.c
@@ -836,18 +836,7 @@ static int batadv_meshif_slave_add(struct net_device *dev,
struct net_device *slave_dev,
struct netlink_ext_ack *extack)
{
- struct batadv_hard_iface *hard_iface;
- int ret = -EINVAL;
-
- hard_iface = batadv_hardif_get_by_netdev(slave_dev);
- if (!hard_iface || hard_iface->mesh_iface)
- goto out;
-
- ret = batadv_hardif_enable_interface(hard_iface, dev);
-
-out:
- batadv_hardif_put(hard_iface);
- return ret;
+ return batadv_hardif_enable_interface(slave_dev, dev);
}
/**
--
2.47.3
^ permalink raw reply related
* [PATCH net-next 02/15] batman-adv: remove global hardif list
From: Simon Wunderlich @ 2026-06-30 14:06 UTC (permalink / raw)
To: netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, b.a.t.m.a.n, Nora Schiffer, Sven Eckelmann,
Simon Wunderlich
In-Reply-To: <20260630140623.88431-1-sw@simonwunderlich.de>
From: Nora Schiffer <neocturne@universe-factory.net>
With interfaces being kept track of as iflink private data, there is no
need for the global list anymore. batadv_hardif_get_by_netdev() can now
use netdev_master_upper_dev_get()+netdev_lower_dev_get_private() to find
the hardif corresponding to a netdev.
Signed-off-by: Nora Schiffer <neocturne@universe-factory.net>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
net/batman-adv/hard-interface.c | 29 ++++++++++++-----------------
net/batman-adv/hard-interface.h | 2 +-
net/batman-adv/main.c | 5 -----
net/batman-adv/main.h | 1 -
net/batman-adv/netlink.c | 2 ++
net/batman-adv/types.h | 3 ---
6 files changed, 15 insertions(+), 27 deletions(-)
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index 9c9a892d22c6b..ace81348ddef7 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -75,21 +75,21 @@ void batadv_hardif_release(struct kref *ref)
* Return: batadv_hard_iface of net_dev (with increased refcnt), NULL on errors
*/
struct batadv_hard_iface *
-batadv_hardif_get_by_netdev(const struct net_device *net_dev)
+batadv_hardif_get_by_netdev(struct net_device *net_dev)
{
struct batadv_hard_iface *hard_iface;
+ struct net_device *mesh_iface;
- rcu_read_lock();
- list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
- if (hard_iface->net_dev == net_dev &&
- kref_get_unless_zero(&hard_iface->refcount))
- goto out;
- }
+ ASSERT_RTNL();
- hard_iface = NULL;
+ mesh_iface = netdev_master_upper_dev_get(net_dev);
+ if (!mesh_iface || !batadv_meshif_is_valid(mesh_iface))
+ return NULL;
+
+ hard_iface = netdev_lower_dev_get_private(mesh_iface, net_dev);
+ if (!kref_get_unless_zero(&hard_iface->refcount))
+ return NULL;
-out:
- rcu_read_unlock();
return hard_iface;
}
@@ -761,7 +761,6 @@ int batadv_hardif_enable_interface(struct net_device *net_dev,
hard_iface->if_status = BATADV_IF_INACTIVE;
- INIT_LIST_HEAD(&hard_iface->list);
INIT_HLIST_HEAD(&hard_iface->neigh_list);
mutex_init(&hard_iface->bat_iv.ogm_buff_mutex);
@@ -780,6 +779,7 @@ int batadv_hardif_enable_interface(struct net_device *net_dev,
hard_iface->mesh_iface = mesh_iface;
bat_priv = netdev_priv(hard_iface->mesh_iface);
+ batadv_hardif_generation++;
ret = netdev_master_upper_dev_link(hard_iface->net_dev,
mesh_iface, hard_iface, NULL, NULL);
if (ret)
@@ -824,9 +824,6 @@ int batadv_hardif_enable_interface(struct net_device *net_dev,
if (bat_priv->algo_ops->iface.enabled)
bat_priv->algo_ops->iface.enabled(hard_iface);
- list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
- batadv_hardif_generation++;
-
return 0;
err_upper:
@@ -854,9 +851,6 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
if (hard_iface->if_status != BATADV_IF_INACTIVE)
goto out;
- list_del_rcu(&hard_iface->list);
- batadv_hardif_generation++;
-
batadv_info(hard_iface->mesh_iface, "Removing interface: %s\n",
hard_iface->net_dev->name);
dev_remove_pack(&hard_iface->batman_adv_ptype);
@@ -879,6 +873,7 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
batadv_purge_outstanding_packets(bat_priv, hard_iface);
netdev_put(hard_iface->mesh_iface, &hard_iface->meshif_dev_tracker);
+ batadv_hardif_generation++;
netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->mesh_iface);
batadv_hardif_recalc_extra_skbroom(hard_iface->mesh_iface);
diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h
index 6d72dbdd5c203..aa9275dec0976 100644
--- a/net/batman-adv/hard-interface.h
+++ b/net/batman-adv/hard-interface.h
@@ -74,7 +74,7 @@ u32 batadv_netdev_get_wifi_flags(struct net_device *net_dev);
u32 batadv_hardif_get_wifi_flags(struct batadv_hard_iface *hard_iface);
bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface);
struct batadv_hard_iface*
-batadv_hardif_get_by_netdev(const struct net_device *net_dev);
+batadv_hardif_get_by_netdev(struct net_device *net_dev);
int batadv_hardif_enable_interface(struct net_device *net_dev,
struct net_device *mesh_iface);
void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface);
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index 3c4572284b532..1d82f3a841a1b 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -59,10 +59,6 @@
#include "tp_meter.h"
#include "translation-table.h"
-/* List manipulations on hardif_list have to be rtnl_lock()'ed,
- * list traversals just rcu-locked
- */
-struct list_head batadv_hardif_list;
unsigned int batadv_hardif_generation;
static int (*batadv_rx_handler[256])(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
@@ -95,7 +91,6 @@ static int __init batadv_init(void)
if (ret < 0)
return ret;
- INIT_LIST_HEAD(&batadv_hardif_list);
batadv_algo_init();
batadv_recv_handler_init();
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index f68fc8b7239cd..e34145047a341 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -226,7 +226,6 @@ static inline int batadv_print_vid(unsigned short vid)
return -1;
}
-extern struct list_head batadv_hardif_list;
extern unsigned int batadv_hardif_generation;
extern struct workqueue_struct *batadv_event_workqueue;
diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c
index 4cf9e3c54ad3b..62ea91aa3ead6 100644
--- a/net/batman-adv/netlink.c
+++ b/net/batman-adv/netlink.c
@@ -1211,7 +1211,9 @@ batadv_netlink_get_hardif_from_ifindex(struct batadv_priv *bat_priv,
if (!hard_dev)
return ERR_PTR(-ENODEV);
+ rtnl_lock();
hard_iface = batadv_hardif_get_by_netdev(hard_dev);
+ rtnl_unlock();
if (!hard_iface)
goto err_put_harddev;
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index b1f9f8964c3fd..1671380b37929 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -214,9 +214,6 @@ struct batadv_wifi_net_device_state {
* struct batadv_hard_iface - network device known to batman-adv
*/
struct batadv_hard_iface {
- /** @list: list node for batadv_hardif_list */
- struct list_head list;
-
/** @if_status: status of the interface for batman-adv */
char if_status;
--
2.47.3
^ permalink raw reply related
* [PATCH net-next 03/15] batman-adv: make hard_iface->mesh_iface immutable
From: Simon Wunderlich @ 2026-06-30 14:06 UTC (permalink / raw)
To: netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, b.a.t.m.a.n, Sven Eckelmann, Nora Schiffer,
Simon Wunderlich
In-Reply-To: <20260630140623.88431-1-sw@simonwunderlich.de>
From: Sven Eckelmann <sven@narfation.org>
With the hard_iface now being created for a specific mesh_iface, it is
beneficial not to set mesh_iface to NULL when the interface is disabled,
but instead keeping it immutable after the initial setup of the
hard_iface. By also holding the reference to the mesh_iface until the
hard_iface is released, hard_ifaces iterated over under RCU will always
point to a valid mesh_iface.
Co-developed-by: Nora Schiffer <neocturne@universe-factory.net>
Signed-off-by: Nora Schiffer <neocturne@universe-factory.net>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
net/batman-adv/hard-interface.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index ace81348ddef7..a0b8b06f9a644 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -63,6 +63,7 @@ void batadv_hardif_release(struct kref *ref)
struct batadv_hard_iface *hard_iface;
hard_iface = container_of(ref, struct batadv_hard_iface, refcount);
+ netdev_put(hard_iface->mesh_iface, &hard_iface->meshif_dev_tracker);
netdev_put(hard_iface->net_dev, &hard_iface->dev_tracker);
kfree_rcu(hard_iface, rcu);
@@ -829,8 +830,6 @@ int batadv_hardif_enable_interface(struct net_device *net_dev,
err_upper:
netdev_upper_dev_unlink(hard_iface->net_dev, mesh_iface);
err_dev:
- hard_iface->mesh_iface = NULL;
- netdev_put(mesh_iface, &hard_iface->meshif_dev_tracker);
batadv_hardif_put(hard_iface);
return ret;
}
@@ -871,7 +870,6 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
/* delete all references to this hard_iface */
batadv_purge_orig_ref(bat_priv);
batadv_purge_outstanding_packets(bat_priv, hard_iface);
- netdev_put(hard_iface->mesh_iface, &hard_iface->meshif_dev_tracker);
batadv_hardif_generation++;
netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->mesh_iface);
@@ -881,7 +879,6 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
if (list_empty(&hard_iface->mesh_iface->adj_list.lower))
batadv_gw_check_client_stop(bat_priv);
- hard_iface->mesh_iface = NULL;
batadv_hardif_put(hard_iface);
out:
--
2.47.3
^ permalink raw reply related
* [PATCH net-next 04/15] batman-adv: remove BATADV_IF_NOT_IN_USE hardif state
From: Simon Wunderlich @ 2026-06-30 14:06 UTC (permalink / raw)
To: netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, b.a.t.m.a.n, Nora Schiffer, Sven Eckelmann,
Simon Wunderlich
In-Reply-To: <20260630140623.88431-1-sw@simonwunderlich.de>
From: Nora Schiffer <neocturne@universe-factory.net>
With hardifs only existing while an interface is part of a mesh, the
BATADV_IF_NOT_IN_USE state has become redundant.
Signed-off-by: Nora Schiffer <neocturne@universe-factory.net>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
net/batman-adv/bat_iv_ogm.c | 3 +--
net/batman-adv/bat_v_elp.c | 3 +--
net/batman-adv/hard-interface.c | 9 ---------
net/batman-adv/hard-interface.h | 6 ------
net/batman-adv/originator.c | 4 ----
5 files changed, 2 insertions(+), 23 deletions(-)
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index bb2f012b454ea..4514c51bba777 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -910,8 +910,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
{
- if (hard_iface->if_status == BATADV_IF_NOT_IN_USE ||
- hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
+ if (hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
return;
mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c
index 4841f0f1a9b13..bc3e4f264afa1 100644
--- a/net/batman-adv/bat_v_elp.c
+++ b/net/batman-adv/bat_v_elp.c
@@ -311,8 +311,7 @@ static void batadv_v_elp_periodic_work(struct work_struct *work)
goto out;
/* we are in the process of shutting this interface down */
- if (hard_iface->if_status == BATADV_IF_NOT_IN_USE ||
- hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
+ if (hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
goto out;
/* the interface was enabled but may not be ready yet */
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index a0b8b06f9a644..86010bc32818e 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -547,9 +547,6 @@ static void batadv_check_known_mac_addr(const struct batadv_hard_iface *hard_ifa
if (tmp_hard_iface == hard_iface)
continue;
- if (tmp_hard_iface->if_status == BATADV_IF_NOT_IN_USE)
- continue;
-
if (!batadv_compare_eth(tmp_hard_iface->net_dev->dev_addr,
hard_iface->net_dev->dev_addr))
continue;
@@ -575,9 +572,6 @@ static void batadv_hardif_recalc_extra_skbroom(struct net_device *mesh_iface)
rcu_read_lock();
netdev_for_each_lower_private_rcu(mesh_iface, hard_iface, iter) {
- if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
- continue;
-
lower_header_len = max_t(unsigned short, lower_header_len,
hard_iface->net_dev->hard_header_len);
@@ -1065,9 +1059,6 @@ static int batadv_hard_if_event(struct notifier_block *this,
batadv_update_min_mtu(hard_iface->mesh_iface);
break;
case NETDEV_CHANGEADDR:
- if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
- goto hardif_put;
-
batadv_check_known_mac_addr(hard_iface);
bat_priv = netdev_priv(hard_iface->mesh_iface);
diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h
index aa9275dec0976..935f47ca9a48f 100644
--- a/net/batman-adv/hard-interface.h
+++ b/net/batman-adv/hard-interface.h
@@ -21,12 +21,6 @@
* enum batadv_hard_if_state - State of a hard interface
*/
enum batadv_hard_if_state {
- /**
- * @BATADV_IF_NOT_IN_USE: interface is not used as slave interface of a
- * batman-adv mesh interface
- */
- BATADV_IF_NOT_IN_USE,
-
/**
* @BATADV_IF_TO_BE_REMOVED: interface will be removed from mesh
* interface
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c
index 9b38bd9e8da7e..48f837cf665a2 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -1033,7 +1033,6 @@ batadv_purge_neigh_ifinfo(struct batadv_priv *bat_priv,
/* don't purge if the interface is not (going) down */
if (if_outgoing->if_status != BATADV_IF_INACTIVE &&
- if_outgoing->if_status != BATADV_IF_NOT_IN_USE &&
if_outgoing->if_status != BATADV_IF_TO_BE_REMOVED)
continue;
@@ -1077,7 +1076,6 @@ batadv_purge_orig_ifinfo(struct batadv_priv *bat_priv,
/* don't purge if the interface is not (going) down */
if (if_outgoing->if_status != BATADV_IF_INACTIVE &&
- if_outgoing->if_status != BATADV_IF_NOT_IN_USE &&
if_outgoing->if_status != BATADV_IF_TO_BE_REMOVED)
continue;
@@ -1127,10 +1125,8 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv,
if (batadv_has_timed_out(last_seen, BATADV_PURGE_TIMEOUT) ||
if_incoming->if_status == BATADV_IF_INACTIVE ||
- if_incoming->if_status == BATADV_IF_NOT_IN_USE ||
if_incoming->if_status == BATADV_IF_TO_BE_REMOVED) {
if (if_incoming->if_status == BATADV_IF_INACTIVE ||
- if_incoming->if_status == BATADV_IF_NOT_IN_USE ||
if_incoming->if_status == BATADV_IF_TO_BE_REMOVED)
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"neighbor purge: originator %pM, neighbor: %pM, iface: %s\n",
--
2.47.3
^ permalink raw reply related
* [PATCH net-next 05/15] batman-adv: move hardif generation counter into batadv_priv
From: Simon Wunderlich @ 2026-06-30 14:06 UTC (permalink / raw)
To: netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, b.a.t.m.a.n, Nora Schiffer, Sven Eckelmann,
Simon Wunderlich
In-Reply-To: <20260630140623.88431-1-sw@simonwunderlich.de>
From: Nora Schiffer <neocturne@universe-factory.net>
The counter doesn't need to be global.
Signed-off-by: Nora Schiffer <neocturne@universe-factory.net>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
net/batman-adv/hard-interface.c | 4 ++--
net/batman-adv/main.c | 1 -
net/batman-adv/main.h | 2 --
net/batman-adv/netlink.c | 2 +-
net/batman-adv/types.h | 3 +++
5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index 86010bc32818e..9b8108d464dbc 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -774,7 +774,7 @@ int batadv_hardif_enable_interface(struct net_device *net_dev,
hard_iface->mesh_iface = mesh_iface;
bat_priv = netdev_priv(hard_iface->mesh_iface);
- batadv_hardif_generation++;
+ bat_priv->hardif_generation++;
ret = netdev_master_upper_dev_link(hard_iface->net_dev,
mesh_iface, hard_iface, NULL, NULL);
if (ret)
@@ -865,7 +865,7 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
batadv_purge_orig_ref(bat_priv);
batadv_purge_outstanding_packets(bat_priv, hard_iface);
- batadv_hardif_generation++;
+ bat_priv->hardif_generation++;
netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->mesh_iface);
batadv_hardif_recalc_extra_skbroom(hard_iface->mesh_iface);
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index 1d82f3a841a1b..badc1df0af1d5 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -59,7 +59,6 @@
#include "tp_meter.h"
#include "translation-table.h"
-unsigned int batadv_hardif_generation;
static int (*batadv_rx_handler[256])(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index e34145047a341..e738758ee4a7a 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -226,8 +226,6 @@ static inline int batadv_print_vid(unsigned short vid)
return -1;
}
-extern unsigned int batadv_hardif_generation;
-
extern struct workqueue_struct *batadv_event_workqueue;
int batadv_mesh_init(struct net_device *mesh_iface);
diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c
index 62ea91aa3ead6..d2bc48c707143 100644
--- a/net/batman-adv/netlink.c
+++ b/net/batman-adv/netlink.c
@@ -968,7 +968,7 @@ batadv_netlink_dump_hardif(struct sk_buff *msg, struct netlink_callback *cb)
bat_priv = netdev_priv(mesh_iface);
rtnl_lock();
- cb->seq = batadv_hardif_generation << 1 | 1;
+ cb->seq = bat_priv->hardif_generation << 1 | 1;
netdev_for_each_lower_private(mesh_iface, hard_iface, iter) {
if (i++ < skip)
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 1671380b37929..e1463a029e835 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -1676,6 +1676,9 @@ struct batadv_priv {
/** @tp_num: number of currently active tp sessions */
atomic_t tp_num;
+ /** @hardif_generation: generation counter added to netlink hardif dumps */
+ unsigned int hardif_generation;
+
/** @orig_work: work queue callback item for orig node purging */
struct delayed_work orig_work;
--
2.47.3
^ permalink raw reply related
* [PATCH net-next 06/15] batman-adv: drop unneeded goto and initialization from batadv_hardif_disable_interface()
From: Simon Wunderlich @ 2026-06-30 14:06 UTC (permalink / raw)
To: netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, b.a.t.m.a.n, Nora Schiffer, Sven Eckelmann,
Simon Wunderlich
In-Reply-To: <20260630140623.88431-1-sw@simonwunderlich.de>
From: Nora Schiffer <neocturne@universe-factory.net>
The only use of the label was too early for primary_if to be set
anyways.
Also move the put of primary_if further up to hold the reference only as
long as necessary, hopefully avoiding the need to re-introduce the goto
label with future code changes.
Signed-off-by: Nora Schiffer <neocturne@universe-factory.net>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
net/batman-adv/hard-interface.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index 9b8108d464dbc..6fc49ad47fd87 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -835,14 +835,14 @@ int batadv_hardif_enable_interface(struct net_device *net_dev,
void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
- struct batadv_hard_iface *primary_if = NULL;
+ struct batadv_hard_iface *primary_if;
ASSERT_RTNL();
batadv_hardif_deactivate_interface(hard_iface);
if (hard_iface->if_status != BATADV_IF_INACTIVE)
- goto out;
+ return;
batadv_info(hard_iface->mesh_iface, "Removing interface: %s\n",
hard_iface->net_dev->name);
@@ -857,6 +857,7 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
batadv_hardif_put(new_if);
}
+ batadv_hardif_put(primary_if);
bat_priv->algo_ops->iface.disable(hard_iface);
hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
@@ -874,9 +875,6 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
batadv_gw_check_client_stop(bat_priv);
batadv_hardif_put(hard_iface);
-
-out:
- batadv_hardif_put(primary_if);
}
/**
--
2.47.3
^ permalink raw reply related
* [PATCH net-next 07/15] batman-adv: drop NULL check for immutable hardif->mesh_iface
From: Simon Wunderlich @ 2026-06-30 14:06 UTC (permalink / raw)
To: netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, b.a.t.m.a.n, Sven Eckelmann, Simon Wunderlich
In-Reply-To: <20260630140623.88431-1-sw@simonwunderlich.de>
From: Sven Eckelmann <sven@narfation.org>
The batadv_hard_iface->mesh_iface became immutable after the global
batadv_hardif_list was removed and batadv_hard_iface only exists when it is
assigned to an mesh_iface. This member can never become NULL and thus a
check is now unnecessary.
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
net/batman-adv/bat_v_elp.c | 6 ------
net/batman-adv/bridge_loop_avoidance.c | 9 ++-------
net/batman-adv/hard-interface.c | 8 ++------
net/batman-adv/main.c | 3 ---
4 files changed, 4 insertions(+), 22 deletions(-)
diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c
index bc3e4f264afa1..262e40040007c 100644
--- a/net/batman-adv/bat_v_elp.c
+++ b/net/batman-adv/bat_v_elp.c
@@ -90,12 +90,6 @@ static bool batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh,
u32 throughput;
int ret;
- /* don't query throughput when no longer associated with any
- * batman-adv interface
- */
- if (!mesh_iface)
- return false;
-
/* if the user specified a customised value for this interface, then
* return it directly
*/
diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index 5c73f6ba16cff..f9a1fadf8de9e 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -344,7 +344,6 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
struct sk_buff *skb;
struct ethhdr *ethhdr;
struct batadv_hard_iface *primary_if;
- struct net_device *mesh_iface;
u8 *hw_src;
struct batadv_bla_claim_dst local_claim_dest;
__be32 zeroip = 0;
@@ -357,14 +356,10 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
sizeof(local_claim_dest));
local_claim_dest.type = claimtype;
- mesh_iface = READ_ONCE(primary_if->mesh_iface);
- if (!mesh_iface)
- goto out;
-
skb = arp_create(ARPOP_REPLY, ETH_P_ARP,
/* IP DST: 0.0.0.0 */
zeroip,
- mesh_iface,
+ primary_if->mesh_iface,
/* IP SRC: 0.0.0.0 */
zeroip,
/* Ethernet DST: Broadcast */
@@ -442,7 +437,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
}
skb_reset_mac_header(skb);
- skb->protocol = eth_type_trans(skb, mesh_iface);
+ skb->protocol = eth_type_trans(skb, primary_if->mesh_iface);
batadv_inc_counter(bat_priv, BATADV_CNT_RX);
batadv_add_counter(bat_priv, BATADV_CNT_RX_BYTES,
skb->len + ETH_HLEN);
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index 6fc49ad47fd87..b6867576bbafa 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -246,7 +246,7 @@ struct net_device *__batadv_get_real_netdev(struct net_device *netdev)
}
hard_iface = batadv_hardif_get_by_netdev(netdev);
- if (!hard_iface || !hard_iface->mesh_iface)
+ if (!hard_iface)
goto out;
net = dev_net(hard_iface->mesh_iface);
@@ -540,9 +540,6 @@ static void batadv_check_known_mac_addr(const struct batadv_hard_iface *hard_ifa
const struct batadv_hard_iface *tmp_hard_iface;
struct list_head *iter;
- if (!mesh_iface)
- return;
-
netdev_for_each_lower_private(mesh_iface, tmp_hard_iface, iter) {
if (tmp_hard_iface == hard_iface)
continue;
@@ -1053,8 +1050,7 @@ static int batadv_hard_if_event(struct notifier_block *this,
batadv_hardif_disable_interface(hard_iface);
break;
case NETDEV_CHANGEMTU:
- if (hard_iface->mesh_iface)
- batadv_update_min_mtu(hard_iface->mesh_iface);
+ batadv_update_min_mtu(hard_iface->mesh_iface);
break;
case NETDEV_CHANGEADDR:
batadv_check_known_mac_addr(hard_iface);
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index badc1df0af1d5..04bb030ef299a 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -444,9 +444,6 @@ int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
if (unlikely(skb->mac_len != ETH_HLEN || !skb_mac_header(skb)))
goto err_free;
- if (!hard_iface->mesh_iface)
- goto err_free;
-
bat_priv = netdev_priv(hard_iface->mesh_iface);
if (READ_ONCE(bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
--
2.47.3
^ permalink raw reply related
* [PATCH net-next 08/15] Revert "batman-adv: v: stop OGMv2 on disabled interface"
From: Simon Wunderlich @ 2026-06-30 14:06 UTC (permalink / raw)
To: netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, b.a.t.m.a.n, Sven Eckelmann, Simon Wunderlich
In-Reply-To: <20260630140623.88431-1-sw@simonwunderlich.de>
From: Sven Eckelmann <sven@narfation.org>
With the immutability guarantee of batadv_hard_iface->mesh_iface, the check
for "changed" (or NULL) mesh_iface doesn't work anymore and is also no
longer necessary. The extra (complicated) code for the sending of OGMv2s
can therefore be removed and the original code can be used again.
This reverts commit f8ce8b8331a1bc44ad4905886a482214d428b253.
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
net/batman-adv/bat_v_ogm.c | 33 ++++++++++++---------------------
1 file changed, 12 insertions(+), 21 deletions(-)
diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
index 037921aad35d5..e921d49f7eced 100644
--- a/net/batman-adv/bat_v_ogm.c
+++ b/net/batman-adv/bat_v_ogm.c
@@ -115,14 +115,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv)
/**
* batadv_v_ogm_send_to_if() - send a batman ogm using a given interface
- * @bat_priv: the bat priv with all the mesh interface information
* @skb: the OGM to send
* @hard_iface: the interface to use to send the OGM
*/
-static void batadv_v_ogm_send_to_if(struct batadv_priv *bat_priv,
- struct sk_buff *skb,
+static void batadv_v_ogm_send_to_if(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface)
{
+ struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
+
if (hard_iface->if_status != BATADV_IF_ACTIVE) {
kfree_skb(skb);
return;
@@ -189,7 +189,6 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
/**
* batadv_v_ogm_aggr_send() - flush & send aggregation queue
- * @bat_priv: the bat priv with all the mesh interface information
* @hard_iface: the interface with the aggregation queue to flush
*
* Aggregates all OGMv2 packets currently in the aggregation queue into a
@@ -199,8 +198,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
*
* Caller needs to hold the hard_iface->bat_v.aggr_list.lock.
*/
-static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,
- struct batadv_hard_iface *hard_iface)
+static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
{
unsigned int aggr_len = hard_iface->bat_v.aggr_len;
struct sk_buff *skb_aggr;
@@ -230,26 +228,21 @@ static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,
consume_skb(skb);
}
- batadv_v_ogm_send_to_if(bat_priv, skb_aggr, hard_iface);
+ batadv_v_ogm_send_to_if(skb_aggr, hard_iface);
}
/**
* batadv_v_ogm_queue_on_if() - queue a batman ogm on a given interface
- * @bat_priv: the bat priv with all the mesh interface information
* @skb: the OGM to queue
* @hard_iface: the interface to queue the OGM on
*/
-static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
- struct sk_buff *skb,
+static void batadv_v_ogm_queue_on_if(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface)
{
- if (hard_iface->mesh_iface != bat_priv->mesh_iface) {
- kfree_skb(skb);
- return;
- }
+ struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
if (!READ_ONCE(bat_priv->aggregated_ogms)) {
- batadv_v_ogm_send_to_if(bat_priv, skb, hard_iface);
+ batadv_v_ogm_send_to_if(skb, hard_iface);
return;
}
@@ -260,7 +253,7 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
}
if (!batadv_v_ogm_queue_left(skb, hard_iface))
- batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+ batadv_v_ogm_aggr_send(hard_iface);
hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
__skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
@@ -357,7 +350,7 @@ static void batadv_v_ogm_send_meshif(struct batadv_priv *bat_priv)
break;
}
- batadv_v_ogm_queue_on_if(bat_priv, skb_tmp, hard_iface);
+ batadv_v_ogm_queue_on_if(skb_tmp, hard_iface);
batadv_hardif_put(hard_iface);
}
rcu_read_unlock();
@@ -397,14 +390,12 @@ void batadv_v_ogm_aggr_work(struct work_struct *work)
{
struct batadv_hard_iface_bat_v *batv;
struct batadv_hard_iface *hard_iface;
- struct batadv_priv *bat_priv;
batv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work);
hard_iface = container_of(batv, struct batadv_hard_iface, bat_v);
- bat_priv = netdev_priv(hard_iface->mesh_iface);
spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
- batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+ batadv_v_ogm_aggr_send(hard_iface);
spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
batadv_v_ogm_start_queue_timer(hard_iface);
@@ -601,7 +592,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv,
if_outgoing->net_dev->name, ntohl(ogm_forward->throughput),
ogm_forward->ttl, if_incoming->net_dev->name);
- batadv_v_ogm_queue_on_if(bat_priv, skb, if_outgoing);
+ batadv_v_ogm_queue_on_if(skb, if_outgoing);
out:
batadv_orig_ifinfo_put(orig_ifinfo);
--
2.47.3
^ permalink raw reply related
* [PATCH net-next 09/15] batman-adv: iv: drop migration check for batadv_hard_iface
From: Simon Wunderlich @ 2026-06-30 14:06 UTC (permalink / raw)
To: netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, b.a.t.m.a.n, Sven Eckelmann, Simon Wunderlich
In-Reply-To: <20260630140623.88431-1-sw@simonwunderlich.de>
From: Sven Eckelmann <sven@narfation.org>
With the immutability guarantee of batadv_hard_iface->mesh_iface, the check
for "changed" (or NULL) mesh_iface is no longer necessary because a
batadv_hard_iface can no longer migrate from one batadv_mesh_iface to
another one.
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
net/batman-adv/bat_iv_ogm.c | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index 4514c51bba777..22622283f59b0 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -404,23 +404,14 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,
/* send a batman ogm packet */
static void batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet)
{
- struct net_device *mesh_iface;
-
if (!forw_packet->if_incoming) {
pr_err("Error - can't forward packet: incoming iface not specified\n");
return;
}
- mesh_iface = forw_packet->if_incoming->mesh_iface;
-
if (WARN_ON(!forw_packet->if_outgoing))
return;
- if (forw_packet->if_outgoing->mesh_iface != mesh_iface) {
- pr_warn("%s: mesh interface switch for queued OGM\n", __func__);
- return;
- }
-
if (forw_packet->if_incoming->if_status != BATADV_IF_ACTIVE)
return;
--
2.47.3
^ permalink raw reply related
* [PATCH net-next 10/15] batman-adv: tvlv: extract tvlv header iterator
From: Simon Wunderlich @ 2026-06-30 14:06 UTC (permalink / raw)
To: netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, b.a.t.m.a.n, Sven Eckelmann, Simon Wunderlich
In-Reply-To: <20260630140623.88431-1-sw@simonwunderlich.de>
From: Sven Eckelmann <sven@narfation.org>
batadv_tvlv_containers_contain() and batadv_tvlv_containers_process() are
using the same code to iterate through the TVLV containers. To simplify the
code, extract the shared portions of both functions.
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
net/batman-adv/tvlv.c | 86 +++++++++++++++++++++++++------------------
1 file changed, 51 insertions(+), 35 deletions(-)
diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
index 1c9fb21985f6a..49bf2ed9ecdc3 100644
--- a/net/batman-adv/tvlv.c
+++ b/net/batman-adv/tvlv.c
@@ -442,6 +442,54 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
return NET_RX_SUCCESS;
}
+/**
+ * batadv_tvlv_hdr_next() - move a tvlv buffer cursor to the next container
+ * @tvlv_value: cursor into the tvlv buffer, advanced past the returned
+ * container's content on success
+ * @tvlv_value_len: remaining length of the tvlv buffer, reduced by the returned
+ * container's size on success
+ *
+ * Parses a single container header at the current cursor position and, if a
+ * complete container is available, advances the cursor and remaining length
+ * past it. The returned header stays valid; its content is located at
+ * (returned header + 1) and is ntohs(hdr->len) bytes long.
+ *
+ * Return: pointer to the next tvlv container header, or NULL if no further
+ * complete container is present in the buffer.
+ */
+static struct batadv_tvlv_hdr *batadv_tvlv_hdr_next(void **tvlv_value, u16 *tvlv_value_len)
+{
+ struct batadv_tvlv_hdr *tvlv_hdr;
+ u16 tvlv_value_cont_len;
+ void *tvlv_value_cont;
+ u16 tvlv_len;
+
+ tvlv_value_cont = *tvlv_value;
+ tvlv_len = *tvlv_value_len;
+
+ if (tvlv_len < sizeof(*tvlv_hdr))
+ return NULL;
+
+ tvlv_hdr = tvlv_value_cont;
+ tvlv_value_cont_len = ntohs(tvlv_hdr->len);
+ tvlv_value_cont = tvlv_hdr + 1;
+ tvlv_len -= sizeof(*tvlv_hdr);
+
+ if (tvlv_value_cont_len > tvlv_len)
+ return NULL;
+
+ /* the next tvlv header is accessed assuming (at least) 2-byte
+ * alignment, so it must start at an even offset.
+ */
+ if (tvlv_value_cont_len & 1)
+ return NULL;
+
+ *tvlv_value = (u8 *)tvlv_value_cont + tvlv_value_cont_len;
+ *tvlv_value_len = tvlv_len - tvlv_value_cont_len;
+
+ return tvlv_hdr;
+}
+
/**
* batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container
* @tvlv_value: tvlv content
@@ -457,28 +505,10 @@ static bool batadv_tvlv_containers_contain(void *tvlv_value,
u8 version)
{
struct batadv_tvlv_hdr *tvlv_hdr;
- u16 tvlv_value_cont_len;
-
- while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
- tvlv_hdr = tvlv_value;
- tvlv_value_cont_len = ntohs(tvlv_hdr->len);
- tvlv_value = tvlv_hdr + 1;
- tvlv_value_len -= sizeof(*tvlv_hdr);
-
- if (tvlv_value_cont_len > tvlv_value_len)
- break;
-
- /* the next tvlv header is accessed assuming (at least) 2-byte
- * alignment, so it must start at an even offset.
- */
- if (tvlv_value_cont_len & 1)
- break;
+ while ((tvlv_hdr = batadv_tvlv_hdr_next(&tvlv_value, &tvlv_value_len))) {
if (tvlv_hdr->type == type && tvlv_hdr->version == version)
return true;
-
- tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
- tvlv_value_len -= tvlv_value_cont_len;
}
return false;
@@ -511,20 +541,8 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
int ret = NET_RX_SUCCESS;
- while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
- tvlv_hdr = tvlv_value;
+ while ((tvlv_hdr = batadv_tvlv_hdr_next(&tvlv_value, &tvlv_value_len))) {
tvlv_value_cont_len = ntohs(tvlv_hdr->len);
- tvlv_value = tvlv_hdr + 1;
- tvlv_value_len -= sizeof(*tvlv_hdr);
-
- if (tvlv_value_cont_len > tvlv_value_len)
- break;
-
- /* the next tvlv header is accessed assuming (at least) 2-byte
- * alignment, so it must start at an even offset.
- */
- if (tvlv_value_cont_len & 1)
- break;
tvlv_handler = batadv_tvlv_handler_get(bat_priv,
tvlv_hdr->type,
@@ -532,11 +550,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
ret |= batadv_tvlv_call_handler(bat_priv, tvlv_handler,
packet_type, orig_node, skb,
- tvlv_value,
+ tvlv_hdr + 1,
tvlv_value_cont_len);
batadv_tvlv_handler_put(tvlv_handler);
- tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
- tvlv_value_len -= tvlv_value_cont_len;
}
if (packet_type != BATADV_IV_OGM &&
--
2.47.3
^ permalink raw reply related
* [PATCH net-next 11/15] batman-adv: tp_meter: simplify unordered ack calculation
From: Simon Wunderlich @ 2026-06-30 14:06 UTC (permalink / raw)
To: netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, b.a.t.m.a.n, Sven Eckelmann, Simon Wunderlich
In-Reply-To: <20260630140623.88431-1-sw@simonwunderlich.de>
From: Sven Eckelmann <sven@narfation.org>
When batadv_tp_ack_unordered() goes through the list of unacked sequence
numbers and checks for now closed gaps, it is first calculating a delta of
the sequence numbers which could be acked. Just to revert this calculation
in the next steps to the sequence number which would be ackable.
Skip the delta step and directly work with the sequence numbers.
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
net/batman-adv/tp_meter.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
index c2eea7dbc4883..b7fee6e55f032 100644
--- a/net/batman-adv/tp_meter.c
+++ b/net/batman-adv/tp_meter.c
@@ -1493,10 +1493,10 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
if (batadv_seq_before(tp_vars->last_recv, un->seqno))
break;
- to_ack = un->seqno + un->len - tp_vars->last_recv;
+ to_ack = un->seqno + un->len;
- if (batadv_seq_before(tp_vars->last_recv, un->seqno + un->len))
- tp_vars->last_recv += to_ack;
+ if (batadv_seq_before(tp_vars->last_recv, to_ack))
+ tp_vars->last_recv = to_ack;
list_del(&un->list);
kfree(un);
--
2.47.3
^ permalink raw reply related
* [PATCH net-next 12/15] batman-adv: tp_meter: combine adjacent/overlapping unacked entries
From: Simon Wunderlich @ 2026-06-30 14:06 UTC (permalink / raw)
To: netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, b.a.t.m.a.n, Sven Eckelmann, Simon Wunderlich
In-Reply-To: <20260630140623.88431-1-sw@simonwunderlich.de>
From: Sven Eckelmann <sven@narfation.org>
Right at the point when the receiver gets the first packet with a seqno gap
(due to some packet loss/reordering), entries in the unacked list are
created. They are (besides direct seqno matches) are not combined. A lot
more then necessary entries are therefore created. Not for each gap but for
each packet.
This increases the memory consumption and management overhead. But it is
trivial to handle overlapping or adjacent sequence number ranges during the
insert. Only the handling of closed gaps by a new packets requires an extra
step after the insert.
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
net/batman-adv/tp_meter.c | 61 +++++++++++++++++++++++++++++++++------
net/batman-adv/types.h | 2 +-
2 files changed, 53 insertions(+), 10 deletions(-)
diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
index b7fee6e55f032..5cc719c81ea0b 100644
--- a/net/batman-adv/tp_meter.c
+++ b/net/batman-adv/tp_meter.c
@@ -1406,6 +1406,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
__must_hold(&tp_vars->common.unacked_lock)
{
struct batadv_tp_unacked *un, *new;
+ struct batadv_tp_unacked *safe;
bool added = false;
new = kmalloc_obj(*new, GFP_ATOMIC);
@@ -1430,20 +1431,46 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
* seqno than all the others already stored.
*/
list_for_each_entry_reverse(un, &tp_vars->common.unacked_list, list) {
- /* check for duplicates */
- if (new->seqno == un->seqno) {
- if (new->len > un->len)
- un->len = new->len;
+ /* look for the right position - an un which is smaller */
+ if (batadv_seq_before(new->seqno, un->seqno))
+ continue;
+
+ /* smaller/equal seqno was found but they might be directly
+ * after another or overlapping. keep only a single entry
+ *
+ * It is already known that:
+ *
+ * un->seqno <= new->seqno
+ *
+ * When establishing that:
+ *
+ * new->seqno <= un->seqno + un->len
+ *
+ * Then it is not necessary to add a new entry because the
+ * smaller/equal seqno of un might already contain the new
+ * received packet or we only add new data directly after
+ * the end of un. The latter can be identified using:
+ *
+ * un->seqno + un->len <= new->seqno + new->len
+ */
+ if (!batadv_seq_before(un->seqno + un->len, new->seqno)) {
+ /* new data directly after un? */
+ if (!batadv_seq_before(new->seqno + new->len,
+ un->seqno + un->len))
+ un->len = new->seqno + new->len - un->seqno;
+
+ /* un now represents both old un + new */
kfree(new);
added = true;
+
+ /* un has to be used to check if the gap to the next
+ * seqno range was closed
+ */
+ new = un;
break;
}
- /* look for the right position */
- if (batadv_seq_before(new->seqno, un->seqno))
- continue;
-
- /* as soon as an entry having a bigger seqno is found, the new
+ /* as soon as an entry having a smaller seqno is found, the new
* one is attached _after_ it. In this way the list is kept in
* ascending order
*/
@@ -1459,6 +1486,22 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
tp_vars->common.unacked_count++;
}
+ /* check if new filled the gap to the next list entries */
+ un = new;
+ list_for_each_entry_safe_continue(un, safe, &tp_vars->common.unacked_list, list) {
+ if (batadv_seq_before(new->seqno + new->len, un->seqno))
+ break;
+
+ /* next entry is overlapping or adjacent - combine both */
+ if (batadv_seq_before(new->seqno + new->len,
+ un->seqno + un->len))
+ new->len = un->seqno + un->len - new->seqno;
+
+ list_del(&un->list);
+ kfree(un);
+ tp_vars->common.unacked_count--;
+ }
+
/* remove the last (biggest) unacked seqno when list is too large */
if (tp_vars->common.unacked_count > BATADV_TP_MAX_UNACKED) {
un = list_last_entry(&tp_vars->common.unacked_list,
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index e1463a029e835..c2ab00d8ef160 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -1332,7 +1332,7 @@ struct batadv_tp_unacked {
u32 seqno;
/** @len: length of the packet */
- u16 len;
+ u32 len;
/** @list: list node for &batadv_tp_vars_common.unacked_list */
struct list_head list;
--
2.47.3
^ permalink raw reply related
* [PATCH net-next 14/15] batman-adv: tp_meter: adjust name of receiver lock
From: Simon Wunderlich @ 2026-06-30 14:06 UTC (permalink / raw)
To: netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, b.a.t.m.a.n, Sven Eckelmann, Simon Wunderlich
In-Reply-To: <20260630140623.88431-1-sw@simonwunderlich.de>
From: Sven Eckelmann <sven@narfation.org>
The lock used to protect the receiver from reading/writing in parallel to
ack sequence number relevant data was still called unacked_lock. But it is
no longer only about the unacked_list. Use a broader term to reflect this.
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
net/batman-adv/tp_meter.c | 20 ++++++++++----------
net/batman-adv/types.h | 4 ++--
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
index f18ce360839d3..ffd3171d4b992 100644
--- a/net/batman-adv/tp_meter.c
+++ b/net/batman-adv/tp_meter.c
@@ -1252,13 +1252,13 @@ static void batadv_tp_receiver_release(struct kref *ref)
/* lock should not be needed because this object is now out of any
* context!
*/
- spin_lock_bh(&tp_vars->unacked_lock);
+ spin_lock_bh(&tp_vars->ack_seqno_lock);
list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
list_del(&un->list);
kfree(un);
tp_vars->unacked_count--;
}
- spin_unlock_bh(&tp_vars->unacked_lock);
+ spin_unlock_bh(&tp_vars->ack_seqno_lock);
kfree_rcu(tp_vars, common.rcu);
}
@@ -1316,13 +1316,13 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
if (batadv_tp_list_detach(&tp_vars->common))
batadv_tp_receiver_put(tp_vars);
- spin_lock_bh(&tp_vars->unacked_lock);
+ spin_lock_bh(&tp_vars->ack_seqno_lock);
list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
list_del(&un->list);
kfree(un);
tp_vars->unacked_count--;
}
- spin_unlock_bh(&tp_vars->unacked_lock);
+ spin_unlock_bh(&tp_vars->ack_seqno_lock);
/* drop reference of timer */
if (WARN_ON(atomic_xchg(&tp_vars->receiving, 0) != 1))
@@ -1415,7 +1415,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
*/
static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
u32 seqno, u32 payload_len)
- __must_hold(&tp_vars->unacked_lock)
+ __must_hold(&tp_vars->ack_seqno_lock)
{
struct batadv_tp_unacked *un, *new;
struct batadv_tp_unacked *safe;
@@ -1532,7 +1532,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
* @tp_vars: the private data of the current TP meter session
*/
static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
- __must_hold(&tp_vars->unacked_lock)
+ __must_hold(&tp_vars->ack_seqno_lock)
{
struct batadv_tp_unacked *un, *safe;
u32 to_ack;
@@ -1602,7 +1602,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
tp_vars->common.bat_priv = bat_priv;
kref_init(&tp_vars->common.refcount);
- spin_lock_init(&tp_vars->unacked_lock);
+ spin_lock_init(&tp_vars->ack_seqno_lock);
INIT_LIST_HEAD(&tp_vars->unacked_list);
tp_vars->unacked_count = 0;
@@ -1664,7 +1664,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
WRITE_ONCE(tp_vars->last_recv_time, jiffies);
}
- spin_lock_bh(&tp_vars->unacked_lock);
+ spin_lock_bh(&tp_vars->ack_seqno_lock);
/* if the packet is a duplicate, it may be the case that an ACK has been
* lost. Resend the ACK
@@ -1680,7 +1680,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
* not been enqueued correctly
*/
if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) {
- spin_unlock_bh(&tp_vars->unacked_lock);
+ spin_unlock_bh(&tp_vars->ack_seqno_lock);
goto out;
}
@@ -1696,7 +1696,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
send_ack:
to_ack = tp_vars->last_recv;
- spin_unlock_bh(&tp_vars->unacked_lock);
+ spin_unlock_bh(&tp_vars->ack_seqno_lock);
/* send the ACK. If the received packet was out of order, the ACK that
* is going to be sent is a duplicate (the sender will count them and
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index c194d8069774c..cd12755d21f35 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -1474,8 +1474,8 @@ struct batadv_tp_receiver {
/** @unacked_list: list of unacked packets (meta-info only) */
struct list_head unacked_list;
- /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
- spinlock_t unacked_lock;
+ /** @ack_seqno_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
+ spinlock_t ack_seqno_lock;
/** @unacked_count: number of unacked entries */
size_t unacked_count;
--
2.47.3
^ permalink raw reply related
* [PATCH net-next 13/15] batman-adv: tp_meter: keep unacked list for receivers
From: Simon Wunderlich @ 2026-06-30 14:06 UTC (permalink / raw)
To: netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, b.a.t.m.a.n, Sven Eckelmann, Simon Wunderlich
In-Reply-To: <20260630140623.88431-1-sw@simonwunderlich.de>
From: Sven Eckelmann <sven@narfation.org>
There is no need to share the unacked list between sender and receivers.
Only receivers will ever write to and read from it. The initialization in
batadv_tp_start() was therefore never needed. After its removal, it is
enough to just store it in struct batadv_tp_receiver.
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
net/batman-adv/tp_meter.c | 110 +++++++++++++++++++++-----------------
net/batman-adv/types.h | 20 +++----
2 files changed, 71 insertions(+), 59 deletions(-)
diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
index 5cc719c81ea0b..f18ce360839d3 100644
--- a/net/batman-adv/tp_meter.c
+++ b/net/batman-adv/tp_meter.c
@@ -358,28 +358,16 @@ batadv_tp_list_find_sender_session(struct batadv_priv *bat_priv, const u8 *dst,
}
/**
- * batadv_tp_vars_common_release() - release batadv_tp_vars_common from lists
+ * batadv_tp_sender_release() - release batadv_tp_sender
* and queue for free after rcu grace period
- * @ref: kref pointer of the batadv_tp_vars_common
+ * @ref: kref pointer of the batadv_tp_sender
*/
-static void batadv_tp_vars_common_release(struct kref *ref)
+static void batadv_tp_sender_release(struct kref *ref)
{
- struct batadv_tp_vars_common *tp_vars;
- struct batadv_tp_unacked *un, *safe;
-
- tp_vars = container_of(ref, struct batadv_tp_vars_common, refcount);
-
- /* lock should not be needed because this object is now out of any
- * context!
- */
- spin_lock_bh(&tp_vars->unacked_lock);
- list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
- list_del(&un->list);
- kfree(un);
- }
- spin_unlock_bh(&tp_vars->unacked_lock);
+ struct batadv_tp_sender *tp_vars;
- kfree_rcu(tp_vars, rcu);
+ tp_vars = container_of(ref, struct batadv_tp_sender, common.refcount);
+ kfree_rcu(tp_vars, common.rcu);
}
/**
@@ -392,7 +380,7 @@ static void batadv_tp_sender_put(struct batadv_tp_sender *tp_vars)
if (!tp_vars)
return;
- kref_put(&tp_vars->common.refcount, batadv_tp_vars_common_release);
+ kref_put(&tp_vars->common.refcount, batadv_tp_sender_release);
}
/**
@@ -1145,9 +1133,6 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
init_waitqueue_head(&tp_vars->more_bytes);
init_completion(&tp_vars->finished);
- spin_lock_init(&tp_vars->common.unacked_lock);
- INIT_LIST_HEAD(&tp_vars->common.unacked_list);
-
spin_lock_init(&tp_vars->cc_lock);
tp_vars->prerandom_offset = 0;
@@ -1251,6 +1236,33 @@ batadv_tp_list_find_receiver_session(struct batadv_priv *bat_priv, const u8 *dst
return tp_vars;
}
+/**
+ * batadv_tp_receiver_release() - release batadv_tp_receiver
+ * and queue for free after rcu grace period
+ * @ref: kref pointer of the batadv_tp_receiver
+ */
+static void batadv_tp_receiver_release(struct kref *ref)
+{
+ struct batadv_tp_receiver *tp_vars;
+ struct batadv_tp_unacked *safe;
+ struct batadv_tp_unacked *un;
+
+ tp_vars = container_of(ref, struct batadv_tp_receiver, common.refcount);
+
+ /* lock should not be needed because this object is now out of any
+ * context!
+ */
+ spin_lock_bh(&tp_vars->unacked_lock);
+ list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+ list_del(&un->list);
+ kfree(un);
+ tp_vars->unacked_count--;
+ }
+ spin_unlock_bh(&tp_vars->unacked_lock);
+
+ kfree_rcu(tp_vars, common.rcu);
+}
+
/**
* batadv_tp_receiver_put() - decrement the batadv_tp_receiver
* refcounter and possibly release it
@@ -1261,7 +1273,7 @@ static void batadv_tp_receiver_put(struct batadv_tp_receiver *tp_vars)
if (!tp_vars)
return;
- kref_put(&tp_vars->common.refcount, batadv_tp_vars_common_release);
+ kref_put(&tp_vars->common.refcount, batadv_tp_receiver_release);
}
/**
@@ -1304,13 +1316,13 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
if (batadv_tp_list_detach(&tp_vars->common))
batadv_tp_receiver_put(tp_vars);
- spin_lock_bh(&tp_vars->common.unacked_lock);
- list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) {
+ spin_lock_bh(&tp_vars->unacked_lock);
+ list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
list_del(&un->list);
kfree(un);
- tp_vars->common.unacked_count--;
+ tp_vars->unacked_count--;
}
- spin_unlock_bh(&tp_vars->common.unacked_lock);
+ spin_unlock_bh(&tp_vars->unacked_lock);
/* drop reference of timer */
if (WARN_ON(atomic_xchg(&tp_vars->receiving, 0) != 1))
@@ -1403,7 +1415,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
*/
static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
u32 seqno, u32 payload_len)
- __must_hold(&tp_vars->common.unacked_lock)
+ __must_hold(&tp_vars->unacked_lock)
{
struct batadv_tp_unacked *un, *new;
struct batadv_tp_unacked *safe;
@@ -1417,9 +1429,9 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
new->len = payload_len;
/* if the list is empty immediately attach this new object */
- if (list_empty(&tp_vars->common.unacked_list)) {
- list_add(&new->list, &tp_vars->common.unacked_list);
- tp_vars->common.unacked_count++;
+ if (list_empty(&tp_vars->unacked_list)) {
+ list_add(&new->list, &tp_vars->unacked_list);
+ tp_vars->unacked_count++;
return true;
}
@@ -1430,7 +1442,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
* the last received packet (the one being processed now) has a bigger
* seqno than all the others already stored.
*/
- list_for_each_entry_reverse(un, &tp_vars->common.unacked_list, list) {
+ list_for_each_entry_reverse(un, &tp_vars->unacked_list, list) {
/* look for the right position - an un which is smaller */
if (batadv_seq_before(new->seqno, un->seqno))
continue;
@@ -1476,19 +1488,19 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
*/
list_add(&new->list, &un->list);
added = true;
- tp_vars->common.unacked_count++;
+ tp_vars->unacked_count++;
break;
}
/* received packet with smallest seqno out of order; add it to front */
if (!added) {
- list_add(&new->list, &tp_vars->common.unacked_list);
- tp_vars->common.unacked_count++;
+ list_add(&new->list, &tp_vars->unacked_list);
+ tp_vars->unacked_count++;
}
/* check if new filled the gap to the next list entries */
un = new;
- list_for_each_entry_safe_continue(un, safe, &tp_vars->common.unacked_list, list) {
+ list_for_each_entry_safe_continue(un, safe, &tp_vars->unacked_list, list) {
if (batadv_seq_before(new->seqno + new->len, un->seqno))
break;
@@ -1499,16 +1511,16 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
list_del(&un->list);
kfree(un);
- tp_vars->common.unacked_count--;
+ tp_vars->unacked_count--;
}
/* remove the last (biggest) unacked seqno when list is too large */
- if (tp_vars->common.unacked_count > BATADV_TP_MAX_UNACKED) {
- un = list_last_entry(&tp_vars->common.unacked_list,
+ if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) {
+ un = list_last_entry(&tp_vars->unacked_list,
struct batadv_tp_unacked, list);
list_del(&un->list);
kfree(un);
- tp_vars->common.unacked_count--;
+ tp_vars->unacked_count--;
}
return true;
@@ -1520,7 +1532,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
* @tp_vars: the private data of the current TP meter session
*/
static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
- __must_hold(&tp_vars->common.unacked_lock)
+ __must_hold(&tp_vars->unacked_lock)
{
struct batadv_tp_unacked *un, *safe;
u32 to_ack;
@@ -1528,7 +1540,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
/* go through the unacked packet list and possibly ACK them as
* well
*/
- list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) {
+ list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
/* the list is ordered, therefore it is possible to stop as soon
* there is a gap between the last acked seqno and the seqno of
* the packet under inspection
@@ -1543,7 +1555,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
list_del(&un->list);
kfree(un);
- tp_vars->common.unacked_count--;
+ tp_vars->unacked_count--;
}
}
@@ -1590,9 +1602,9 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
tp_vars->common.bat_priv = bat_priv;
kref_init(&tp_vars->common.refcount);
- spin_lock_init(&tp_vars->common.unacked_lock);
- INIT_LIST_HEAD(&tp_vars->common.unacked_list);
- tp_vars->common.unacked_count = 0;
+ spin_lock_init(&tp_vars->unacked_lock);
+ INIT_LIST_HEAD(&tp_vars->unacked_list);
+ tp_vars->unacked_count = 0;
kref_get(&tp_vars->common.refcount);
timer_setup(&tp_vars->common.timer, batadv_tp_receiver_shutdown, 0);
@@ -1652,7 +1664,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
WRITE_ONCE(tp_vars->last_recv_time, jiffies);
}
- spin_lock_bh(&tp_vars->common.unacked_lock);
+ spin_lock_bh(&tp_vars->unacked_lock);
/* if the packet is a duplicate, it may be the case that an ACK has been
* lost. Resend the ACK
@@ -1668,7 +1680,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
* not been enqueued correctly
*/
if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) {
- spin_unlock_bh(&tp_vars->common.unacked_lock);
+ spin_unlock_bh(&tp_vars->unacked_lock);
goto out;
}
@@ -1684,7 +1696,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
send_ack:
to_ack = tp_vars->last_recv;
- spin_unlock_bh(&tp_vars->common.unacked_lock);
+ spin_unlock_bh(&tp_vars->unacked_lock);
/* send the ACK. If the received packet was out of order, the ACK that
* is going to be sent is a duplicate (the sender will count them and
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index c2ab00d8ef160..c194d8069774c 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -1334,7 +1334,7 @@ struct batadv_tp_unacked {
/** @len: length of the packet */
u32 len;
- /** @list: list node for &batadv_tp_vars_common.unacked_list */
+ /** @list: list node for &batadv_tp_receiver.unacked_list */
struct list_head list;
};
@@ -1357,15 +1357,6 @@ struct batadv_tp_vars_common {
/** @session: TP session identifier */
u8 session[2];
- /** @unacked_list: list of unacked packets (meta-info only) */
- struct list_head unacked_list;
-
- /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
- spinlock_t unacked_lock;
-
- /** @unacked_count: number of unacked entries */
- size_t unacked_count;
-
/** @refcount: number of context where the object is used */
struct kref refcount;
@@ -1479,6 +1470,15 @@ struct batadv_tp_receiver {
/** @last_recv_time: time (jiffies) a msg was received */
unsigned long last_recv_time;
+
+ /** @unacked_list: list of unacked packets (meta-info only) */
+ struct list_head unacked_list;
+
+ /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
+ spinlock_t unacked_lock;
+
+ /** @unacked_count: number of unacked entries */
+ size_t unacked_count;
};
/**
--
2.47.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox