From: Geliang Tang <tanggeliang@kylinos.cn>
The helper mptcp_pm_is_userspace() is used to distinguish userspace PM
operations from in-kernel PM in mptcp_pm_subflow_check_next(). It seems
reasonable to add a mandatory .subflow_check_next interface for struct
mptcp_pm_ops.
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
include/net/mptcp.h | 3 +++
net/mptcp/pm.c | 31 +++----------------------------
net/mptcp/pm_kernel.c | 20 ++++++++++++++++++++
net/mptcp/pm_userspace.c | 15 +++++++++++++++
4 files changed, 41 insertions(+), 28 deletions(-)
diff --git a/include/net/mptcp.h b/include/net/mptcp.h
index 6f9bc0ca4d37..5f2d94b2f97f 100644
--- a/include/net/mptcp.h
+++ b/include/net/mptcp.h
@@ -15,6 +15,7 @@
struct mptcp_info;
struct mptcp_sock;
struct mptcp_pm_addr_entry;
+struct mptcp_subflow_context;
struct seq_file;
/* MPTCP sk_buff extension data */
@@ -125,6 +126,8 @@ struct mptcp_pm_ops {
bool (*allow_new_subflow)(struct mptcp_sock *msk);
bool (*accept_new_subflow)(const struct mptcp_sock *msk);
+ void (*subflow_check_next)(struct mptcp_sock *msk,
+ const struct mptcp_subflow_context *subflow);
char name[MPTCP_PM_NAME_MAX];
struct module *owner;
diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index 31a2c66a1af3..d8a5c2c06500 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -526,33 +526,7 @@ void mptcp_pm_subflow_established(struct mptcp_sock *msk)
void mptcp_pm_subflow_check_next(struct mptcp_sock *msk,
const struct mptcp_subflow_context *subflow)
{
- struct mptcp_pm_data *pm = &msk->pm;
- bool update_subflows;
-
- update_subflows = subflow->request_join || subflow->mp_join;
- if (mptcp_pm_is_userspace(msk)) {
- if (update_subflows) {
- spin_lock_bh(&pm->lock);
- pm->subflows--;
- spin_unlock_bh(&pm->lock);
- }
- return;
- }
-
- if (!READ_ONCE(pm->work_pending) && !update_subflows)
- return;
-
- spin_lock_bh(&pm->lock);
- if (update_subflows)
- __mptcp_pm_close_subflow(msk);
-
- /* Even if this subflow is not really established, tell the PM to try
- * to pick the next ones, if possible.
- */
- if (mptcp_pm_nl_check_work_pending(msk))
- mptcp_pm_schedule_work(msk, MPTCP_PM_SUBFLOW_ESTABLISHED);
-
- spin_unlock_bh(&pm->lock);
+ msk->pm.ops->subflow_check_next(msk, subflow);
}
void mptcp_pm_add_addr_received(const struct sock *ssk,
@@ -1017,7 +991,8 @@ struct mptcp_pm_ops *mptcp_pm_find(const char *name)
int mptcp_pm_validate(struct mptcp_pm_ops *pm_ops)
{
if (!pm_ops->get_local_id || !pm_ops->get_priority ||
- !pm_ops->allow_new_subflow || !pm_ops->accept_new_subflow) {
+ !pm_ops->allow_new_subflow || !pm_ops->accept_new_subflow ||
+ !pm_ops->subflow_check_next) {
pr_err("%s does not implement required ops\n", pm_ops->name);
return -EINVAL;
}
diff --git a/net/mptcp/pm_kernel.c b/net/mptcp/pm_kernel.c
index 708a96951cba..b40d39232f70 100644
--- a/net/mptcp/pm_kernel.c
+++ b/net/mptcp/pm_kernel.c
@@ -1430,6 +1430,25 @@ static bool mptcp_pm_kernel_accept_new_subflow(const struct mptcp_sock *msk)
return READ_ONCE(msk->pm.accept_subflow);
}
+static void mptcp_pm_kernel_subflow_check_next(struct mptcp_sock *msk,
+ const struct mptcp_subflow_context *subflow)
+{
+ struct mptcp_pm_data *pm = &msk->pm;
+ bool update_subflows;
+
+ update_subflows = subflow->request_join || subflow->mp_join;
+
+ if (!READ_ONCE(pm->work_pending) && !update_subflows)
+ return;
+
+ spin_lock_bh(&pm->lock);
+ if (update_subflows)
+ __mptcp_pm_close_subflow(msk);
+ spin_unlock_bh(&pm->lock);
+
+ mptcp_pm_subflow_established(msk);
+}
+
static void mptcp_pm_kernel_init(struct mptcp_sock *msk)
{
bool subflows_allowed = !!mptcp_pm_get_subflows_max(msk);
@@ -1455,6 +1474,7 @@ struct mptcp_pm_ops mptcp_pm_kernel = {
.get_priority = mptcp_pm_kernel_get_priority,
.allow_new_subflow = mptcp_pm_kernel_allow_new_subflow,
.accept_new_subflow = mptcp_pm_kernel_accept_new_subflow,
+ .subflow_check_next = mptcp_pm_kernel_subflow_check_next,
.init = mptcp_pm_kernel_init,
.name = "kernel",
.owner = THIS_MODULE,
diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c
index 4cd9a84477c8..68247ec4cdaa 100644
--- a/net/mptcp/pm_userspace.c
+++ b/net/mptcp/pm_userspace.c
@@ -701,6 +701,20 @@ static bool mptcp_pm_userspace_accept_new_subflow(const struct mptcp_sock *msk)
return mptcp_userspace_pm_active(msk);
}
+static void mptcp_pm_userspace_subflow_check_next(struct mptcp_sock *msk,
+ const struct mptcp_subflow_context *subflow)
+{
+ struct mptcp_pm_data *pm = &msk->pm;
+ bool update_subflows;
+
+ update_subflows = subflow->request_join || subflow->mp_join;
+ if (update_subflows) {
+ spin_lock_bh(&pm->lock);
+ pm->subflows--;
+ spin_unlock_bh(&pm->lock);
+ }
+}
+
static void mptcp_pm_userspace_release(struct mptcp_sock *msk)
{
mptcp_userspace_pm_free_local_addr_list(msk);
@@ -711,6 +725,7 @@ static struct mptcp_pm_ops mptcp_pm_userspace = {
.get_priority = mptcp_pm_userspace_get_priority,
.allow_new_subflow = mptcp_pm_userspace_allow_new_subflow,
.accept_new_subflow = mptcp_pm_userspace_accept_new_subflow,
+ .subflow_check_next = mptcp_pm_userspace_subflow_check_next,
.release = mptcp_pm_userspace_release,
.name = "userspace",
.owner = THIS_MODULE,
--
2.43.0
Hi Geliang,
On 11/03/2025 10:32, Geliang Tang wrote:
> From: Geliang Tang <tanggeliang@kylinos.cn>
>
> The helper mptcp_pm_is_userspace() is used to distinguish userspace PM
> operations from in-kernel PM in mptcp_pm_subflow_check_next(). It seems
> reasonable to add a mandatory .subflow_check_next interface for struct
> mptcp_pm_ops.
>
> Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
> ---
> include/net/mptcp.h | 3 +++
> net/mptcp/pm.c | 31 +++----------------------------
> net/mptcp/pm_kernel.c | 20 ++++++++++++++++++++
> net/mptcp/pm_userspace.c | 15 +++++++++++++++
> 4 files changed, 41 insertions(+), 28 deletions(-)
>
> diff --git a/include/net/mptcp.h b/include/net/mptcp.h
> index 6f9bc0ca4d37..5f2d94b2f97f 100644
> --- a/include/net/mptcp.h
> +++ b/include/net/mptcp.h
> @@ -15,6 +15,7 @@
> struct mptcp_info;
> struct mptcp_sock;
> struct mptcp_pm_addr_entry;
> +struct mptcp_subflow_context;
> struct seq_file;
>
> /* MPTCP sk_buff extension data */
> @@ -125,6 +126,8 @@ struct mptcp_pm_ops {
>
> bool (*allow_new_subflow)(struct mptcp_sock *msk);
> bool (*accept_new_subflow)(const struct mptcp_sock *msk);
> + void (*subflow_check_next)(struct mptcp_sock *msk,
Maybe we should rename this: subflow_established?
'check_next' is not really explicit.
> + const struct mptcp_subflow_context *subflow);
>
> char name[MPTCP_PM_NAME_MAX];
> struct module *owner;
> diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
> index 31a2c66a1af3..d8a5c2c06500 100644
> --- a/net/mptcp/pm.c
> +++ b/net/mptcp/pm.c
> @@ -526,33 +526,7 @@ void mptcp_pm_subflow_established(struct mptcp_sock *msk)
> void mptcp_pm_subflow_check_next(struct mptcp_sock *msk,
> const struct mptcp_subflow_context *subflow)
> {
> - struct mptcp_pm_data *pm = &msk->pm;
> - bool update_subflows;
> -
> - update_subflows = subflow->request_join || subflow->mp_join;
> - if (mptcp_pm_is_userspace(msk)) {
> - if (update_subflows) {
> - spin_lock_bh(&pm->lock);
> - pm->subflows--;
> - spin_unlock_bh(&pm->lock);
> - }
I wonder if... this 'if' should not be done for all PMs here. I guess
mptcp_pm_close_subflow(msk) could then be called.
Then I suggest scheduling the worker if msk->pm.ops->subflow_established
is set, and ... I'm not sure how to deal with work_pending. Maybe a new
callback? Or maybe we can deal with that later, but the current name is
not good :)
Anyway: then msk->pm.ops->subflow_check_next(), when the msk lock is
held. I think all these ops should only be called when the msk lock is
held, no? I don't think a BPF PM should schedule the worker.
> - return;
> - }
> -
> - if (!READ_ONCE(pm->work_pending) && !update_subflows)
> - return;
> -
> - spin_lock_bh(&pm->lock);
> - if (update_subflows)
> - __mptcp_pm_close_subflow(msk);
> -
> - /* Even if this subflow is not really established, tell the PM to try
> - * to pick the next ones, if possible.
> - */
> - if (mptcp_pm_nl_check_work_pending(msk))
> - mptcp_pm_schedule_work(msk, MPTCP_PM_SUBFLOW_ESTABLISHED);
> -
> - spin_unlock_bh(&pm->lock);
> + msk->pm.ops->subflow_check_next(msk, subflow);
> }
>
> void mptcp_pm_add_addr_received(const struct sock *ssk,
> @@ -1017,7 +991,8 @@ struct mptcp_pm_ops *mptcp_pm_find(const char *name)
> int mptcp_pm_validate(struct mptcp_pm_ops *pm_ops)
> {
> if (!pm_ops->get_local_id || !pm_ops->get_priority ||
> - !pm_ops->allow_new_subflow || !pm_ops->accept_new_subflow) {
> + !pm_ops->allow_new_subflow || !pm_ops->accept_new_subflow ||
> + !pm_ops->subflow_check_next) {
See above: this new callback would not be mandatory then.
Cheers,
Matt
--
Sponsored by the NGI0 Core fund.
© 2016 - 2026 Red Hat, Inc.