MPTCP Linux Development
 help / color / mirror / Atom feed
* [PATCH mptcp-next v3 0/2] add distinct lockdep class for MPTCP sockets
@ 2026-10-19  3:04 Geliang Tang
  2026-10-19  3:04 ` [PATCH mptcp-next v3 1/2] mptcp: give MPTCP sockets a distinct lockdep class Geliang Tang
  2026-10-19  3:04 ` [PATCH mptcp-next v3 2/2] mptcp: kunit: add sockopt test Geliang Tang
  0 siblings, 2 replies; 5+ messages in thread
From: Geliang Tang @ 2026-10-19  3:04 UTC (permalink / raw)
  To: mptcp; +Cc: Geliang Tang

From: Geliang Tang <tanggeliang@kylinos.cn>

The mptcp_nvme.sh selftest reports lockdep "possible recursive locking"
warnings when running NVMe over MPTCP. The issue occurs because MPTCP
sockets and their subflows share the same k-sk_lock-AF_INET lockdep class,
causing lockdep to flag false warnings in paths that hold the MPTCP socket
lock and then lock a subflow (setsockopt, connect, bind, worker send-ack).

This series fixes the issue by giving MPTCP sockets their own distinct
lockdep class (sk_lock-AF_INET-MPTCP for IPv4, sk_lock-AF_INET6-MPTCP for
IPv6), similar to how nvme-tcp handles it. This approach is more efficient
than reclassifying each subflow individually, properly supports IPv6, and
follows existing kernel patterns.

A KUnit test is included to verify the fix works correctly.

v3:
 - Assign separate lock classes for kernel and user MPTCP sockets to
   prevent lockdep from conflating their usage patterns (kernel sockets
   may nest with fs/storage locks, user sockets record mmap_lock
   dependencies through page faults)
 - Call mptcp_reclassify_socket() from mptcp_init_sock() for active sockets
   and from mptcp_sk_clone_init() before bh_lock_sock() for passive sockets
 - Remove duplicate lock reclassification from pm_kernel.c (PM listener
   sockets are now reclassified through mptcp_init_sock() path)
 - Enhanced KUnit test to cover both IPv4/IPv6 and active/passive sockets,
   with direct lockdep key comparison when CONFIG_PROVE_LOCKING is enabled
 - Update Kconfig help text to mention sockopt tests

v2:
 - Move reclassification to __mptcp_init_sock() to cover cloned sockets,
   as Matt suggested
 - Extract mptcp_reclassify_socket() helper for reuse in PM listener socket
   creation
 - Remove #ifdef CONFIG_DEBUG_LOCK_ALLOC wrapper as suggested by Matt
 - Update lock class names to msk_lock-AF_INET/msk_lock-AF_INET6
 - Note: checkpatch.pl warns about line length > 80 columns in pm_kernel.c,
   but kept for consistency with existing code style
 - https://patchwork.kernel.org/project/mptcp/cover/cover.1792114102.git.tanggeliang@kylinos.cn/

v1:
 - https://patchwork.kernel.org/project/mptcp/cover/cover.1792060837.git.tanggeliang@kylinos.cn/

Geliang Tang (2):
  mptcp: give MPTCP sockets a distinct lockdep class
  mptcp: kunit: add sockopt test

 net/mptcp/Kconfig        |   2 +-
 net/mptcp/Makefile       |   4 +-
 net/mptcp/pm_kernel.c    |  16 -----
 net/mptcp/protocol.c     |  37 +++++++++-
 net/mptcp/sockopt_test.c | 145 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 185 insertions(+), 19 deletions(-)
 create mode 100644 net/mptcp/sockopt_test.c

-- 
2.55.0


^ permalink raw reply	[flat|nested] 5+ messages in thread
* Re: [PATCH mptcp-next v3 2/2] mptcp: kunit: add sockopt test
@ 2026-10-20 16:06 netdev-bot+sashiko
  0 siblings, 0 replies; 5+ messages in thread
From: netdev-bot+sashiko @ 2026-10-20 16:06 UTC (permalink / raw)
  To: geliang; +Cc: mptcp, tanggeliang, kuba

Thank you for your contribution! Sashiko AI review found 4 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 2 · Low: 2

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] mptcp_sockopt_test() in net/mptcp/sockopt_test.c binds to
  127.0.0.1 or ::1 in init_net (lines 31-61).
- [Medium] Under CONFIG_PROVE_LOCKING, mptcp_sockopt_test() passes
  accepted_sock->sk to mptcp_check_lockdep_class() (sockopt_test.c:109).
- [Low] The MPTCP_KUNIT_TEST help text in net/mptcp/Kconfig (line 31) was
  changed to "Currently covers the MPTCP crypto and token helpers, and…
- [Low] mptcp_sockopt_test() releases server_sock, client_sock and
  accepted_sock only at the end of the function (sockopt_test.c:112-114).

--

mptcp: kunit: add sockopt test

This adds a KUnit suite that creates kernel MPTCP sockets in init_net,
sets SO_REUSEADDR, connects over loopback and transfers data. With
CONFIG_PROVE_LOCKING it also checks that the client and accepted MPTCP
sockets use a different lockdep key than their first subflow. Both IPv4
and IPv6 are covered.

> diff --git a/net/mptcp/Kconfig b/net/mptcp/Kconfig
> index a9f58edc354c..cdae7be802fb 100644
> --- a/net/mptcp/Kconfig
> +++ b/net/mptcp/Kconfig
> @@ -28,7 +28,7 @@ config MPTCP_KUNIT_TEST
>  	depends on KUNIT
>  	default KUNIT_ALL_TESTS
>  	help
> -	  Currently covers the MPTCP crypto and token helpers.
> +	  Currently covers the MPTCP crypto and token helpers, and sockopt tests
>  	  Only useful for kernel devs running KUnit test harness and are not
>  	  for inclusion into a production build.

[Severity: Low]
This isn't a bug, but the new help line has lost its trailing period, so
it runs into the "Only useful for kernel devs ..." sentence after it.

[ ... ]

> diff --git a/net/mptcp/sockopt_test.c b/net/mptcp/sockopt_test.c
> new file mode 100644
> index 000000000000..b4b9c40be92f
> --- /dev/null
> +++ b/net/mptcp/sockopt_test.c
> @@ -0,0 +1,145 @@

[ ... ]

> +#ifdef CONFIG_PROVE_LOCKING
> +static void mptcp_check_lockdep_class(struct kunit *test, struct sock *sk)
> +{
> +	struct mptcp_sock *msk = mptcp_sk(sk);
> +	struct sock *ssk = msk->first;
> +
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ssk);
> +	KUNIT_EXPECT_PTR_NE(test,
> +			    sk->sk_lock.dep_map.key,
> +			    ssk->sk_lock.dep_map.key);
> +}
> +#endif

[Severity: Medium]
What happens here if the accepted socket fell back to plain TCP?

mptcp_sockopt_test() passes accepted_sock->sk to this helper. The helper
casts it with mptcp_sk() and reads msk->first, and it never checks
sk_is_mptcp() or sk->sk_protocol first.

On passive fallback, mptcp_stream_accept() grafts the plain TCP subflow
onto the new socket:

net/mptcp/protocol.c:mptcp_stream_accept() {
    ...
tcpfallback:
		newsk->sk_kern_sock = arg->kern;
		lock_sock(newsk);
		__inet_accept(sock, newsock, newsk);
    ...
}

When that happens, accepted_sock->sk is a struct tcp_sock. msk->first then
reads unrelated tcp_sock bytes, and the resulting pointer is dereferenced
for ssk->sk_lock.dep_map.key. KUNIT_ASSERT_NOT_ERR_OR_NULL() only catches
NULL and ERR_PTR values.

Passive fallback does not need malformed input. Some examples:

- The client is in the active-disable window, so mptcp_connect() calls
  mptcp_early_fallback(..., MPTCP_MIB_MPCAPABLEACTIVEDISABLED) and sends
  a plain SYN.
- Token allocation fails in mptcp_connect().
- mptcp_sk_clone_init() fails in subflow_syn_recv_sock(), which then
  sets fallback = true.
- Another local process connects with plain TCP to the ephemeral
  listening port in init_net before the test client does.
  kernel_accept() takes whichever connection is queued first.

Send and recv still work over TCP in each of these cases, so the test
gets as far as this cast. The test also never checks that MPTCP was
actually negotiated.

Could the test confirm that accepted_sock->sk really is an MPTCP socket
before it dereferences msk->first?

[ ... ]

> +	err = kernel_bind(server_sock, (struct sockaddr_unsized *)&addr,
> +			  addrlen);
> +	KUNIT_ASSERT_EQ(test, err, 0);

[Severity: Medium]
Can this bind succeed when the suite is built in?

The test binds to 127.0.0.1 or ::1 in init_net but never brings lo up.
The kernel does not set IFF_UP on lo by itself. The only in-kernel path
that opens it is ic_open_devs(), and ip_auto_config() skips that unless
ip= is on the command line:

net/ipv4/ipconfig.c:ip_auto_config() {
    ...
	if (!ic_enable)
		return 0;
    ...
}

The loopback addresses are only installed after lo gets NETDEV_UP. Until
then, __inet_bind() fails here:

net/ipv4/af_inet.c:__inet_bind() {
    ...
	err = -EADDRNOTAVAIL;
	if (!inet_addr_valid_or_nonlocal(net, inet, addr->sin_addr.s_addr,
	                                 chk_addr_ret))
		goto out;
    ...
}

The IPv6 bind fails the same way.

Built-in suites run from kernel_init_freeable() through
kunit_run_all_tests(), right after do_basic_setup(). That is before
userspace has a chance to run "ip link set lo up".

MPTCP_KUNIT_TEST defaults to KUNIT_ALL_TESTS. Won't both the v4 and v6
cases fail in a normal kunit.py or KUNIT_ALL_TESTS run, before the
lockdep check is reached?

Until now MPTCP_KUNIT_TEST only covered pure unit tests (crypto and
token). Neither the commit message nor the Kconfig help says this test
has to run as a module after lo is up.

Other KUnit tests that need a live netdev open it themselves. For
example, net/mctp/test/utils.c calls dev_open(ndev, NULL).

[ ... ]

> +#ifdef CONFIG_PROVE_LOCKING
> +	mptcp_check_lockdep_class(test, client_sock->sk);
> +	mptcp_check_lockdep_class(test, accepted_sock->sk);
> +#endif
> +
> +	sock_release(accepted_sock);
> +	sock_release(client_sock);
> +	sock_release(server_sock);
> +}

[Severity: Low]
Does a failed assertion leak server_sock, client_sock and accepted_sock?

These sock_release() calls are the only cleanup in the test. Between
sock_create_kern() and here there are many KUNIT_ASSERT_*() checks,
including the one in mptcp_check_lockdep_class().

A failed assertion ends the test thread through kunit_try_catch_throw():

lib/kunit/try-catch.c:kunit_try_catch_throw() {
    ...
	try_catch->try_result = -EFAULT;
	kthread_exit(0);
}

That skips these releases. No cleanup is registered either, for example
through kunit_add_action_or_reset().

A failure after the server socket is created leaks that MPTCP socket and
its subflow. A later failure also leaks the client and accepted sockets,
plus the established loopback connection in init_net.

In a built-in run with lo down, the kernel_bind() assertion above already
fails, so server_sock leaks in both the v4 and v6 cases.

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/cover.1792378794.git.tanggeliang%40kylinos.cn

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-10-20 16:07 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-19  3:04 [PATCH mptcp-next v3 0/2] add distinct lockdep class for MPTCP sockets Geliang Tang
2026-10-19  3:04 ` [PATCH mptcp-next v3 1/2] mptcp: give MPTCP sockets a distinct lockdep class Geliang Tang
2026-10-19  3:04 ` [PATCH mptcp-next v3 2/2] mptcp: kunit: add sockopt test Geliang Tang
2026-10-19  3:16   ` sashiko-bot
  -- strict thread matches above, loose matches on Subject: below --
2026-10-20 16:06 netdev-bot+sashiko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox