mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge branch 'next' of https://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux
This commit is contained in:
@@ -410,7 +410,7 @@ workqueues (see Documentation/core-api/workqueue.rst).
|
||||
|
||||
The requesting task still does counter snapshotting and funnel-lock
|
||||
processing, but the task reaching the top of the funnel lock does a
|
||||
``schedule_work()`` (from ``_synchronize_rcu_expedited()`` so that a
|
||||
``schedule_work()`` (from ``_synchronize_rcu_expedited()``) so that a
|
||||
workqueue kthread does the actual grace-period processing. Because
|
||||
workqueue kthreads do not accept POSIX signals, grace-period-wait
|
||||
processing need not allow for POSIX signals. In addition, this approach
|
||||
|
||||
@@ -3933,7 +3933,7 @@
|
||||
font-style="normal"
|
||||
y="-3914.085"
|
||||
x="3745.7725"
|
||||
xml:space="preserve">rcu__report_qs_rdp())</text>
|
||||
xml:space="preserve">rcu__report_qs_rdp()</text>
|
||||
</g>
|
||||
<g
|
||||
id="g4504-3"
|
||||
|
||||
|
Before Width: | Height: | Size: 208 KiB After Width: | Height: | Size: 208 KiB |
@@ -815,7 +815,7 @@
|
||||
font-style="normal"
|
||||
y="-3914.085"
|
||||
x="3745.7725"
|
||||
xml:space="preserve">rcu__report_qs_rdp())</text>
|
||||
xml:space="preserve">rcu__report_qs_rdp()</text>
|
||||
</g>
|
||||
<g
|
||||
id="g4504-3"
|
||||
|
||||
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
@@ -2785,7 +2785,7 @@ both srcu_read_lock() and srcu_read_unlock(). This need is handled by
|
||||
a Tasks Trace RCU API implemented as thin wrappers around SRCU-fast,
|
||||
which avoids the read-side memory barriers, at least for architectures
|
||||
that apply noinstr to kernel entry/exit code (or that build with
|
||||
``CONFIG_TASKS_TRACE_RCU_NO_MB=y``.
|
||||
``CONFIG_TASKS_TRACE_RCU_NO_MB=y``).
|
||||
|
||||
Now that the implementation is based on SRCU-fast, a call
|
||||
to synchronize_rcu_tasks_trace() implies at least one call to
|
||||
|
||||
@@ -236,7 +236,7 @@ precautions. To see this, consider the following code fragment::
|
||||
{
|
||||
struct foo *p;
|
||||
|
||||
p = kmalloc(...);
|
||||
p = kmalloc_obj(*p);
|
||||
if (p == NULL)
|
||||
deal_with_it();
|
||||
p->a = 42; /* Each field in its own cache line. */
|
||||
@@ -293,7 +293,7 @@ Then one approach is to use locking, for example, as follows::
|
||||
{
|
||||
struct foo *p;
|
||||
|
||||
p = kmalloc(...);
|
||||
p = kmalloc_obj(*p);
|
||||
if (p == NULL)
|
||||
deal_with_it();
|
||||
spin_lock(&p->lock);
|
||||
|
||||
@@ -6140,6 +6140,11 @@ Kernel parameters
|
||||
the number of CPUs. For example, -2 selects N
|
||||
(the number of CPUs), -3 selects N+1, and so on.
|
||||
|
||||
rcutorture.nwriters= [KNL]
|
||||
Set number of RCU writers, which must be either
|
||||
zero or one. For additional writers, use instead
|
||||
the rcutorture.nfakewriters parameter.
|
||||
|
||||
rcutorture.object_debug= [KNL]
|
||||
Enable debug-object double-call_rcu() testing.
|
||||
|
||||
@@ -6229,6 +6234,15 @@ Kernel parameters
|
||||
and stall_gp_kthread are specified, the
|
||||
kthread is starved first, then the CPU.
|
||||
|
||||
rcutorture.stall_only= [KNL]
|
||||
Shut off all rcutorture kthreads other than the
|
||||
RCU CPU stall-warning test kthreads. The purpose
|
||||
of this is to test production applictions'
|
||||
reactions to CPU stalls, and with minimal
|
||||
additional overhead. Or you can omit the
|
||||
stall-warning tests as well and get a heavy
|
||||
no-op, your choice!
|
||||
|
||||
rcutorture.stat_interval= [KNL]
|
||||
Time (s) between statistics printk()s.
|
||||
|
||||
|
||||
@@ -25046,6 +25046,7 @@ SLEEPABLE READ-COPY UPDATE (SRCU)
|
||||
M: Lai Jiangshan <jiangshanlai@gmail.com>
|
||||
M: "Paul E. McKenney" <paulmck@kernel.org>
|
||||
M: Josh Triplett <josh@joshtriplett.org>
|
||||
M: Onur Özkan <work@onurozkan.dev> (RUST)
|
||||
R: Steven Rostedt <rostedt@goodmis.org>
|
||||
R: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
|
||||
L: rcu@vger.kernel.org
|
||||
@@ -25054,6 +25055,8 @@ W: http://www.rdrop.com/users/paulmck/RCU/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux.git rcu/dev
|
||||
F: include/linux/srcu*.h
|
||||
F: kernel/rcu/srcu*.c
|
||||
F: rust/helpers/srcu.c
|
||||
F: rust/kernel/sync/srcu.rs
|
||||
|
||||
SMACK SECURITY MODULE
|
||||
M: Casey Schaufler <casey@schaufler-ca.com>
|
||||
|
||||
@@ -50,12 +50,14 @@ struct rcu_cblist {
|
||||
* Note that RCU_WAIT_TAIL cannot be empty unless RCU_NEXT_READY_TAIL is also
|
||||
* empty.
|
||||
*
|
||||
* The ->gp_seq[] array contains the grace-period number at which the
|
||||
* corresponding segment of callbacks will be ready to invoke. A given
|
||||
* element of this array is meaningful only when the corresponding segment
|
||||
* is non-empty, and it is never valid for RCU_DONE_TAIL (whose callbacks
|
||||
* are already ready to invoke) or for RCU_NEXT_TAIL (whose callbacks have
|
||||
* not yet been assigned a grace-period number).
|
||||
* The ->gp_seq[] array contains the grace-period state at which the
|
||||
* corresponding segment of callbacks will be ready to invoke. This tracks
|
||||
* both normal and expedited grace periods, allowing callbacks to complete
|
||||
* when either type of GP finishes. A given element of this array is
|
||||
* meaningful only when the corresponding segment is non-empty, and it is
|
||||
* never valid for RCU_DONE_TAIL (whose callbacks are already ready to
|
||||
* invoke) or for RCU_NEXT_TAIL (whose callbacks have not yet been assigned
|
||||
* a grace-period state).
|
||||
*/
|
||||
#define RCU_DONE_TAIL 0 /* Also RCU_WAIT head. */
|
||||
#define RCU_WAIT_TAIL 1 /* Also RCU_NEXT_READY head. */
|
||||
@@ -190,7 +192,7 @@ struct rcu_cblist {
|
||||
struct rcu_segcblist {
|
||||
struct rcu_head *head;
|
||||
struct rcu_head **tails[RCU_CBLIST_NSEGS];
|
||||
unsigned long gp_seq[RCU_CBLIST_NSEGS];
|
||||
struct rcu_gp_seq gp_seq[RCU_CBLIST_NSEGS];
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
atomic_long_t len;
|
||||
#else
|
||||
|
||||
@@ -52,9 +52,18 @@ void call_rcu(struct rcu_head *head, rcu_callback_t func);
|
||||
void rcu_barrier_tasks(void);
|
||||
void synchronize_rcu(void);
|
||||
|
||||
struct rcu_gp_oldstate;
|
||||
/*
|
||||
* Grace-period sequence snapshot for the polled RCU APIs: ->norm for the
|
||||
* normal grace period and ->exp for the expedited one. ->exp is unused by
|
||||
* Tiny RCU, but is present unconditionally so that a single definition
|
||||
* serves both Tiny RCU and Tree RCU.
|
||||
*/
|
||||
struct rcu_gp_seq {
|
||||
unsigned long norm;
|
||||
unsigned long exp;
|
||||
};
|
||||
unsigned long get_completed_synchronize_rcu(void);
|
||||
void get_completed_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp);
|
||||
void get_completed_synchronize_rcu_full(struct rcu_gp_seq *gsp);
|
||||
|
||||
// Maximum number of unsigned long values corresponding to
|
||||
// not-yet-completed RCU grace periods.
|
||||
@@ -490,12 +499,12 @@ context_unsafe( \
|
||||
*/
|
||||
#define unrcu_pointer(p) __unrcu_pointer(p, __UNIQUE_ID(rcu))
|
||||
|
||||
#define __rcu_access_pointer(p, local, space) \
|
||||
#define __rcu_access_pointer(p, local, space) context_unsafe( \
|
||||
({ \
|
||||
typeof(*p) *local = (typeof(*p) *__force)READ_ONCE(p); \
|
||||
rcu_check_sparse(p, space); \
|
||||
((typeof(*p) __force __kernel *)(local)); \
|
||||
})
|
||||
}) )
|
||||
#define __rcu_dereference_check(p, local, c, space) \
|
||||
({ \
|
||||
/* Dependency order vs. p above. */ \
|
||||
|
||||
@@ -95,10 +95,13 @@ static inline void rcu_read_unlock_tasks_trace(struct srcu_ctr __percpu *scp)
|
||||
*/
|
||||
static inline void rcu_read_lock_trace(void)
|
||||
{
|
||||
int n;
|
||||
struct task_struct *t = current;
|
||||
|
||||
rcu_try_lock_acquire(&rcu_tasks_trace_srcu_struct.dep_map);
|
||||
if (t->trc_reader_nesting++) {
|
||||
n = READ_ONCE(t->trc_reader_nesting);
|
||||
WRITE_ONCE(t->trc_reader_nesting, n + 1);
|
||||
if (n) {
|
||||
// In case we interrupted a Tasks Trace RCU reader.
|
||||
return;
|
||||
}
|
||||
@@ -119,12 +122,17 @@ static inline void rcu_read_lock_trace(void)
|
||||
*/
|
||||
static inline void rcu_read_unlock_trace(void)
|
||||
{
|
||||
int n;
|
||||
struct srcu_ctr __percpu *scp;
|
||||
struct task_struct *t = current;
|
||||
|
||||
scp = t->trc_reader_scp;
|
||||
barrier(); // scp before nesting to protect against interrupt handler.
|
||||
if (!--t->trc_reader_nesting) {
|
||||
n = READ_ONCE(t->trc_reader_nesting) - 1;
|
||||
if (n) {
|
||||
WRITE_ONCE(t->trc_reader_nesting, n);
|
||||
} else {
|
||||
scp = t->trc_reader_scp; // Compiler cannot hoist load due to data raciness.
|
||||
barrier(); // scp before nesting to protect against interrupt handler.
|
||||
WRITE_ONCE(t->trc_reader_nesting, n);
|
||||
if (!IS_ENABLED(CONFIG_TASKS_TRACE_RCU_NO_MB))
|
||||
smp_mb(); // Placeholder for more selective ordering
|
||||
__srcu_read_unlock_fast(&rcu_tasks_trace_srcu_struct, scp);
|
||||
@@ -198,10 +206,13 @@ static inline void rcu_tasks_trace_expedite_current(void)
|
||||
srcu_expedite_current(&rcu_tasks_trace_srcu_struct);
|
||||
}
|
||||
|
||||
unsigned long rcu_tasks_trace_batches_completed(void);
|
||||
|
||||
// Placeholders to enable stepwise transition.
|
||||
void __init rcu_tasks_trace_suppress_unused(void);
|
||||
|
||||
#else
|
||||
static inline unsigned long rcu_tasks_trace_batches_completed(void) { return 0; }
|
||||
/*
|
||||
* The BPF JIT forms these addresses even when it doesn't call these
|
||||
* functions, so provide definitions that result in runtime errors.
|
||||
|
||||
@@ -18,7 +18,7 @@ struct rcu_synchronize {
|
||||
struct completion completion;
|
||||
|
||||
/* This is for debugging. */
|
||||
struct rcu_gp_oldstate oldstate;
|
||||
struct rcu_gp_seq oldstate;
|
||||
};
|
||||
void wakeme_after_rcu(struct rcu_head *head);
|
||||
|
||||
|
||||
+16
-20
@@ -14,11 +14,7 @@
|
||||
|
||||
#include <asm/param.h> /* for HZ */
|
||||
|
||||
struct rcu_gp_oldstate {
|
||||
unsigned long rgos_norm;
|
||||
};
|
||||
|
||||
// Maximum number of rcu_gp_oldstate values corresponding to
|
||||
// Maximum number of rcu_gp_seq values corresponding to
|
||||
// not-yet-completed RCU grace periods.
|
||||
#define NUM_ACTIVE_RCU_POLL_FULL_OLDSTATE 2
|
||||
|
||||
@@ -26,31 +22,31 @@ struct rcu_gp_oldstate {
|
||||
* 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)
|
||||
static inline bool same_state_synchronize_rcu_full(struct rcu_gp_seq *rgosp1,
|
||||
struct rcu_gp_seq *rgosp2)
|
||||
{
|
||||
return rgosp1->rgos_norm == rgosp2->rgos_norm;
|
||||
return rgosp1->norm == rgosp2->norm;
|
||||
}
|
||||
|
||||
unsigned long get_state_synchronize_rcu(void);
|
||||
|
||||
static inline void get_state_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)
|
||||
static inline void get_state_synchronize_rcu_full(struct rcu_gp_seq *gsp)
|
||||
{
|
||||
rgosp->rgos_norm = get_state_synchronize_rcu();
|
||||
gsp->norm = get_state_synchronize_rcu();
|
||||
}
|
||||
|
||||
unsigned long start_poll_synchronize_rcu(void);
|
||||
|
||||
static inline void start_poll_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)
|
||||
static inline void start_poll_synchronize_rcu_full(struct rcu_gp_seq *gsp)
|
||||
{
|
||||
rgosp->rgos_norm = start_poll_synchronize_rcu();
|
||||
gsp->norm = start_poll_synchronize_rcu();
|
||||
}
|
||||
|
||||
bool poll_state_synchronize_rcu(unsigned long oldstate);
|
||||
|
||||
static inline bool poll_state_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)
|
||||
static inline bool poll_state_synchronize_rcu_full(struct rcu_gp_seq *gsp)
|
||||
{
|
||||
return poll_state_synchronize_rcu(rgosp->rgos_norm);
|
||||
return poll_state_synchronize_rcu(gsp->norm);
|
||||
}
|
||||
|
||||
static inline void cond_synchronize_rcu(unsigned long oldstate)
|
||||
@@ -58,9 +54,9 @@ static inline void cond_synchronize_rcu(unsigned long oldstate)
|
||||
might_sleep();
|
||||
}
|
||||
|
||||
static inline void cond_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)
|
||||
static inline void cond_synchronize_rcu_full(struct rcu_gp_seq *gsp)
|
||||
{
|
||||
cond_synchronize_rcu(rgosp->rgos_norm);
|
||||
cond_synchronize_rcu(gsp->norm);
|
||||
}
|
||||
|
||||
static inline unsigned long start_poll_synchronize_rcu_expedited(void)
|
||||
@@ -68,9 +64,9 @@ static inline unsigned long start_poll_synchronize_rcu_expedited(void)
|
||||
return start_poll_synchronize_rcu();
|
||||
}
|
||||
|
||||
static inline void start_poll_synchronize_rcu_expedited_full(struct rcu_gp_oldstate *rgosp)
|
||||
static inline void start_poll_synchronize_rcu_expedited_full(struct rcu_gp_seq *gsp)
|
||||
{
|
||||
rgosp->rgos_norm = start_poll_synchronize_rcu_expedited();
|
||||
gsp->norm = start_poll_synchronize_rcu_expedited();
|
||||
}
|
||||
|
||||
static inline void cond_synchronize_rcu_expedited(unsigned long oldstate)
|
||||
@@ -78,9 +74,9 @@ static inline void cond_synchronize_rcu_expedited(unsigned long oldstate)
|
||||
cond_synchronize_rcu(oldstate);
|
||||
}
|
||||
|
||||
static inline void cond_synchronize_rcu_expedited_full(struct rcu_gp_oldstate *rgosp)
|
||||
static inline void cond_synchronize_rcu_expedited_full(struct rcu_gp_seq *gsp)
|
||||
{
|
||||
cond_synchronize_rcu_expedited(rgosp->rgos_norm);
|
||||
cond_synchronize_rcu_expedited(gsp->norm);
|
||||
}
|
||||
|
||||
extern void rcu_barrier(void);
|
||||
|
||||
+12
-17
@@ -38,12 +38,7 @@ void synchronize_rcu_expedited(void);
|
||||
void rcu_barrier(void);
|
||||
void rcu_momentary_eqs(void);
|
||||
|
||||
struct rcu_gp_oldstate {
|
||||
unsigned long rgos_norm;
|
||||
unsigned long rgos_exp;
|
||||
};
|
||||
|
||||
// Maximum number of rcu_gp_oldstate values corresponding to
|
||||
// Maximum number of rcu_gp_seq values corresponding to
|
||||
// not-yet-completed RCU grace periods.
|
||||
#define NUM_ACTIVE_RCU_POLL_FULL_OLDSTATE 4
|
||||
|
||||
@@ -60,29 +55,29 @@ struct rcu_gp_oldstate {
|
||||
* 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
|
||||
* @rcu_gp_seq 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
|
||||
* in the other field. After all, the @rcu_gp_seq 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)
|
||||
static inline bool same_state_synchronize_rcu_full(struct rcu_gp_seq *rgosp1,
|
||||
struct rcu_gp_seq *rgosp2)
|
||||
{
|
||||
return rgosp1->rgos_norm == rgosp2->rgos_norm && rgosp1->rgos_exp == rgosp2->rgos_exp;
|
||||
return rgosp1->norm == rgosp2->norm && rgosp1->exp == rgosp2->exp;
|
||||
}
|
||||
|
||||
unsigned long start_poll_synchronize_rcu_expedited(void);
|
||||
void start_poll_synchronize_rcu_expedited_full(struct rcu_gp_oldstate *rgosp);
|
||||
void start_poll_synchronize_rcu_expedited_full(struct rcu_gp_seq *gsp);
|
||||
void cond_synchronize_rcu_expedited(unsigned long oldstate);
|
||||
void cond_synchronize_rcu_expedited_full(struct rcu_gp_oldstate *rgosp);
|
||||
void cond_synchronize_rcu_expedited_full(struct rcu_gp_seq *gsp);
|
||||
unsigned long get_state_synchronize_rcu(void);
|
||||
void get_state_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp);
|
||||
void get_state_synchronize_rcu_full(struct rcu_gp_seq *gsp);
|
||||
unsigned long start_poll_synchronize_rcu(void);
|
||||
void start_poll_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp);
|
||||
void start_poll_synchronize_rcu_full(struct rcu_gp_seq *gsp);
|
||||
bool poll_state_synchronize_rcu(unsigned long oldstate);
|
||||
bool poll_state_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp);
|
||||
bool poll_state_synchronize_rcu_full(struct rcu_gp_seq *gsp);
|
||||
void cond_synchronize_rcu(unsigned long oldstate);
|
||||
void cond_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp);
|
||||
void cond_synchronize_rcu_full(struct rcu_gp_seq *gsp);
|
||||
|
||||
#ifdef CONFIG_PROVE_RCU
|
||||
void rcu_irq_exit_check_preempt(void);
|
||||
|
||||
+20
-9
@@ -25,20 +25,19 @@ context_lock_struct(srcu_struct, __reentrant_ctx_lock);
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
|
||||
int __init_srcu_struct(struct srcu_struct *ssp, const char *name, struct lock_class_key *key);
|
||||
int init_srcu_struct_lockdep(struct srcu_struct *ssp, const char *name,
|
||||
struct lock_class_key *key);
|
||||
static inline int __init_srcu_struct(struct srcu_struct *ssp, const char *name,
|
||||
struct lock_class_key *key)
|
||||
{
|
||||
return init_srcu_struct_lockdep(ssp, name, key);
|
||||
}
|
||||
#ifndef CONFIG_TINY_SRCU
|
||||
int __init_srcu_struct_fast(struct srcu_struct *ssp, const char *name, struct lock_class_key *key);
|
||||
int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name,
|
||||
struct lock_class_key *key);
|
||||
#endif // #ifndef CONFIG_TINY_SRCU
|
||||
|
||||
#define init_srcu_struct(ssp) \
|
||||
({ \
|
||||
static struct lock_class_key __srcu_key; \
|
||||
\
|
||||
__init_srcu_struct((ssp), #ssp, &__srcu_key); \
|
||||
})
|
||||
|
||||
#define init_srcu_struct_fast(ssp) \
|
||||
({ \
|
||||
static struct lock_class_key __srcu_key; \
|
||||
@@ -56,7 +55,12 @@ int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name,
|
||||
#define __SRCU_DEP_MAP_INIT(srcu_name) .dep_map = { .name = #srcu_name },
|
||||
#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
|
||||
|
||||
int init_srcu_struct(struct srcu_struct *ssp);
|
||||
int init_srcu_struct_generic(struct srcu_struct *ssp);
|
||||
static inline int __init_srcu_struct(struct srcu_struct *ssp, const char *name,
|
||||
struct lock_class_key *key)
|
||||
{
|
||||
return init_srcu_struct_generic(ssp);
|
||||
}
|
||||
#ifndef CONFIG_TINY_SRCU
|
||||
int init_srcu_struct_fast(struct srcu_struct *ssp);
|
||||
int init_srcu_struct_fast_updown(struct srcu_struct *ssp);
|
||||
@@ -65,6 +69,13 @@ int init_srcu_struct_fast_updown(struct srcu_struct *ssp);
|
||||
#define __SRCU_DEP_MAP_INIT(srcu_name)
|
||||
#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
|
||||
|
||||
#define init_srcu_struct(ssp) \
|
||||
({ \
|
||||
static struct lock_class_key __srcu_key; \
|
||||
\
|
||||
__init_srcu_struct((ssp), #ssp, &__srcu_key); \
|
||||
})
|
||||
|
||||
/* Values for SRCU Tree srcu_data ->srcu_reader_flavor, but also used by rcutorture. */
|
||||
#define SRCU_READ_FLAVOR_NORMAL 0x1 // srcu_read_lock().
|
||||
#define SRCU_READ_FLAVOR_NMI 0x2 // srcu_read_lock_nmisafe().
|
||||
|
||||
@@ -154,4 +154,17 @@ static inline void srcu_torture_stats_print(struct srcu_struct *ssp,
|
||||
data_race(READ_ONCE(ssp->srcu_idx_max)));
|
||||
}
|
||||
|
||||
/**
|
||||
* srcu_readers_active - returns true if there are readers. and false otherwise.
|
||||
* @ssp: which srcu_struct to count active readers (holding srcu_read_lock).
|
||||
*
|
||||
* Note that this is not an atomic primitive, and can therefore suffer
|
||||
* severe errors when invoked on an active srcu_struct. That said, it
|
||||
* can be useful as an error check at cleanup time.
|
||||
*/
|
||||
static inline bool srcu_readers_active(struct srcu_struct *ssp)
|
||||
{
|
||||
return READ_ONCE(ssp->srcu_lock_nesting[0]) || READ_ONCE(ssp->srcu_lock_nesting[1]);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -374,4 +374,28 @@ static inline void srcu_check_read_flavor(struct srcu_struct *ssp, int read_flav
|
||||
__srcu_check_read_flavor(ssp, read_flavor);
|
||||
}
|
||||
|
||||
/**
|
||||
* srcu_readers_active - returns true if there are readers. and false otherwise.
|
||||
* @ssp: which srcu_struct to count active readers (holding srcu_read_lock).
|
||||
*
|
||||
* Note that this is not an atomic primitive, and can therefore suffer
|
||||
* severe errors when invoked on an active srcu_struct. That said, it
|
||||
* can be useful as an error check at cleanup time.
|
||||
*/
|
||||
static inline bool srcu_readers_active(struct srcu_struct *ssp)
|
||||
{
|
||||
int cpu;
|
||||
unsigned long sum = 0;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct srcu_data *sdp = per_cpu_ptr(ssp->sda, cpu);
|
||||
|
||||
sum += atomic_long_read(&sdp->srcu_ctrs[0].srcu_locks);
|
||||
sum += atomic_long_read(&sdp->srcu_ctrs[1].srcu_locks);
|
||||
sum -= atomic_long_read(&sdp->srcu_ctrs[0].srcu_unlocks);
|
||||
sum -= atomic_long_read(&sdp->srcu_ctrs[1].srcu_unlocks);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -547,10 +547,11 @@ TRACE_EVENT_RCU(rcu_segcb_stats,
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
int i;
|
||||
__entry->ctx = ctx;
|
||||
memcpy(__entry->seglen, rs->seglen, RCU_CBLIST_NSEGS * sizeof(long));
|
||||
memcpy(__entry->gp_seq, rs->gp_seq, RCU_CBLIST_NSEGS * sizeof(unsigned long));
|
||||
|
||||
for (i = 0; i < RCU_CBLIST_NSEGS; i++)
|
||||
__entry->gp_seq[i] = rs->gp_seq[i].norm;
|
||||
),
|
||||
|
||||
TP_printk("%s seglen: (DONE=%ld, WAIT=%ld, NEXT_READY=%ld, NEXT=%ld) "
|
||||
|
||||
@@ -140,7 +140,6 @@ config FORCE_TASKS_TRACE_RCU
|
||||
config TASKS_TRACE_RCU
|
||||
bool
|
||||
default n
|
||||
select IRQ_WORK
|
||||
|
||||
config TASKS_TRACE_RCU_NO_MB
|
||||
bool "Override RCU Tasks Trace inclusion of read-side memory barriers"
|
||||
|
||||
+18
-2
@@ -46,16 +46,25 @@
|
||||
* the number of pending readers that will use
|
||||
* this inactive index is bounded).
|
||||
*
|
||||
* RCU polled GP special control value:
|
||||
* RCU polled GP special control values:
|
||||
*
|
||||
* RCU_GET_STATE_COMPLETED : State value indicating an already-completed
|
||||
* polled GP has completed. This value covers
|
||||
* both the state and the counter of the
|
||||
* grace-period sequence number.
|
||||
*
|
||||
* RCU_GET_STATE_NOT_TRACKED : State value indicating that a GP component
|
||||
* is not tracked by this subsystem and should
|
||||
* not be checked. Used by SRCU and RCU Tasks
|
||||
* which do not track expedited GPs, to prevent
|
||||
* false-positive completion when their
|
||||
* gp_seq entries are checked via
|
||||
* poll_state_synchronize_rcu_full().
|
||||
*/
|
||||
|
||||
/* Low-order bit definition for polled grace-period APIs. */
|
||||
/* Low-order bit definitions for polled grace-period APIs. */
|
||||
#define RCU_GET_STATE_COMPLETED 0x1
|
||||
#define RCU_GET_STATE_NOT_TRACKED 0x2
|
||||
|
||||
/* A complete grace period count */
|
||||
#define RCU_SEQ_GP (RCU_SEQ_STATE_MASK + 1)
|
||||
@@ -695,4 +704,11 @@ static inline int rcu_stall_notifier_call_chain(unsigned long val, void *v) { re
|
||||
void synchronize_rcu_trivial_preempt(void);
|
||||
#endif // #ifdef CONFIG_TRIVIAL_PREEMPT_RCU
|
||||
|
||||
#if defined(CONFIG_RCU_TORTURE_TEST) && defined(CONFIG_RCU_BOOST)
|
||||
bool rcu_is_task_rcu_boosted(void);
|
||||
#else // #if defined(CONFIG_RCU_TORTURE_TEST) && defined(CONFIG_RCU_BOOST)
|
||||
static inline bool rcu_is_task_rcu_boosted(void) { return false; }
|
||||
#endif // #else // #if defined(CONFIG_RCU_TORTURE_TEST) && defined(CONFIG_RCU_BOOST)
|
||||
|
||||
|
||||
#endif /* __LINUX_RCU_H */
|
||||
|
||||
+98
-41
@@ -12,6 +12,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "rcu.h"
|
||||
#include "rcu_segcblist.h"
|
||||
|
||||
/* Initialize simple callback list. */
|
||||
@@ -307,13 +308,13 @@ struct rcu_head *rcu_segcblist_first_pend_cb(struct rcu_segcblist *rsclp)
|
||||
|
||||
/*
|
||||
* Return false if there are no CBs awaiting grace periods, otherwise,
|
||||
* return true and store the nearest waited-upon grace period into *lp.
|
||||
* return true and store the nearest waited-upon grace period state into *gsp.
|
||||
*/
|
||||
bool rcu_segcblist_nextgp(struct rcu_segcblist *rsclp, unsigned long *lp)
|
||||
bool rcu_segcblist_nextgp(struct rcu_segcblist *rsclp, struct rcu_gp_seq *gsp)
|
||||
{
|
||||
if (!rcu_segcblist_pend_cbs(rsclp))
|
||||
return false;
|
||||
*lp = rsclp->gp_seq[RCU_WAIT_TAIL];
|
||||
*gsp = rsclp->gp_seq[RCU_WAIT_TAIL];
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -463,31 +464,15 @@ void rcu_segcblist_insert_pend_cbs(struct rcu_segcblist *rsclp,
|
||||
}
|
||||
|
||||
/*
|
||||
* Advance the callbacks in the specified rcu_segcblist structure based
|
||||
* on the current value passed in for the grace-period counter.
|
||||
* Clean up and compact the segmented callback list after callbacks have been
|
||||
* advanced to the RCU_DONE_TAIL segment. The @i parameter is the index of the
|
||||
* first segment that was NOT advanced (i.e., the segment after the last one
|
||||
* moved to RCU_DONE_TAIL). This function fixes up tail pointers and compacts
|
||||
* any gaps left by the moved segments.
|
||||
*/
|
||||
void rcu_segcblist_advance(struct rcu_segcblist *rsclp, unsigned long seq)
|
||||
static void rcu_segcblist_advance_compact(struct rcu_segcblist *rsclp, int i)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
WARN_ON_ONCE(!rcu_segcblist_is_enabled(rsclp));
|
||||
if (rcu_segcblist_restempty(rsclp, RCU_DONE_TAIL))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Find all callbacks whose ->gp_seq numbers indicate that they
|
||||
* are ready to invoke, and put them into the RCU_DONE_TAIL segment.
|
||||
*/
|
||||
for (i = RCU_WAIT_TAIL; i < RCU_NEXT_TAIL; i++) {
|
||||
if (ULONG_CMP_LT(seq, rsclp->gp_seq[i]))
|
||||
break;
|
||||
WRITE_ONCE(rsclp->tails[RCU_DONE_TAIL], rsclp->tails[i]);
|
||||
rcu_segcblist_move_seglen(rsclp, i, RCU_DONE_TAIL);
|
||||
}
|
||||
|
||||
/* If no callbacks moved, nothing more need be done. */
|
||||
if (i == RCU_WAIT_TAIL)
|
||||
return;
|
||||
int j;
|
||||
|
||||
/* Clean up tail pointers that might have been misordered above. */
|
||||
for (j = RCU_WAIT_TAIL; j < i; j++)
|
||||
@@ -508,6 +493,39 @@ void rcu_segcblist_advance(struct rcu_segcblist *rsclp, unsigned long seq)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Advance the callbacks in the specified rcu_segcblist structure based
|
||||
* on the current grace-period state. Checks both normal and expedited
|
||||
* grace periods, advancing callbacks when either GP type completes.
|
||||
*/
|
||||
void rcu_segcblist_advance(struct rcu_segcblist *rsclp)
|
||||
{
|
||||
int i;
|
||||
|
||||
WARN_ON_ONCE(!rcu_segcblist_is_enabled(rsclp));
|
||||
if (rcu_segcblist_restempty(rsclp, RCU_DONE_TAIL))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Find all callbacks whose grace periods have completed (either
|
||||
* normal or expedited) and put them into the RCU_DONE_TAIL segment.
|
||||
* We check against the current global GP state, which includes
|
||||
* proper memory barriers and handles special completion values.
|
||||
*/
|
||||
for (i = RCU_WAIT_TAIL; i < RCU_NEXT_TAIL; i++) {
|
||||
if (!poll_state_synchronize_rcu_full(&rsclp->gp_seq[i]))
|
||||
break;
|
||||
WRITE_ONCE(rsclp->tails[RCU_DONE_TAIL], rsclp->tails[i]);
|
||||
rcu_segcblist_move_seglen(rsclp, i, RCU_DONE_TAIL);
|
||||
}
|
||||
|
||||
/* If no callbacks moved, nothing more need be done. */
|
||||
if (i == RCU_WAIT_TAIL)
|
||||
return;
|
||||
|
||||
rcu_segcblist_advance_compact(rsclp, i);
|
||||
}
|
||||
|
||||
/*
|
||||
* "Accelerate" callbacks based on more-accurate grace-period information.
|
||||
* The reason for this is that RCU does not synchronize the beginnings and
|
||||
@@ -519,11 +537,11 @@ void rcu_segcblist_advance(struct rcu_segcblist *rsclp, unsigned long seq)
|
||||
* them to complete at the end of the earlier grace period.
|
||||
*
|
||||
* This function operates on an rcu_segcblist structure, and also the
|
||||
* grace-period sequence number seq at which new callbacks would become
|
||||
* grace-period state gsp at which new callbacks would become
|
||||
* ready to invoke. Returns true if there are callbacks that won't be
|
||||
* ready to invoke until seq, false otherwise.
|
||||
* ready to invoke until the grace period represented by gsp, false otherwise.
|
||||
*/
|
||||
bool rcu_segcblist_accelerate(struct rcu_segcblist *rsclp, unsigned long seq)
|
||||
bool rcu_segcblist_accelerate(struct rcu_segcblist *rsclp, struct rcu_gp_seq *gsp)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
@@ -533,20 +551,20 @@ bool rcu_segcblist_accelerate(struct rcu_segcblist *rsclp, unsigned long seq)
|
||||
|
||||
/*
|
||||
* Find the segment preceding the oldest segment of callbacks
|
||||
* whose ->gp_seq[] completion is at or after that passed in via
|
||||
* "seq", skipping any empty segments. This oldest segment, along
|
||||
* whose grace period completion is at or after that passed in via
|
||||
* "gsp", skipping any empty segments. This oldest segment, along
|
||||
* with any later segments, can be merged in with any newly arrived
|
||||
* callbacks in the RCU_NEXT_TAIL segment, and assigned "seq"
|
||||
* as their ->gp_seq[] grace-period completion sequence number.
|
||||
* callbacks in the RCU_NEXT_TAIL segment, and assigned "gsp"
|
||||
* as their grace-period completion state.
|
||||
*/
|
||||
for (i = RCU_NEXT_READY_TAIL; i > RCU_DONE_TAIL; i--)
|
||||
if (!rcu_segcblist_segempty(rsclp, i) &&
|
||||
ULONG_CMP_LT(rsclp->gp_seq[i], seq))
|
||||
ULONG_CMP_LT(rsclp->gp_seq[i].norm, gsp->norm))
|
||||
break;
|
||||
|
||||
/*
|
||||
* If all the segments contain callbacks that correspond to
|
||||
* earlier grace-period sequence numbers than "seq", leave.
|
||||
* earlier grace-period sequence numbers than "gsp", leave.
|
||||
* Assuming that the rcu_segcblist structure has enough
|
||||
* segments in its arrays, this can only happen if some of
|
||||
* the non-done segments contain callbacks that really are
|
||||
@@ -554,15 +572,15 @@ bool rcu_segcblist_accelerate(struct rcu_segcblist *rsclp, unsigned long seq)
|
||||
* out by the next call to rcu_segcblist_advance().
|
||||
*
|
||||
* Also advance to the oldest segment of callbacks whose
|
||||
* ->gp_seq[] completion is at or after that passed in via "seq",
|
||||
* ->gp_seq[] completion is at or after that passed in via "gsp",
|
||||
* skipping any empty segments.
|
||||
*
|
||||
* Note that segment "i" (and any lower-numbered segments
|
||||
* containing older callbacks) will be unaffected, and their
|
||||
* grace-period numbers remain unchanged. For example, if i ==
|
||||
* grace-period states remain unchanged. For example, if i ==
|
||||
* WAIT_TAIL, then neither WAIT_TAIL nor DONE_TAIL will be touched.
|
||||
* Instead, the CBs in NEXT_TAIL will be merged with those in
|
||||
* NEXT_READY_TAIL and the grace-period number of NEXT_READY_TAIL
|
||||
* NEXT_READY_TAIL and the grace-period state of NEXT_READY_TAIL
|
||||
* would be updated. NEXT_TAIL would then be empty.
|
||||
*/
|
||||
if (rcu_segcblist_restempty(rsclp, i) || ++i >= RCU_NEXT_TAIL)
|
||||
@@ -574,14 +592,14 @@ bool rcu_segcblist_accelerate(struct rcu_segcblist *rsclp, unsigned long seq)
|
||||
|
||||
/*
|
||||
* Merge all later callbacks, including newly arrived callbacks,
|
||||
* into the segment located by the for-loop above. Assign "seq"
|
||||
* as the ->gp_seq[] value in order to correctly handle the case
|
||||
* into the segment located by the for-loop above. Assign "gsp"
|
||||
* as the grace-period state in order to correctly handle the case
|
||||
* where there were no pending callbacks in the rcu_segcblist
|
||||
* structure other than in the RCU_NEXT_TAIL segment.
|
||||
*/
|
||||
for (; i < RCU_NEXT_TAIL; i++) {
|
||||
WRITE_ONCE(rsclp->tails[i], rsclp->tails[RCU_NEXT_TAIL]);
|
||||
rsclp->gp_seq[i] = seq;
|
||||
rsclp->gp_seq[i] = *gsp;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -620,3 +638,42 @@ void rcu_segcblist_merge(struct rcu_segcblist *dst_rsclp,
|
||||
|
||||
rcu_segcblist_init(src_rsclp);
|
||||
}
|
||||
|
||||
void srcu_segcblist_advance(struct rcu_segcblist *rsclp, unsigned long seq)
|
||||
{
|
||||
int i;
|
||||
|
||||
WARN_ON_ONCE(!rcu_segcblist_is_enabled(rsclp));
|
||||
if (rcu_segcblist_restempty(rsclp, RCU_DONE_TAIL))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Find all callbacks whose normal GP sequence numbers indicate
|
||||
* that they are ready to invoke. For SRCU, we only check norm.
|
||||
*/
|
||||
for (i = RCU_WAIT_TAIL; i < RCU_NEXT_TAIL; i++) {
|
||||
if (ULONG_CMP_LT(seq, rsclp->gp_seq[i].norm))
|
||||
break;
|
||||
WRITE_ONCE(rsclp->tails[RCU_DONE_TAIL], rsclp->tails[i]);
|
||||
rcu_segcblist_move_seglen(rsclp, i, RCU_DONE_TAIL);
|
||||
}
|
||||
|
||||
/* If no callbacks moved, nothing more need be done. */
|
||||
if (i == RCU_WAIT_TAIL)
|
||||
return;
|
||||
|
||||
rcu_segcblist_advance_compact(rsclp, i);
|
||||
}
|
||||
|
||||
/*
|
||||
* SRCU wrapper for rcu_segcblist_accelerate() - converts SRCU's unsigned
|
||||
* long GP sequence to rcu_gp_seq format with exp set to
|
||||
* RCU_GET_STATE_NOT_TRACKED (since SRCU does not use expedited GPs)
|
||||
* and calls the core rcu_segcblist_accelerate().
|
||||
*/
|
||||
bool srcu_segcblist_accelerate(struct rcu_segcblist *rsclp, unsigned long seq)
|
||||
{
|
||||
struct rcu_gp_seq gs = { .norm = seq, .exp = RCU_GET_STATE_NOT_TRACKED };
|
||||
|
||||
return rcu_segcblist_accelerate(rsclp, &gs);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user