Skip to content

Commit 77a40f9

Browse files
joelagnelpaulmckrcu
authored andcommitted
rcu: Remove kfree_rcu() special casing and lazy-callback handling
This commit removes kfree_rcu() special-casing and the lazy-callback handling from Tree RCU. It moves some of this special casing to Tiny RCU, the removal of which will be the subject of later commits. This results in a nice negative delta. Suggested-by: Paul E. McKenney <[email protected]> Signed-off-by: Joel Fernandes (Google) <[email protected]> [ paulmck: Add slab.h #include, thanks to kbuild test robot <[email protected]>. ] Signed-off-by: Paul E. McKenney <[email protected]>
1 parent e99637b commit 77a40f9

File tree

12 files changed

+90
-159
lines changed

12 files changed

+90
-159
lines changed

Documentation/RCU/stallwarn.txt

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -225,18 +225,13 @@ an estimate of the total number of RCU callbacks queued across all CPUs
225225
In kernels with CONFIG_RCU_FAST_NO_HZ, more information is printed
226226
for each CPU:
227227

228-
0: (64628 ticks this GP) idle=dd5/3fffffffffffffff/0 softirq=82/543 last_accelerate: a345/d342 Nonlazy posted: ..D
228+
0: (64628 ticks this GP) idle=dd5/3fffffffffffffff/0 softirq=82/543 last_accelerate: a345/d342 dyntick_enabled: 1
229229

230230
The "last_accelerate:" prints the low-order 16 bits (in hex) of the
231231
jiffies counter when this CPU last invoked rcu_try_advance_all_cbs()
232232
from rcu_needs_cpu() or last invoked rcu_accelerate_cbs() from
233-
rcu_prepare_for_idle(). The "Nonlazy posted:" indicates lazy-callback
234-
status, so that an "l" indicates that all callbacks were lazy at the start
235-
of the last idle period and an "L" indicates that there are currently
236-
no non-lazy callbacks (in both cases, "." is printed otherwise, as
237-
shown above) and "D" indicates that dyntick-idle processing is enabled
238-
("." is printed otherwise, for example, if disabled via the "nohz="
239-
kernel boot parameter).
233+
rcu_prepare_for_idle(). "dyntick_enabled: 1" indicates that dyntick-idle
234+
processing is enabled.
240235

241236
If the grace period ends just as the stall warning starts printing,
242237
there will be a spurious stall-warning message, which will include

include/linux/rcu_segcblist.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ struct rcu_cblist {
2222
struct rcu_head *head;
2323
struct rcu_head **tail;
2424
long len;
25-
long len_lazy;
2625
};
2726

2827
#define RCU_CBLIST_INITIALIZER(n) { .head = NULL, .tail = &n.head }
@@ -73,7 +72,6 @@ struct rcu_segcblist {
7372
#else
7473
long len;
7574
#endif
76-
long len_lazy;
7775
u8 enabled;
7876
u8 offloaded;
7977
};

include/trace/events/rcu.h

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -481,30 +481,27 @@ TRACE_EVENT_RCU(rcu_dyntick,
481481
*/
482482
TRACE_EVENT_RCU(rcu_callback,
483483

484-
TP_PROTO(const char *rcuname, struct rcu_head *rhp, long qlen_lazy,
485-
long qlen),
484+
TP_PROTO(const char *rcuname, struct rcu_head *rhp, long qlen),
486485

487-
TP_ARGS(rcuname, rhp, qlen_lazy, qlen),
486+
TP_ARGS(rcuname, rhp, qlen),
488487

489488
TP_STRUCT__entry(
490489
__field(const char *, rcuname)
491490
__field(void *, rhp)
492491
__field(void *, func)
493-
__field(long, qlen_lazy)
494492
__field(long, qlen)
495493
),
496494

497495
TP_fast_assign(
498496
__entry->rcuname = rcuname;
499497
__entry->rhp = rhp;
500498
__entry->func = rhp->func;
501-
__entry->qlen_lazy = qlen_lazy;
502499
__entry->qlen = qlen;
503500
),
504501

505-
TP_printk("%s rhp=%p func=%ps %ld/%ld",
502+
TP_printk("%s rhp=%p func=%ps %ld",
506503
__entry->rcuname, __entry->rhp, __entry->func,
507-
__entry->qlen_lazy, __entry->qlen)
504+
__entry->qlen)
508505
);
509506

510507
/*
@@ -518,29 +515,27 @@ TRACE_EVENT_RCU(rcu_callback,
518515
TRACE_EVENT_RCU(rcu_kfree_callback,
519516

520517
TP_PROTO(const char *rcuname, struct rcu_head *rhp, unsigned long offset,
521-
long qlen_lazy, long qlen),
518+
long qlen),
522519

523-
TP_ARGS(rcuname, rhp, offset, qlen_lazy, qlen),
520+
TP_ARGS(rcuname, rhp, offset, qlen),
524521

525522
TP_STRUCT__entry(
526523
__field(const char *, rcuname)
527524
__field(void *, rhp)
528525
__field(unsigned long, offset)
529-
__field(long, qlen_lazy)
530526
__field(long, qlen)
531527
),
532528

533529
TP_fast_assign(
534530
__entry->rcuname = rcuname;
535531
__entry->rhp = rhp;
536532
__entry->offset = offset;
537-
__entry->qlen_lazy = qlen_lazy;
538533
__entry->qlen = qlen;
539534
),
540535

541-
TP_printk("%s rhp=%p func=%ld %ld/%ld",
536+
TP_printk("%s rhp=%p func=%ld %ld",
542537
__entry->rcuname, __entry->rhp, __entry->offset,
543-
__entry->qlen_lazy, __entry->qlen)
538+
__entry->qlen)
544539
);
545540

546541
/*
@@ -552,27 +547,24 @@ TRACE_EVENT_RCU(rcu_kfree_callback,
552547
*/
553548
TRACE_EVENT_RCU(rcu_batch_start,
554549

555-
TP_PROTO(const char *rcuname, long qlen_lazy, long qlen, long blimit),
550+
TP_PROTO(const char *rcuname, long qlen, long blimit),
556551

557-
TP_ARGS(rcuname, qlen_lazy, qlen, blimit),
552+
TP_ARGS(rcuname, qlen, blimit),
558553

559554
TP_STRUCT__entry(
560555
__field(const char *, rcuname)
561-
__field(long, qlen_lazy)
562556
__field(long, qlen)
563557
__field(long, blimit)
564558
),
565559

566560
TP_fast_assign(
567561
__entry->rcuname = rcuname;
568-
__entry->qlen_lazy = qlen_lazy;
569562
__entry->qlen = qlen;
570563
__entry->blimit = blimit;
571564
),
572565

573-
TP_printk("%s CBs=%ld/%ld bl=%ld",
574-
__entry->rcuname, __entry->qlen_lazy, __entry->qlen,
575-
__entry->blimit)
566+
TP_printk("%s CBs=%ld bl=%ld",
567+
__entry->rcuname, __entry->qlen, __entry->blimit)
576568
);
577569

578570
/*

kernel/rcu/rcu.h

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -198,33 +198,6 @@ static inline void debug_rcu_head_unqueue(struct rcu_head *head)
198198
}
199199
#endif /* #else !CONFIG_DEBUG_OBJECTS_RCU_HEAD */
200200

201-
void kfree(const void *);
202-
203-
/*
204-
* Reclaim the specified callback, either by invoking it (non-lazy case)
205-
* or freeing it directly (lazy case). Return true if lazy, false otherwise.
206-
*/
207-
static inline bool __rcu_reclaim(const char *rn, struct rcu_head *head)
208-
{
209-
rcu_callback_t f;
210-
unsigned long offset = (unsigned long)head->func;
211-
212-
rcu_lock_acquire(&rcu_callback_map);
213-
if (__is_kfree_rcu_offset(offset)) {
214-
trace_rcu_invoke_kfree_callback(rn, head, offset);
215-
kfree((void *)head - offset);
216-
rcu_lock_release(&rcu_callback_map);
217-
return true;
218-
} else {
219-
trace_rcu_invoke_callback(rn, head);
220-
f = head->func;
221-
WRITE_ONCE(head->func, (rcu_callback_t)0L);
222-
f(head);
223-
rcu_lock_release(&rcu_callback_map);
224-
return false;
225-
}
226-
}
227-
228201
#ifdef CONFIG_RCU_STALL_COMMON
229202

230203
extern int rcu_cpu_stall_ftrace_dump;

kernel/rcu/rcu_segcblist.c

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,10 @@ void rcu_cblist_init(struct rcu_cblist *rclp)
2020
rclp->head = NULL;
2121
rclp->tail = &rclp->head;
2222
rclp->len = 0;
23-
rclp->len_lazy = 0;
2423
}
2524

2625
/*
2726
* Enqueue an rcu_head structure onto the specified callback list.
28-
* This function assumes that the callback is non-lazy because it
29-
* is intended for use by no-CBs CPUs, which do not distinguish
30-
* between lazy and non-lazy RCU callbacks.
3127
*/
3228
void rcu_cblist_enqueue(struct rcu_cblist *rclp, struct rcu_head *rhp)
3329
{
@@ -54,24 +50,19 @@ void rcu_cblist_flush_enqueue(struct rcu_cblist *drclp,
5450
else
5551
drclp->tail = &drclp->head;
5652
drclp->len = srclp->len;
57-
drclp->len_lazy = srclp->len_lazy;
5853
if (!rhp) {
5954
rcu_cblist_init(srclp);
6055
} else {
6156
rhp->next = NULL;
6257
srclp->head = rhp;
6358
srclp->tail = &rhp->next;
6459
WRITE_ONCE(srclp->len, 1);
65-
srclp->len_lazy = 0;
6660
}
6761
}
6862

6963
/*
7064
* Dequeue the oldest rcu_head structure from the specified callback
71-
* list. This function assumes that the callback is non-lazy, but
72-
* the caller can later invoke rcu_cblist_dequeued_lazy() if it
73-
* finds otherwise (and if it cares about laziness). This allows
74-
* different users to have different ways of determining laziness.
65+
* list.
7566
*/
7667
struct rcu_head *rcu_cblist_dequeue(struct rcu_cblist *rclp)
7768
{
@@ -161,7 +152,6 @@ void rcu_segcblist_init(struct rcu_segcblist *rsclp)
161152
for (i = 0; i < RCU_CBLIST_NSEGS; i++)
162153
rsclp->tails[i] = &rsclp->head;
163154
rcu_segcblist_set_len(rsclp, 0);
164-
rsclp->len_lazy = 0;
165155
rsclp->enabled = 1;
166156
}
167157

@@ -173,7 +163,6 @@ void rcu_segcblist_disable(struct rcu_segcblist *rsclp)
173163
{
174164
WARN_ON_ONCE(!rcu_segcblist_empty(rsclp));
175165
WARN_ON_ONCE(rcu_segcblist_n_cbs(rsclp));
176-
WARN_ON_ONCE(rcu_segcblist_n_lazy_cbs(rsclp));
177166
rsclp->enabled = 0;
178167
}
179168

@@ -253,11 +242,9 @@ bool rcu_segcblist_nextgp(struct rcu_segcblist *rsclp, unsigned long *lp)
253242
* absolutely not OK for it to ever miss posting a callback.
254243
*/
255244
void rcu_segcblist_enqueue(struct rcu_segcblist *rsclp,
256-
struct rcu_head *rhp, bool lazy)
245+
struct rcu_head *rhp)
257246
{
258247
rcu_segcblist_inc_len(rsclp);
259-
if (lazy)
260-
rsclp->len_lazy++;
261248
smp_mb(); /* Ensure counts are updated before callback is enqueued. */
262249
rhp->next = NULL;
263250
WRITE_ONCE(*rsclp->tails[RCU_NEXT_TAIL], rhp);
@@ -275,15 +262,13 @@ void rcu_segcblist_enqueue(struct rcu_segcblist *rsclp,
275262
* period. You have been warned.
276263
*/
277264
bool rcu_segcblist_entrain(struct rcu_segcblist *rsclp,
278-
struct rcu_head *rhp, bool lazy)
265+
struct rcu_head *rhp)
279266
{
280267
int i;
281268

282269
if (rcu_segcblist_n_cbs(rsclp) == 0)
283270
return false;
284271
rcu_segcblist_inc_len(rsclp);
285-
if (lazy)
286-
rsclp->len_lazy++;
287272
smp_mb(); /* Ensure counts are updated before callback is entrained. */
288273
rhp->next = NULL;
289274
for (i = RCU_NEXT_TAIL; i > RCU_DONE_TAIL; i--)
@@ -307,8 +292,6 @@ bool rcu_segcblist_entrain(struct rcu_segcblist *rsclp,
307292
void rcu_segcblist_extract_count(struct rcu_segcblist *rsclp,
308293
struct rcu_cblist *rclp)
309294
{
310-
rclp->len_lazy += rsclp->len_lazy;
311-
rsclp->len_lazy = 0;
312295
rclp->len = rcu_segcblist_xchg_len(rsclp, 0);
313296
}
314297

@@ -361,9 +344,7 @@ void rcu_segcblist_extract_pend_cbs(struct rcu_segcblist *rsclp,
361344
void rcu_segcblist_insert_count(struct rcu_segcblist *rsclp,
362345
struct rcu_cblist *rclp)
363346
{
364-
rsclp->len_lazy += rclp->len_lazy;
365347
rcu_segcblist_add_len(rsclp, rclp->len);
366-
rclp->len_lazy = 0;
367348
rclp->len = 0;
368349
}
369350

kernel/rcu/rcu_segcblist.h

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,6 @@ static inline long rcu_cblist_n_cbs(struct rcu_cblist *rclp)
1515
return READ_ONCE(rclp->len);
1616
}
1717

18-
/*
19-
* Account for the fact that a previously dequeued callback turned out
20-
* to be marked as lazy.
21-
*/
22-
static inline void rcu_cblist_dequeued_lazy(struct rcu_cblist *rclp)
23-
{
24-
rclp->len_lazy--;
25-
}
26-
2718
void rcu_cblist_init(struct rcu_cblist *rclp);
2819
void rcu_cblist_enqueue(struct rcu_cblist *rclp, struct rcu_head *rhp);
2920
void rcu_cblist_flush_enqueue(struct rcu_cblist *drclp,
@@ -59,18 +50,6 @@ static inline long rcu_segcblist_n_cbs(struct rcu_segcblist *rsclp)
5950
#endif
6051
}
6152

62-
/* Return number of lazy callbacks in segmented callback list. */
63-
static inline long rcu_segcblist_n_lazy_cbs(struct rcu_segcblist *rsclp)
64-
{
65-
return rsclp->len_lazy;
66-
}
67-
68-
/* Return number of lazy callbacks in segmented callback list. */
69-
static inline long rcu_segcblist_n_nonlazy_cbs(struct rcu_segcblist *rsclp)
70-
{
71-
return rcu_segcblist_n_cbs(rsclp) - rsclp->len_lazy;
72-
}
73-
7453
/*
7554
* Is the specified rcu_segcblist enabled, for example, not corresponding
7655
* to an offline CPU?
@@ -106,9 +85,9 @@ struct rcu_head *rcu_segcblist_first_cb(struct rcu_segcblist *rsclp);
10685
struct rcu_head *rcu_segcblist_first_pend_cb(struct rcu_segcblist *rsclp);
10786
bool rcu_segcblist_nextgp(struct rcu_segcblist *rsclp, unsigned long *lp);
10887
void rcu_segcblist_enqueue(struct rcu_segcblist *rsclp,
109-
struct rcu_head *rhp, bool lazy);
88+
struct rcu_head *rhp);
11089
bool rcu_segcblist_entrain(struct rcu_segcblist *rsclp,
111-
struct rcu_head *rhp, bool lazy);
90+
struct rcu_head *rhp);
11291
void rcu_segcblist_extract_count(struct rcu_segcblist *rsclp,
11392
struct rcu_cblist *rclp);
11493
void rcu_segcblist_extract_done_cbs(struct rcu_segcblist *rsclp,

kernel/rcu/srcutree.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -853,7 +853,7 @@ static void __call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp,
853853
local_irq_save(flags);
854854
sdp = this_cpu_ptr(ssp->sda);
855855
spin_lock_rcu_node(sdp);
856-
rcu_segcblist_enqueue(&sdp->srcu_cblist, rhp, false);
856+
rcu_segcblist_enqueue(&sdp->srcu_cblist, rhp);
857857
rcu_segcblist_advance(&sdp->srcu_cblist,
858858
rcu_seq_current(&ssp->srcu_gp_seq));
859859
s = rcu_seq_snap(&ssp->srcu_gp_seq);
@@ -1052,7 +1052,7 @@ void srcu_barrier(struct srcu_struct *ssp)
10521052
sdp->srcu_barrier_head.func = srcu_barrier_cb;
10531053
debug_rcu_head_queue(&sdp->srcu_barrier_head);
10541054
if (!rcu_segcblist_entrain(&sdp->srcu_cblist,
1055-
&sdp->srcu_barrier_head, 0)) {
1055+
&sdp->srcu_barrier_head)) {
10561056
debug_rcu_head_unqueue(&sdp->srcu_barrier_head);
10571057
atomic_dec(&ssp->srcu_barrier_cpu_cnt);
10581058
}

kernel/rcu/tiny.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <linux/time.h>
2323
#include <linux/cpu.h>
2424
#include <linux/prefetch.h>
25+
#include <linux/slab.h>
2526

2627
#include "rcu.h"
2728

@@ -73,6 +74,31 @@ void rcu_sched_clock_irq(int user)
7374
}
7475
}
7576

77+
/*
78+
* Reclaim the specified callback, either by invoking it for non-kfree cases or
79+
* freeing it directly (for kfree). Return true if kfreeing, false otherwise.
80+
*/
81+
static inline bool rcu_reclaim_tiny(struct rcu_head *head)
82+
{
83+
rcu_callback_t f;
84+
unsigned long offset = (unsigned long)head->func;
85+
86+
rcu_lock_acquire(&rcu_callback_map);
87+
if (__is_kfree_rcu_offset(offset)) {
88+
trace_rcu_invoke_kfree_callback("", head, offset);
89+
kfree((void *)head - offset);
90+
rcu_lock_release(&rcu_callback_map);
91+
return true;
92+
}
93+
94+
trace_rcu_invoke_callback("", head);
95+
f = head->func;
96+
WRITE_ONCE(head->func, (rcu_callback_t)0L);
97+
f(head);
98+
rcu_lock_release(&rcu_callback_map);
99+
return false;
100+
}
101+
76102
/* Invoke the RCU callbacks whose grace period has elapsed. */
77103
static __latent_entropy void rcu_process_callbacks(struct softirq_action *unused)
78104
{
@@ -100,7 +126,7 @@ static __latent_entropy void rcu_process_callbacks(struct softirq_action *unused
100126
prefetch(next);
101127
debug_rcu_head_unqueue(list);
102128
local_bh_disable();
103-
__rcu_reclaim("", list);
129+
rcu_reclaim_tiny(list);
104130
local_bh_enable();
105131
list = next;
106132
}

0 commit comments

Comments
 (0)