* [PATCH 1/3] mptcp: propagate RECVERR sockopts to subflows
2026-04-21 15:22 [PATCH 0/3] mptcp: add RECVERR and MSG_ERRQUEUE support David Carlier
@ 2026-04-21 15:22 ` David Carlier
2026-04-21 15:22 ` [PATCH 2/3] mptcp: support MSG_ERRQUEUE on the parent socket David Carlier
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: David Carlier @ 2026-04-21 15:22 UTC (permalink / raw)
To: netdev, mptcp
Cc: matttbe, martineau, geliang, davem, edumazet, kuba, pabeni, horms,
David Carlier
Propagate IP_RECVERR/IP_RECVERR_RFC4884 and
IPV6_RECVERR/IPV6_RECVERR_RFC4884 from the MPTCP socket to
existing and future subflows.
Apply the matching sockopt according to the subflow family so mixed-
family subflows stay aligned with the parent socket configuration,
including disable-time errqueue purge semantics.
Signed-off-by: David Carlier <devnexen@gmail.com>
Assisted-by: Codex:gpt-5
---
net/mptcp/sockopt.c | 125 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 125 insertions(+)
diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
index de90a2897d2d..b2b7ef888dff 100644
--- a/net/mptcp/sockopt.c
+++ b/net/mptcp/sockopt.c
@@ -8,6 +8,8 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
#include <net/sock.h>
#include <net/protocol.h>
#include <net/tcp.h>
@@ -384,6 +386,70 @@ static int mptcp_setsockopt_sol_socket(struct mptcp_sock *msk, int optname,
return -EOPNOTSUPP;
}
+static bool mptcp_recverr_enabled(const struct sock *sk, bool rfc4884)
+{
+ bool enabled;
+
+ enabled = rfc4884 ? inet_test_bit(RECVERR_RFC4884, sk) :
+ inet_test_bit(RECVERR, sk);
+
+#if IS_ENABLED(CONFIG_IPV6)
+ if (sk->sk_family == AF_INET6)
+ enabled |= rfc4884 ? inet6_test_bit(RECVERR6_RFC4884, sk) :
+ inet6_test_bit(RECVERR6, sk);
+#endif
+
+ return enabled;
+}
+
+static int mptcp_subflow_set_recverr(struct sock *sk, struct sock *ssk,
+ bool rfc4884)
+{
+ int level, optname, val;
+
+#if IS_ENABLED(CONFIG_IPV6)
+ if (ssk->sk_family == AF_INET6) {
+ level = SOL_IPV6;
+ optname = rfc4884 ? IPV6_RECVERR_RFC4884 : IPV6_RECVERR;
+ } else
+#endif
+ {
+ level = SOL_IP;
+ optname = rfc4884 ? IP_RECVERR_RFC4884 : IP_RECVERR;
+ }
+
+ val = mptcp_recverr_enabled(sk, rfc4884);
+ return tcp_setsockopt(ssk, level, optname, KERNEL_SOCKPTR(&val),
+ sizeof(val));
+}
+
+static int mptcp_setsockopt_v6_recverr(struct mptcp_sock *msk, int optname,
+ sockptr_t optval, unsigned int optlen)
+{
+ struct mptcp_subflow_context *subflow;
+ struct sock *sk = (struct sock *)msk;
+ int ret;
+
+ ret = ipv6_setsockopt(sk, SOL_IPV6, optname, optval, optlen);
+ if (ret)
+ return ret;
+
+ lock_sock(sk);
+ sockopt_seq_inc(msk);
+ mptcp_for_each_subflow(msk, subflow) {
+ struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+ bool rfc4884 = optname == IPV6_RECVERR_RFC4884;
+
+ ret = mptcp_subflow_set_recverr(sk, ssk, rfc4884);
+ if (ret)
+ break;
+ subflow->setsockopt_seq = msk->setsockopt_seq;
+ }
+ release_sock(sk);
+
+ return ret;
+}
+
static int mptcp_setsockopt_v6(struct mptcp_sock *msk, int optname,
sockptr_t optval, unsigned int optlen)
{
@@ -426,6 +492,10 @@ static int mptcp_setsockopt_v6(struct mptcp_sock *msk, int optname,
release_sock(sk);
break;
+ case IPV6_RECVERR:
+ case IPV6_RECVERR_RFC4884:
+ ret = mptcp_setsockopt_v6_recverr(msk, optname, optval, optlen);
+ break;
}
return ret;
@@ -760,6 +830,33 @@ static int mptcp_setsockopt_v4_set_tos(struct mptcp_sock *msk, int optname,
return 0;
}
+static int mptcp_setsockopt_v4_recverr(struct mptcp_sock *msk, int optname,
+ sockptr_t optval, unsigned int optlen)
+{
+ struct mptcp_subflow_context *subflow;
+ struct sock *sk = (struct sock *)msk;
+ int err;
+
+ err = ip_setsockopt(sk, SOL_IP, optname, optval, optlen);
+ if (err)
+ return err;
+
+ lock_sock(sk);
+ sockopt_seq_inc(msk);
+ mptcp_for_each_subflow(msk, subflow) {
+ struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+ bool rfc4884 = optname == IP_RECVERR_RFC4884;
+
+ err = mptcp_subflow_set_recverr(sk, ssk, rfc4884);
+ if (err)
+ break;
+ subflow->setsockopt_seq = msk->setsockopt_seq;
+ }
+ release_sock(sk);
+
+ return err;
+}
+
static int mptcp_setsockopt_v4(struct mptcp_sock *msk, int optname,
sockptr_t optval, unsigned int optlen)
{
@@ -771,6 +868,9 @@ static int mptcp_setsockopt_v4(struct mptcp_sock *msk, int optname,
return mptcp_setsockopt_sol_ip_set(msk, optname, optval, optlen);
case IP_TOS:
return mptcp_setsockopt_v4_set_tos(msk, optname, optval, optlen);
+ case IP_RECVERR:
+ case IP_RECVERR_RFC4884:
+ return mptcp_setsockopt_v4_recverr(msk, optname, optval, optlen);
}
return -EOPNOTSUPP;
@@ -1459,6 +1559,12 @@ static int mptcp_getsockopt_v4(struct mptcp_sock *msk, int optname,
case IP_LOCAL_PORT_RANGE:
return mptcp_put_int_option(msk, optval, optlen,
READ_ONCE(inet_sk(sk)->local_port_range));
+ case IP_RECVERR:
+ return mptcp_put_int_option(msk, optval, optlen,
+ inet_test_bit(RECVERR, sk));
+ case IP_RECVERR_RFC4884:
+ return mptcp_put_int_option(msk, optval, optlen,
+ inet_test_bit(RECVERR_RFC4884, sk));
}
return -EOPNOTSUPP;
@@ -1479,6 +1585,12 @@ static int mptcp_getsockopt_v6(struct mptcp_sock *msk, int optname,
case IPV6_FREEBIND:
return mptcp_put_int_option(msk, optval, optlen,
inet_test_bit(FREEBIND, sk));
+ case IPV6_RECVERR:
+ return mptcp_put_int_option(msk, optval, optlen,
+ inet6_test_bit(RECVERR6, sk));
+ case IPV6_RECVERR_RFC4884:
+ return mptcp_put_int_option(msk, optval, optlen,
+ inet6_test_bit(RECVERR6_RFC4884, sk));
}
return -EOPNOTSUPP;
@@ -1536,6 +1648,7 @@ static void sync_socket_options(struct mptcp_sock *msk, struct sock *ssk)
{
static const unsigned int tx_rx_locks = SOCK_RCVBUF_LOCK | SOCK_SNDBUF_LOCK;
struct sock *sk = (struct sock *)msk;
+ bool recverr, recverr_rfc4884;
bool keep_open;
keep_open = sock_flag(sk, SOCK_KEEPOPEN);
@@ -1586,6 +1699,18 @@ static void sync_socket_options(struct mptcp_sock *msk, struct sock *ssk)
inet_assign_bit(FREEBIND, ssk, inet_test_bit(FREEBIND, sk));
inet_assign_bit(BIND_ADDRESS_NO_PORT, ssk, inet_test_bit(BIND_ADDRESS_NO_PORT, sk));
WRITE_ONCE(inet_sk(ssk)->local_port_range, READ_ONCE(inet_sk(sk)->local_port_range));
+ recverr = mptcp_recverr_enabled(sk, false);
+ recverr_rfc4884 = mptcp_recverr_enabled(sk, true);
+#if IS_ENABLED(CONFIG_IPV6)
+ if (ssk->sk_family == AF_INET6) {
+ inet6_assign_bit(RECVERR6, ssk, recverr);
+ inet6_assign_bit(RECVERR6_RFC4884, ssk, recverr_rfc4884);
+ } else
+#endif
+ {
+ inet_assign_bit(RECVERR, ssk, recverr);
+ inet_assign_bit(RECVERR_RFC4884, ssk, recverr_rfc4884);
+ }
}
void mptcp_sockopt_sync_locked(struct mptcp_sock *msk, struct sock *ssk)
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 2/3] mptcp: support MSG_ERRQUEUE on the parent socket
2026-04-21 15:22 [PATCH 0/3] mptcp: add RECVERR and MSG_ERRQUEUE support David Carlier
2026-04-21 15:22 ` [PATCH 1/3] mptcp: propagate RECVERR sockopts to subflows David Carlier
@ 2026-04-21 15:22 ` David Carlier
2026-04-21 15:22 ` [PATCH 3/3] selftests: mptcp: cover RECVERR and MSG_ERRQUEUE David Carlier
2026-04-21 16:07 ` [PATCH 0/3] mptcp: add RECVERR and MSG_ERRQUEUE support Matthieu Baerts
3 siblings, 0 replies; 6+ messages in thread
From: David Carlier @ 2026-04-21 15:22 UTC (permalink / raw)
To: netdev, mptcp
Cc: matttbe, martineau, geliang, davem, edumazet, kuba, pabeni, horms,
David Carlier
Handle MSG_ERRQUEUE on the MPTCP socket by selecting a subflow with
pending errqueue data, moving one error skb to the parent socket, and
consuming it through the parent socket ABI.
This surfaces subflow errqueue activity through poll(), keeps the
userspace ABI tied to the socket being used, and restores the skb to
the subflow errqueue if requeueing to the parent fails under rmem
pressure.
Signed-off-by: David Carlier <devnexen@gmail.com>
Assisted-by: Codex:gpt-5
---
net/mptcp/protocol.c | 121 ++++++++++++++++++++++++++++++++++++-------
1 file changed, 103 insertions(+), 18 deletions(-)
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index fbffd3a43fe8..1b2e3bede122 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -819,26 +819,29 @@ static bool __mptcp_subflow_error_report(struct sock *sk, struct sock *ssk)
{
int ssk_state;
int err;
+ bool has_errqueue;
- /* only propagate errors on fallen-back sockets or
- * on MPC connect
- */
- if (sk->sk_state != TCP_SYN_SENT && !__mptcp_check_fallback(mptcp_sk(sk)))
- return false;
-
+ has_errqueue = !skb_queue_empty_lockless(&ssk->sk_error_queue);
err = sock_error(ssk);
- if (!err)
+ if (!err && !has_errqueue)
return false;
- /* We need to propagate only transition to CLOSE state.
- * Orphaned socket will see such state change via
- * subflow_sched_work_if_closed() and that path will properly
- * destroy the msk as needed.
+ /* Errqueue notifications should wake poll()/recvmsg(MSG_ERRQUEUE) on
+ * the MPTCP socket, but only fallback sockets and the MPC connect path
+ * inherit TCP's sk_err semantics.
*/
- ssk_state = inet_sk_state_load(ssk);
- if (ssk_state == TCP_CLOSE && !sock_flag(sk, SOCK_DEAD))
- mptcp_set_state(sk, ssk_state);
- WRITE_ONCE(sk->sk_err, -err);
+ if (err &&
+ (sk->sk_state == TCP_SYN_SENT || __mptcp_check_fallback(mptcp_sk(sk)))) {
+ /* We need to propagate only transition to CLOSE state.
+ * Orphaned socket will see such state change via
+ * subflow_sched_work_if_closed() and that path will properly
+ * destroy the msk as needed.
+ */
+ ssk_state = inet_sk_state_load(ssk);
+ if (ssk_state == TCP_CLOSE && !sock_flag(sk, SOCK_DEAD))
+ mptcp_set_state(sk, ssk_state);
+ WRITE_ONCE(sk->sk_err, -err);
+ }
/* This barrier is coupled with smp_rmb() in mptcp_poll() */
smp_wmb();
@@ -2286,6 +2289,68 @@ static unsigned int mptcp_inq_hint(const struct sock *sk)
return 0;
}
+static struct sock *mptcp_pick_errqueue_subflow(struct sock *sk)
+{
+ struct mptcp_subflow_context *subflow;
+ struct sock *ssk = NULL;
+
+ lock_sock(sk);
+ mptcp_for_each_subflow(mptcp_sk(sk), subflow) {
+ struct sock *subflow_sk = mptcp_subflow_tcp_sock(subflow);
+
+ if (skb_queue_empty_lockless(&subflow_sk->sk_error_queue))
+ continue;
+
+ if (!refcount_inc_not_zero(&subflow_sk->sk_refcnt))
+ continue;
+
+ ssk = subflow_sk;
+ break;
+ }
+ release_sock(sk);
+
+ return ssk;
+}
+
+static bool mptcp_has_error_queue(const struct sock *sk)
+{
+ return !skb_queue_empty_lockless(&sk->sk_error_queue);
+}
+
+static int mptcp_recv_error(struct sock *sk, struct msghdr *msg, int len)
+{
+ struct sk_buff *skb;
+ struct sock *ssk;
+ int ret, ret2;
+
+ if (mptcp_has_error_queue(sk))
+ return inet_recv_error(sk, msg, len);
+
+ ssk = mptcp_pick_errqueue_subflow(sk);
+ if (!ssk)
+ return -EAGAIN;
+
+ skb = sock_dequeue_err_skb(ssk);
+ if (!skb)
+ goto put_ssk;
+
+ ret = sock_queue_err_skb(sk, skb);
+ if (ret) {
+ ret2 = sock_queue_err_skb(ssk, skb);
+ sock_put(ssk);
+ if (ret2)
+ kfree_skb(skb);
+ return ret;
+ }
+
+ sock_put(ssk);
+ return inet_recv_error(sk, msg, len);
+
+put_ssk:
+ sock_put(ssk);
+ return -EAGAIN;
+}
+
static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
int flags)
{
@@ -2295,9 +2360,8 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
int target;
long timeo;
- /* MSG_ERRQUEUE is really a no-op till we support IP_RECVERR */
if (unlikely(flags & MSG_ERRQUEUE))
- return inet_recv_error(sk, msg, len);
+ return mptcp_recv_error(sk, msg, len);
lock_sock(sk);
if (unlikely(sk->sk_state == TCP_LISTEN)) {
@@ -4296,6 +4360,26 @@ static __poll_t mptcp_check_writeable(struct mptcp_sock *msk)
return 0;
}
+static bool mptcp_subflow_has_error(struct sock *sk)
+{
+ struct mptcp_subflow_context *subflow;
+ bool has_error = false;
+
+ mptcp_data_lock(sk);
+ mptcp_for_each_subflow(mptcp_sk(sk), subflow) {
+ struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+
+ if (READ_ONCE(ssk->sk_err) ||
+ !skb_queue_empty_lockless(&ssk->sk_error_queue)) {
+ has_error = true;
+ break;
+ }
+ }
+ mptcp_data_unlock(sk);
+
+ return has_error;
+}
+
static __poll_t mptcp_poll(struct file *file, struct socket *sock,
struct poll_table_struct *wait)
{
@@ -4339,7 +4423,8 @@ static __poll_t mptcp_poll(struct file *file, struct socket *sock,
/* This barrier is coupled with smp_wmb() in __mptcp_error_report() */
smp_rmb();
- if (READ_ONCE(sk->sk_err))
+ if (READ_ONCE(sk->sk_err) || mptcp_has_error_queue(sk) ||
+ mptcp_subflow_has_error(sk))
mask |= EPOLLERR;
return mask;
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 3/3] selftests: mptcp: cover RECVERR and MSG_ERRQUEUE
2026-04-21 15:22 [PATCH 0/3] mptcp: add RECVERR and MSG_ERRQUEUE support David Carlier
2026-04-21 15:22 ` [PATCH 1/3] mptcp: propagate RECVERR sockopts to subflows David Carlier
2026-04-21 15:22 ` [PATCH 2/3] mptcp: support MSG_ERRQUEUE on the parent socket David Carlier
@ 2026-04-21 15:22 ` David Carlier
2026-04-21 16:07 ` [PATCH 0/3] mptcp: add RECVERR and MSG_ERRQUEUE support Matthieu Baerts
3 siblings, 0 replies; 6+ messages in thread
From: David Carlier @ 2026-04-21 15:22 UTC (permalink / raw)
To: netdev, mptcp
Cc: matttbe, martineau, geliang, davem, edumazet, kuba, pabeni, horms,
David Carlier
Add MPTCP selftest coverage for RECVERR sockopt round-trips and
parent-socket MSG_ERRQUEUE delivery.
Enable TX software timestamping, send data over an MPTCP socket, wait
for POLLERR, and verify that recvmsg(MSG_ERRQUEUE) returns timestamping
metadata on the MPTCP parent socket.
Signed-off-by: David Carlier <devnexen@gmail.com>
Assisted-by: Codex:gpt-5
---
.../selftests/net/mptcp/mptcp_sockopt.c | 152 ++++++++++++++++++
1 file changed, 152 insertions(+)
diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
index b6e58d936ebe..b499e7585d38 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
@@ -26,9 +26,17 @@
#include <linux/tcp.h>
#include <linux/compiler.h>
+#include <linux/errqueue.h>
+#include <linux/net_tstamp.h>
+
+#include <poll.h>
static int pf = AF_INET;
+#ifndef SCM_TIMESTAMPING
+#define SCM_TIMESTAMPING SO_TIMESTAMPING
+#endif
+
#ifndef IPPROTO_MPTCP
#define IPPROTO_MPTCP 262
#endif
@@ -128,6 +136,9 @@ struct so_state {
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
+static void enable_tx_timestamping(int fd);
+static void test_msg_errqueue_timestamping(int fd);
+
static void __noreturn die_perror(const char *msg)
{
perror(msg);
@@ -598,6 +609,8 @@ static void connect_one_server(int fd, int pipefd)
assert(strncmp(buf2, "xmit", 4) == 0);
+ enable_tx_timestamping(fd);
+
ret = write(fd, buf, len);
if (ret < 0)
die_perror("write");
@@ -605,6 +618,8 @@ static void connect_one_server(int fd, int pipefd)
if (ret != (ssize_t)len)
xerror("short write");
+ test_msg_errqueue_timestamping(fd);
+
total = 0;
do {
ret = read(fd, buf2 + total, sizeof(buf2) - total);
@@ -769,6 +784,142 @@ static void test_ip_tos_sockopt(int fd)
xerror("expect socklen_t == -1");
}
+static void test_ip_recverr_sockopt(int fd)
+{
+ struct iovec iov = {
+ .iov_base = &(char){ 0 },
+ .iov_len = 1,
+ };
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ int one = 1, zero = 0, val = -1;
+ socklen_t s = sizeof(val);
+ int level, optname, r;
+
+ switch (pf) {
+ case AF_INET:
+ level = SOL_IP;
+ optname = IP_RECVERR;
+ break;
+ case AF_INET6:
+ level = SOL_IPV6;
+ optname = IPV6_RECVERR;
+ break;
+ default:
+ xerror("Unknown pf %d\n", pf);
+ }
+
+ r = setsockopt(fd, level, optname, &one, sizeof(one));
+ if (r)
+ die_perror("setsockopt recverr on");
+
+ r = getsockopt(fd, level, optname, &val, &s);
+ if (r)
+ die_perror("getsockopt recverr on");
+ if (s != sizeof(val) || val != one)
+ xerror("recverr on mismatch val=%d len=%u", val, s);
+
+ r = recvmsg(fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT);
+ if (r != -1 || errno != EAGAIN)
+ xerror("expected empty errqueue to return EAGAIN, ret=%d errno=%d", r, errno);
+
+ r = setsockopt(fd, level, optname, &zero, sizeof(zero));
+ if (r)
+ die_perror("setsockopt recverr off");
+
+ val = -1;
+ s = sizeof(val);
+ r = getsockopt(fd, level, optname, &val, &s);
+ if (r)
+ die_perror("getsockopt recverr off");
+ if (s != sizeof(val) || val != zero)
+ xerror("recverr off mismatch val=%d len=%u", val, s);
+}
+
+static void enable_tx_timestamping(int fd)
+{
+ int val = SOF_TIMESTAMPING_SOFTWARE |
+ SOF_TIMESTAMPING_TX_SOFTWARE |
+ SOF_TIMESTAMPING_OPT_TSONLY;
+ int ret;
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING_OLD,
+ &val, sizeof(val));
+ if (ret)
+ die_perror("setsockopt SO_TIMESTAMPING");
+}
+
+static void test_msg_errqueue_timestamping(int fd)
+{
+ char ctrl[512] = { 0 };
+ char data[32] = { 0 };
+ struct iovec iov = {
+ .iov_base = data,
+ .iov_len = sizeof(data),
+ };
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = ctrl,
+ .msg_controllen = sizeof(ctrl),
+ };
+ struct pollfd pfd = {
+ .fd = fd,
+ .events = POLLERR,
+ };
+ struct cmsghdr *cm;
+ struct scm_timestamping *tss = NULL;
+ struct sock_extended_err *serr = NULL;
+ int ret, i;
+
+ for (i = 0; i < 10; i++) {
+ ret = poll(&pfd, 1, 1000);
+ if (ret < 0)
+ die_perror("poll errqueue");
+ if (ret == 0)
+ continue;
+ if (!(pfd.revents & POLLERR))
+ xerror("expected POLLERR, got revents %#x", pfd.revents);
+ break;
+ }
+
+ if (i == 10)
+ xerror("timed out waiting for MSG_ERRQUEUE event");
+
+ ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
+ if (ret < 0)
+ die_perror("recvmsg timestamping errqueue");
+ if (!(msg.msg_flags & MSG_ERRQUEUE))
+ xerror("expected MSG_ERRQUEUE in msg_flags, got %#x",
+ msg.msg_flags);
+
+ for (cm = CMSG_FIRSTHDR(&msg); cm; cm = CMSG_NXTHDR(&msg, cm)) {
+ if (cm->cmsg_level == SOL_SOCKET &&
+ cm->cmsg_type == SCM_TIMESTAMPING)
+ tss = (void *)CMSG_DATA(cm);
+ if ((cm->cmsg_level == SOL_IP &&
+ cm->cmsg_type == IP_RECVERR) ||
+ (cm->cmsg_level == SOL_IPV6 &&
+ cm->cmsg_type == IPV6_RECVERR))
+ serr = (void *)CMSG_DATA(cm);
+ }
+
+ if (!tss)
+ xerror("missing SCM_TIMESTAMPING cmsg");
+ if (!serr)
+ xerror("missing sock_extended_err cmsg");
+ if (serr->ee_errno != ENOMSG ||
+ serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING)
+ xerror("unexpected timestamping err ee_errno=%u ee_origin=%u",
+ serr->ee_errno, serr->ee_origin);
+ if (!tss->ts[0].tv_sec && !tss->ts[0].tv_nsec &&
+ !tss->ts[1].tv_sec && !tss->ts[1].tv_nsec &&
+ !tss->ts[2].tv_sec && !tss->ts[2].tv_nsec)
+ xerror("all timestamp slots are zero");
+}
+
static int client(int pipefd)
{
int fd = -1;
@@ -787,6 +938,7 @@ static int client(int pipefd)
}
test_ip_tos_sockopt(fd);
+ test_ip_recverr_sockopt(fd);
connect_one_server(fd, pipefd);
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH 0/3] mptcp: add RECVERR and MSG_ERRQUEUE support
2026-04-21 15:22 [PATCH 0/3] mptcp: add RECVERR and MSG_ERRQUEUE support David Carlier
` (2 preceding siblings ...)
2026-04-21 15:22 ` [PATCH 3/3] selftests: mptcp: cover RECVERR and MSG_ERRQUEUE David Carlier
@ 2026-04-21 16:07 ` Matthieu Baerts
2026-04-21 17:16 ` David CARLIER
3 siblings, 1 reply; 6+ messages in thread
From: Matthieu Baerts @ 2026-04-21 16:07 UTC (permalink / raw)
To: David Carlier, netdev, mptcp
Cc: martineau, geliang, davem, edumazet, kuba, pabeni, horms
Hi David,
On 21/04/2026 17:22, David Carlier wrote:
> MPTCP already advertises IP_RECVERR/IPV6_RECVERR as supported, but the
> parent socket does not currently provide usable MSG_ERRQUEUE handling.
>
> This series wires the MPTCP socket up to the IPv4/IPv6 error queue
> paths. It propagates RECVERR-related sockopts to existing and future
> subflows, makes poll() report pending errqueue activity through the
> parent socket, and allows recvmsg(MSG_ERRQUEUE) on the MPTCP socket to
> consume queued errors with the parent socket ABI.
>
> The series also handles mixed-family subflows by applying the matching
> sockopt according to each subflow family, and avoids silently losing an
> error skb if requeueing to the parent socket fails under rmem pressure.
Thank you for this series!
Even if I agree it would be good to have full MSG_ERRQUEUE support,
net-next is currently closed, and only bug fixes are accepted, see:
https://docs.kernel.org/process/maintainer-netdev.html
pw-bot: defer
Instead, I suggest switching the discussions only to the MPTCP ML if
that's OK. If the CI is happy, someone will try to review it over there,
when time permits. If not, please send the new versions only to the
MPTCP ML, with the 'PATCH mptcp-next' prefix, and ideally on top of the
'export' (or 'for-review') branch of our tree. For more details:
https://www.mptcp.dev/contributing.html#kernel-development
Cheers,
Matt
--
Sponsored by the NGI0 Core fund.
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH 0/3] mptcp: add RECVERR and MSG_ERRQUEUE support
2026-04-21 16:07 ` [PATCH 0/3] mptcp: add RECVERR and MSG_ERRQUEUE support Matthieu Baerts
@ 2026-04-21 17:16 ` David CARLIER
0 siblings, 0 replies; 6+ messages in thread
From: David CARLIER @ 2026-04-21 17:16 UTC (permalink / raw)
To: Matthieu Baerts
Cc: netdev, mptcp, martineau, geliang, davem, edumazet, kuba, pabeni,
horms
Thanks for the head-up, cheers !
On Tue, 21 Apr 2026 at 17:07, Matthieu Baerts <matttbe@kernel.org> wrote:
>
> Hi David,
>
> On 21/04/2026 17:22, David Carlier wrote:
> > MPTCP already advertises IP_RECVERR/IPV6_RECVERR as supported, but the
> > parent socket does not currently provide usable MSG_ERRQUEUE handling.
> >
> > This series wires the MPTCP socket up to the IPv4/IPv6 error queue
> > paths. It propagates RECVERR-related sockopts to existing and future
> > subflows, makes poll() report pending errqueue activity through the
> > parent socket, and allows recvmsg(MSG_ERRQUEUE) on the MPTCP socket to
> > consume queued errors with the parent socket ABI.
> >
> > The series also handles mixed-family subflows by applying the matching
> > sockopt according to each subflow family, and avoids silently losing an
> > error skb if requeueing to the parent socket fails under rmem pressure.
> Thank you for this series!
>
> Even if I agree it would be good to have full MSG_ERRQUEUE support,
> net-next is currently closed, and only bug fixes are accepted, see:
>
> https://docs.kernel.org/process/maintainer-netdev.html
>
> pw-bot: defer
>
> Instead, I suggest switching the discussions only to the MPTCP ML if
> that's OK. If the CI is happy, someone will try to review it over there,
> when time permits. If not, please send the new versions only to the
> MPTCP ML, with the 'PATCH mptcp-next' prefix, and ideally on top of the
> 'export' (or 'for-review') branch of our tree. For more details:
>
> https://www.mptcp.dev/contributing.html#kernel-development
>
> Cheers,
> Matt
> --
> Sponsored by the NGI0 Core fund.
>
^ permalink raw reply [flat|nested] 6+ messages in thread