Skip to content

Commit 6be7436

Browse files
committed
rcu: Add rcu_gp_might_be_stalled()
This commit adds rcu_gp_might_be_stalled(), which returns true if there is some reason to believe that the RCU grace period is stalled. The use case is where an RCU free-memory path needs to allocate memory in order to free it, a situation that should be avoided where possible. But where it is necessary, there is always the alternative of using synchronize_rcu() to wait for a grace period in order to avoid the allocation. And if the grace period is stalled, allocating memory to asynchronously wait for it is a bad idea of epic proportions: Far better to let others use the memory, because these others might actually be able to free that memory before the grace period ends. Thus, rcu_gp_might_be_stalled() can be used to help decide whether allocating memory on an RCU free path is a semi-reasonable course of action. Cc: Joel Fernandes <[email protected]> Cc: Uladzislau Rezki <[email protected]> Signed-off-by: Paul E. McKenney <[email protected]>
1 parent a6a82ce commit 6be7436

File tree

3 files changed

+38
-4
lines changed

3 files changed

+38
-4
lines changed

include/linux/rcutiny.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ static inline bool rcu_inkernel_boot_has_ended(void) { return true; }
8787
static inline bool rcu_is_watching(void) { return true; }
8888
static inline void rcu_momentary_dyntick_idle(void) { }
8989
static inline void kfree_rcu_scheduler_running(void) { }
90+
static inline bool rcu_gp_might_be_stalled(void) { return false; }
9091

9192
/* Avoid RCU read-side critical sections leaking across. */
9293
static inline void rcu_all_qs(void) { barrier(); }

include/linux/rcutree.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ void rcu_barrier(void);
3939
bool rcu_eqs_special_set(int cpu);
4040
void rcu_momentary_dyntick_idle(void);
4141
void kfree_rcu_scheduler_running(void);
42+
bool rcu_gp_might_be_stalled(void);
4243
unsigned long get_state_synchronize_rcu(void);
4344
void cond_synchronize_rcu(unsigned long oldstate);
4445

kernel/rcu/tree_stall.h

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
int sysctl_panic_on_rcu_stall __read_mostly;
1616

1717
#ifdef CONFIG_PROVE_RCU
18-
#define RCU_STALL_DELAY_DELTA (5 * HZ)
18+
#define RCU_STALL_DELAY_DELTA (5 * HZ)
1919
#else
20-
#define RCU_STALL_DELAY_DELTA 0
20+
#define RCU_STALL_DELAY_DELTA 0
2121
#endif
22+
#define RCU_STALL_MIGHT_DIV 8
23+
#define RCU_STALL_MIGHT_MIN (2 * HZ)
2224

2325
/* Limit-check stall timeouts specified at boottime and runtime. */
2426
int rcu_jiffies_till_stall_check(void)
@@ -40,6 +42,36 @@ int rcu_jiffies_till_stall_check(void)
4042
}
4143
EXPORT_SYMBOL_GPL(rcu_jiffies_till_stall_check);
4244

45+
/**
46+
* rcu_gp_might_be_stalled - Is it likely that the grace period is stalled?
47+
*
48+
* Returns @true if the current grace period is sufficiently old that
49+
* it is reasonable to assume that it might be stalled. This can be
50+
* useful when deciding whether to allocate memory to enable RCU-mediated
51+
* freeing on the one hand or just invoking synchronize_rcu() on the other.
52+
* The latter is preferable when the grace period is stalled.
53+
*
54+
* Note that sampling of the .gp_start and .gp_seq fields must be done
55+
* carefully to avoid false positives at the beginnings and ends of
56+
* grace periods.
57+
*/
58+
bool rcu_gp_might_be_stalled(void)
59+
{
60+
unsigned long d = rcu_jiffies_till_stall_check() / RCU_STALL_MIGHT_DIV;
61+
unsigned long j = jiffies;
62+
63+
if (d < RCU_STALL_MIGHT_MIN)
64+
d = RCU_STALL_MIGHT_MIN;
65+
smp_mb(); // jiffies before .gp_seq to avoid false positives.
66+
if (!rcu_gp_in_progress())
67+
return false;
68+
// Long delays at this point avoids false positive, but a delay
69+
// of ULONG_MAX/4 jiffies voids your no-false-positive warranty.
70+
smp_mb(); // .gp_seq before second .gp_start
71+
// And ditto here.
72+
return !time_before(j, READ_ONCE(rcu_state.gp_start) + d);
73+
}
74+
4375
/* Don't do RCU CPU stall warnings during long sysrq printouts. */
4476
void rcu_sysrq_start(void)
4577
{
@@ -104,8 +136,8 @@ static void record_gp_stall_check_time(void)
104136

105137
WRITE_ONCE(rcu_state.gp_start, j);
106138
j1 = rcu_jiffies_till_stall_check();
107-
/* Record ->gp_start before ->jiffies_stall. */
108-
smp_store_release(&rcu_state.jiffies_stall, j + j1); /* ^^^ */
139+
smp_mb(); // ->gp_start before ->jiffies_stall and caller's ->gp_seq.
140+
WRITE_ONCE(rcu_state.jiffies_stall, j + j1);
109141
rcu_state.jiffies_resched = j + j1 / 2;
110142
rcu_state.n_force_qs_gpstart = READ_ONCE(rcu_state.n_force_qs);
111143
}

0 commit comments

Comments
 (0)