@@ -25,6 +25,8 @@ typedef void (*postgp_func_t)(struct rcu_tasks *rtp);
25
25
* @cblist: Callback list.
26
26
* @lock: Lock protecting per-CPU callback list.
27
27
* @rtp_jiffies: Jiffies counter value for statistics.
28
+ * @lazy_timer: Timer to unlazify callbacks.
29
+ * @urgent_gp: Number of additional non-lazy grace periods.
28
30
* @rtp_n_lock_retries: Rough lock-contention statistic.
29
31
* @rtp_work: Work queue for invoking callbacks.
30
32
* @rtp_irq_work: IRQ work queue for deferred wakeups.
@@ -38,6 +40,8 @@ struct rcu_tasks_percpu {
38
40
raw_spinlock_t __private lock ;
39
41
unsigned long rtp_jiffies ;
40
42
unsigned long rtp_n_lock_retries ;
43
+ struct timer_list lazy_timer ;
44
+ unsigned int urgent_gp ;
41
45
struct work_struct rtp_work ;
42
46
struct irq_work rtp_irq_work ;
43
47
struct rcu_head barrier_q_head ;
@@ -51,7 +55,6 @@ struct rcu_tasks_percpu {
51
55
* @cbs_wait: RCU wait allowing a new callback to get kthread's attention.
52
56
* @cbs_gbl_lock: Lock protecting callback list.
53
57
* @tasks_gp_mutex: Mutex protecting grace period, needed during mid-boot dead zone.
54
- * @kthread_ptr: This flavor's grace-period/callback-invocation kthread.
55
58
* @gp_func: This flavor's grace-period-wait function.
56
59
* @gp_state: Grace period's most recent state transition (debugging).
57
60
* @gp_sleep: Per-grace-period sleep to prevent CPU-bound looping.
@@ -61,6 +64,8 @@ struct rcu_tasks_percpu {
61
64
* @tasks_gp_seq: Number of grace periods completed since boot.
62
65
* @n_ipis: Number of IPIs sent to encourage grace periods to end.
63
66
* @n_ipis_fails: Number of IPI-send failures.
67
+ * @kthread_ptr: This flavor's grace-period/callback-invocation kthread.
68
+ * @lazy_jiffies: Number of jiffies to allow callbacks to be lazy.
64
69
* @pregp_func: This flavor's pre-grace-period function (optional).
65
70
* @pertask_func: This flavor's per-task scan function (optional).
66
71
* @postscan_func: This flavor's post-task scan function (optional).
@@ -92,6 +97,7 @@ struct rcu_tasks {
92
97
unsigned long n_ipis ;
93
98
unsigned long n_ipis_fails ;
94
99
struct task_struct * kthread_ptr ;
100
+ unsigned long lazy_jiffies ;
95
101
rcu_tasks_gp_func_t gp_func ;
96
102
pregp_func_t pregp_func ;
97
103
pertask_func_t pertask_func ;
@@ -127,6 +133,7 @@ static struct rcu_tasks rt_name = \
127
133
.gp_func = gp, \
128
134
.call_func = call, \
129
135
.rtpcpu = &rt_name ## __percpu, \
136
+ .lazy_jiffies = DIV_ROUND_UP(HZ, 4), \
130
137
.name = n, \
131
138
.percpu_enqueue_shift = order_base_2(CONFIG_NR_CPUS), \
132
139
.percpu_enqueue_lim = 1, \
@@ -276,6 +283,33 @@ static void cblist_init_generic(struct rcu_tasks *rtp)
276
283
data_race (rtp -> percpu_enqueue_shift ), data_race (rtp -> percpu_enqueue_lim ), rcu_task_cb_adjust );
277
284
}
278
285
286
+ // Compute wakeup time for lazy callback timer.
287
+ static unsigned long rcu_tasks_lazy_time (struct rcu_tasks * rtp )
288
+ {
289
+ return jiffies + rtp -> lazy_jiffies ;
290
+ }
291
+
292
+ // Timer handler that unlazifies lazy callbacks.
293
+ static void call_rcu_tasks_generic_timer (struct timer_list * tlp )
294
+ {
295
+ unsigned long flags ;
296
+ bool needwake = false;
297
+ struct rcu_tasks * rtp ;
298
+ struct rcu_tasks_percpu * rtpcp = from_timer (rtpcp , tlp , lazy_timer );
299
+
300
+ rtp = rtpcp -> rtpp ;
301
+ raw_spin_lock_irqsave_rcu_node (rtpcp , flags );
302
+ if (!rcu_segcblist_empty (& rtpcp -> cblist ) && rtp -> lazy_jiffies ) {
303
+ if (!rtpcp -> urgent_gp )
304
+ rtpcp -> urgent_gp = 1 ;
305
+ needwake = true;
306
+ mod_timer (& rtpcp -> lazy_timer , rcu_tasks_lazy_time (rtp ));
307
+ }
308
+ raw_spin_unlock_irqrestore_rcu_node (rtpcp , flags );
309
+ if (needwake )
310
+ rcuwait_wake_up (& rtp -> cbs_wait );
311
+ }
312
+
279
313
// IRQ-work handler that does deferred wakeup for call_rcu_tasks_generic().
280
314
static void call_rcu_tasks_iw_wakeup (struct irq_work * iwp )
281
315
{
@@ -292,6 +326,7 @@ static void call_rcu_tasks_generic(struct rcu_head *rhp, rcu_callback_t func,
292
326
{
293
327
int chosen_cpu ;
294
328
unsigned long flags ;
329
+ bool havekthread = smp_load_acquire (& rtp -> kthread_ptr );
295
330
int ideal_cpu ;
296
331
unsigned long j ;
297
332
bool needadjust = false;
@@ -321,7 +356,15 @@ static void call_rcu_tasks_generic(struct rcu_head *rhp, rcu_callback_t func,
321
356
cblist_init_generic (rtp );
322
357
raw_spin_lock_rcu_node (rtpcp ); // irqs already disabled.
323
358
}
324
- needwake = rcu_segcblist_empty (& rtpcp -> cblist );
359
+ needwake = func == wakeme_after_rcu ;
360
+ if (havekthread && !timer_pending (& rtpcp -> lazy_timer )) {
361
+ if (rtp -> lazy_jiffies )
362
+ mod_timer (& rtpcp -> lazy_timer , rcu_tasks_lazy_time (rtp ));
363
+ else
364
+ needwake = rcu_segcblist_empty (& rtpcp -> cblist );
365
+ }
366
+ if (needwake )
367
+ rtpcp -> urgent_gp = 3 ;
325
368
rcu_segcblist_enqueue (& rtpcp -> cblist , rhp );
326
369
raw_spin_unlock_irqrestore_rcu_node (rtpcp , flags );
327
370
if (unlikely (needadjust )) {
@@ -415,9 +458,14 @@ static int rcu_tasks_need_gpcb(struct rcu_tasks *rtp)
415
458
}
416
459
rcu_segcblist_advance (& rtpcp -> cblist , rcu_seq_current (& rtp -> tasks_gp_seq ));
417
460
(void )rcu_segcblist_accelerate (& rtpcp -> cblist , rcu_seq_snap (& rtp -> tasks_gp_seq ));
418
- if (rcu_segcblist_pend_cbs (& rtpcp -> cblist ))
461
+ if (rtpcp -> urgent_gp > 0 && rcu_segcblist_pend_cbs (& rtpcp -> cblist )) {
462
+ if (rtp -> lazy_jiffies )
463
+ rtpcp -> urgent_gp -- ;
419
464
needgpcb |= 0x3 ;
420
- if (!rcu_segcblist_empty (& rtpcp -> cblist ))
465
+ } else if (rcu_segcblist_empty (& rtpcp -> cblist )) {
466
+ rtpcp -> urgent_gp = 0 ;
467
+ }
468
+ if (rcu_segcblist_ready_cbs (& rtpcp -> cblist ))
421
469
needgpcb |= 0x1 ;
422
470
raw_spin_unlock_irqrestore_rcu_node (rtpcp , flags );
423
471
}
@@ -549,11 +597,19 @@ static void rcu_tasks_one_gp(struct rcu_tasks *rtp, bool midboot)
549
597
// RCU-tasks kthread that detects grace periods and invokes callbacks.
550
598
static int __noreturn rcu_tasks_kthread (void * arg )
551
599
{
600
+ int cpu ;
552
601
struct rcu_tasks * rtp = arg ;
553
602
603
+ for_each_possible_cpu (cpu ) {
604
+ struct rcu_tasks_percpu * rtpcp = per_cpu_ptr (rtp -> rtpcpu , cpu );
605
+
606
+ timer_setup (& rtpcp -> lazy_timer , call_rcu_tasks_generic_timer , 0 );
607
+ rtpcp -> urgent_gp = 1 ;
608
+ }
609
+
554
610
/* Run on housekeeping CPUs by default. Sysadm can move if desired. */
555
611
housekeeping_affine (current , HK_TYPE_RCU );
556
- WRITE_ONCE ( rtp -> kthread_ptr , current ); // Let GPs start!
612
+ smp_store_release ( & rtp -> kthread_ptr , current ); // Let GPs start!
557
613
558
614
/*
559
615
* Each pass through the following loop makes one check for
@@ -635,23 +691,32 @@ static void show_rcu_tasks_generic_gp_kthread(struct rcu_tasks *rtp, char *s)
635
691
{
636
692
int cpu ;
637
693
bool havecbs = false;
694
+ bool haveurgent = false;
695
+ bool haveurgentcbs = false;
638
696
639
697
for_each_possible_cpu (cpu ) {
640
698
struct rcu_tasks_percpu * rtpcp = per_cpu_ptr (rtp -> rtpcpu , cpu );
641
699
642
- if (!data_race (rcu_segcblist_empty (& rtpcp -> cblist ))) {
700
+ if (!data_race (rcu_segcblist_empty (& rtpcp -> cblist )))
643
701
havecbs = true;
702
+ if (data_race (rtpcp -> urgent_gp ))
703
+ haveurgent = true;
704
+ if (!data_race (rcu_segcblist_empty (& rtpcp -> cblist )) && data_race (rtpcp -> urgent_gp ))
705
+ haveurgentcbs = true;
706
+ if (havecbs && haveurgent && haveurgentcbs )
644
707
break ;
645
- }
646
708
}
647
- pr_info ("%s: %s(%d) since %lu g:%lu i:%lu/%lu %c%c %s\n" ,
709
+ pr_info ("%s: %s(%d) since %lu g:%lu i:%lu/%lu %c%c%c%c l:%lu %s\n" ,
648
710
rtp -> kname ,
649
711
tasks_gp_state_getname (rtp ), data_race (rtp -> gp_state ),
650
712
jiffies - data_race (rtp -> gp_jiffies ),
651
713
data_race (rcu_seq_current (& rtp -> tasks_gp_seq )),
652
714
data_race (rtp -> n_ipis_fails ), data_race (rtp -> n_ipis ),
653
715
".k" [!!data_race (rtp -> kthread_ptr )],
654
716
".C" [havecbs ],
717
+ ".u" [haveurgent ],
718
+ ".U" [haveurgentcbs ],
719
+ rtp -> lazy_jiffies ,
655
720
s );
656
721
}
657
722
#endif // #ifndef CONFIG_TINY_RCU
0 commit comments