Commit 5c0ec490 authored by Paul E. McKenney's avatar Paul E. McKenney

Merge branches 'doc.2022.08.31b', 'fixes.2022.08.31b', 'kvfree.2022.08.31b',...

Merge branches 'doc.2022.08.31b', 'fixes.2022.08.31b', 'kvfree.2022.08.31b', 'nocb.2022.09.01a', 'poll.2022.08.31b', 'poll-srcu.2022.08.31b' and 'tasks.2022.08.31b' into HEAD

doc.2022.08.31b: Documentation updates
fixes.2022.08.31b: Miscellaneous fixes
kvfree.2022.08.31b: kvfree_rcu() updates
nocb.2022.09.01a: NOCB CPU updates
poll.2022.08.31b: Full-oldstate RCU polling grace-period API
poll-srcu.2022.08.31b: Polled SRCU grace-period updates
tasks.2022.08.31b: Tasks RCU updates
...@@ -42,7 +42,31 @@ void call_rcu(struct rcu_head *head, rcu_callback_t func); ...@@ -42,7 +42,31 @@ void call_rcu(struct rcu_head *head, rcu_callback_t func);
void rcu_barrier_tasks(void); void rcu_barrier_tasks(void);
void rcu_barrier_tasks_rude(void); void rcu_barrier_tasks_rude(void);
void synchronize_rcu(void); void synchronize_rcu(void);
struct rcu_gp_oldstate;
unsigned long get_completed_synchronize_rcu(void); unsigned long get_completed_synchronize_rcu(void);
void get_completed_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp);
// Maximum number of unsigned long values corresponding to
// not-yet-completed RCU grace periods.
#define NUM_ACTIVE_RCU_POLL_OLDSTATE 2
/**
* same_state_synchronize_rcu - Are two old-state values identical?
* @oldstate1: First old-state value.
* @oldstate2: Second old-state value.
*
* The two old-state values must have been obtained from either
* get_state_synchronize_rcu(), start_poll_synchronize_rcu(), or
* get_completed_synchronize_rcu(). Returns @true if the two values are
* identical and @false otherwise. This allows structures whose lifetimes
* are tracked by old-state values to push these values to a list header,
* allowing those structures to be slightly smaller.
*/
static inline bool same_state_synchronize_rcu(unsigned long oldstate1, unsigned long oldstate2)
{
return oldstate1 == oldstate2;
}
#ifdef CONFIG_PREEMPT_RCU #ifdef CONFIG_PREEMPT_RCU
...@@ -496,13 +520,21 @@ do { \ ...@@ -496,13 +520,21 @@ do { \
* against NULL. Although rcu_access_pointer() may also be used in cases * against NULL. Although rcu_access_pointer() may also be used in cases
* where update-side locks prevent the value of the pointer from changing, * where update-side locks prevent the value of the pointer from changing,
* you should instead use rcu_dereference_protected() for this use case. * you should instead use rcu_dereference_protected() for this use case.
* Within an RCU read-side critical section, there is little reason to
* use rcu_access_pointer().
*
* It is usually best to test the rcu_access_pointer() return value
* directly in order to avoid accidental dereferences being introduced
* by later inattentive changes. In other words, assigning the
* rcu_access_pointer() return value to a local variable results in an
* accident waiting to happen.
* *
* It is also permissible to use rcu_access_pointer() when read-side * It is also permissible to use rcu_access_pointer() when read-side
* access to the pointer was removed at least one grace period ago, as * access to the pointer was removed at least one grace period ago, as is
* is the case in the context of the RCU callback that is freeing up * the case in the context of the RCU callback that is freeing up the data,
* the data, or after a synchronize_rcu() returns. This can be useful * or after a synchronize_rcu() returns. This can be useful when tearing
* when tearing down multi-linked structures after a grace period * down multi-linked structures after a grace period has elapsed. However,
* has elapsed. * rcu_dereference_protected() is normally preferred for this use case.
*/ */
#define rcu_access_pointer(p) __rcu_access_pointer((p), __UNIQUE_ID(rcu), __rcu) #define rcu_access_pointer(p) __rcu_access_pointer((p), __UNIQUE_ID(rcu), __rcu)
......
...@@ -14,25 +14,75 @@ ...@@ -14,25 +14,75 @@
#include <asm/param.h> /* for HZ */ #include <asm/param.h> /* for HZ */
struct rcu_gp_oldstate {
unsigned long rgos_norm;
};
// Maximum number of rcu_gp_oldstate values corresponding to
// not-yet-completed RCU grace periods.
#define NUM_ACTIVE_RCU_POLL_FULL_OLDSTATE 2
/*
* Are the two oldstate values the same? See the Tree RCU version for
* docbook header.
*/
static inline bool same_state_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp1,
struct rcu_gp_oldstate *rgosp2)
{
return rgosp1->rgos_norm == rgosp2->rgos_norm;
}
unsigned long get_state_synchronize_rcu(void); unsigned long get_state_synchronize_rcu(void);
static inline void get_state_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)
{
rgosp->rgos_norm = get_state_synchronize_rcu();
}
unsigned long start_poll_synchronize_rcu(void); unsigned long start_poll_synchronize_rcu(void);
static inline void start_poll_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)
{
rgosp->rgos_norm = start_poll_synchronize_rcu();
}
bool poll_state_synchronize_rcu(unsigned long oldstate); bool poll_state_synchronize_rcu(unsigned long oldstate);
static inline bool poll_state_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)
{
return poll_state_synchronize_rcu(rgosp->rgos_norm);
}
static inline void cond_synchronize_rcu(unsigned long oldstate) static inline void cond_synchronize_rcu(unsigned long oldstate)
{ {
might_sleep(); might_sleep();
} }
static inline void cond_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)
{
cond_synchronize_rcu(rgosp->rgos_norm);
}
static inline unsigned long start_poll_synchronize_rcu_expedited(void) static inline unsigned long start_poll_synchronize_rcu_expedited(void)
{ {
return start_poll_synchronize_rcu(); return start_poll_synchronize_rcu();
} }
static inline void start_poll_synchronize_rcu_expedited_full(struct rcu_gp_oldstate *rgosp)
{
rgosp->rgos_norm = start_poll_synchronize_rcu_expedited();
}
static inline void cond_synchronize_rcu_expedited(unsigned long oldstate) static inline void cond_synchronize_rcu_expedited(unsigned long oldstate)
{ {
cond_synchronize_rcu(oldstate); cond_synchronize_rcu(oldstate);
} }
static inline void cond_synchronize_rcu_expedited_full(struct rcu_gp_oldstate *rgosp)
{
cond_synchronize_rcu_expedited(rgosp->rgos_norm);
}
extern void rcu_barrier(void); extern void rcu_barrier(void);
static inline void synchronize_rcu_expedited(void) static inline void synchronize_rcu_expedited(void)
......
...@@ -40,12 +40,52 @@ bool rcu_eqs_special_set(int cpu); ...@@ -40,12 +40,52 @@ bool rcu_eqs_special_set(int cpu);
void rcu_momentary_dyntick_idle(void); void rcu_momentary_dyntick_idle(void);
void kfree_rcu_scheduler_running(void); void kfree_rcu_scheduler_running(void);
bool rcu_gp_might_be_stalled(void); bool rcu_gp_might_be_stalled(void);
struct rcu_gp_oldstate {
unsigned long rgos_norm;
unsigned long rgos_exp;
};
// Maximum number of rcu_gp_oldstate values corresponding to
// not-yet-completed RCU grace periods.
#define NUM_ACTIVE_RCU_POLL_FULL_OLDSTATE 4
/**
* same_state_synchronize_rcu_full - Are two old-state values identical?
* @rgosp1: First old-state value.
* @rgosp2: Second old-state value.
*
* The two old-state values must have been obtained from either
* get_state_synchronize_rcu_full(), start_poll_synchronize_rcu_full(),
* or get_completed_synchronize_rcu_full(). Returns @true if the two
* values are identical and @false otherwise. This allows structures
* whose lifetimes are tracked by old-state values to push these values
* to a list header, allowing those structures to be slightly smaller.
*
* Note that equality is judged on a bitwise basis, so that an
* @rcu_gp_oldstate structure with an already-completed state in one field
* will compare not-equal to a structure with an already-completed state
* in the other field. After all, the @rcu_gp_oldstate structure is opaque
* so how did such a situation come to pass in the first place?
*/
static inline bool same_state_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp1,
struct rcu_gp_oldstate *rgosp2)
{
return rgosp1->rgos_norm == rgosp2->rgos_norm && rgosp1->rgos_exp == rgosp2->rgos_exp;
}
unsigned long start_poll_synchronize_rcu_expedited(void); unsigned long start_poll_synchronize_rcu_expedited(void);
void start_poll_synchronize_rcu_expedited_full(struct rcu_gp_oldstate *rgosp);
void cond_synchronize_rcu_expedited(unsigned long oldstate); void cond_synchronize_rcu_expedited(unsigned long oldstate);
void cond_synchronize_rcu_expedited_full(struct rcu_gp_oldstate *rgosp);
unsigned long get_state_synchronize_rcu(void); unsigned long get_state_synchronize_rcu(void);
void get_state_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp);
unsigned long start_poll_synchronize_rcu(void); unsigned long start_poll_synchronize_rcu(void);
void start_poll_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp);
bool poll_state_synchronize_rcu(unsigned long oldstate); bool poll_state_synchronize_rcu(unsigned long oldstate);
bool poll_state_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp);
void cond_synchronize_rcu(unsigned long oldstate); void cond_synchronize_rcu(unsigned long oldstate);
void cond_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp);
bool rcu_is_idle_cpu(int cpu); bool rcu_is_idle_cpu(int cpu);
......
...@@ -15,10 +15,10 @@ ...@@ -15,10 +15,10 @@
struct srcu_struct { struct srcu_struct {
short srcu_lock_nesting[2]; /* srcu_read_lock() nesting depth. */ short srcu_lock_nesting[2]; /* srcu_read_lock() nesting depth. */
unsigned short srcu_idx; /* Current reader array element in bit 0x2. */
unsigned short srcu_idx_max; /* Furthest future srcu_idx request. */
u8 srcu_gp_running; /* GP workqueue running? */ u8 srcu_gp_running; /* GP workqueue running? */
u8 srcu_gp_waiting; /* GP waiting for readers? */ u8 srcu_gp_waiting; /* GP waiting for readers? */
unsigned long srcu_idx; /* Current reader array element in bit 0x2. */
unsigned long srcu_idx_max; /* Furthest future srcu_idx request. */
struct swait_queue_head srcu_wq; struct swait_queue_head srcu_wq;
/* Last srcu_read_unlock() wakes GP. */ /* Last srcu_read_unlock() wakes GP. */
struct rcu_head *srcu_cb_head; /* Pending callbacks: Head. */ struct rcu_head *srcu_cb_head; /* Pending callbacks: Head. */
...@@ -82,10 +82,12 @@ static inline void srcu_torture_stats_print(struct srcu_struct *ssp, ...@@ -82,10 +82,12 @@ static inline void srcu_torture_stats_print(struct srcu_struct *ssp,
int idx; int idx;
idx = ((data_race(READ_ONCE(ssp->srcu_idx)) + 1) & 0x2) >> 1; idx = ((data_race(READ_ONCE(ssp->srcu_idx)) + 1) & 0x2) >> 1;
pr_alert("%s%s Tiny SRCU per-CPU(idx=%d): (%hd,%hd)\n", pr_alert("%s%s Tiny SRCU per-CPU(idx=%d): (%hd,%hd) gp: %lu->%lu\n",
tt, tf, idx, tt, tf, idx,
data_race(READ_ONCE(ssp->srcu_lock_nesting[!idx])), data_race(READ_ONCE(ssp->srcu_lock_nesting[!idx])),
data_race(READ_ONCE(ssp->srcu_lock_nesting[idx]))); data_race(READ_ONCE(ssp->srcu_lock_nesting[idx])),
data_race(READ_ONCE(ssp->srcu_idx)),
data_race(READ_ONCE(ssp->srcu_idx_max)));
} }
#endif #endif
...@@ -84,10 +84,15 @@ torture_param(int, fwd_progress_holdoff, 60, "Time between forward-progress test ...@@ -84,10 +84,15 @@ torture_param(int, fwd_progress_holdoff, 60, "Time between forward-progress test
torture_param(bool, fwd_progress_need_resched, 1, "Hide cond_resched() behind need_resched()"); torture_param(bool, fwd_progress_need_resched, 1, "Hide cond_resched() behind need_resched()");
torture_param(bool, gp_cond, false, "Use conditional/async GP wait primitives"); torture_param(bool, gp_cond, false, "Use conditional/async GP wait primitives");
torture_param(bool, gp_cond_exp, false, "Use conditional/async expedited GP wait primitives"); torture_param(bool, gp_cond_exp, false, "Use conditional/async expedited GP wait primitives");
torture_param(bool, gp_cond_full, false, "Use conditional/async full-state GP wait primitives");
torture_param(bool, gp_cond_exp_full, false,
"Use conditional/async full-stateexpedited GP wait primitives");
torture_param(bool, gp_exp, false, "Use expedited GP wait primitives"); torture_param(bool, gp_exp, false, "Use expedited GP wait primitives");
torture_param(bool, gp_normal, false, "Use normal (non-expedited) GP wait primitives"); torture_param(bool, gp_normal, false, "Use normal (non-expedited) GP wait primitives");
torture_param(bool, gp_poll, false, "Use polling GP wait primitives"); torture_param(bool, gp_poll, false, "Use polling GP wait primitives");
torture_param(bool, gp_poll_exp, false, "Use polling expedited GP wait primitives"); torture_param(bool, gp_poll_exp, false, "Use polling expedited GP wait primitives");
torture_param(bool, gp_poll_full, false, "Use polling full-state GP wait primitives");
torture_param(bool, gp_poll_exp_full, false, "Use polling full-state expedited GP wait primitives");
torture_param(bool, gp_sync, false, "Use synchronous GP wait primitives"); torture_param(bool, gp_sync, false, "Use synchronous GP wait primitives");
torture_param(int, irqreader, 1, "Allow RCU readers from irq handlers"); torture_param(int, irqreader, 1, "Allow RCU readers from irq handlers");
torture_param(int, leakpointer, 0, "Leak pointer dereferences from readers"); torture_param(int, leakpointer, 0, "Leak pointer dereferences from readers");
...@@ -194,16 +199,24 @@ static int rcu_torture_writer_state; ...@@ -194,16 +199,24 @@ static int rcu_torture_writer_state;
#define RTWS_DEF_FREE 3 #define RTWS_DEF_FREE 3
#define RTWS_EXP_SYNC 4 #define RTWS_EXP_SYNC 4
#define RTWS_COND_GET 5 #define RTWS_COND_GET 5
#define RTWS_COND_GET_EXP 6 #define RTWS_COND_GET_FULL 6
#define RTWS_COND_SYNC 7 #define RTWS_COND_GET_EXP 7
#define RTWS_COND_SYNC_EXP 8 #define RTWS_COND_GET_EXP_FULL 8
#define RTWS_POLL_GET 9 #define RTWS_COND_SYNC 9
#define RTWS_POLL_GET_EXP 10 #define RTWS_COND_SYNC_FULL 10
#define RTWS_POLL_WAIT 11 #define RTWS_COND_SYNC_EXP 11
#define RTWS_POLL_WAIT_EXP 12 #define RTWS_COND_SYNC_EXP_FULL 12
#define RTWS_SYNC 13 #define RTWS_POLL_GET 13
#define RTWS_STUTTER 14 #define RTWS_POLL_GET_FULL 14
#define RTWS_STOPPING 15 #define RTWS_POLL_GET_EXP 15
#define RTWS_POLL_GET_EXP_FULL 16
#define RTWS_POLL_WAIT 17
#define RTWS_POLL_WAIT_FULL 18
#define RTWS_POLL_WAIT_EXP 19
#define RTWS_POLL_WAIT_EXP_FULL 20
#define RTWS_SYNC 21
#define RTWS_STUTTER 22
#define RTWS_STOPPING 23
static const char * const rcu_torture_writer_state_names[] = { static const char * const rcu_torture_writer_state_names[] = {
"RTWS_FIXED_DELAY", "RTWS_FIXED_DELAY",
"RTWS_DELAY", "RTWS_DELAY",
...@@ -211,13 +224,21 @@ static const char * const rcu_torture_writer_state_names[] = { ...@@ -211,13 +224,21 @@ static const char * const rcu_torture_writer_state_names[] = {
"RTWS_DEF_FREE", "RTWS_DEF_FREE",
"RTWS_EXP_SYNC", "RTWS_EXP_SYNC",
"RTWS_COND_GET", "RTWS_COND_GET",
"RTWS_COND_GET_FULL",
"RTWS_COND_GET_EXP", "RTWS_COND_GET_EXP",
"RTWS_COND_GET_EXP_FULL",
"RTWS_COND_SYNC", "RTWS_COND_SYNC",
"RTWS_COND_SYNC_FULL",
"RTWS_COND_SYNC_EXP", "RTWS_COND_SYNC_EXP",
"RTWS_COND_SYNC_EXP_FULL",
"RTWS_POLL_GET", "RTWS_POLL_GET",
"RTWS_POLL_GET_FULL",
"RTWS_POLL_GET_EXP", "RTWS_POLL_GET_EXP",
"RTWS_POLL_GET_EXP_FULL",
"RTWS_POLL_WAIT", "RTWS_POLL_WAIT",
"RTWS_POLL_WAIT_FULL",
"RTWS_POLL_WAIT_EXP", "RTWS_POLL_WAIT_EXP",
"RTWS_POLL_WAIT_EXP_FULL",
"RTWS_SYNC", "RTWS_SYNC",
"RTWS_STUTTER", "RTWS_STUTTER",
"RTWS_STOPPING", "RTWS_STOPPING",
...@@ -332,13 +353,21 @@ struct rcu_torture_ops { ...@@ -332,13 +353,21 @@ struct rcu_torture_ops {
void (*exp_sync)(void); void (*exp_sync)(void);
unsigned long (*get_gp_state_exp)(void); unsigned long (*get_gp_state_exp)(void);
unsigned long (*start_gp_poll_exp)(void); unsigned long (*start_gp_poll_exp)(void);
void (*start_gp_poll_exp_full)(struct rcu_gp_oldstate *rgosp);
bool (*poll_gp_state_exp)(unsigned long oldstate); bool (*poll_gp_state_exp)(unsigned long oldstate);
void (*cond_sync_exp)(unsigned long oldstate); void (*cond_sync_exp)(unsigned long oldstate);
void (*cond_sync_exp_full)(struct rcu_gp_oldstate *rgosp);
unsigned long (*get_gp_state)(void); unsigned long (*get_gp_state)(void);
void (*get_gp_state_full)(struct rcu_gp_oldstate *rgosp);
unsigned long (*get_gp_completed)(void); unsigned long (*get_gp_completed)(void);
void (*get_gp_completed_full)(struct rcu_gp_oldstate *rgosp);
unsigned long (*start_gp_poll)(void); unsigned long (*start_gp_poll)(void);
void (*start_gp_poll_full)(struct rcu_gp_oldstate *rgosp);
bool (*poll_gp_state)(unsigned long oldstate); bool (*poll_gp_state)(unsigned long oldstate);
bool (*poll_gp_state_full)(struct rcu_gp_oldstate *rgosp);
bool (*poll_need_2gp)(bool poll, bool poll_full);
void (*cond_sync)(unsigned long oldstate); void (*cond_sync)(unsigned long oldstate);
void (*cond_sync_full)(struct rcu_gp_oldstate *rgosp);
call_rcu_func_t call; call_rcu_func_t call;
void (*cb_barrier)(void); void (*cb_barrier)(void);
void (*fqs)(void); void (*fqs)(void);
...@@ -489,6 +518,11 @@ static void rcu_sync_torture_init(void) ...@@ -489,6 +518,11 @@ static void rcu_sync_torture_init(void)
INIT_LIST_HEAD(&rcu_torture_removed); INIT_LIST_HEAD(&rcu_torture_removed);
} }
static bool rcu_poll_need_2gp(bool poll, bool poll_full)
{
return poll;
}
static struct rcu_torture_ops rcu_ops = { static struct rcu_torture_ops rcu_ops = {
.ttype = RCU_FLAVOR, .ttype = RCU_FLAVOR,
.init = rcu_sync_torture_init, .init = rcu_sync_torture_init,
...@@ -502,12 +536,19 @@ static struct rcu_torture_ops rcu_ops = { ...@@ -502,12 +536,19 @@ static struct rcu_torture_ops rcu_ops = {
.sync = synchronize_rcu, .sync = synchronize_rcu,
.exp_sync = synchronize_rcu_expedited, .exp_sync = synchronize_rcu_expedited,
.get_gp_state = get_state_synchronize_rcu, .get_gp_state = get_state_synchronize_rcu,
.get_gp_state_full = get_state_synchronize_rcu_full,
.get_gp_completed = get_completed_synchronize_rcu, .get_gp_completed = get_completed_synchronize_rcu,
.get_gp_completed_full = get_completed_synchronize_rcu_full,
.start_gp_poll = start_poll_synchronize_rcu, .start_gp_poll = start_poll_synchronize_rcu,
.start_gp_poll_full = start_poll_synchronize_rcu_full,
.poll_gp_state = poll_state_synchronize_rcu, .poll_gp_state = poll_state_synchronize_rcu,
.poll_gp_state_full = poll_state_synchronize_rcu_full,
.poll_need_2gp = rcu_poll_need_2gp,
.cond_sync = cond_synchronize_rcu, .cond_sync = cond_synchronize_rcu,
.cond_sync_full = cond_synchronize_rcu_full,
.get_gp_state_exp = get_state_synchronize_rcu, .get_gp_state_exp = get_state_synchronize_rcu,
.start_gp_poll_exp = start_poll_synchronize_rcu_expedited, .start_gp_poll_exp = start_poll_synchronize_rcu_expedited,
.start_gp_poll_exp_full = start_poll_synchronize_rcu_expedited_full,
.poll_gp_state_exp = poll_state_synchronize_rcu, .poll_gp_state_exp = poll_state_synchronize_rcu,
.cond_sync_exp = cond_synchronize_rcu_expedited, .cond_sync_exp = cond_synchronize_rcu_expedited,
.call = call_rcu, .call = call_rcu,
...@@ -709,6 +750,9 @@ static struct rcu_torture_ops srcud_ops = { ...@@ -709,6 +750,9 @@ static struct rcu_torture_ops srcud_ops = {
.deferred_free = srcu_torture_deferred_free, .deferred_free = srcu_torture_deferred_free,
.sync = srcu_torture_synchronize, .sync = srcu_torture_synchronize,
.exp_sync = srcu_torture_synchronize_expedited, .exp_sync = srcu_torture_synchronize_expedited,
.get_gp_state = srcu_torture_get_gp_state,
.start_gp_poll = srcu_torture_start_gp_poll,
.poll_gp_state = srcu_torture_poll_gp_state,
.call = srcu_torture_call, .call = srcu_torture_call,
.cb_barrier = srcu_torture_barrier, .cb_barrier = srcu_torture_barrier,
.stats = srcu_torture_stats, .stats = srcu_torture_stats,
...@@ -1148,15 +1192,35 @@ static int nsynctypes; ...@@ -1148,15 +1192,35 @@ static int nsynctypes;
*/ */
static void rcu_torture_write_types(void) static void rcu_torture_write_types(void)
{ {
bool gp_cond1 = gp_cond, gp_cond_exp1 = gp_cond_exp, gp_exp1 = gp_exp; bool gp_cond1 = gp_cond, gp_cond_exp1 = gp_cond_exp, gp_cond_full1 = gp_cond_full;
bool gp_poll_exp1 = gp_poll_exp, gp_normal1 = gp_normal, gp_poll1 = gp_poll; bool gp_cond_exp_full1 = gp_cond_exp_full, gp_exp1 = gp_exp, gp_poll_exp1 = gp_poll_exp;
bool gp_sync1 = gp_sync; bool gp_poll_exp_full1 = gp_poll_exp_full, gp_normal1 = gp_normal, gp_poll1 = gp_poll;
bool gp_poll_full1 = gp_poll_full, gp_sync1 = gp_sync;
/* Initialize synctype[] array. If none set, take default. */ /* Initialize synctype[] array. If none set, take default. */
if (!gp_cond1 && !gp_cond_exp1 && !gp_exp1 && !gp_poll_exp && if (!gp_cond1 &&
!gp_normal1 && !gp_poll1 && !gp_sync1) !gp_cond_exp1 &&
gp_cond1 = gp_cond_exp1 = gp_exp1 = gp_poll_exp1 = !gp_cond_full1 &&
gp_normal1 = gp_poll1 = gp_sync1 = true; !gp_cond_exp_full1 &&
!gp_exp1 &&
!gp_poll_exp1 &&
!gp_poll_exp_full1 &&
!gp_normal1 &&
!gp_poll1 &&
!gp_poll_full1 &&
!gp_sync1) {
gp_cond1 = true;
gp_cond_exp1 = true;
gp_cond_full1 = true;
gp_cond_exp_full1 = true;
gp_exp1 = true;
gp_poll_exp1 = true;
gp_poll_exp_full1 = true;
gp_normal1 = true;
gp_poll1 = true;
gp_poll_full1 = true;
gp_sync1 = true;
}
if (gp_cond1 && cur_ops->get_gp_state && cur_ops->cond_sync) { if (gp_cond1 && cur_ops->get_gp_state && cur_ops->cond_sync) {
synctype[nsynctypes++] = RTWS_COND_GET; synctype[nsynctypes++] = RTWS_COND_GET;
pr_info("%s: Testing conditional GPs.\n", __func__); pr_info("%s: Testing conditional GPs.\n", __func__);
...@@ -1169,6 +1233,19 @@ static void rcu_torture_write_types(void) ...@@ -1169,6 +1233,19 @@ static void rcu_torture_write_types(void)
} else if (gp_cond_exp && (!cur_ops->get_gp_state_exp || !cur_ops->cond_sync_exp)) { } else if (gp_cond_exp && (!cur_ops->get_gp_state_exp || !cur_ops->cond_sync_exp)) {
pr_alert("%s: gp_cond_exp without primitives.\n", __func__); pr_alert("%s: gp_cond_exp without primitives.\n", __func__);
} }
if (gp_cond_full1 && cur_ops->get_gp_state && cur_ops->cond_sync_full) {
synctype[nsynctypes++] = RTWS_COND_GET_FULL;
pr_info("%s: Testing conditional full-state GPs.\n", __func__);
} else if (gp_cond_full && (!cur_ops->get_gp_state || !cur_ops->cond_sync_full)) {
pr_alert("%s: gp_cond_full without primitives.\n", __func__);
}
if (gp_cond_exp_full1 && cur_ops->get_gp_state_exp && cur_ops->cond_sync_exp_full) {
synctype[nsynctypes++] = RTWS_COND_GET_EXP_FULL;
pr_info("%s: Testing conditional full-state expedited GPs.\n", __func__);
} else if (gp_cond_exp_full &&
(!cur_ops->get_gp_state_exp || !cur_ops->cond_sync_exp_full)) {
pr_alert("%s: gp_cond_exp_full without primitives.\n", __func__);
}
if (gp_exp1 && cur_ops->exp_sync) { if (gp_exp1 && cur_ops->exp_sync) {
synctype[nsynctypes++] = RTWS_EXP_SYNC; synctype[nsynctypes++] = RTWS_EXP_SYNC;
pr_info("%s: Testing expedited GPs.\n", __func__); pr_info("%s: Testing expedited GPs.\n", __func__);
...@@ -1187,12 +1264,25 @@ static void rcu_torture_write_types(void) ...@@ -1187,12 +1264,25 @@ static void rcu_torture_write_types(void)
} else if (gp_poll && (!cur_ops->start_gp_poll || !cur_ops->poll_gp_state)) { } else if (gp_poll && (!cur_ops->start_gp_poll || !cur_ops->poll_gp_state)) {
pr_alert("%s: gp_poll without primitives.\n", __func__); pr_alert("%s: gp_poll without primitives.\n", __func__);
} }
if (gp_poll_full1 && cur_ops->start_gp_poll_full && cur_ops->poll_gp_state_full) {
synctype[nsynctypes++] = RTWS_POLL_GET_FULL;
pr_info("%s: Testing polling full-state GPs.\n", __func__);
} else if (gp_poll_full && (!cur_ops->start_gp_poll_full || !cur_ops->poll_gp_state_full)) {
pr_alert("%s: gp_poll_full without primitives.\n", __func__);
}
if (gp_poll_exp1 && cur_ops->start_gp_poll_exp && cur_ops->poll_gp_state_exp) { if (gp_poll_exp1 && cur_ops->start_gp_poll_exp && cur_ops->poll_gp_state_exp) {
synctype[nsynctypes++] = RTWS_POLL_GET_EXP; synctype[nsynctypes++] = RTWS_POLL_GET_EXP;
pr_info("%s: Testing polling expedited GPs.\n", __func__); pr_info("%s: Testing polling expedited GPs.\n", __func__);
} else if (gp_poll_exp && (!cur_ops->start_gp_poll_exp || !cur_ops->poll_gp_state_exp)) { } else if (gp_poll_exp && (!cur_ops->start_gp_poll_exp || !cur_ops->poll_gp_state_exp)) {
pr_alert("%s: gp_poll_exp without primitives.\n", __func__); pr_alert("%s: gp_poll_exp without primitives.\n", __func__);
} }
if (gp_poll_exp_full1 && cur_ops->start_gp_poll_exp_full && cur_ops->poll_gp_state_full) {
synctype[nsynctypes++] = RTWS_POLL_GET_EXP_FULL;
pr_info("%s: Testing polling full-state expedited GPs.\n", __func__);
} else if (gp_poll_exp_full &&
(!cur_ops->start_gp_poll_exp_full || !cur_ops->poll_gp_state_full)) {
pr_alert("%s: gp_poll_exp_full without primitives.\n", __func__);
}
if (gp_sync1 && cur_ops->sync) { if (gp_sync1 && cur_ops->sync) {
synctype[nsynctypes++] = RTWS_SYNC; synctype[nsynctypes++] = RTWS_SYNC;
pr_info("%s: Testing normal GPs.\n", __func__); pr_info("%s: Testing normal GPs.\n", __func__);
...@@ -1201,6 +1291,40 @@ static void rcu_torture_write_types(void) ...@@ -1201,6 +1291,40 @@ static void rcu_torture_write_types(void)
} }
} }
/*
* Do the specified rcu_torture_writer() synchronous grace period,
* while also testing out the polled APIs. Note well that the single-CPU
* grace-period optimizations must be accounted for.
*/
static void do_rtws_sync(struct torture_random_state *trsp, void (*sync)(void))
{
unsigned long cookie;
struct rcu_gp_oldstate cookie_full;
bool dopoll;
bool dopoll_full;
unsigned long r = torture_random(trsp);
dopoll = cur_ops->get_gp_state && cur_ops->poll_gp_state && !(r & 0x300);
dopoll_full = cur_ops->get_gp_state_full && cur_ops->poll_gp_state_full && !(r & 0xc00);
if (dopoll || dopoll_full)
cpus_read_lock();
if (dopoll)
cookie = cur_ops->get_gp_state();
if (dopoll_full)
cur_ops->get_gp_state_full(&cookie_full);
if (cur_ops->poll_need_2gp && cur_ops->poll_need_2gp(dopoll, dopoll_full))
sync();
sync();
WARN_ONCE(dopoll && !cur_ops->poll_gp_state(cookie),
"%s: Cookie check 3 failed %pS() online %*pbl.",
__func__, sync, cpumask_pr_args(cpu_online_mask));
WARN_ONCE(dopoll_full && !cur_ops->poll_gp_state_full(&cookie_full),
"%s: Cookie check 4 failed %pS() online %*pbl",
__func__, sync, cpumask_pr_args(cpu_online_mask));
if (dopoll || dopoll_full)
cpus_read_unlock();
}
/* /*
* RCU torture writer kthread. Repeatedly substitutes a new structure * RCU torture writer kthread. Repeatedly substitutes a new structure
* for that pointed to by rcu_torture_current, freeing the old structure * for that pointed to by rcu_torture_current, freeing the old structure
...@@ -1212,8 +1336,10 @@ rcu_torture_writer(void *arg) ...@@ -1212,8 +1336,10 @@ rcu_torture_writer(void *arg)
bool boot_ended; bool boot_ended;
bool can_expedite = !rcu_gp_is_expedited() && !rcu_gp_is_normal(); bool can_expedite = !rcu_gp_is_expedited() && !rcu_gp_is_normal();
unsigned long cookie; unsigned long cookie;
struct rcu_gp_oldstate cookie_full;
int expediting = 0; int expediting = 0;
unsigned long gp_snap; unsigned long gp_snap;
struct rcu_gp_oldstate gp_snap_full;
int i; int i;
int idx; int idx;
int oldnice = task_nice(current); int oldnice = task_nice(current);
...@@ -1261,11 +1387,12 @@ rcu_torture_writer(void *arg) ...@@ -1261,11 +1387,12 @@ rcu_torture_writer(void *arg)
atomic_inc(&rcu_torture_wcount[i]); atomic_inc(&rcu_torture_wcount[i]);
WRITE_ONCE(old_rp->rtort_pipe_count, WRITE_ONCE(old_rp->rtort_pipe_count,
old_rp->rtort_pipe_count + 1); old_rp->rtort_pipe_count + 1);
// Make sure readers block polled grace periods.
if (cur_ops->get_gp_state && cur_ops->poll_gp_state) { if (cur_ops->get_gp_state && cur_ops->poll_gp_state) {
idx = cur_ops->readlock(); idx = cur_ops->readlock();
cookie = cur_ops->get_gp_state(); cookie = cur_ops->get_gp_state();
WARN_ONCE(rcu_torture_writer_state != RTWS_DEF_FREE && WARN_ONCE(cur_ops->poll_gp_state(cookie),
cur_ops->poll_gp_state(cookie),
"%s: Cookie check 1 failed %s(%d) %lu->%lu\n", "%s: Cookie check 1 failed %s(%d) %lu->%lu\n",
__func__, __func__,
rcu_torture_writer_state_getname(), rcu_torture_writer_state_getname(),
...@@ -1277,6 +1404,21 @@ rcu_torture_writer(void *arg) ...@@ -1277,6 +1404,21 @@ rcu_torture_writer(void *arg)
} }
cur_ops->readunlock(idx); cur_ops->readunlock(idx);
} }
if (cur_ops->get_gp_state_full && cur_ops->poll_gp_state_full) {
idx = cur_ops->readlock();
cur_ops->get_gp_state_full(&cookie_full);
WARN_ONCE(cur_ops->poll_gp_state_full(&cookie_full),
"%s: Cookie check 5 failed %s(%d) online %*pbl\n",
__func__,
rcu_torture_writer_state_getname(),
rcu_torture_writer_state,
cpumask_pr_args(cpu_online_mask));
if (cur_ops->get_gp_completed_full) {
cur_ops->get_gp_completed_full(&cookie_full);
WARN_ON_ONCE(!cur_ops->poll_gp_state_full(&cookie_full));
}
cur_ops->readunlock(idx);
}
switch (synctype[torture_random(&rand) % nsynctypes]) { switch (synctype[torture_random(&rand) % nsynctypes]) {
case RTWS_DEF_FREE: case RTWS_DEF_FREE:
rcu_torture_writer_state = RTWS_DEF_FREE; rcu_torture_writer_state = RTWS_DEF_FREE;
...@@ -1284,12 +1426,7 @@ rcu_torture_writer(void *arg) ...@@ -1284,12 +1426,7 @@ rcu_torture_writer(void *arg)
break; break;
case RTWS_EXP_SYNC: case RTWS_EXP_SYNC:
rcu_torture_writer_state = RTWS_EXP_SYNC; rcu_torture_writer_state = RTWS_EXP_SYNC;
if (cur_ops->get_gp_state && cur_ops->poll_gp_state) do_rtws_sync(&rand, cur_ops->exp_sync);
cookie = cur_ops->get_gp_state();
cur_ops->exp_sync();
cur_ops->exp_sync();
if (cur_ops->get_gp_state && cur_ops->poll_gp_state)
WARN_ON_ONCE(!cur_ops->poll_gp_state(cookie));
rcu_torture_pipe_update(old_rp); rcu_torture_pipe_update(old_rp);
break; break;
case RTWS_COND_GET: case RTWS_COND_GET:
...@@ -1308,6 +1445,22 @@ rcu_torture_writer(void *arg) ...@@ -1308,6 +1445,22 @@ rcu_torture_writer(void *arg)
cur_ops->cond_sync_exp(gp_snap); cur_ops->cond_sync_exp(gp_snap);
rcu_torture_pipe_update(old_rp); rcu_torture_pipe_update(old_rp);
break; break;
case RTWS_COND_GET_FULL:
rcu_torture_writer_state = RTWS_COND_GET_FULL;
cur_ops->get_gp_state_full(&gp_snap_full);
torture_hrtimeout_jiffies(torture_random(&rand) % 16, &rand);
rcu_torture_writer_state = RTWS_COND_SYNC_FULL;
cur_ops->cond_sync_full(&gp_snap_full);
rcu_torture_pipe_update(old_rp);
break;
case RTWS_COND_GET_EXP_FULL:
rcu_torture_writer_state = RTWS_COND_GET_EXP_FULL;
cur_ops->get_gp_state_full(&gp_snap_full);
torture_hrtimeout_jiffies(torture_random(&rand) % 16, &rand);
rcu_torture_writer_state = RTWS_COND_SYNC_EXP_FULL;
cur_ops->cond_sync_exp_full(&gp_snap_full);
rcu_torture_pipe_update(old_rp);
break;
case RTWS_POLL_GET: case RTWS_POLL_GET:
rcu_torture_writer_state = RTWS_POLL_GET; rcu_torture_writer_state = RTWS_POLL_GET;
gp_snap = cur_ops->start_gp_poll(); gp_snap = cur_ops->start_gp_poll();
...@@ -1317,6 +1470,15 @@ rcu_torture_writer(void *arg) ...@@ -1317,6 +1470,15 @@ rcu_torture_writer(void *arg)
&rand); &rand);
rcu_torture_pipe_update(old_rp); rcu_torture_pipe_update(old_rp);
break; break;
case RTWS_POLL_GET_FULL:
rcu_torture_writer_state = RTWS_POLL_GET_FULL;
cur_ops->start_gp_poll_full(&gp_snap_full);
rcu_torture_writer_state = RTWS_POLL_WAIT_FULL;
while (!cur_ops->poll_gp_state_full(&gp_snap_full))
torture_hrtimeout_jiffies(torture_random(&rand) % 16,
&rand);
rcu_torture_pipe_update(old_rp);
break;
case RTWS_POLL_GET_EXP: case RTWS_POLL_GET_EXP:
rcu_torture_writer_state = RTWS_POLL_GET_EXP; rcu_torture_writer_state = RTWS_POLL_GET_EXP;
gp_snap = cur_ops->start_gp_poll_exp(); gp_snap = cur_ops->start_gp_poll_exp();
...@@ -1326,14 +1488,18 @@ rcu_torture_writer(void *arg) ...@@ -1326,14 +1488,18 @@ rcu_torture_writer(void *arg)
&rand); &rand);
rcu_torture_pipe_update(old_rp); rcu_torture_pipe_update(old_rp);
break; break;
case RTWS_POLL_GET_EXP_FULL:
rcu_torture_writer_state = RTWS_POLL_GET_EXP_FULL;
cur_ops->start_gp_poll_exp_full(&gp_snap_full);
rcu_torture_writer_state = RTWS_POLL_WAIT_EXP_FULL;
while (!cur_ops->poll_gp_state_full(&gp_snap_full))
torture_hrtimeout_jiffies(torture_random(&rand) % 16,
&rand);
rcu_torture_pipe_update(old_rp);
break;
case RTWS_SYNC: case RTWS_SYNC:
rcu_torture_writer_state = RTWS_SYNC; rcu_torture_writer_state = RTWS_SYNC;
if (cur_ops->get_gp_state && cur_ops->poll_gp_state) do_rtws_sync(&rand, cur_ops->sync);
cookie = cur_ops->get_gp_state();
cur_ops->sync();
cur_ops->sync();
if (cur_ops->get_gp_state && cur_ops->poll_gp_state)
WARN_ON_ONCE(!cur_ops->poll_gp_state(cookie));
rcu_torture_pipe_update(old_rp); rcu_torture_pipe_update(old_rp);
break; break;
default: default:
...@@ -1400,6 +1566,7 @@ static int ...@@ -1400,6 +1566,7 @@ static int
rcu_torture_fakewriter(void *arg) rcu_torture_fakewriter(void *arg)
{ {
unsigned long gp_snap; unsigned long gp_snap;
struct rcu_gp_oldstate gp_snap_full;
DEFINE_TORTURE_RANDOM(rand); DEFINE_TORTURE_RANDOM(rand);
VERBOSE_TOROUT_STRING("rcu_torture_fakewriter task started"); VERBOSE_TOROUT_STRING("rcu_torture_fakewriter task started");
...@@ -1438,6 +1605,16 @@ rcu_torture_fakewriter(void *arg) ...@@ -1438,6 +1605,16 @@ rcu_torture_fakewriter(void *arg)
torture_hrtimeout_jiffies(torture_random(&rand) % 16, &rand); torture_hrtimeout_jiffies(torture_random(&rand) % 16, &rand);
cur_ops->cond_sync_exp(gp_snap); cur_ops->cond_sync_exp(gp_snap);
break; break;
case RTWS_COND_GET_FULL:
cur_ops->get_gp_state_full(&gp_snap_full);
torture_hrtimeout_jiffies(torture_random(&rand) % 16, &rand);
cur_ops->cond_sync_full(&gp_snap_full);
break;
case RTWS_COND_GET_EXP_FULL:
cur_ops->get_gp_state_full(&gp_snap_full);
torture_hrtimeout_jiffies(torture_random(&rand) % 16, &rand);
cur_ops->cond_sync_exp_full(&gp_snap_full);
break;
case RTWS_POLL_GET: case RTWS_POLL_GET:
gp_snap = cur_ops->start_gp_poll(); gp_snap = cur_ops->start_gp_poll();
while (!cur_ops->poll_gp_state(gp_snap)) { while (!cur_ops->poll_gp_state(gp_snap)) {
...@@ -1445,6 +1622,13 @@ rcu_torture_fakewriter(void *arg) ...@@ -1445,6 +1622,13 @@ rcu_torture_fakewriter(void *arg)
&rand); &rand);
} }
break; break;
case RTWS_POLL_GET_FULL:
cur_ops->start_gp_poll_full(&gp_snap_full);
while (!cur_ops->poll_gp_state_full(&gp_snap_full)) {
torture_hrtimeout_jiffies(torture_random(&rand) % 16,
&rand);
}
break;
case RTWS_POLL_GET_EXP: case RTWS_POLL_GET_EXP:
gp_snap = cur_ops->start_gp_poll_exp(); gp_snap = cur_ops->start_gp_poll_exp();
while (!cur_ops->poll_gp_state_exp(gp_snap)) { while (!cur_ops->poll_gp_state_exp(gp_snap)) {
...@@ -1452,6 +1636,13 @@ rcu_torture_fakewriter(void *arg) ...@@ -1452,6 +1636,13 @@ rcu_torture_fakewriter(void *arg)
&rand); &rand);
} }
break; break;
case RTWS_POLL_GET_EXP_FULL:
cur_ops->start_gp_poll_exp_full(&gp_snap_full);
while (!cur_ops->poll_gp_state_full(&gp_snap_full)) {
torture_hrtimeout_jiffies(torture_random(&rand) % 16,
&rand);
}
break;
case RTWS_SYNC: case RTWS_SYNC:
cur_ops->sync(); cur_ops->sync();
break; break;
...@@ -1715,7 +1906,9 @@ rcutorture_loop_extend(int *readstate, struct torture_random_state *trsp, ...@@ -1715,7 +1906,9 @@ rcutorture_loop_extend(int *readstate, struct torture_random_state *trsp,
*/ */
static bool rcu_torture_one_read(struct torture_random_state *trsp, long myid) static bool rcu_torture_one_read(struct torture_random_state *trsp, long myid)
{ {
bool checkpolling = !(torture_random(trsp) & 0xfff);
unsigned long cookie; unsigned long cookie;
struct rcu_gp_oldstate cookie_full;
int i; int i;
unsigned long started; unsigned long started;
unsigned long completed; unsigned long completed;
...@@ -1731,8 +1924,12 @@ static bool rcu_torture_one_read(struct torture_random_state *trsp, long myid) ...@@ -1731,8 +1924,12 @@ static bool rcu_torture_one_read(struct torture_random_state *trsp, long myid)
WARN_ON_ONCE(!rcu_is_watching()); WARN_ON_ONCE(!rcu_is_watching());
newstate = rcutorture_extend_mask(readstate, trsp); newstate = rcutorture_extend_mask(readstate, trsp);
rcutorture_one_extend(&readstate, newstate, trsp, rtrsp++); rcutorture_one_extend(&readstate, newstate, trsp, rtrsp++);
if (cur_ops->get_gp_state && cur_ops->poll_gp_state) if (checkpolling) {
cookie = cur_ops->get_gp_state(); if (cur_ops->get_gp_state && cur_ops->poll_gp_state)
cookie = cur_ops->get_gp_state();
if (cur_ops->get_gp_state_full && cur_ops->poll_gp_state_full)
cur_ops->get_gp_state_full(&cookie_full);
}
started = cur_ops->get_gp_seq(); started = cur_ops->get_gp_seq();
ts = rcu_trace_clock_local(); ts = rcu_trace_clock_local();
p = rcu_dereference_check(rcu_torture_current, p = rcu_dereference_check(rcu_torture_current,
...@@ -1766,13 +1963,22 @@ static bool rcu_torture_one_read(struct torture_random_state *trsp, long myid) ...@@ -1766,13 +1963,22 @@ static bool rcu_torture_one_read(struct torture_random_state *trsp, long myid)
} }
__this_cpu_inc(rcu_torture_batch[completed]); __this_cpu_inc(rcu_torture_batch[completed]);
preempt_enable(); preempt_enable();
if (cur_ops->get_gp_state && cur_ops->poll_gp_state) if (checkpolling) {
WARN_ONCE(cur_ops->poll_gp_state(cookie), if (cur_ops->get_gp_state && cur_ops->poll_gp_state)
"%s: Cookie check 2 failed %s(%d) %lu->%lu\n", WARN_ONCE(cur_ops->poll_gp_state(cookie),
__func__, "%s: Cookie check 2 failed %s(%d) %lu->%lu\n",
rcu_torture_writer_state_getname(), __func__,
rcu_torture_writer_state, rcu_torture_writer_state_getname(),
cookie, cur_ops->get_gp_state()); rcu_torture_writer_state,
cookie, cur_ops->get_gp_state());
if (cur_ops->get_gp_state_full && cur_ops->poll_gp_state_full)
WARN_ONCE(cur_ops->poll_gp_state_full(&cookie_full),
"%s: Cookie check 6 failed %s(%d) online %*pbl\n",
__func__,
rcu_torture_writer_state_getname(),
rcu_torture_writer_state,
cpumask_pr_args(cpu_online_mask));
}
rcutorture_one_extend(&readstate, 0, trsp, rtrsp); rcutorture_one_extend(&readstate, 0, trsp, rtrsp);
WARN_ON_ONCE(readstate); WARN_ON_ONCE(readstate);
// This next splat is expected behavior if leakpointer, especially // This next splat is expected behavior if leakpointer, especially
...@@ -2600,12 +2806,12 @@ static int rcutorture_oom_notify(struct notifier_block *self, ...@@ -2600,12 +2806,12 @@ static int rcutorture_oom_notify(struct notifier_block *self,
for (i = 0; i < fwd_progress; i++) for (i = 0; i < fwd_progress; i++)
ncbs += rcu_torture_fwd_prog_cbfree(&rfp[i]); ncbs += rcu_torture_fwd_prog_cbfree(&rfp[i]);
pr_info("%s: Freed %lu RCU callbacks.\n", __func__, ncbs); pr_info("%s: Freed %lu RCU callbacks.\n", __func__, ncbs);
rcu_barrier(); cur_ops->cb_barrier();
ncbs = 0; ncbs = 0;
for (i = 0; i < fwd_progress; i++) for (i = 0; i < fwd_progress; i++)
ncbs += rcu_torture_fwd_prog_cbfree(&rfp[i]); ncbs += rcu_torture_fwd_prog_cbfree(&rfp[i]);
pr_info("%s: Freed %lu RCU callbacks.\n", __func__, ncbs); pr_info("%s: Freed %lu RCU callbacks.\n", __func__, ncbs);
rcu_barrier(); cur_ops->cb_barrier();
ncbs = 0; ncbs = 0;
for (i = 0; i < fwd_progress; i++) for (i = 0; i < fwd_progress; i++)
ncbs += rcu_torture_fwd_prog_cbfree(&rfp[i]); ncbs += rcu_torture_fwd_prog_cbfree(&rfp[i]);
......
...@@ -117,7 +117,7 @@ void srcu_drive_gp(struct work_struct *wp) ...@@ -117,7 +117,7 @@ void srcu_drive_gp(struct work_struct *wp)
struct srcu_struct *ssp; struct srcu_struct *ssp;
ssp = container_of(wp, struct srcu_struct, srcu_work); ssp = container_of(wp, struct srcu_struct, srcu_work);
if (ssp->srcu_gp_running || USHORT_CMP_GE(ssp->srcu_idx, READ_ONCE(ssp->srcu_idx_max))) if (ssp->srcu_gp_running || ULONG_CMP_GE(ssp->srcu_idx, READ_ONCE(ssp->srcu_idx_max)))
return; /* Already running or nothing to do. */ return; /* Already running or nothing to do. */
/* Remove recently arrived callbacks and wait for readers. */ /* Remove recently arrived callbacks and wait for readers. */
...@@ -150,17 +150,17 @@ void srcu_drive_gp(struct work_struct *wp) ...@@ -150,17 +150,17 @@ void srcu_drive_gp(struct work_struct *wp)
* straighten that out. * straighten that out.
*/ */
WRITE_ONCE(ssp->srcu_gp_running, false); WRITE_ONCE(ssp->srcu_gp_running, false);
if (USHORT_CMP_LT(ssp->srcu_idx, READ_ONCE(ssp->srcu_idx_max))) if (ULONG_CMP_LT(ssp->srcu_idx, READ_ONCE(ssp->srcu_idx_max)))
schedule_work(&ssp->srcu_work); schedule_work(&ssp->srcu_work);
} }
EXPORT_SYMBOL_GPL(srcu_drive_gp); EXPORT_SYMBOL_GPL(srcu_drive_gp);
static void srcu_gp_start_if_needed(struct srcu_struct *ssp) static void srcu_gp_start_if_needed(struct srcu_struct *ssp)
{ {
unsigned short cookie; unsigned long cookie;
cookie = get_state_synchronize_srcu(ssp); cookie = get_state_synchronize_srcu(ssp);
if (USHORT_CMP_GE(READ_ONCE(ssp->srcu_idx_max), cookie)) if (ULONG_CMP_GE(READ_ONCE(ssp->srcu_idx_max), cookie))
return; return;
WRITE_ONCE(ssp->srcu_idx_max, cookie); WRITE_ONCE(ssp->srcu_idx_max, cookie);
if (!READ_ONCE(ssp->srcu_gp_running)) { if (!READ_ONCE(ssp->srcu_gp_running)) {
...@@ -215,7 +215,7 @@ unsigned long get_state_synchronize_srcu(struct srcu_struct *ssp) ...@@ -215,7 +215,7 @@ unsigned long get_state_synchronize_srcu(struct srcu_struct *ssp)
barrier(); barrier();
ret = (READ_ONCE(ssp->srcu_idx) + 3) & ~0x1; ret = (READ_ONCE(ssp->srcu_idx) + 3) & ~0x1;
barrier(); barrier();
return ret & USHRT_MAX; return ret;
} }
EXPORT_SYMBOL_GPL(get_state_synchronize_srcu); EXPORT_SYMBOL_GPL(get_state_synchronize_srcu);
...@@ -240,10 +240,10 @@ EXPORT_SYMBOL_GPL(start_poll_synchronize_srcu); ...@@ -240,10 +240,10 @@ EXPORT_SYMBOL_GPL(start_poll_synchronize_srcu);
*/ */
bool poll_state_synchronize_srcu(struct srcu_struct *ssp, unsigned long cookie) bool poll_state_synchronize_srcu(struct srcu_struct *ssp, unsigned long cookie)
{ {
bool ret = USHORT_CMP_GE(READ_ONCE(ssp->srcu_idx), cookie); unsigned long cur_s = READ_ONCE(ssp->srcu_idx);
barrier(); barrier();
return ret; return ULONG_CMP_GE(cur_s, cookie) || ULONG_CMP_LT(cur_s, cookie - 3);
} }
EXPORT_SYMBOL_GPL(poll_state_synchronize_srcu); EXPORT_SYMBOL_GPL(poll_state_synchronize_srcu);
......
...@@ -560,7 +560,7 @@ static int __noreturn rcu_tasks_kthread(void *arg) ...@@ -560,7 +560,7 @@ static int __noreturn rcu_tasks_kthread(void *arg)
static void synchronize_rcu_tasks_generic(struct rcu_tasks *rtp) static void synchronize_rcu_tasks_generic(struct rcu_tasks *rtp)
{ {
/* Complain if the scheduler has not started. */ /* Complain if the scheduler has not started. */
RCU_LOCKDEP_WARN(rcu_scheduler_active == RCU_SCHEDULER_INACTIVE, WARN_ONCE(rcu_scheduler_active == RCU_SCHEDULER_INACTIVE,
"synchronize_rcu_tasks called too soon"); "synchronize_rcu_tasks called too soon");
// If the grace-period kthread is running, use it. // If the grace-period kthread is running, use it.
...@@ -1500,6 +1500,7 @@ static void rcu_tasks_trace_pregp_step(struct list_head *hop) ...@@ -1500,6 +1500,7 @@ static void rcu_tasks_trace_pregp_step(struct list_head *hop)
if (rcu_tasks_trace_pertask_prep(t, true)) if (rcu_tasks_trace_pertask_prep(t, true))
trc_add_holdout(t, hop); trc_add_holdout(t, hop);
rcu_read_unlock(); rcu_read_unlock();
cond_resched_tasks_rcu_qs();
} }
// Only after all running tasks have been accounted for is it // Only after all running tasks have been accounted for is it
...@@ -1520,6 +1521,7 @@ static void rcu_tasks_trace_pregp_step(struct list_head *hop) ...@@ -1520,6 +1521,7 @@ static void rcu_tasks_trace_pregp_step(struct list_head *hop)
raw_spin_lock_irqsave_rcu_node(rtpcp, flags); raw_spin_lock_irqsave_rcu_node(rtpcp, flags);
} }
raw_spin_unlock_irqrestore_rcu_node(rtpcp, flags); raw_spin_unlock_irqrestore_rcu_node(rtpcp, flags);
cond_resched_tasks_rcu_qs();
} }
// Re-enable CPU hotplug now that the holdout list is populated. // Re-enable CPU hotplug now that the holdout list is populated.
...@@ -1619,6 +1621,7 @@ static void check_all_holdout_tasks_trace(struct list_head *hop, ...@@ -1619,6 +1621,7 @@ static void check_all_holdout_tasks_trace(struct list_head *hop,
trc_del_holdout(t); trc_del_holdout(t);
else if (needreport) else if (needreport)
show_stalled_task_trace(t, firstreport); show_stalled_task_trace(t, firstreport);
cond_resched_tasks_rcu_qs();
} }
// Re-enable CPU hotplug now that the holdout list scan has completed. // Re-enable CPU hotplug now that the holdout list scan has completed.
......
...@@ -158,6 +158,10 @@ void synchronize_rcu(void) ...@@ -158,6 +158,10 @@ void synchronize_rcu(void)
} }
EXPORT_SYMBOL_GPL(synchronize_rcu); EXPORT_SYMBOL_GPL(synchronize_rcu);
static void tiny_rcu_leak_callback(struct rcu_head *rhp)
{
}
/* /*
* Post an RCU callback to be invoked after the end of an RCU grace * Post an RCU callback to be invoked after the end of an RCU grace
* period. But since we have but one CPU, that would be after any * period. But since we have but one CPU, that would be after any
...@@ -165,9 +169,20 @@ EXPORT_SYMBOL_GPL(synchronize_rcu); ...@@ -165,9 +169,20 @@ EXPORT_SYMBOL_GPL(synchronize_rcu);
*/ */
void call_rcu(struct rcu_head *head, rcu_callback_t func) void call_rcu(struct rcu_head *head, rcu_callback_t func)
{ {
static atomic_t doublefrees;
unsigned long flags; unsigned long flags;
debug_rcu_head_queue(head); if (debug_rcu_head_queue(head)) {
if (atomic_inc_return(&doublefrees) < 4) {
pr_err("%s(): Double-freed CB %p->%pS()!!! ", __func__, head, head->func);
mem_dump_obj(head);
}
if (!__is_kvfree_rcu_offset((unsigned long)head->func))
WRITE_ONCE(head->func, tiny_rcu_leak_callback);
return;
}
head->func = func; head->func = func;
head->next = NULL; head->next = NULL;
...@@ -183,6 +198,16 @@ void call_rcu(struct rcu_head *head, rcu_callback_t func) ...@@ -183,6 +198,16 @@ void call_rcu(struct rcu_head *head, rcu_callback_t func)
} }
EXPORT_SYMBOL_GPL(call_rcu); EXPORT_SYMBOL_GPL(call_rcu);
/*
* Store a grace-period-counter "cookie". For more information,
* see the Tree RCU header comment.
*/
void get_completed_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)
{
rgosp->rgos_norm = RCU_GET_STATE_COMPLETED;
}
EXPORT_SYMBOL_GPL(get_completed_synchronize_rcu_full);
/* /*
* Return a grace-period-counter "cookie". For more information, * Return a grace-period-counter "cookie". For more information,
* see the Tree RCU header comment. * see the Tree RCU header comment.
......
...@@ -76,6 +76,7 @@ ...@@ -76,6 +76,7 @@
/* Data structures. */ /* Data structures. */
static DEFINE_PER_CPU_SHARED_ALIGNED(struct rcu_data, rcu_data) = { static DEFINE_PER_CPU_SHARED_ALIGNED(struct rcu_data, rcu_data) = {
.gpwrap = true,
#ifdef CONFIG_RCU_NOCB_CPU #ifdef CONFIG_RCU_NOCB_CPU
.cblist.flags = SEGCBLIST_RCU_CORE, .cblist.flags = SEGCBLIST_RCU_CORE,
#endif #endif
...@@ -1755,6 +1756,8 @@ static noinline void rcu_gp_cleanup(void) ...@@ -1755,6 +1756,8 @@ static noinline void rcu_gp_cleanup(void)
dump_blkd_tasks(rnp, 10); dump_blkd_tasks(rnp, 10);
WARN_ON_ONCE(rnp->qsmask); WARN_ON_ONCE(rnp->qsmask);
WRITE_ONCE(rnp->gp_seq, new_gp_seq); WRITE_ONCE(rnp->gp_seq, new_gp_seq);
if (!rnp->parent)
smp_mb(); // Order against failing poll_state_synchronize_rcu_full().
rdp = this_cpu_ptr(&rcu_data); rdp = this_cpu_ptr(&rcu_data);
if (rnp == rdp->mynode) if (rnp == rdp->mynode)
needgp = __note_gp_changes(rnp, rdp) || needgp; needgp = __note_gp_changes(rnp, rdp) || needgp;
...@@ -2341,8 +2344,8 @@ void rcu_sched_clock_irq(int user) ...@@ -2341,8 +2344,8 @@ void rcu_sched_clock_irq(int user)
rcu_flavor_sched_clock_irq(user); rcu_flavor_sched_clock_irq(user);
if (rcu_pending(user)) if (rcu_pending(user))
invoke_rcu_core(); invoke_rcu_core();
if (user) if (user || rcu_is_cpu_rrupt_from_idle())
rcu_tasks_classic_qs(current, false); rcu_note_voluntary_context_switch(current);
lockdep_assert_irqs_disabled(); lockdep_assert_irqs_disabled();
trace_rcu_utilization(TPS("End scheduler-tick")); trace_rcu_utilization(TPS("End scheduler-tick"));
...@@ -2832,7 +2835,7 @@ EXPORT_SYMBOL_GPL(call_rcu); ...@@ -2832,7 +2835,7 @@ EXPORT_SYMBOL_GPL(call_rcu);
/* Maximum number of jiffies to wait before draining a batch. */ /* Maximum number of jiffies to wait before draining a batch. */
#define KFREE_DRAIN_JIFFIES (HZ / 50) #define KFREE_DRAIN_JIFFIES (5 * HZ)
#define KFREE_N_BATCHES 2 #define KFREE_N_BATCHES 2
#define FREE_N_CHANNELS 2 #define FREE_N_CHANNELS 2
...@@ -3093,6 +3096,21 @@ need_offload_krc(struct kfree_rcu_cpu *krcp) ...@@ -3093,6 +3096,21 @@ need_offload_krc(struct kfree_rcu_cpu *krcp)
return !!krcp->head; return !!krcp->head;
} }
static void
schedule_delayed_monitor_work(struct kfree_rcu_cpu *krcp)
{
long delay, delay_left;
delay = READ_ONCE(krcp->count) >= KVFREE_BULK_MAX_ENTR ? 1:KFREE_DRAIN_JIFFIES;
if (delayed_work_pending(&krcp->monitor_work)) {
delay_left = krcp->monitor_work.timer.expires - jiffies;
if (delay < delay_left)
mod_delayed_work(system_wq, &krcp->monitor_work, delay);
return;
}
queue_delayed_work(system_wq, &krcp->monitor_work, delay);
}
/* /*
* This function is invoked after the KFREE_DRAIN_JIFFIES timeout. * This function is invoked after the KFREE_DRAIN_JIFFIES timeout.
*/ */
...@@ -3150,7 +3168,7 @@ static void kfree_rcu_monitor(struct work_struct *work) ...@@ -3150,7 +3168,7 @@ static void kfree_rcu_monitor(struct work_struct *work)
// work to repeat an attempt. Because previous batches are // work to repeat an attempt. Because previous batches are
// still in progress. // still in progress.
if (need_offload_krc(krcp)) if (need_offload_krc(krcp))
schedule_delayed_work(&krcp->monitor_work, KFREE_DRAIN_JIFFIES); schedule_delayed_monitor_work(krcp);
raw_spin_unlock_irqrestore(&krcp->lock, flags); raw_spin_unlock_irqrestore(&krcp->lock, flags);
} }
...@@ -3183,15 +3201,16 @@ static void fill_page_cache_func(struct work_struct *work) ...@@ -3183,15 +3201,16 @@ static void fill_page_cache_func(struct work_struct *work)
bnode = (struct kvfree_rcu_bulk_data *) bnode = (struct kvfree_rcu_bulk_data *)
__get_free_page(GFP_KERNEL | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN); __get_free_page(GFP_KERNEL | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN);
if (bnode) { if (!bnode)
raw_spin_lock_irqsave(&krcp->lock, flags); break;
pushed = put_cached_bnode(krcp, bnode);
raw_spin_unlock_irqrestore(&krcp->lock, flags);
if (!pushed) { raw_spin_lock_irqsave(&krcp->lock, flags);
free_page((unsigned long) bnode); pushed = put_cached_bnode(krcp, bnode);
break; raw_spin_unlock_irqrestore(&krcp->lock, flags);
}
if (!pushed) {
free_page((unsigned long) bnode);
break;
} }
} }
...@@ -3338,7 +3357,7 @@ void kvfree_call_rcu(struct rcu_head *head, rcu_callback_t func) ...@@ -3338,7 +3357,7 @@ void kvfree_call_rcu(struct rcu_head *head, rcu_callback_t func)
// Set timer to drain after KFREE_DRAIN_JIFFIES. // Set timer to drain after KFREE_DRAIN_JIFFIES.
if (rcu_scheduler_active == RCU_SCHEDULER_RUNNING) if (rcu_scheduler_active == RCU_SCHEDULER_RUNNING)
schedule_delayed_work(&krcp->monitor_work, KFREE_DRAIN_JIFFIES); schedule_delayed_monitor_work(krcp);
unlock_return: unlock_return:
krc_this_cpu_unlock(krcp, flags); krc_this_cpu_unlock(krcp, flags);
...@@ -3371,7 +3390,7 @@ kfree_rcu_shrink_count(struct shrinker *shrink, struct shrink_control *sc) ...@@ -3371,7 +3390,7 @@ kfree_rcu_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
atomic_set(&krcp->backoff_page_cache_fill, 1); atomic_set(&krcp->backoff_page_cache_fill, 1);
} }
return count; return count == 0 ? SHRINK_EMPTY : count;
} }
static unsigned long static unsigned long
...@@ -3414,49 +3433,27 @@ void __init kfree_rcu_scheduler_running(void) ...@@ -3414,49 +3433,27 @@ void __init kfree_rcu_scheduler_running(void)
raw_spin_lock_irqsave(&krcp->lock, flags); raw_spin_lock_irqsave(&krcp->lock, flags);
if (need_offload_krc(krcp)) if (need_offload_krc(krcp))
schedule_delayed_work_on(cpu, &krcp->monitor_work, KFREE_DRAIN_JIFFIES); schedule_delayed_monitor_work(krcp);
raw_spin_unlock_irqrestore(&krcp->lock, flags); raw_spin_unlock_irqrestore(&krcp->lock, flags);
} }
} }
/* /*
* During early boot, any blocking grace-period wait automatically * During early boot, any blocking grace-period wait automatically
* implies a grace period. Later on, this is never the case for PREEMPTION. * implies a grace period.
* *
* However, because a context switch is a grace period for !PREEMPTION, any * Later on, this could in theory be the case for kernels built with
* blocking grace-period wait automatically implies a grace period if * CONFIG_SMP=y && CONFIG_PREEMPTION=y running on a single CPU, but this
* there is only one CPU online at any point time during execution of * is not a common case. Furthermore, this optimization would cause
* either synchronize_rcu() or synchronize_rcu_expedited(). It is OK to * the rcu_gp_oldstate structure to expand by 50%, so this potential
* occasionally incorrectly indicate that there are multiple CPUs online * grace-period optimization is ignored once the scheduler is running.
* when there was in fact only one the whole time, as this just adds some
* overhead: RCU still operates correctly.
*/ */
static int rcu_blocking_is_gp(void) static int rcu_blocking_is_gp(void)
{ {
int ret; if (rcu_scheduler_active != RCU_SCHEDULER_INACTIVE)
return false;
// Invoking preempt_model_*() too early gets a splat.
if (rcu_scheduler_active == RCU_SCHEDULER_INACTIVE ||
preempt_model_full() || preempt_model_rt())
return rcu_scheduler_active == RCU_SCHEDULER_INACTIVE;
might_sleep(); /* Check for RCU read-side critical section. */ might_sleep(); /* Check for RCU read-side critical section. */
preempt_disable(); return true;
/*
* If the rcu_state.n_online_cpus counter is equal to one,
* there is only one CPU, and that CPU sees all prior accesses
* made by any CPU that was online at the time of its access.
* Furthermore, if this counter is equal to one, its value cannot
* change until after the preempt_enable() below.
*
* Furthermore, if rcu_state.n_online_cpus is equal to one here,
* all later CPUs (both this one and any that come online later
* on) are guaranteed to see all accesses prior to this point
* in the code, without the need for additional memory barriers.
* Those memory barriers are provided by CPU-hotplug code.
*/
ret = READ_ONCE(rcu_state.n_online_cpus) <= 1;
preempt_enable();
return ret;
} }
/** /**
...@@ -3499,29 +3496,58 @@ static int rcu_blocking_is_gp(void) ...@@ -3499,29 +3496,58 @@ static int rcu_blocking_is_gp(void)
*/ */
void synchronize_rcu(void) void synchronize_rcu(void)
{ {
unsigned long flags;
struct rcu_node *rnp;
RCU_LOCKDEP_WARN(lock_is_held(&rcu_bh_lock_map) || RCU_LOCKDEP_WARN(lock_is_held(&rcu_bh_lock_map) ||
lock_is_held(&rcu_lock_map) || lock_is_held(&rcu_lock_map) ||
lock_is_held(&rcu_sched_lock_map), lock_is_held(&rcu_sched_lock_map),
"Illegal synchronize_rcu() in RCU read-side critical section"); "Illegal synchronize_rcu() in RCU read-side critical section");
if (rcu_blocking_is_gp()) { if (!rcu_blocking_is_gp()) {
// Note well that this code runs with !PREEMPT && !SMP. if (rcu_gp_is_expedited())
// In addition, all code that advances grace periods runs at synchronize_rcu_expedited();
// process level. Therefore, this normal GP overlaps with else
// other normal GPs only by being fully nested within them, wait_rcu_gp(call_rcu);
// which allows reuse of ->gp_seq_polled_snap. return;
rcu_poll_gp_seq_start_unlocked(&rcu_state.gp_seq_polled_snap);
rcu_poll_gp_seq_end_unlocked(&rcu_state.gp_seq_polled_snap);
if (rcu_init_invoked())
cond_resched_tasks_rcu_qs();
return; // Context allows vacuous grace periods.
} }
if (rcu_gp_is_expedited())
synchronize_rcu_expedited(); // Context allows vacuous grace periods.
else // Note well that this code runs with !PREEMPT && !SMP.
wait_rcu_gp(call_rcu); // In addition, all code that advances grace periods runs at
// process level. Therefore, this normal GP overlaps with other
// normal GPs only by being fully nested within them, which allows
// reuse of ->gp_seq_polled_snap.
rcu_poll_gp_seq_start_unlocked(&rcu_state.gp_seq_polled_snap);
rcu_poll_gp_seq_end_unlocked(&rcu_state.gp_seq_polled_snap);
// Update the normal grace-period counters to record
// this grace period, but only those used by the boot CPU.
// The rcu_scheduler_starting() will take care of the rest of
// these counters.
local_irq_save(flags);
WARN_ON_ONCE(num_online_cpus() > 1);
rcu_state.gp_seq += (1 << RCU_SEQ_CTR_SHIFT);
for (rnp = this_cpu_ptr(&rcu_data)->mynode; rnp; rnp = rnp->parent)
rnp->gp_seq_needed = rnp->gp_seq = rcu_state.gp_seq;
local_irq_restore(flags);
} }
EXPORT_SYMBOL_GPL(synchronize_rcu); EXPORT_SYMBOL_GPL(synchronize_rcu);
/**
* get_completed_synchronize_rcu_full - Return a full pre-completed polled state cookie
* @rgosp: Place to put state cookie
*
* Stores into @rgosp a value that will always be treated by functions
* like poll_state_synchronize_rcu_full() as a cookie whose grace period
* has already completed.
*/
void get_completed_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)
{
rgosp->rgos_norm = RCU_GET_STATE_COMPLETED;
rgosp->rgos_exp = RCU_GET_STATE_COMPLETED;
}
EXPORT_SYMBOL_GPL(get_completed_synchronize_rcu_full);
/** /**
* get_state_synchronize_rcu - Snapshot current RCU state * get_state_synchronize_rcu - Snapshot current RCU state
* *
...@@ -3541,21 +3567,42 @@ unsigned long get_state_synchronize_rcu(void) ...@@ -3541,21 +3567,42 @@ unsigned long get_state_synchronize_rcu(void)
EXPORT_SYMBOL_GPL(get_state_synchronize_rcu); EXPORT_SYMBOL_GPL(get_state_synchronize_rcu);
/** /**
* start_poll_synchronize_rcu - Snapshot and start RCU grace period * get_state_synchronize_rcu_full - Snapshot RCU state, both normal and expedited
* @rgosp: location to place combined normal/expedited grace-period state
* *
* Returns a cookie that is used by a later call to cond_synchronize_rcu() * Places the normal and expedited grace-period states in @rgosp. This
* or poll_state_synchronize_rcu() to determine whether or not a full * state value can be passed to a later call to cond_synchronize_rcu_full()
* grace period has elapsed in the meantime. If the needed grace period * or poll_state_synchronize_rcu_full() to determine whether or not a
* is not already slated to start, notifies RCU core of the need for that * grace period (whether normal or expedited) has elapsed in the meantime.
* grace period. * The rcu_gp_oldstate structure takes up twice the memory of an unsigned
* long, but is guaranteed to see all grace periods. In contrast, the
* combined state occupies less memory, but can sometimes fail to take
* grace periods into account.
* *
* Interrupts must be enabled for the case where it is necessary to awaken * This does not guarantee that the needed grace period will actually
* the grace-period kthread. * start.
*/ */
unsigned long start_poll_synchronize_rcu(void) void get_state_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)
{
struct rcu_node *rnp = rcu_get_root();
/*
* Any prior manipulation of RCU-protected data must happen
* before the loads from ->gp_seq and ->expedited_sequence.
*/
smp_mb(); /* ^^^ */
rgosp->rgos_norm = rcu_seq_snap(&rnp->gp_seq);
rgosp->rgos_exp = rcu_seq_snap(&rcu_state.expedited_sequence);
}
EXPORT_SYMBOL_GPL(get_state_synchronize_rcu_full);
/*
* Helper function for start_poll_synchronize_rcu() and
* start_poll_synchronize_rcu_full().
*/
static void start_poll_synchronize_rcu_common(void)
{ {
unsigned long flags; unsigned long flags;
unsigned long gp_seq = get_state_synchronize_rcu();
bool needwake; bool needwake;
struct rcu_data *rdp; struct rcu_data *rdp;
struct rcu_node *rnp; struct rcu_node *rnp;
...@@ -3575,17 +3622,57 @@ unsigned long start_poll_synchronize_rcu(void) ...@@ -3575,17 +3622,57 @@ unsigned long start_poll_synchronize_rcu(void)
raw_spin_unlock_irqrestore_rcu_node(rnp, flags); raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
if (needwake) if (needwake)
rcu_gp_kthread_wake(); rcu_gp_kthread_wake();
}
/**
* start_poll_synchronize_rcu - Snapshot and start RCU grace period
*
* Returns a cookie that is used by a later call to cond_synchronize_rcu()
* or poll_state_synchronize_rcu() to determine whether or not a full
* grace period has elapsed in the meantime. If the needed grace period
* is not already slated to start, notifies RCU core of the need for that
* grace period.
*
* Interrupts must be enabled for the case where it is necessary to awaken
* the grace-period kthread.
*/
unsigned long start_poll_synchronize_rcu(void)
{
unsigned long gp_seq = get_state_synchronize_rcu();
start_poll_synchronize_rcu_common();
return gp_seq; return gp_seq;
} }
EXPORT_SYMBOL_GPL(start_poll_synchronize_rcu); EXPORT_SYMBOL_GPL(start_poll_synchronize_rcu);
/** /**
* poll_state_synchronize_rcu - Conditionally wait for an RCU grace period * start_poll_synchronize_rcu_full - Take a full snapshot and start RCU grace period
* @rgosp: value from get_state_synchronize_rcu_full() or start_poll_synchronize_rcu_full()
* *
* Places the normal and expedited grace-period states in *@rgos. This
* state value can be passed to a later call to cond_synchronize_rcu_full()
* or poll_state_synchronize_rcu_full() to determine whether or not a
* grace period (whether normal or expedited) has elapsed in the meantime.
* If the needed grace period is not already slated to start, notifies
* RCU core of the need for that grace period.
*
* Interrupts must be enabled for the case where it is necessary to awaken
* the grace-period kthread.
*/
void start_poll_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)
{
get_state_synchronize_rcu_full(rgosp);
start_poll_synchronize_rcu_common();
}
EXPORT_SYMBOL_GPL(start_poll_synchronize_rcu_full);
/**
* poll_state_synchronize_rcu - Has the specified RCU grace period completed?
* @oldstate: value from get_state_synchronize_rcu() or start_poll_synchronize_rcu() * @oldstate: value from get_state_synchronize_rcu() or start_poll_synchronize_rcu()
* *
* If a full RCU grace period has elapsed since the earlier call from * If a full RCU grace period has elapsed since the earlier call from
* which oldstate was obtained, return @true, otherwise return @false. * which @oldstate was obtained, return @true, otherwise return @false.
* If @false is returned, it is the caller's responsibility to invoke this * If @false is returned, it is the caller's responsibility to invoke this
* function later on until it does return @true. Alternatively, the caller * function later on until it does return @true. Alternatively, the caller
* can explicitly wait for a grace period, for example, by passing @oldstate * can explicitly wait for a grace period, for example, by passing @oldstate
...@@ -3594,10 +3681,11 @@ EXPORT_SYMBOL_GPL(start_poll_synchronize_rcu); ...@@ -3594,10 +3681,11 @@ EXPORT_SYMBOL_GPL(start_poll_synchronize_rcu);
* Yes, this function does not take counter wrap into account. * Yes, this function does not take counter wrap into account.
* But counter wrap is harmless. If the counter wraps, we have waited for * But counter wrap is harmless. If the counter wraps, we have waited for
* more than a billion grace periods (and way more on a 64-bit system!). * more than a billion grace periods (and way more on a 64-bit system!).
* Those needing to keep oldstate values for very long time periods * Those needing to keep old state values for very long time periods
* (many hours even on 32-bit systems) should check them occasionally * (many hours even on 32-bit systems) should check them occasionally and
* and either refresh them or set a flag indicating that the grace period * either refresh them or set a flag indicating that the grace period has
* has completed. * completed. Alternatively, they can use get_completed_synchronize_rcu()
* to get a guaranteed-completed grace-period state.
* *
* This function provides the same memory-ordering guarantees that * This function provides the same memory-ordering guarantees that
* would be provided by a synchronize_rcu() that was invoked at the call * would be provided by a synchronize_rcu() that was invoked at the call
...@@ -3616,8 +3704,56 @@ bool poll_state_synchronize_rcu(unsigned long oldstate) ...@@ -3616,8 +3704,56 @@ bool poll_state_synchronize_rcu(unsigned long oldstate)
EXPORT_SYMBOL_GPL(poll_state_synchronize_rcu); EXPORT_SYMBOL_GPL(poll_state_synchronize_rcu);
/** /**
* cond_synchronize_rcu - Conditionally wait for an RCU grace period * poll_state_synchronize_rcu_full - Has the specified RCU grace period completed?
* @rgosp: value from get_state_synchronize_rcu_full() or start_poll_synchronize_rcu_full()
* *
* If a full RCU grace period has elapsed since the earlier call from
* which *rgosp was obtained, return @true, otherwise return @false.
* If @false is returned, it is the caller's responsibility to invoke this
* function later on until it does return @true. Alternatively, the caller
* can explicitly wait for a grace period, for example, by passing @rgosp
* to cond_synchronize_rcu() or by directly invoking synchronize_rcu().
*
* Yes, this function does not take counter wrap into account.
* But counter wrap is harmless. If the counter wraps, we have waited
* for more than a billion grace periods (and way more on a 64-bit
* system!). Those needing to keep rcu_gp_oldstate values for very
* long time periods (many hours even on 32-bit systems) should check
* them occasionally and either refresh them or set a flag indicating
* that the grace period has completed. Alternatively, they can use
* get_completed_synchronize_rcu_full() to get a guaranteed-completed
* grace-period state.
*
* This function provides the same memory-ordering guarantees that would
* be provided by a synchronize_rcu() that was invoked at the call to
* the function that provided @rgosp, and that returned at the end of this
* function. And this guarantee requires that the root rcu_node structure's
* ->gp_seq field be checked instead of that of the rcu_state structure.
* The problem is that the just-ending grace-period's callbacks can be
* invoked between the time that the root rcu_node structure's ->gp_seq
* field is updated and the time that the rcu_state structure's ->gp_seq
* field is updated. Therefore, if a single synchronize_rcu() is to
* cause a subsequent poll_state_synchronize_rcu_full() to return @true,
* then the root rcu_node structure is the one that needs to be polled.
*/
bool poll_state_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)
{
struct rcu_node *rnp = rcu_get_root();
smp_mb(); // Order against root rcu_node structure grace-period cleanup.
if (rgosp->rgos_norm == RCU_GET_STATE_COMPLETED ||
rcu_seq_done_exact(&rnp->gp_seq, rgosp->rgos_norm) ||
rgosp->rgos_exp == RCU_GET_STATE_COMPLETED ||
rcu_seq_done_exact(&rcu_state.expedited_sequence, rgosp->rgos_exp)) {
smp_mb(); /* Ensure GP ends before subsequent accesses. */
return true;
}
return false;
}
EXPORT_SYMBOL_GPL(poll_state_synchronize_rcu_full);
/**
* cond_synchronize_rcu - Conditionally wait for an RCU grace period
* @oldstate: value from get_state_synchronize_rcu(), start_poll_synchronize_rcu(), or start_poll_synchronize_rcu_expedited() * @oldstate: value from get_state_synchronize_rcu(), start_poll_synchronize_rcu(), or start_poll_synchronize_rcu_expedited()
* *
* If a full RCU grace period has elapsed since the earlier call to * If a full RCU grace period has elapsed since the earlier call to
...@@ -3641,6 +3777,33 @@ void cond_synchronize_rcu(unsigned long oldstate) ...@@ -3641,6 +3777,33 @@ void cond_synchronize_rcu(unsigned long oldstate)
} }
EXPORT_SYMBOL_GPL(cond_synchronize_rcu); EXPORT_SYMBOL_GPL(cond_synchronize_rcu);
/**
* cond_synchronize_rcu_full - Conditionally wait for an RCU grace period
* @rgosp: value from get_state_synchronize_rcu_full(), start_poll_synchronize_rcu_full(), or start_poll_synchronize_rcu_expedited_full()
*
* If a full RCU grace period has elapsed since the call to
* get_state_synchronize_rcu_full(), start_poll_synchronize_rcu_full(),
* or start_poll_synchronize_rcu_expedited_full() from which @rgosp was
* obtained, just return. Otherwise, invoke synchronize_rcu() to wait
* for a full grace period.
*
* Yes, this function does not take counter wrap into account.
* But counter wrap is harmless. If the counter wraps, we have waited for
* more than 2 billion grace periods (and way more on a 64-bit system!),
* so waiting for a couple of additional grace periods should be just fine.
*
* This function provides the same memory-ordering guarantees that
* would be provided by a synchronize_rcu() that was invoked at the call
* to the function that provided @rgosp and that returned at the end of
* this function.
*/
void cond_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)
{
if (!poll_state_synchronize_rcu_full(rgosp))
synchronize_rcu();
}
EXPORT_SYMBOL_GPL(cond_synchronize_rcu_full);
/* /*
* Check to see if there is any immediate RCU-related work to be done by * Check to see if there is any immediate RCU-related work to be done by
* the current CPU, returning 1 if so and zero otherwise. The checks are * the current CPU, returning 1 if so and zero otherwise. The checks are
...@@ -4312,9 +4475,20 @@ early_initcall(rcu_spawn_gp_kthread); ...@@ -4312,9 +4475,20 @@ early_initcall(rcu_spawn_gp_kthread);
*/ */
void rcu_scheduler_starting(void) void rcu_scheduler_starting(void)
{ {
unsigned long flags;
struct rcu_node *rnp;
WARN_ON(num_online_cpus() != 1); WARN_ON(num_online_cpus() != 1);
WARN_ON(nr_context_switches() > 0); WARN_ON(nr_context_switches() > 0);
rcu_test_sync_prims(); rcu_test_sync_prims();
// Fix up the ->gp_seq counters.
local_irq_save(flags);
rcu_for_each_node_breadth_first(rnp)
rnp->gp_seq_needed = rnp->gp_seq = rcu_state.gp_seq;
local_irq_restore(flags);
// Switch out of early boot mode.
rcu_scheduler_active = RCU_SCHEDULER_INIT; rcu_scheduler_active = RCU_SCHEDULER_INIT;
rcu_test_sync_prims(); rcu_test_sync_prims();
} }
......
...@@ -828,11 +828,13 @@ static void rcu_exp_handler(void *unused) ...@@ -828,11 +828,13 @@ static void rcu_exp_handler(void *unused)
{ {
struct rcu_data *rdp = this_cpu_ptr(&rcu_data); struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
struct rcu_node *rnp = rdp->mynode; struct rcu_node *rnp = rdp->mynode;
bool preempt_bh_enabled = !(preempt_count() & (PREEMPT_MASK | SOFTIRQ_MASK));
if (!(READ_ONCE(rnp->expmask) & rdp->grpmask) || if (!(READ_ONCE(rnp->expmask) & rdp->grpmask) ||
__this_cpu_read(rcu_data.cpu_no_qs.b.exp)) __this_cpu_read(rcu_data.cpu_no_qs.b.exp))
return; return;
if (rcu_is_cpu_rrupt_from_idle()) { if (rcu_is_cpu_rrupt_from_idle() ||
(IS_ENABLED(CONFIG_PREEMPT_COUNT) && preempt_bh_enabled)) {
rcu_report_exp_rdp(this_cpu_ptr(&rcu_data)); rcu_report_exp_rdp(this_cpu_ptr(&rcu_data));
return; return;
} }
...@@ -906,6 +908,7 @@ static int rcu_print_task_exp_stall(struct rcu_node *rnp) ...@@ -906,6 +908,7 @@ static int rcu_print_task_exp_stall(struct rcu_node *rnp)
void synchronize_rcu_expedited(void) void synchronize_rcu_expedited(void)
{ {
bool boottime = (rcu_scheduler_active == RCU_SCHEDULER_INIT); bool boottime = (rcu_scheduler_active == RCU_SCHEDULER_INIT);
unsigned long flags;
struct rcu_exp_work rew; struct rcu_exp_work rew;
struct rcu_node *rnp; struct rcu_node *rnp;
unsigned long s; unsigned long s;
...@@ -924,8 +927,11 @@ void synchronize_rcu_expedited(void) ...@@ -924,8 +927,11 @@ void synchronize_rcu_expedited(void)
// them, which allows reuse of ->gp_seq_polled_exp_snap. // them, which allows reuse of ->gp_seq_polled_exp_snap.
rcu_poll_gp_seq_start_unlocked(&rcu_state.gp_seq_polled_exp_snap); rcu_poll_gp_seq_start_unlocked(&rcu_state.gp_seq_polled_exp_snap);
rcu_poll_gp_seq_end_unlocked(&rcu_state.gp_seq_polled_exp_snap); rcu_poll_gp_seq_end_unlocked(&rcu_state.gp_seq_polled_exp_snap);
if (rcu_init_invoked())
cond_resched(); local_irq_save(flags);
WARN_ON_ONCE(num_online_cpus() > 1);
rcu_state.expedited_sequence += (1 << RCU_SEQ_CTR_SHIFT);
local_irq_restore(flags);
return; // Context allows vacuous grace periods. return; // Context allows vacuous grace periods.
} }
...@@ -1027,6 +1033,24 @@ unsigned long start_poll_synchronize_rcu_expedited(void) ...@@ -1027,6 +1033,24 @@ unsigned long start_poll_synchronize_rcu_expedited(void)
} }
EXPORT_SYMBOL_GPL(start_poll_synchronize_rcu_expedited); EXPORT_SYMBOL_GPL(start_poll_synchronize_rcu_expedited);
/**
* start_poll_synchronize_rcu_expedited_full - Take a full snapshot and start expedited grace period
* @rgosp: Place to put snapshot of grace-period state
*
* Places the normal and expedited grace-period states in rgosp. This
* state value can be passed to a later call to cond_synchronize_rcu_full()
* or poll_state_synchronize_rcu_full() to determine whether or not a
* grace period (whether normal or expedited) has elapsed in the meantime.
* If the needed expedited grace period is not already slated to start,
* initiates that grace period.
*/
void start_poll_synchronize_rcu_expedited_full(struct rcu_gp_oldstate *rgosp)
{
get_state_synchronize_rcu_full(rgosp);
(void)start_poll_synchronize_rcu_expedited();
}
EXPORT_SYMBOL_GPL(start_poll_synchronize_rcu_expedited_full);
/** /**
* cond_synchronize_rcu_expedited - Conditionally wait for an expedited RCU grace period * cond_synchronize_rcu_expedited - Conditionally wait for an expedited RCU grace period
* *
...@@ -1053,3 +1077,30 @@ void cond_synchronize_rcu_expedited(unsigned long oldstate) ...@@ -1053,3 +1077,30 @@ void cond_synchronize_rcu_expedited(unsigned long oldstate)
synchronize_rcu_expedited(); synchronize_rcu_expedited();
} }
EXPORT_SYMBOL_GPL(cond_synchronize_rcu_expedited); EXPORT_SYMBOL_GPL(cond_synchronize_rcu_expedited);
/**
* cond_synchronize_rcu_expedited_full - Conditionally wait for an expedited RCU grace period
* @rgosp: value from get_state_synchronize_rcu_full(), start_poll_synchronize_rcu_full(), or start_poll_synchronize_rcu_expedited_full()
*
* If a full RCU grace period has elapsed since the call to
* get_state_synchronize_rcu_full(), start_poll_synchronize_rcu_full(),
* or start_poll_synchronize_rcu_expedited_full() from which @rgosp was
* obtained, just return. Otherwise, invoke synchronize_rcu_expedited()
* to wait for a full grace period.
*
* Yes, this function does not take counter wrap into account.
* But counter wrap is harmless. If the counter wraps, we have waited for
* more than 2 billion grace periods (and way more on a 64-bit system!),
* so waiting for a couple of additional grace periods should be just fine.
*
* This function provides the same memory-ordering guarantees that
* would be provided by a synchronize_rcu() that was invoked at the call
* to the function that provided @rgosp and that returned at the end of
* this function.
*/
void cond_synchronize_rcu_expedited_full(struct rcu_gp_oldstate *rgosp)
{
if (!poll_state_synchronize_rcu_full(rgosp))
synchronize_rcu_expedited();
}
EXPORT_SYMBOL_GPL(cond_synchronize_rcu_expedited_full);
...@@ -1111,7 +1111,7 @@ int rcu_nocb_cpu_deoffload(int cpu) ...@@ -1111,7 +1111,7 @@ int rcu_nocb_cpu_deoffload(int cpu)
if (!ret) if (!ret)
cpumask_clear_cpu(cpu, rcu_nocb_mask); cpumask_clear_cpu(cpu, rcu_nocb_mask);
} else { } else {
pr_info("NOCB: Can't CB-deoffload an offline CPU\n"); pr_info("NOCB: Cannot CB-deoffload offline CPU %d\n", rdp->cpu);
ret = -EINVAL; ret = -EINVAL;
} }
} }
...@@ -1196,7 +1196,7 @@ int rcu_nocb_cpu_offload(int cpu) ...@@ -1196,7 +1196,7 @@ int rcu_nocb_cpu_offload(int cpu)
if (!ret) if (!ret)
cpumask_set_cpu(cpu, rcu_nocb_mask); cpumask_set_cpu(cpu, rcu_nocb_mask);
} else { } else {
pr_info("NOCB: Can't CB-offload an offline CPU\n"); pr_info("NOCB: Cannot CB-offload offline CPU %d\n", rdp->cpu);
ret = -EINVAL; ret = -EINVAL;
} }
} }
...@@ -1452,8 +1452,8 @@ static void show_rcu_nocb_gp_state(struct rcu_data *rdp) ...@@ -1452,8 +1452,8 @@ static void show_rcu_nocb_gp_state(struct rcu_data *rdp)
(long)rdp->nocb_gp_seq, (long)rdp->nocb_gp_seq,
rnp->grplo, rnp->grphi, READ_ONCE(rdp->nocb_gp_loops), rnp->grplo, rnp->grphi, READ_ONCE(rdp->nocb_gp_loops),
rdp->nocb_gp_kthread ? task_state_to_char(rdp->nocb_gp_kthread) : '.', rdp->nocb_gp_kthread ? task_state_to_char(rdp->nocb_gp_kthread) : '.',
rdp->nocb_cb_kthread ? (int)task_cpu(rdp->nocb_gp_kthread) : -1, rdp->nocb_gp_kthread ? (int)task_cpu(rdp->nocb_gp_kthread) : -1,
show_rcu_should_be_on_cpu(rdp->nocb_cb_kthread)); show_rcu_should_be_on_cpu(rdp->nocb_gp_kthread));
} }
/* Dump out nocb kthread state for the specified rcu_data structure. */ /* Dump out nocb kthread state for the specified rcu_data structure. */
...@@ -1497,7 +1497,7 @@ static void show_rcu_nocb_state(struct rcu_data *rdp) ...@@ -1497,7 +1497,7 @@ static void show_rcu_nocb_state(struct rcu_data *rdp)
".B"[!!rcu_cblist_n_cbs(&rdp->nocb_bypass)], ".B"[!!rcu_cblist_n_cbs(&rdp->nocb_bypass)],
rcu_segcblist_n_cbs(&rdp->cblist), rcu_segcblist_n_cbs(&rdp->cblist),
rdp->nocb_cb_kthread ? task_state_to_char(rdp->nocb_cb_kthread) : '.', rdp->nocb_cb_kthread ? task_state_to_char(rdp->nocb_cb_kthread) : '.',
rdp->nocb_cb_kthread ? (int)task_cpu(rdp->nocb_gp_kthread) : -1, rdp->nocb_cb_kthread ? (int)task_cpu(rdp->nocb_cb_kthread) : -1,
show_rcu_should_be_on_cpu(rdp->nocb_cb_kthread)); show_rcu_should_be_on_cpu(rdp->nocb_cb_kthread));
/* It is OK for GP kthreads to have GP state. */ /* It is OK for GP kthreads to have GP state. */
......
...@@ -641,7 +641,8 @@ static void rcu_read_unlock_special(struct task_struct *t) ...@@ -641,7 +641,8 @@ static void rcu_read_unlock_special(struct task_struct *t)
expboost = (t->rcu_blocked_node && READ_ONCE(t->rcu_blocked_node->exp_tasks)) || expboost = (t->rcu_blocked_node && READ_ONCE(t->rcu_blocked_node->exp_tasks)) ||
(rdp->grpmask & READ_ONCE(rnp->expmask)) || (rdp->grpmask & READ_ONCE(rnp->expmask)) ||
IS_ENABLED(CONFIG_RCU_STRICT_GRACE_PERIOD) || (IS_ENABLED(CONFIG_RCU_STRICT_GRACE_PERIOD) &&
((rdp->grpmask & READ_ONCE(rnp->qsmask)) || t->rcu_blocked_node)) ||
(IS_ENABLED(CONFIG_RCU_BOOST) && irqs_were_disabled && (IS_ENABLED(CONFIG_RCU_BOOST) && irqs_were_disabled &&
t->rcu_blocked_node); t->rcu_blocked_node);
// Need to defer quiescent state until everything is enabled. // Need to defer quiescent state until everything is enabled.
...@@ -718,9 +719,6 @@ static void rcu_flavor_sched_clock_irq(int user) ...@@ -718,9 +719,6 @@ static void rcu_flavor_sched_clock_irq(int user)
struct task_struct *t = current; struct task_struct *t = current;
lockdep_assert_irqs_disabled(); lockdep_assert_irqs_disabled();
if (user || rcu_is_cpu_rrupt_from_idle()) {
rcu_note_voluntary_context_switch(current);
}
if (rcu_preempt_depth() > 0 || if (rcu_preempt_depth() > 0 ||
(preempt_count() & (PREEMPT_MASK | SOFTIRQ_MASK))) { (preempt_count() & (PREEMPT_MASK | SOFTIRQ_MASK))) {
/* No QS, force context switch if deferred. */ /* No QS, force context switch if deferred. */
...@@ -824,6 +822,7 @@ void rcu_read_unlock_strict(void) ...@@ -824,6 +822,7 @@ void rcu_read_unlock_strict(void)
if (irqs_disabled() || preempt_count() || !rcu_state.gp_kthread) if (irqs_disabled() || preempt_count() || !rcu_state.gp_kthread)
return; return;
rdp = this_cpu_ptr(&rcu_data); rdp = this_cpu_ptr(&rcu_data);
rdp->cpu_no_qs.b.norm = false;
rcu_report_qs_rdp(rdp); rcu_report_qs_rdp(rdp);
udelay(rcu_unlock_delay); udelay(rcu_unlock_delay);
} }
...@@ -869,7 +868,7 @@ void rcu_all_qs(void) ...@@ -869,7 +868,7 @@ void rcu_all_qs(void)
if (!raw_cpu_read(rcu_data.rcu_urgent_qs)) if (!raw_cpu_read(rcu_data.rcu_urgent_qs))
return; return;
preempt_disable(); preempt_disable(); // For CONFIG_PREEMPT_COUNT=y kernels
/* Load rcu_urgent_qs before other flags. */ /* Load rcu_urgent_qs before other flags. */
if (!smp_load_acquire(this_cpu_ptr(&rcu_data.rcu_urgent_qs))) { if (!smp_load_acquire(this_cpu_ptr(&rcu_data.rcu_urgent_qs))) {
preempt_enable(); preempt_enable();
...@@ -931,10 +930,13 @@ static notrace bool rcu_preempt_need_deferred_qs(struct task_struct *t) ...@@ -931,10 +930,13 @@ static notrace bool rcu_preempt_need_deferred_qs(struct task_struct *t)
return false; return false;
} }
// Except that we do need to respond to a request by an expedited grace // Except that we do need to respond to a request by an expedited
// period for a quiescent state from this CPU. Note that requests from // grace period for a quiescent state from this CPU. Note that in
// tasks are handled when removing the task from the blocked-tasks list // non-preemptible kernels, there can be no context switches within RCU
// below. // read-side critical sections, which in turn means that the leaf rcu_node
// structure's blocked-tasks list is always empty. is therefore no need to
// actually check it. Instead, a quiescent state from this CPU suffices,
// and this function is only called from such a quiescent state.
notrace void rcu_preempt_deferred_qs(struct task_struct *t) notrace void rcu_preempt_deferred_qs(struct task_struct *t)
{ {
struct rcu_data *rdp = this_cpu_ptr(&rcu_data); struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
...@@ -972,7 +974,6 @@ static void rcu_flavor_sched_clock_irq(int user) ...@@ -972,7 +974,6 @@ static void rcu_flavor_sched_clock_irq(int user)
* neither access nor modify, at least not while the * neither access nor modify, at least not while the
* corresponding CPU is online. * corresponding CPU is online.
*/ */
rcu_qs(); rcu_qs();
} }
} }
...@@ -1238,8 +1239,11 @@ static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu) ...@@ -1238,8 +1239,11 @@ static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu)
cpu != outgoingcpu) cpu != outgoingcpu)
cpumask_set_cpu(cpu, cm); cpumask_set_cpu(cpu, cm);
cpumask_and(cm, cm, housekeeping_cpumask(HK_TYPE_RCU)); cpumask_and(cm, cm, housekeeping_cpumask(HK_TYPE_RCU));
if (cpumask_empty(cm)) if (cpumask_empty(cm)) {
cpumask_copy(cm, housekeeping_cpumask(HK_TYPE_RCU)); cpumask_copy(cm, housekeeping_cpumask(HK_TYPE_RCU));
if (outgoingcpu >= 0)
cpumask_clear_cpu(outgoingcpu, cm);
}
set_cpus_allowed_ptr(t, cm); set_cpus_allowed_ptr(t, cm);
mutex_unlock(&rnp->boost_kthread_mutex); mutex_unlock(&rnp->boost_kthread_mutex);
free_cpumask_var(cm); free_cpumask_var(cm);
......
...@@ -368,7 +368,7 @@ static void rcu_dump_cpu_stacks(void) ...@@ -368,7 +368,7 @@ static void rcu_dump_cpu_stacks(void)
if (rnp->qsmask & leaf_node_cpu_bit(rnp, cpu)) { if (rnp->qsmask & leaf_node_cpu_bit(rnp, cpu)) {
if (cpu_is_offline(cpu)) if (cpu_is_offline(cpu))
pr_err("Offline CPU %d blocking current GP.\n", cpu); pr_err("Offline CPU %d blocking current GP.\n", cpu);
else if (!trigger_single_cpu_backtrace(cpu)) else
dump_cpu_task(cpu); dump_cpu_task(cpu);
} }
raw_spin_unlock_irqrestore_rcu_node(rnp, flags); raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
...@@ -511,8 +511,7 @@ static void rcu_check_gp_kthread_starvation(void) ...@@ -511,8 +511,7 @@ static void rcu_check_gp_kthread_starvation(void)
pr_err("RCU GP kthread last ran on offline CPU %d.\n", cpu); pr_err("RCU GP kthread last ran on offline CPU %d.\n", cpu);
} else { } else {
pr_err("Stack dump where RCU GP kthread last ran:\n"); pr_err("Stack dump where RCU GP kthread last ran:\n");
if (!trigger_single_cpu_backtrace(cpu)) dump_cpu_task(cpu);
dump_cpu_task(cpu);
} }
} }
wake_up_process(gpk); wake_up_process(gpk);
......
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
#include <uapi/linux/sched/types.h> #include <uapi/linux/sched/types.h>
#include <asm/irq_regs.h>
#include <asm/switch_to.h> #include <asm/switch_to.h>
#include <asm/tlb.h> #include <asm/tlb.h>
...@@ -11183,6 +11184,19 @@ struct cgroup_subsys cpu_cgrp_subsys = { ...@@ -11183,6 +11184,19 @@ struct cgroup_subsys cpu_cgrp_subsys = {
void dump_cpu_task(int cpu) void dump_cpu_task(int cpu)
{ {
if (cpu == smp_processor_id() && in_hardirq()) {
struct pt_regs *regs;
regs = get_irq_regs();
if (regs) {
show_regs(regs);
return;
}
}
if (trigger_single_cpu_backtrace(cpu))
return;
pr_info("Task dump for CPU %d:\n", cpu); pr_info("Task dump for CPU %d:\n", cpu);
sched_show_task(cpu_curr(cpu)); sched_show_task(cpu_curr(cpu));
} }
......
...@@ -370,8 +370,7 @@ static bool csd_lock_wait_toolong(struct __call_single_data *csd, u64 ts0, u64 * ...@@ -370,8 +370,7 @@ static bool csd_lock_wait_toolong(struct __call_single_data *csd, u64 ts0, u64 *
if (cpu >= 0) { if (cpu >= 0) {
if (static_branch_unlikely(&csdlock_debug_extended)) if (static_branch_unlikely(&csdlock_debug_extended))
csd_lock_print_extended(csd, cpu); csd_lock_print_extended(csd, cpu);
if (!trigger_single_cpu_backtrace(cpu)) dump_cpu_task(cpu);
dump_cpu_task(cpu);
if (!cpu_cur_csd) { if (!cpu_cur_csd) {
pr_alert("csd: Re-sending CSD lock (#%d) IPI from CPU#%02d to CPU#%02d\n", *bug_id, raw_smp_processor_id(), cpu); pr_alert("csd: Re-sending CSD lock (#%d) IPI from CPU#%02d to CPU#%02d\n", *bug_id, raw_smp_processor_id(), cpu);
arch_send_call_function_single_ipi(cpu); arch_send_call_function_single_ipi(cpu);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment