Instead of iterating over all endpoints, under RCU read lock, just to
check if one of them as the fullmesh flag, we can keep a counter of
fullmesh endpoint, similar to what is done with the other flags.
This counter is now checked, before iterating over all endpoints.
Similar to the other counters, this new one is also exposed. A userspace
app can then know when it is being used in a fullmesh mode, with
potentially (too) many subflows.
Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
---
include/uapi/linux/mptcp.h | 3 ++-
net/mptcp/pm_kernel.c | 38 +++++++++++++++++++++++++++++++++++---
net/mptcp/protocol.h | 1 +
net/mptcp/sockopt.c | 2 ++
4 files changed, 40 insertions(+), 4 deletions(-)
diff --git a/include/uapi/linux/mptcp.h b/include/uapi/linux/mptcp.h
index 87cfab874e24..04eea6d1d0a9 100644
--- a/include/uapi/linux/mptcp.h
+++ b/include/uapi/linux/mptcp.h
@@ -70,7 +70,8 @@ struct mptcp_info {
__u64 mptcpi_bytes_acked;
__u8 mptcpi_subflows_total;
__u8 mptcpi_endp_laminar_max;
- __u8 reserved[2];
+ __u8 mptcpi_endp_fullmesh_max;
+ __u8 reserved;
__u32 mptcpi_last_data_sent;
__u32 mptcpi_last_data_recv;
__u32 mptcpi_last_ack_recv;
diff --git a/net/mptcp/pm_kernel.c b/net/mptcp/pm_kernel.c
index df2bc36593d0..054d85045381 100644
--- a/net/mptcp/pm_kernel.c
+++ b/net/mptcp/pm_kernel.c
@@ -22,6 +22,7 @@ struct pm_nl_pernet {
u8 endp_signal_max;
u8 endp_subflow_max;
u8 endp_laminar_max;
+ u8 endp_fullmesh_max;
u8 limit_add_addr_accepted;
u8 limit_extra_subflows;
u8 next_id;
@@ -70,6 +71,14 @@ u8 mptcp_pm_get_endp_laminar_max(const struct mptcp_sock *msk)
}
EXPORT_SYMBOL_GPL(mptcp_pm_get_endp_laminar_max);
+u8 mptcp_pm_get_endp_fullmesh_max(const struct mptcp_sock *msk)
+{
+ struct pm_nl_pernet *pernet = pm_nl_get_pernet_from_msk(msk);
+
+ return READ_ONCE(pernet->endp_fullmesh_max);
+}
+EXPORT_SYMBOL_GPL(mptcp_pm_get_endp_fullmesh_max);
+
u8 mptcp_pm_get_limit_add_addr_accepted(const struct mptcp_sock *msk)
{
struct pm_nl_pernet *pernet = pm_nl_get_pernet_from_msk(msk);
@@ -603,9 +612,12 @@ fill_local_addresses_vec(struct mptcp_sock *msk, struct mptcp_addr_info *remote,
int i;
/* If there is at least one MPTCP endpoint with a fullmesh flag */
- i = fill_local_addresses_vec_fullmesh(msk, remote, locals, c_flag_case);
- if (i)
- return i;
+ if (mptcp_pm_get_endp_fullmesh_max(msk)) {
+ i = fill_local_addresses_vec_fullmesh(msk, remote, locals,
+ c_flag_case);
+ if (i)
+ return i;
+ }
/* If there is at least one MPTCP endpoint with a laminar flag */
if (mptcp_pm_get_endp_laminar_max(msk))
@@ -790,6 +802,10 @@ static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet,
addr_max = pernet->endp_laminar_max;
WRITE_ONCE(pernet->endp_laminar_max, addr_max + 1);
}
+ if (entry->flags & MPTCP_PM_ADDR_FLAG_FULLMESH) {
+ addr_max = pernet->endp_fullmesh_max;
+ WRITE_ONCE(pernet->endp_fullmesh_max, addr_max + 1);
+ }
pernet->endpoints++;
if (!entry->addr.port)
@@ -1188,6 +1204,10 @@ int mptcp_pm_nl_del_addr_doit(struct sk_buff *skb, struct genl_info *info)
addr_max = pernet->endp_laminar_max;
WRITE_ONCE(pernet->endp_laminar_max, addr_max - 1);
}
+ if (entry->flags & MPTCP_PM_ADDR_FLAG_FULLMESH) {
+ addr_max = pernet->endp_fullmesh_max;
+ WRITE_ONCE(pernet->endp_fullmesh_max, addr_max - 1);
+ }
pernet->endpoints--;
list_del_rcu(&entry->list);
@@ -1503,6 +1523,18 @@ int mptcp_pm_nl_set_flags(struct mptcp_pm_addr_entry *local,
changed = (local->flags ^ entry->flags) & mask;
entry->flags = (entry->flags & ~mask) | (local->flags & mask);
*local = *entry;
+
+ if (changed & MPTCP_PM_ADDR_FLAG_FULLMESH) {
+ u8 addr_max = pernet->endp_fullmesh_max;
+
+ if (entry->flags & MPTCP_PM_ADDR_FLAG_FULLMESH)
+ addr_max++;
+ else
+ addr_max--;
+
+ WRITE_ONCE(pernet->endp_fullmesh_max, addr_max);
+ }
+
spin_unlock_bh(&pernet->lock);
mptcp_pm_nl_set_flags_all(net, local, changed);
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 0545eab23125..1e8f40bb7870 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -1180,6 +1180,7 @@ void __mptcp_pm_kernel_worker(struct mptcp_sock *msk);
u8 mptcp_pm_get_endp_signal_max(const struct mptcp_sock *msk);
u8 mptcp_pm_get_endp_subflow_max(const struct mptcp_sock *msk);
u8 mptcp_pm_get_endp_laminar_max(const struct mptcp_sock *msk);
+u8 mptcp_pm_get_endp_fullmesh_max(const struct mptcp_sock *msk);
u8 mptcp_pm_get_limit_add_addr_accepted(const struct mptcp_sock *msk);
u8 mptcp_pm_get_limit_extra_subflows(const struct mptcp_sock *msk);
diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
index a28a48385885..de90a2897d2d 100644
--- a/net/mptcp/sockopt.c
+++ b/net/mptcp/sockopt.c
@@ -982,6 +982,8 @@ void mptcp_diag_fill_info(struct mptcp_sock *msk, struct mptcp_info *info)
mptcp_pm_get_endp_subflow_max(msk);
info->mptcpi_endp_laminar_max =
mptcp_pm_get_endp_laminar_max(msk);
+ info->mptcpi_endp_fullmesh_max =
+ mptcp_pm_get_endp_fullmesh_max(msk);
}
if (__mptcp_check_fallback(msk))
--
2.51.0
Hi Matt,
Thanks for this patch.
On Wed, 2025-10-08 at 16:00 +0200, Matthieu Baerts (NGI0) wrote:
> Instead of iterating over all endpoints, under RCU read lock, just to
> check if one of them as the fullmesh flag, we can keep a counter of
> fullmesh endpoint, similar to what is done with the other flags.
>
> This counter is now checked, before iterating over all endpoints.
>
> Similar to the other counters, this new one is also exposed. A
> userspace
> app can then know when it is being used in a fullmesh mode, with
> potentially (too) many subflows.
>
> Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
> ---
> include/uapi/linux/mptcp.h | 3 ++-
> net/mptcp/pm_kernel.c | 38 +++++++++++++++++++++++++++++++++++-
> --
> net/mptcp/protocol.h | 1 +
> net/mptcp/sockopt.c | 2 ++
> 4 files changed, 40 insertions(+), 4 deletions(-)
>
> diff --git a/include/uapi/linux/mptcp.h b/include/uapi/linux/mptcp.h
> index 87cfab874e24..04eea6d1d0a9 100644
> --- a/include/uapi/linux/mptcp.h
> +++ b/include/uapi/linux/mptcp.h
> @@ -70,7 +70,8 @@ struct mptcp_info {
> __u64 mptcpi_bytes_acked;
> __u8 mptcpi_subflows_total;
> __u8 mptcpi_endp_laminar_max;
> - __u8 reserved[2];
> + __u8 mptcpi_endp_fullmesh_max;
> + __u8 reserved;
> __u32 mptcpi_last_data_sent;
> __u32 mptcpi_last_data_recv;
> __u32 mptcpi_last_ack_recv;
> diff --git a/net/mptcp/pm_kernel.c b/net/mptcp/pm_kernel.c
> index df2bc36593d0..054d85045381 100644
> --- a/net/mptcp/pm_kernel.c
> +++ b/net/mptcp/pm_kernel.c
> @@ -22,6 +22,7 @@ struct pm_nl_pernet {
> u8 endp_signal_max;
> u8 endp_subflow_max;
> u8 endp_laminar_max;
> + u8 endp_fullmesh_max;
> u8 limit_add_addr_accepted;
> u8 limit_extra_subflows;
> u8 next_id;
> @@ -70,6 +71,14 @@ u8 mptcp_pm_get_endp_laminar_max(const struct
> mptcp_sock *msk)
> }
> EXPORT_SYMBOL_GPL(mptcp_pm_get_endp_laminar_max);
>
> +u8 mptcp_pm_get_endp_fullmesh_max(const struct mptcp_sock *msk)
> +{
> + struct pm_nl_pernet *pernet =
> pm_nl_get_pernet_from_msk(msk);
> +
> + return READ_ONCE(pernet->endp_fullmesh_max);
> +}
> +EXPORT_SYMBOL_GPL(mptcp_pm_get_endp_fullmesh_max);
> +
> u8 mptcp_pm_get_limit_add_addr_accepted(const struct mptcp_sock
> *msk)
> {
> struct pm_nl_pernet *pernet =
> pm_nl_get_pernet_from_msk(msk);
> @@ -603,9 +612,12 @@ fill_local_addresses_vec(struct mptcp_sock *msk,
> struct mptcp_addr_info *remote,
> int i;
>
> /* If there is at least one MPTCP endpoint with a fullmesh
> flag */
> - i = fill_local_addresses_vec_fullmesh(msk, remote, locals,
> c_flag_case);
> - if (i)
> - return i;
> + if (mptcp_pm_get_endp_fullmesh_max(msk)) {
> + i = fill_local_addresses_vec_fullmesh(msk, remote,
> locals,
> + c_flag_case);
> + if (i)
> + return i;
> + }
I'm not sure if endp_fullmesh_max needs to be checked in
fill_remote_addresses_vec() too, something like:
if (mptcp_pm_get_endp_fullmesh_max(msk))
return fill_remote_addresses_fullmesh(msk, local, addrs);
Thanks,
-Geliang
>
> /* If there is at least one MPTCP endpoint with a laminar
> flag */
> if (mptcp_pm_get_endp_laminar_max(msk))
> @@ -790,6 +802,10 @@ static int
> mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet,
> addr_max = pernet->endp_laminar_max;
> WRITE_ONCE(pernet->endp_laminar_max, addr_max + 1);
> }
> + if (entry->flags & MPTCP_PM_ADDR_FLAG_FULLMESH) {
> + addr_max = pernet->endp_fullmesh_max;
> + WRITE_ONCE(pernet->endp_fullmesh_max, addr_max + 1);
> + }
>
> pernet->endpoints++;
> if (!entry->addr.port)
> @@ -1188,6 +1204,10 @@ int mptcp_pm_nl_del_addr_doit(struct sk_buff
> *skb, struct genl_info *info)
> addr_max = pernet->endp_laminar_max;
> WRITE_ONCE(pernet->endp_laminar_max, addr_max - 1);
> }
> + if (entry->flags & MPTCP_PM_ADDR_FLAG_FULLMESH) {
> + addr_max = pernet->endp_fullmesh_max;
> + WRITE_ONCE(pernet->endp_fullmesh_max, addr_max - 1);
> + }
>
> pernet->endpoints--;
> list_del_rcu(&entry->list);
> @@ -1503,6 +1523,18 @@ int mptcp_pm_nl_set_flags(struct
> mptcp_pm_addr_entry *local,
> changed = (local->flags ^ entry->flags) & mask;
> entry->flags = (entry->flags & ~mask) | (local->flags &
> mask);
> *local = *entry;
> +
> + if (changed & MPTCP_PM_ADDR_FLAG_FULLMESH) {
> + u8 addr_max = pernet->endp_fullmesh_max;
> +
> + if (entry->flags & MPTCP_PM_ADDR_FLAG_FULLMESH)
> + addr_max++;
> + else
> + addr_max--;
> +
> + WRITE_ONCE(pernet->endp_fullmesh_max, addr_max);
> + }
> +
> spin_unlock_bh(&pernet->lock);
>
> mptcp_pm_nl_set_flags_all(net, local, changed);
> diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
> index 0545eab23125..1e8f40bb7870 100644
> --- a/net/mptcp/protocol.h
> +++ b/net/mptcp/protocol.h
> @@ -1180,6 +1180,7 @@ void __mptcp_pm_kernel_worker(struct mptcp_sock
> *msk);
> u8 mptcp_pm_get_endp_signal_max(const struct mptcp_sock *msk);
> u8 mptcp_pm_get_endp_subflow_max(const struct mptcp_sock *msk);
> u8 mptcp_pm_get_endp_laminar_max(const struct mptcp_sock *msk);
> +u8 mptcp_pm_get_endp_fullmesh_max(const struct mptcp_sock *msk);
> u8 mptcp_pm_get_limit_add_addr_accepted(const struct mptcp_sock
> *msk);
> u8 mptcp_pm_get_limit_extra_subflows(const struct mptcp_sock *msk);
>
> diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
> index a28a48385885..de90a2897d2d 100644
> --- a/net/mptcp/sockopt.c
> +++ b/net/mptcp/sockopt.c
> @@ -982,6 +982,8 @@ void mptcp_diag_fill_info(struct mptcp_sock *msk,
> struct mptcp_info *info)
> mptcp_pm_get_endp_subflow_max(msk);
> info->mptcpi_endp_laminar_max =
> mptcp_pm_get_endp_laminar_max(msk);
> + info->mptcpi_endp_fullmesh_max =
> + mptcp_pm_get_endp_fullmesh_max(msk);
> }
>
> if (__mptcp_check_fallback(msk))
>
Hi Geliang,
On 14/10/2025 09:20, Geliang Tang wrote:
> Hi Matt,
>
> Thanks for this patch.
>
> On Wed, 2025-10-08 at 16:00 +0200, Matthieu Baerts (NGI0) wrote:
>> Instead of iterating over all endpoints, under RCU read lock, just to
>> check if one of them as the fullmesh flag, we can keep a counter of
>> fullmesh endpoint, similar to what is done with the other flags.
>>
>> This counter is now checked, before iterating over all endpoints.
>>
>> Similar to the other counters, this new one is also exposed. A
>> userspace
>> app can then know when it is being used in a fullmesh mode, with
>> potentially (too) many subflows.
(...)
>> diff --git a/net/mptcp/pm_kernel.c b/net/mptcp/pm_kernel.c
>> index df2bc36593d0..054d85045381 100644
>> --- a/net/mptcp/pm_kernel.c
>> +++ b/net/mptcp/pm_kernel.c
(...)
>> @@ -603,9 +612,12 @@ fill_local_addresses_vec(struct mptcp_sock *msk,
>> struct mptcp_addr_info *remote,
>> int i;
>>
>> /* If there is at least one MPTCP endpoint with a fullmesh
>> flag */
>> - i = fill_local_addresses_vec_fullmesh(msk, remote, locals,
>> c_flag_case);
>> - if (i)
>> - return i;
>> + if (mptcp_pm_get_endp_fullmesh_max(msk)) {
>> + i = fill_local_addresses_vec_fullmesh(msk, remote,
>> locals,
>> + c_flag_case);
>> + if (i)
>> + return i;
>> + }
>
> I'm not sure if endp_fullmesh_max needs to be checked in
> fill_remote_addresses_vec() too, something like:
>
> if (mptcp_pm_get_endp_fullmesh_max(msk))
> return fill_remote_addresses_fullmesh(msk, local, addrs);
No, it doesn't need to be used with fill_remote_addresses_fullmesh(),
because this helper is only called if the local endpoint has the
fullmesh flag, see fill_remote_addresses_vec().
In other words, when calling fill_remote_(...), we have the local
endpoint (where the fullmesh flag might be set), and we need the remote
addresses. When calling fill_local_(...), it is the opposite: we have a
remote address, and we need to find local endpoint(s). It is only in
this case we need to know in advance if there are any "fullmesh" local
endpoints.
Cheers,
Matt
--
Sponsored by the NGI0 Core fund.
Hi Matt,
On Tue, 2025-10-14 at 12:02 +0200, Matthieu Baerts wrote:
> Hi Geliang,
>
> On 14/10/2025 09:20, Geliang Tang wrote:
> > Hi Matt,
> >
> > Thanks for this patch.
> >
> > On Wed, 2025-10-08 at 16:00 +0200, Matthieu Baerts (NGI0) wrote:
> > > Instead of iterating over all endpoints, under RCU read lock,
> > > just to
> > > check if one of them as the fullmesh flag, we can keep a counter
> > > of
> > > fullmesh endpoint, similar to what is done with the other flags.
> > >
> > > This counter is now checked, before iterating over all endpoints.
> > >
> > > Similar to the other counters, this new one is also exposed. A
> > > userspace
> > > app can then know when it is being used in a fullmesh mode, with
> > > potentially (too) many subflows.
>
> (...)
>
> > > diff --git a/net/mptcp/pm_kernel.c b/net/mptcp/pm_kernel.c
> > > index df2bc36593d0..054d85045381 100644
> > > --- a/net/mptcp/pm_kernel.c
> > > +++ b/net/mptcp/pm_kernel.c
>
> (...)
>
> > > @@ -603,9 +612,12 @@ fill_local_addresses_vec(struct mptcp_sock
> > > *msk,
> > > struct mptcp_addr_info *remote,
> > > int i;
> > >
> > > /* If there is at least one MPTCP endpoint with a
> > > fullmesh
> > > flag */
> > > - i = fill_local_addresses_vec_fullmesh(msk, remote,
> > > locals,
> > > c_flag_case);
> > > - if (i)
> > > - return i;
> > > + if (mptcp_pm_get_endp_fullmesh_max(msk)) {
> > > + i = fill_local_addresses_vec_fullmesh(msk,
> > > remote,
> > > locals,
> > > +
> > > c_flag_case);
> > > + if (i)
> > > + return i;
> > > + }
> >
> > I'm not sure if endp_fullmesh_max needs to be checked in
> > fill_remote_addresses_vec() too, something like:
> >
> > if (mptcp_pm_get_endp_fullmesh_max(msk))
> > return fill_remote_addresses_fullmesh(msk, local,
> > addrs);
>
> No, it doesn't need to be used with fill_remote_addresses_fullmesh(),
> because this helper is only called if the local endpoint has the
> fullmesh flag, see fill_remote_addresses_vec().
>
>
> In other words, when calling fill_remote_(...), we have the local
> endpoint (where the fullmesh flag might be set), and we need the
> remote
> addresses. When calling fill_local_(...), it is the opposite: we have
> a
> remote address, and we need to find local endpoint(s). It is only in
> this case we need to know in advance if there are any "fullmesh"
> local
> endpoints.
Thank you for your explanation. If that's the case, please add my
review-by tag:
Reviewed-by: Geliang Tang <geliang@kernel.org>
Thanks,
-Geliang
>
> Cheers,
> Matt
© 2016 - 2026 Red Hat, Inc.