diff mbox series

[net-next,04/14] net_sched: sch_choke: implement lockless choke_dump()

Message ID 20240415132054.3822230-5-edumazet@google.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series net_sched: first series for RTNL-less qdisc dumps | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 926 this patch: 926
netdev/build_tools success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers success CCed 7 of 7 maintainers
netdev/build_clang success Errors and warnings before: 937 this patch: 937
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 937 this patch: 937
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 71 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
netdev/contest success net-next-2024-04-18--00-00 (tests: 961)

Commit Message

Eric Dumazet April 15, 2024, 1:20 p.m. UTC
Instead of relying on RTNL, choke_dump() can use READ_ONCE()
annotations, paired with WRITE_ONCE() ones in choke_change().

Also use READ_ONCE(q->limit) in choke_enqueue() as the value
could change from choke_change().

Signed-off-by: Eric Dumazet <edumazet@google.com>
---
 include/net/red.h     | 10 +++++-----
 net/sched/sch_choke.c | 23 ++++++++++++-----------
 2 files changed, 17 insertions(+), 16 deletions(-)

Comments

Simon Horman April 17, 2024, 1:14 p.m. UTC | #1
On Mon, Apr 15, 2024 at 01:20:44PM +0000, Eric Dumazet wrote:
> Instead of relying on RTNL, choke_dump() can use READ_ONCE()
> annotations, paired with WRITE_ONCE() ones in choke_change().
> 
> Also use READ_ONCE(q->limit) in choke_enqueue() as the value
> could change from choke_change().

Hi Eric,

I'm wondering if you could expand on why q->limit needs this treatment
but not other fields, f.e. q->parms.qth_min (aka p->qth_min).

> Signed-off-by: Eric Dumazet <edumazet@google.com>
> ---
>  include/net/red.h     | 10 +++++-----
>  net/sched/sch_choke.c | 23 ++++++++++++-----------
>  2 files changed, 17 insertions(+), 16 deletions(-)
> 
> diff --git a/include/net/red.h b/include/net/red.h

...

> @@ -244,7 +244,7 @@ static inline void red_set_parms(struct red_parms *p,
>  		max_P = red_maxp(Plog);
>  		max_P *= delta; /* max_P = (qth_max - qth_min)/2^Plog */
>  	}
> -	p->max_P = max_P;
> +	WRITE_ONCE(p->max_P, max_P);
>  	max_p_delta = max_P / delta;
>  	max_p_delta = max(max_p_delta, 1U);
>  	p->max_P_reciprocal  = reciprocal_value(max_p_delta);

A little further down in this function p->Scell_log is set.
I think it also needs the WRITE_ONCE() treatment as it
is read in choke_dump().

> diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c

...

> @@ -431,15 +431,16 @@ static int choke_init(struct Qdisc *sch, struct nlattr *opt,
>  static int choke_dump(struct Qdisc *sch, struct sk_buff *skb)
>  {
>  	struct choke_sched_data *q = qdisc_priv(sch);
> +	u8 Wlog = READ_ONCE(q->parms.Wlog);
>  	struct nlattr *opts = NULL;
>  	struct tc_red_qopt opt = {
> -		.limit		= q->limit,
> -		.flags		= q->flags,
> -		.qth_min	= q->parms.qth_min >> q->parms.Wlog,
> -		.qth_max	= q->parms.qth_max >> q->parms.Wlog,
> -		.Wlog		= q->parms.Wlog,
> -		.Plog		= q->parms.Plog,
> -		.Scell_log	= q->parms.Scell_log,
> +		.limit		= READ_ONCE(q->limit),
> +		.flags		= READ_ONCE(q->flags),
> +		.qth_min	= READ_ONCE(q->parms.qth_min) >> Wlog,
> +		.qth_max	= READ_ONCE(q->parms.qth_max) >> Wlog,
> +		.Wlog		= Wlog,
> +		.Plog		= READ_ONCE(q->parms.Plog),
> +		.Scell_log	= READ_ONCE(q->parms.Scell_log),
>  	};
>  
>  	opts = nla_nest_start_noflag(skb, TCA_OPTIONS);
Eric Dumazet April 17, 2024, 1:41 p.m. UTC | #2
On Wed, Apr 17, 2024 at 3:14 PM Simon Horman <horms@kernel.org> wrote:
>
> On Mon, Apr 15, 2024 at 01:20:44PM +0000, Eric Dumazet wrote:
> > Instead of relying on RTNL, choke_dump() can use READ_ONCE()
> > annotations, paired with WRITE_ONCE() ones in choke_change().
> >
> > Also use READ_ONCE(q->limit) in choke_enqueue() as the value
> > could change from choke_change().
>
> Hi Eric,
>
> I'm wondering if you could expand on why q->limit needs this treatment
> but not other fields, f.e. q->parms.qth_min (aka p->qth_min).

Other fields got their WRITE_ONCE() in red_set_parms()

+       WRITE_ONCE(p->qth_min, qth_min << Wlog);
+       WRITE_ONCE(p->qth_max, qth_max << Wlog);
+       WRITE_ONCE(p->Wlog, Wlog);
+       WRITE_ONCE(p->Plog, Plog);


>
> > Signed-off-by: Eric Dumazet <edumazet@google.com>
> > ---
> >  include/net/red.h     | 10 +++++-----
> >  net/sched/sch_choke.c | 23 ++++++++++++-----------
> >  2 files changed, 17 insertions(+), 16 deletions(-)
> >
> > diff --git a/include/net/red.h b/include/net/red.h
>
> ...
>
> > @@ -244,7 +244,7 @@ static inline void red_set_parms(struct red_parms *p,
> >               max_P = red_maxp(Plog);
> >               max_P *= delta; /* max_P = (qth_max - qth_min)/2^Plog */
> >       }
> > -     p->max_P = max_P;
> > +     WRITE_ONCE(p->max_P, max_P);
> >       max_p_delta = max_P / delta;
> >       max_p_delta = max(max_p_delta, 1U);
> >       p->max_P_reciprocal  = reciprocal_value(max_p_delta);
>
> A little further down in this function p->Scell_log is set.
> I think it also needs the WRITE_ONCE() treatment as it
> is read in choke_dump().
>

I will squash in v2 the missing WRITE_ONCE(), thanks !

diff --git a/include/net/red.h b/include/net/red.h
index 7daf7cf6130aeccf3d81a77600f4445759f174b7..802287d52c9e37e76ba9154539f511629e4b9780
100644
--- a/include/net/red.h
+++ b/include/net/red.h
@@ -257,7 +257,7 @@ static inline void red_set_parms(struct red_parms *p,
        p->target_min = qth_min + 2*delta;
        p->target_max = qth_min + 3*delta;

-       p->Scell_log    = Scell_log;
+       WRITE_ONCE(p->Scell_log, Scell_log);
        p->Scell_max    = (255 << Scell_log);

        if (stab)
Simon Horman April 17, 2024, 2:44 p.m. UTC | #3
On Wed, Apr 17, 2024 at 03:41:36PM +0200, Eric Dumazet wrote:
> On Wed, Apr 17, 2024 at 3:14 PM Simon Horman <horms@kernel.org> wrote:
> >
> > On Mon, Apr 15, 2024 at 01:20:44PM +0000, Eric Dumazet wrote:
> > > Instead of relying on RTNL, choke_dump() can use READ_ONCE()
> > > annotations, paired with WRITE_ONCE() ones in choke_change().
> > >
> > > Also use READ_ONCE(q->limit) in choke_enqueue() as the value
> > > could change from choke_change().
> >
> > Hi Eric,
> >
> > I'm wondering if you could expand on why q->limit needs this treatment
> > but not other fields, f.e. q->parms.qth_min (aka p->qth_min).
> 
> Other fields got their WRITE_ONCE() in red_set_parms()
> 
> +       WRITE_ONCE(p->qth_min, qth_min << Wlog);
> +       WRITE_ONCE(p->qth_max, qth_max << Wlog);
> +       WRITE_ONCE(p->Wlog, Wlog);
> +       WRITE_ONCE(p->Plog, Plog);

Thanks, understood.

I do wonder if other schedulers may need WRITE_ONCE() in
their enqueue() function. But I have not analysed this yet.

> > > Signed-off-by: Eric Dumazet <edumazet@google.com>
> > > ---
> > >  include/net/red.h     | 10 +++++-----
> > >  net/sched/sch_choke.c | 23 ++++++++++++-----------
> > >  2 files changed, 17 insertions(+), 16 deletions(-)
> > >
> > > diff --git a/include/net/red.h b/include/net/red.h
> >
> > ...
> >
> > > @@ -244,7 +244,7 @@ static inline void red_set_parms(struct red_parms *p,
> > >               max_P = red_maxp(Plog);
> > >               max_P *= delta; /* max_P = (qth_max - qth_min)/2^Plog */
> > >       }
> > > -     p->max_P = max_P;
> > > +     WRITE_ONCE(p->max_P, max_P);
> > >       max_p_delta = max_P / delta;
> > >       max_p_delta = max(max_p_delta, 1U);
> > >       p->max_P_reciprocal  = reciprocal_value(max_p_delta);
> >
> > A little further down in this function p->Scell_log is set.
> > I think it also needs the WRITE_ONCE() treatment as it
> > is read in choke_dump().
> >
> 
> I will squash in v2 the missing WRITE_ONCE(), thanks !

Likewise, thanks.
Your proposed update looks good to me.

> diff --git a/include/net/red.h b/include/net/red.h
> index 7daf7cf6130aeccf3d81a77600f4445759f174b7..802287d52c9e37e76ba9154539f511629e4b9780
> 100644
> --- a/include/net/red.h
> +++ b/include/net/red.h
> @@ -257,7 +257,7 @@ static inline void red_set_parms(struct red_parms *p,
>         p->target_min = qth_min + 2*delta;
>         p->target_max = qth_min + 3*delta;
> 
> -       p->Scell_log    = Scell_log;
> +       WRITE_ONCE(p->Scell_log, Scell_log);
>         p->Scell_max    = (255 << Scell_log);
> 
>         if (stab)
>
diff mbox series

Patch

diff --git a/include/net/red.h b/include/net/red.h
index 425364de0df7913cdd6361a0eb8d18b418372787..7daf7cf6130aeccf3d81a77600f4445759f174b7 100644
--- a/include/net/red.h
+++ b/include/net/red.h
@@ -233,10 +233,10 @@  static inline void red_set_parms(struct red_parms *p,
 	int delta = qth_max - qth_min;
 	u32 max_p_delta;
 
-	p->qth_min	= qth_min << Wlog;
-	p->qth_max	= qth_max << Wlog;
-	p->Wlog		= Wlog;
-	p->Plog		= Plog;
+	WRITE_ONCE(p->qth_min, qth_min << Wlog);
+	WRITE_ONCE(p->qth_max, qth_max << Wlog);
+	WRITE_ONCE(p->Wlog, Wlog);
+	WRITE_ONCE(p->Plog, Plog);
 	if (delta <= 0)
 		delta = 1;
 	p->qth_delta	= delta;
@@ -244,7 +244,7 @@  static inline void red_set_parms(struct red_parms *p,
 		max_P = red_maxp(Plog);
 		max_P *= delta; /* max_P = (qth_max - qth_min)/2^Plog */
 	}
-	p->max_P = max_P;
+	WRITE_ONCE(p->max_P, max_P);
 	max_p_delta = max_P / delta;
 	max_p_delta = max(max_p_delta, 1U);
 	p->max_P_reciprocal  = reciprocal_value(max_p_delta);
diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c
index ea108030c6b410418f0b58ba7ea51e1b524d4df9..51e68c9661727e64862392a76880bed5fa0c27a2 100644
--- a/net/sched/sch_choke.c
+++ b/net/sched/sch_choke.c
@@ -264,7 +264,7 @@  static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 	}
 
 	/* Admit new packet */
-	if (sch->q.qlen < q->limit) {
+	if (sch->q.qlen < READ_ONCE(q->limit)) {
 		q->tab[q->tail] = skb;
 		q->tail = (q->tail + 1) & q->tab_mask;
 		++sch->q.qlen;
@@ -405,8 +405,8 @@  static int choke_change(struct Qdisc *sch, struct nlattr *opt,
 	} else
 		sch_tree_lock(sch);
 
-	q->flags = ctl->flags;
-	q->limit = ctl->limit;
+	WRITE_ONCE(q->flags, ctl->flags);
+	WRITE_ONCE(q->limit, ctl->limit);
 
 	red_set_parms(&q->parms, ctl->qth_min, ctl->qth_max, ctl->Wlog,
 		      ctl->Plog, ctl->Scell_log,
@@ -431,15 +431,16 @@  static int choke_init(struct Qdisc *sch, struct nlattr *opt,
 static int choke_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
 	struct choke_sched_data *q = qdisc_priv(sch);
+	u8 Wlog = READ_ONCE(q->parms.Wlog);
 	struct nlattr *opts = NULL;
 	struct tc_red_qopt opt = {
-		.limit		= q->limit,
-		.flags		= q->flags,
-		.qth_min	= q->parms.qth_min >> q->parms.Wlog,
-		.qth_max	= q->parms.qth_max >> q->parms.Wlog,
-		.Wlog		= q->parms.Wlog,
-		.Plog		= q->parms.Plog,
-		.Scell_log	= q->parms.Scell_log,
+		.limit		= READ_ONCE(q->limit),
+		.flags		= READ_ONCE(q->flags),
+		.qth_min	= READ_ONCE(q->parms.qth_min) >> Wlog,
+		.qth_max	= READ_ONCE(q->parms.qth_max) >> Wlog,
+		.Wlog		= Wlog,
+		.Plog		= READ_ONCE(q->parms.Plog),
+		.Scell_log	= READ_ONCE(q->parms.Scell_log),
 	};
 
 	opts = nla_nest_start_noflag(skb, TCA_OPTIONS);
@@ -447,7 +448,7 @@  static int choke_dump(struct Qdisc *sch, struct sk_buff *skb)
 		goto nla_put_failure;
 
 	if (nla_put(skb, TCA_CHOKE_PARMS, sizeof(opt), &opt) ||
-	    nla_put_u32(skb, TCA_CHOKE_MAX_P, q->parms.max_P))
+	    nla_put_u32(skb, TCA_CHOKE_MAX_P, READ_ONCE(q->parms.max_P)))
 		goto nla_put_failure;
 	return nla_nest_end(skb, opts);