From: Christoph Paasch <cpaasch@openai.com>
Add a helper to check if RFS is needed or not. Allows to make the code a
bit cleaner and the next patch to have MPTCP use this helper to decide
whether or not to iterate over the subflows.
tun_flow_update() was calling sock_rps_record_flow_hash() regardless of
the state of rfs_needed. This was not really a bug as sock_flow_table
simply ends up being NULL and thus everything will be fine.
This commit here thus also implicitly makes tun_flow_update() respect
the state of rfs_needed.
Suggested-by: Matthieu Baerts <matttbe@kernel.org>
Signed-off-by: Christoph Paasch <cpaasch@openai.com>
---
include/net/rps.h | 85 ++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 56 insertions(+), 29 deletions(-)
diff --git a/include/net/rps.h b/include/net/rps.h
index d8ab3a08bcc4882e2ad9c84c22ef26b254c14680..a89d2822a257a1fcf54e9722fdc22d01c28cc87b 100644
--- a/include/net/rps.h
+++ b/include/net/rps.h
@@ -82,11 +82,8 @@ static inline void rps_record_sock_flow(struct rps_sock_flow_table *table,
WRITE_ONCE(table->ents[index], val);
}
-#endif /* CONFIG_RPS */
-
-static inline void sock_rps_record_flow_hash(__u32 hash)
+static inline void _sock_rps_record_flow_hash(__u32 hash)
{
-#ifdef CONFIG_RPS
struct rps_sock_flow_table *sock_flow_table;
if (!hash)
@@ -96,42 +93,33 @@ static inline void sock_rps_record_flow_hash(__u32 hash)
if (sock_flow_table)
rps_record_sock_flow(sock_flow_table, hash);
rcu_read_unlock();
-#endif
}
-static inline void sock_rps_record_flow(const struct sock *sk)
+static inline void _sock_rps_record_flow(const struct sock *sk)
{
-#ifdef CONFIG_RPS
- if (static_branch_unlikely(&rfs_needed)) {
- /* Reading sk->sk_rxhash might incur an expensive cache line
- * miss.
- *
- * TCP_ESTABLISHED does cover almost all states where RFS
- * might be useful, and is cheaper [1] than testing :
- * IPv4: inet_sk(sk)->inet_daddr
- * IPv6: ipv6_addr_any(&sk->sk_v6_daddr)
- * OR an additional socket flag
- * [1] : sk_state and sk_prot are in the same cache line.
+ /* Reading sk->sk_rxhash might incur an expensive cache line
+ * miss.
+ *
+ * TCP_ESTABLISHED does cover almost all states where RFS
+ * might be useful, and is cheaper [1] than testing :
+ * IPv4: inet_sk(sk)->inet_daddr
+ * IPv6: ipv6_addr_any(&sk->sk_v6_daddr)
+ * OR an additional socket flag
+ * [1] : sk_state and sk_prot are in the same cache line.
+ */
+ if (sk->sk_state == TCP_ESTABLISHED) {
+ /* This READ_ONCE() is paired with the WRITE_ONCE()
+ * from sock_rps_save_rxhash() and sock_rps_reset_rxhash().
*/
- if (sk->sk_state == TCP_ESTABLISHED) {
- /* This READ_ONCE() is paired with the WRITE_ONCE()
- * from sock_rps_save_rxhash() and sock_rps_reset_rxhash().
- */
- sock_rps_record_flow_hash(READ_ONCE(sk->sk_rxhash));
- }
+ _sock_rps_record_flow_hash(READ_ONCE(sk->sk_rxhash));
}
-#endif
}
-static inline void sock_rps_delete_flow(const struct sock *sk)
+static inline void _sock_rps_delete_flow(const struct sock *sk)
{
-#ifdef CONFIG_RPS
struct rps_sock_flow_table *table;
u32 hash, index;
- if (!static_branch_unlikely(&rfs_needed))
- return;
-
hash = READ_ONCE(sk->sk_rxhash);
if (!hash)
return;
@@ -144,6 +132,45 @@ static inline void sock_rps_delete_flow(const struct sock *sk)
WRITE_ONCE(table->ents[index], RPS_NO_CPU);
}
rcu_read_unlock();
+}
+#endif /* CONFIG_RPS */
+
+static inline bool rfs_is_needed(void)
+{
+#ifdef CONFIG_RPS
+ return static_branch_unlikely(&rfs_needed);
+#else
+ return false;
+#endif
+}
+
+static inline void sock_rps_record_flow_hash(__u32 hash)
+{
+#ifdef CONFIG_RPS
+ if (!rfs_is_needed())
+ return;
+
+ _sock_rps_record_flow_hash(hash);
+#endif
+}
+
+static inline void sock_rps_record_flow(const struct sock *sk)
+{
+#ifdef CONFIG_RPS
+ if (!rfs_is_needed())
+ return;
+
+ _sock_rps_record_flow(sk);
+#endif
+}
+
+static inline void sock_rps_delete_flow(const struct sock *sk)
+{
+#ifdef CONFIG_RPS
+ if (!rfs_is_needed())
+ return;
+
+ _sock_rps_delete_flow(sk);
#endif
}
--
2.50.1
Hi Christoph,
On 26/08/2025 06:30, Christoph Paasch via B4 Relay wrote:
> From: Christoph Paasch <cpaasch@openai.com>
>
> Add a helper to check if RFS is needed or not. Allows to make the code a
> bit cleaner and the next patch to have MPTCP use this helper to decide
> whether or not to iterate over the subflows.
>
> tun_flow_update() was calling sock_rps_record_flow_hash() regardless of
> the state of rfs_needed. This was not really a bug as sock_flow_table
> simply ends up being NULL and thus everything will be fine.
> This commit here thus also implicitly makes tun_flow_update() respect
> the state of rfs_needed.
Thank you for having added this new helper!
Acked-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
> Suggested-by: Matthieu Baerts <matttbe@kernel.org>
> Signed-off-by: Christoph Paasch <cpaasch@openai.com>
> ---
> include/net/rps.h | 85 ++++++++++++++++++++++++++++++++++++-------------------
> 1 file changed, 56 insertions(+), 29 deletions(-)
>
> diff --git a/include/net/rps.h b/include/net/rps.h
> index d8ab3a08bcc4882e2ad9c84c22ef26b254c14680..a89d2822a257a1fcf54e9722fdc22d01c28cc87b 100644
> --- a/include/net/rps.h
> +++ b/include/net/rps.h
(...)
> @@ -144,6 +132,45 @@ static inline void sock_rps_delete_flow(const struct sock *sk)
> WRITE_ONCE(table->ents[index], RPS_NO_CPU);
> }
> rcu_read_unlock();
> +}
> +#endif /* CONFIG_RPS */
> +
> +static inline bool rfs_is_needed(void)
> +{
> +#ifdef CONFIG_RPS
> + return static_branch_unlikely(&rfs_needed);
> +#else
> + return false;
> +#endif
It looks strange to see all these #ifdef/#else inside the functions,
instead of "empty" functions at the end when CONFIG_RPS is not defined
as it is usually being done. But I see you are following what is
currently being done in this file, so all good!
Cheers,
Matt
--
Sponsored by the NGI0 Core fund.
[resend without HTML - how do I turn on "Plain Text Mode" in the gmail app ???]
On Thu, Aug 28, 2025 at 8:50 AM Matthieu Baerts <matttbe@kernel.org> wrote:
>
> Hi Christoph,
>
> On 26/08/2025 06:30, Christoph Paasch via B4 Relay wrote:
> > From: Christoph Paasch <cpaasch@openai.com>
> >
> > Add a helper to check if RFS is needed or not. Allows to make the code a
> > bit cleaner and the next patch to have MPTCP use this helper to decide
> > whether or not to iterate over the subflows.
> >
> > tun_flow_update() was calling sock_rps_record_flow_hash() regardless of
> > the state of rfs_needed. This was not really a bug as sock_flow_table
> > simply ends up being NULL and thus everything will be fine.
> > This commit here thus also implicitly makes tun_flow_update() respect
> > the state of rfs_needed.
>
> Thank you for having added this new helper!
>
> Acked-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
>
> > Suggested-by: Matthieu Baerts <matttbe@kernel.org>
> > Signed-off-by: Christoph Paasch <cpaasch@openai.com>
> > ---
> > include/net/rps.h | 85 ++++++++++++++++++++++++++++++++++++-------------------
> > 1 file changed, 56 insertions(+), 29 deletions(-)
> >
> > diff --git a/include/net/rps.h b/include/net/rps.h
> > index d8ab3a08bcc4882e2ad9c84c22ef26b254c14680..a89d2822a257a1fcf54e9722fdc22d01c28cc87b 100644
> > --- a/include/net/rps.h
> > +++ b/include/net/rps.h
>
> (...)
> > @@ -144,6 +132,45 @@ static inline void sock_rps_delete_flow(const struct sock *sk)
> > WRITE_ONCE(table->ents[index], RPS_NO_CPU);
> > }
> > rcu_read_unlock();
> > +}
> > +#endif /* CONFIG_RPS */
> > +
> > +static inline bool rfs_is_needed(void)
> > +{
> > +#ifdef CONFIG_RPS
> > + return static_branch_unlikely(&rfs_needed);
> > +#else
> > + return false;
> > +#endif
>
> It looks strange to see all these #ifdef/#else inside the functions,
> instead of "empty" functions at the end when CONFIG_RPS is not defined
> as it is usually being done. But I see you are following what is
> currently being done in this file, so all good!
Yes, I also prefer the style with the empty function. But also yes, I
just followed the style of the file.
Christoph
© 2016 - 2026 Red Hat, Inc.