[v2,rcu,13/16] workqueue: Make queue_rcu_work() use call_rcu_flush()

Message ID 20221122010421.3799681-13-paulmck@kernel.org
State New
Headers
Series [v2,rcu,01/16] rcu: Simplify rcu_init_nohz() cpumask handling |

Commit Message

Paul E. McKenney Nov. 22, 2022, 1:04 a.m. UTC
  From: Uladzislau Rezki <urezki@gmail.com>

Earlier commits in this series allow battery-powered systems to build
their kernels with the default-disabled CONFIG_RCU_LAZY=y Kconfig option.
This Kconfig option causes call_rcu() to delay its callbacks in order
to batch them.  This means that a given RCU grace period covers more
callbacks, thus reducing the number of grace periods, in turn reducing
the amount of energy consumed, which increases battery lifetime which
can be a very good thing.  This is not a subtle effect: In some important
use cases, the battery lifetime is increased by more than 10%.

This CONFIG_RCU_LAZY=y option is available only for CPUs that offload
callbacks, for example, CPUs mentioned in the rcu_nocbs kernel boot
parameter passed to kernels built with CONFIG_RCU_NOCB_CPU=y.

Delaying callbacks is normally not a problem because most callbacks do
nothing but free memory.  If the system is short on memory, a shrinker
will kick all currently queued lazy callbacks out of their laziness,
thus freeing their memory in short order.  Similarly, the rcu_barrier()
function, which blocks until all currently queued callbacks are invoked,
will also kick lazy callbacks, thus enabling rcu_barrier() to complete
in a timely manner.

However, there are some cases where laziness is not a good option.
For example, synchronize_rcu() invokes call_rcu(), and blocks until
the newly queued callback is invoked.  It would not be a good for
synchronize_rcu() to block for ten seconds, even on an idle system.
Therefore, synchronize_rcu() invokes call_rcu_flush() instead of
call_rcu().  The arrival of a non-lazy call_rcu_flush() callback on a
given CPU kicks any lazy callbacks that might be already queued on that
CPU.  After all, if there is going to be a grace period, all callbacks
might as well get full benefit from it.

Yes, this could be done the other way around by creating a
call_rcu_lazy(), but earlier experience with this approach and
feedback at the 2022 Linux Plumbers Conference shifted the approach
to call_rcu() being lazy with call_rcu_flush() for the few places
where laziness is inappropriate.

And another call_rcu() instance that cannot be lazy is the one
in queue_rcu_work(), given that callers to queue_rcu_work() are
not necessarily OK with long delays.

Therefore, make queue_rcu_work() use call_rcu_flush() in order to revert
to the old behavior.

Signed-off-by: Uladzislau Rezki <urezki@gmail.com>
Signed-off-by: Joel Fernandes (Google) <joel@joelfernandes.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Lai Jiangshan <jiangshanlai@gmail.com>
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
---
 kernel/workqueue.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
  

Comments

Tejun Heo Nov. 22, 2022, 1:09 a.m. UTC | #1
On Mon, Nov 21, 2022 at 05:04:18PM -0800, Paul E. McKenney wrote:
> And another call_rcu() instance that cannot be lazy is the one
> in queue_rcu_work(), given that callers to queue_rcu_work() are
> not necessarily OK with long delays.

So, this is fine but another thing we can do is propagating the distinction
through the workqueue interface so that the the choice can be made by
workqueue users. Would that make sense?

Thanks.
  
Paul E. McKenney Nov. 22, 2022, 1:23 a.m. UTC | #2
On Mon, Nov 21, 2022 at 03:09:29PM -1000, Tejun Heo wrote:
> On Mon, Nov 21, 2022 at 05:04:18PM -0800, Paul E. McKenney wrote:
> > And another call_rcu() instance that cannot be lazy is the one
> > in queue_rcu_work(), given that callers to queue_rcu_work() are
> > not necessarily OK with long delays.
> 
> So, this is fine but another thing we can do is propagating the distinction
> through the workqueue interface so that the the choice can be made by
> workqueue users. Would that make sense?

It might well!  My thought was to wait to suggest that until we found a
real-life case where this was needed, but I have no objection to being
proactive here.

But the hard part...  Thought for a good name?  ;-)

							Thanx, Paul
  
Tejun Heo Nov. 22, 2022, 1:37 a.m. UTC | #3
On Mon, Nov 21, 2022 at 05:23:57PM -0800, Paul E. McKenney wrote:
> On Mon, Nov 21, 2022 at 03:09:29PM -1000, Tejun Heo wrote:
> > On Mon, Nov 21, 2022 at 05:04:18PM -0800, Paul E. McKenney wrote:
> > > And another call_rcu() instance that cannot be lazy is the one
> > > in queue_rcu_work(), given that callers to queue_rcu_work() are
> > > not necessarily OK with long delays.
> > 
> > So, this is fine but another thing we can do is propagating the distinction
> > through the workqueue interface so that the the choice can be made by
> > workqueue users. Would that make sense?
> 
> It might well!  My thought was to wait to suggest that until we found a
> real-life case where this was needed, but I have no objection to being
> proactive here.

Oh yeah, I'm completely fine either way too.

> But the hard part...  Thought for a good name?  ;-)

If we go with a separate interface, yeah, _flush would be confusing for
workqueue. Maybe _quick or _hurry? Hmm... it'd be nice to keep the suffix
consistent with RCU. What's the relationship with
synchronize_rcu_expedited()? Would using _expedited be confusing?

Thanks.
  
Paul E. McKenney Nov. 22, 2022, 1:52 a.m. UTC | #4
On Mon, Nov 21, 2022 at 03:37:56PM -1000, Tejun Heo wrote:
> On Mon, Nov 21, 2022 at 05:23:57PM -0800, Paul E. McKenney wrote:
> > On Mon, Nov 21, 2022 at 03:09:29PM -1000, Tejun Heo wrote:
> > > On Mon, Nov 21, 2022 at 05:04:18PM -0800, Paul E. McKenney wrote:
> > > > And another call_rcu() instance that cannot be lazy is the one
> > > > in queue_rcu_work(), given that callers to queue_rcu_work() are
> > > > not necessarily OK with long delays.
> > > 
> > > So, this is fine but another thing we can do is propagating the distinction
> > > through the workqueue interface so that the the choice can be made by
> > > workqueue users. Would that make sense?
> > 
> > It might well!  My thought was to wait to suggest that until we found a
> > real-life case where this was needed, but I have no objection to being
> > proactive here.
> 
> Oh yeah, I'm completely fine either way too.
> 
> > But the hard part...  Thought for a good name?  ;-)
> 
> If we go with a separate interface, yeah, _flush would be confusing for
> workqueue. Maybe _quick or _hurry? Hmm... it'd be nice to keep the suffix
> consistent with RCU. What's the relationship with
> synchronize_rcu_expedited()? Would using _expedited be confusing?

I expect that _expedited() would be because this has nothing whatever
to do with expedited grace periods.

Maybe we should have used call_rcu_hurry()?

What is in a name?  ;-)

							Thanx, Paul
  
Tejun Heo Nov. 22, 2022, 9:30 p.m. UTC | #5
On Mon, Nov 21, 2022 at 05:52:02PM -0800, Paul E. McKenney wrote:
> Maybe we should have used call_rcu_hurry()?

That sounds good to me.

Thanks.
  

Patch

diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 7cd5f5e7e0a1b..b4b0e828b529e 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -1771,7 +1771,7 @@  bool queue_rcu_work(struct workqueue_struct *wq, struct rcu_work *rwork)
 
 	if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) {
 		rwork->wq = wq;
-		call_rcu(&rwork->rcu, rcu_work_rcufn);
+		call_rcu_flush(&rwork->rcu, rcu_work_rcufn);
 		return true;
 	}