From: Gang Yan <gang.yan@linux.dev>
To: mptcp@lists.linux.dev
Cc: Gang Yan <yangang@kylinos.cn>, Tao Cui <cuitao@kylinos.cn>
Subject: [PATCH mptcp-next 2/3] mptcp: pm: change path_manager sysctl atomically
Date: Mon, 17 Aug 2026 09:24:51 +0800 [thread overview]
Message-ID: <20260817012452.7519-3-gang.yan@linux.dev> (raw)
In-Reply-To: <20260817012452.7519-1-gang.yan@linux.dev>
From: Gang Yan <yangang@kylinos.cn>
The per-netns path manager name is stored as an inline char[] buffer and
updated via strscpy() from the sysctl handler; a concurrent reader can
observe a half-written name (and KCSAN flags the race), which READ_ONCE()
cannot fix for a multi-byte string.
Following the tcp_congestion_control() model (and the scheduler change in
the previous patch), store a pointer to the immutable struct mptcp_pm_ops
instead of the name string:
- mptcp_set_path_manager() looks the ops up, takes a module reference,
and atomically swaps the pernet pointer with xchg(), releasing the
previous reference;
- mptcp_get_path_manager() copies the ops name out under rcu_read_lock();
- the pm_type field derived from the name is now written
with WRITE_ONCE() to pair with its READ_ONCE() readers;
- the default reference is taken in mptcp_pernet_set_defaults() and
released in mptcp_net_exit().
Assisted-by: Claude:GLM5.2
Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/626
Co-developed-by: Tao Cui <cuitao@kylinos.cn>
Signed-off-by: Tao Cui <cuitao@kylinos.cn>
Signed-off-by: Gang Yan <yangang@kylinos.cn>
---
net/mptcp/ctrl.c | 50 +++++++++++++++++++++++++++++++-------------
net/mptcp/pm.c | 3 ++-
net/mptcp/protocol.h | 3 +--
3 files changed, 39 insertions(+), 17 deletions(-)
diff --git a/net/mptcp/ctrl.c b/net/mptcp/ctrl.c
index 85e9d3e996db..fa28aecfb856 100644
--- a/net/mptcp/ctrl.c
+++ b/net/mptcp/ctrl.c
@@ -40,7 +40,7 @@ struct mptcp_pernet {
u8 pm_type;
u8 add_addr_v6_port_drop_ts;
struct mptcp_sched_ops __rcu *scheduler;
- char path_manager[MPTCP_PM_NAME_MAX];
+ struct mptcp_pm_ops __rcu *path_manager;
};
static struct mptcp_pernet *mptcp_get_pernet(const struct net *net)
@@ -85,9 +85,14 @@ int mptcp_get_pm_type(const struct net *net)
return mptcp_get_pernet(net)->pm_type;
}
-const char *mptcp_get_path_manager(const struct net *net)
+void mptcp_get_path_manager(const struct net *net, char *name)
{
- return mptcp_get_pernet(net)->path_manager;
+ struct mptcp_pm_ops *pm_ops;
+
+ rcu_read_lock();
+ pm_ops = rcu_dereference(mptcp_get_pernet(net)->path_manager);
+ strscpy(name, pm_ops ? pm_ops->name : "kernel", MPTCP_PM_NAME_MAX);
+ rcu_read_unlock();
}
void mptcp_get_scheduler(const struct net *net, char *name)
@@ -121,7 +126,9 @@ static void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet)
if (bpf_try_module_get(&mptcp_sched_default, mptcp_sched_default.owner))
RCU_INIT_POINTER(pernet->scheduler, &mptcp_sched_default);
- strscpy(pernet->path_manager, "kernel", sizeof(pernet->path_manager));
+ if (bpf_try_module_get(&mptcp_pm_kernel, mptcp_pm_kernel.owner))
+ RCU_INIT_POINTER(pernet->path_manager, &mptcp_pm_kernel);
+
pernet->add_addr_v6_port_drop_ts = 1;
}
@@ -209,17 +216,24 @@ static int proc_blackhole_detect_timeout(const struct ctl_table *table,
return ret;
}
-static int mptcp_set_path_manager(char *path_manager, const char *name)
+static int mptcp_set_path_manager(struct mptcp_pernet *pernet, const char *name)
{
- struct mptcp_pm_ops *pm_ops;
+ struct mptcp_pm_ops *pm_ops, *prev;
int ret = 0;
rcu_read_lock();
pm_ops = mptcp_pm_find(name);
- if (pm_ops)
- strscpy(path_manager, name, MPTCP_PM_NAME_MAX);
- else
+ if (pm_ops) {
+ if (bpf_try_module_get(pm_ops, pm_ops->owner)) {
+ prev = xchg(&pernet->path_manager, pm_ops);
+ if (prev)
+ bpf_module_put(prev, prev->owner);
+ } else {
+ ret = -EBUSY;
+ }
+ } else {
ret = -ENOENT;
+ }
rcu_read_unlock();
return ret;
@@ -231,7 +245,7 @@ static int proc_path_manager(const struct ctl_table *ctl, int write,
struct mptcp_pernet *pernet = container_of(ctl->data,
struct mptcp_pernet,
path_manager);
- char (*path_manager)[MPTCP_PM_NAME_MAX] = ctl->data;
+ struct mptcp_pm_ops *pm_ops;
char pm_name[MPTCP_PM_NAME_MAX];
const struct ctl_table tbl = {
.data = pm_name,
@@ -239,11 +253,14 @@ static int proc_path_manager(const struct ctl_table *ctl, int write,
};
int ret;
- strscpy(pm_name, *path_manager, MPTCP_PM_NAME_MAX);
+ rcu_read_lock();
+ pm_ops = rcu_dereference(pernet->path_manager);
+ strscpy(pm_name, pm_ops ? pm_ops->name : "kernel", MPTCP_PM_NAME_MAX);
+ rcu_read_unlock();
ret = proc_dostring(&tbl, write, buffer, lenp, ppos);
if (write && ret == 0) {
- ret = mptcp_set_path_manager(*path_manager, pm_name);
+ ret = mptcp_set_path_manager(pernet, pm_name);
if (ret == 0) {
u8 pm_type = __MPTCP_PM_TYPE_NR;
@@ -251,7 +268,7 @@ static int proc_path_manager(const struct ctl_table *ctl, int write,
pm_type = MPTCP_PM_TYPE_KERNEL;
else if (strncmp(pm_name, "userspace", MPTCP_PM_NAME_MAX) == 0)
pm_type = MPTCP_PM_TYPE_USERSPACE;
- pernet->pm_type = pm_type;
+ WRITE_ONCE(pernet->pm_type, pm_type);
}
}
@@ -275,7 +292,7 @@ static int proc_pm_type(const struct ctl_table *ctl, int write,
pm_name = "kernel";
else if (pm_type == MPTCP_PM_TYPE_USERSPACE)
pm_name = "userspace";
- mptcp_set_path_manager(pernet->path_manager, pm_name);
+ mptcp_set_path_manager(pernet, pm_name);
}
return ret;
@@ -595,12 +612,17 @@ static void __net_exit mptcp_net_exit(struct net *net)
{
struct mptcp_pernet *pernet = mptcp_get_pernet(net);
struct mptcp_sched_ops *sched;
+ struct mptcp_pm_ops *pm;
mptcp_pernet_del_table(pernet);
sched = rcu_dereference_protected(pernet->scheduler, true);
if (sched)
bpf_module_put(sched, sched->owner);
+
+ pm = rcu_dereference_protected(pernet->path_manager, true);
+ if (pm)
+ bpf_module_put(pm, pm->owner);
}
static struct pernet_operations mptcp_pernet_ops = {
diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index ba7c6f80a183..09f99bcd827c 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -1204,7 +1204,7 @@ void mptcp_pm_destroy(struct mptcp_sock *msk)
void mptcp_pm_data_reset(struct mptcp_sock *msk)
{
const struct net *net = sock_net((struct sock *)msk);
- const char *pm_name = mptcp_get_path_manager(net);
+ char pm_name[MPTCP_PM_NAME_MAX];
u8 pm_type = mptcp_get_pm_type(net);
struct mptcp_pm_data *pm = &msk->pm;
@@ -1213,6 +1213,7 @@ void mptcp_pm_data_reset(struct mptcp_sock *msk)
pm->rm_list_rx.nr = 0;
WRITE_ONCE(pm->pm_type, pm_type);
+ mptcp_get_path_manager(net, pm_name);
rcu_read_lock();
mptcp_pm_ops_init(msk, pm_name);
rcu_read_unlock();
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index af79b3450ab7..99f447f2808f 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -802,7 +802,7 @@ int mptcp_allow_join_id0(const struct net *net);
unsigned int mptcp_stale_loss_cnt(const struct net *net);
unsigned int mptcp_close_timeout(const struct sock *sk);
int mptcp_get_pm_type(const struct net *net);
-const char *mptcp_get_path_manager(const struct net *net);
+void mptcp_get_path_manager(const struct net *net, char *name);
void mptcp_get_scheduler(const struct net *net, char *name);
unsigned int mptcp_add_addr_v6_port_drop_ts(const struct net *net);
@@ -1153,7 +1153,6 @@ int mptcp_pm_announce_addr(struct mptcp_sock *msk,
bool echo);
int mptcp_pm_remove_addr(struct mptcp_sock *msk, const struct mptcp_rm_list *rm_list);
-/* the default path manager, used in mptcp_pm_unregister */
extern struct mptcp_pm_ops mptcp_pm_kernel;
extern struct mptcp_sched_ops mptcp_sched_default;
--
2.43.0
next prev parent reply other threads:[~2026-08-17 1:25 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-17 1:24 [PATCH mptcp-next 0/3] mptcp: avoid data-races around the sysctls Gang Yan
2026-08-17 1:24 ` [PATCH mptcp-next 1/3] mptcp: sched: change scheduler sysctl atomically Gang Yan
2026-08-17 1:24 ` Gang Yan [this message]
2026-08-17 1:24 ` [PATCH mptcp-next 3/3] mptcp: use READ_ONCE() over sysctls Gang Yan
2026-08-17 2:31 ` [PATCH mptcp-next 0/3] mptcp: avoid data-races around the sysctls MPTCP CI
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260817012452.7519-3-gang.yan@linux.dev \
--to=gang.yan@linux.dev \
--cc=cuitao@kylinos.cn \
--cc=mptcp@lists.linux.dev \
--cc=yangang@kylinos.cn \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.