mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge branches 'doc.2021.01.06a', 'fixes.2021.01.04b', 'kfree_rcu.2021.01.04a', 'mmdumpobj.2021.01.22a', 'nocb.2021.01.06a', 'rt.2021.01.04a', 'stall.2021.01.06a', 'torture.2021.01.12a' and 'tortureall.2021.01.06a' into HEAD
doc.2021.01.06a: Documentation updates. fixes.2021.01.04b: Miscellaneous fixes. kfree_rcu.2021.01.04a: kfree_rcu() updates. mmdumpobj.2021.01.22a: Dump allocation point for memory blocks. nocb.2021.01.06a: RCU callback offload updates and cblist segment lengths. rt.2021.01.04a: Real-time updates. stall.2021.01.06a: RCU CPU stall warning updates. torture.2021.01.12a: Torture-test updates and polling SRCU grace-period API. tortureall.2021.01.06a: Torture-test script updates.
This commit is contained in:
@@ -2598,6 +2598,24 @@ also includes DEFINE_SRCU(), DEFINE_STATIC_SRCU(), and
|
||||
init_srcu_struct() APIs for defining and initializing
|
||||
``srcu_struct`` structures.
|
||||
|
||||
More recently, the SRCU API has added polling interfaces:
|
||||
|
||||
#. start_poll_synchronize_srcu() returns a cookie identifying
|
||||
the completion of a future SRCU grace period and ensures
|
||||
that this grace period will be started.
|
||||
#. poll_state_synchronize_srcu() returns ``true`` iff the
|
||||
specified cookie corresponds to an already-completed
|
||||
SRCU grace period.
|
||||
#. get_state_synchronize_srcu() returns a cookie just like
|
||||
start_poll_synchronize_srcu() does, but differs in that
|
||||
it does nothing to ensure that any future SRCU grace period
|
||||
will be started.
|
||||
|
||||
These functions are used to avoid unnecessary SRCU grace periods in
|
||||
certain types of buffer-cache algorithms having multi-stage age-out
|
||||
mechanisms. The idea is that by the time the block has aged completely
|
||||
from the cache, an SRCU grace period will be very likely to have elapsed.
|
||||
|
||||
Tasks RCU
|
||||
~~~~~~~~~
|
||||
|
||||
|
||||
@@ -92,7 +92,9 @@ warnings:
|
||||
buggy timer hardware through bugs in the interrupt or exception
|
||||
path (whether hardware, firmware, or software) through bugs
|
||||
in Linux's timer subsystem through bugs in the scheduler, and,
|
||||
yes, even including bugs in RCU itself.
|
||||
yes, even including bugs in RCU itself. It can also result in
|
||||
the ``rcu_.*timer wakeup didn't happen for`` console-log message,
|
||||
which will include additional debugging information.
|
||||
|
||||
- A bug in the RCU implementation.
|
||||
|
||||
@@ -292,6 +294,25 @@ kthread is waiting for a short timeout, the "state" precedes value of the
|
||||
task_struct ->state field, and the "cpu" indicates that the grace-period
|
||||
kthread last ran on CPU 5.
|
||||
|
||||
If the relevant grace-period kthread does not wake from FQS wait in a
|
||||
reasonable time, then the following additional line is printed::
|
||||
|
||||
kthread timer wakeup didn't happen for 23804 jiffies! g7076 f0x0 RCU_GP_WAIT_FQS(5) ->state=0x402
|
||||
|
||||
The "23804" indicates that kthread's timer expired more than 23 thousand
|
||||
jiffies ago. The rest of the line has meaning similar to the kthread
|
||||
starvation case.
|
||||
|
||||
Additionally, the following line is printed::
|
||||
|
||||
Possible timer handling issue on cpu=4 timer-softirq=11142
|
||||
|
||||
Here "cpu" indicates that the grace-period kthread last ran on CPU 4,
|
||||
where it queued the fqs timer. The number following the "timer-softirq"
|
||||
is the current ``TIMER_SOFTIRQ`` count on cpu 4. If this value does not
|
||||
change on successive RCU CPU stall warnings, there is further reason to
|
||||
suspect a timer problem.
|
||||
|
||||
|
||||
Multiple Warnings From One Stall
|
||||
================================
|
||||
|
||||
@@ -4092,6 +4092,10 @@
|
||||
value, meaning that RCU_SOFTIRQ is used by default.
|
||||
Specify rcutree.use_softirq=0 to use rcuc kthreads.
|
||||
|
||||
But note that CONFIG_PREEMPT_RT=y kernels disable
|
||||
this kernel boot parameter, forcibly setting it
|
||||
to zero.
|
||||
|
||||
rcutree.rcu_fanout_exact= [KNL]
|
||||
Disable autobalancing of the rcu_node combining
|
||||
tree. This is used by rcutorture, and might
|
||||
@@ -4332,6 +4336,14 @@
|
||||
stress RCU, they don't participate in the actual
|
||||
test, hence the "fake".
|
||||
|
||||
rcutorture.nocbs_nthreads= [KNL]
|
||||
Set number of RCU callback-offload togglers.
|
||||
Zero (the default) disables toggling.
|
||||
|
||||
rcutorture.nocbs_toggle= [KNL]
|
||||
Set the delay in milliseconds between successive
|
||||
callback-offload toggling attempts.
|
||||
|
||||
rcutorture.nreaders= [KNL]
|
||||
Set number of RCU readers. The value -1 selects
|
||||
N-1, where N is the number of CPUs. A value
|
||||
@@ -4464,6 +4476,13 @@
|
||||
only normal grace-period primitives. No effect
|
||||
on CONFIG_TINY_RCU kernels.
|
||||
|
||||
But note that CONFIG_PREEMPT_RT=y kernels enables
|
||||
this kernel boot parameter, forcibly setting
|
||||
it to the value one, that is, converting any
|
||||
post-boot attempt at an expedited RCU grace
|
||||
period to instead use normal non-expedited
|
||||
grace-period processing.
|
||||
|
||||
rcupdate.rcu_task_ipi_delay= [KNL]
|
||||
Set time in jiffies during which RCU tasks will
|
||||
avoid sending IPIs, starting with the beginning
|
||||
@@ -4551,6 +4570,12 @@
|
||||
refscale.verbose= [KNL]
|
||||
Enable additional printk() statements.
|
||||
|
||||
refscale.verbose_batched= [KNL]
|
||||
Batch the additional printk() statements. If zero
|
||||
(the default) or negative, print everything. Otherwise,
|
||||
print every Nth verbose statement, where N is the value
|
||||
specified.
|
||||
|
||||
relax_domain_level=
|
||||
[KNL, SMP] Set scheduler's default relax_domain_level.
|
||||
See Documentation/admin-guide/cgroup-v1/cpusets.rst.
|
||||
@@ -5325,6 +5350,14 @@
|
||||
are running concurrently, especially on systems
|
||||
with rotating-rust storage.
|
||||
|
||||
torture.verbose_sleep_frequency= [KNL]
|
||||
Specifies how many verbose printk()s should be
|
||||
emitted between each sleep. The default of zero
|
||||
disables verbose-printk() sleeping.
|
||||
|
||||
torture.verbose_sleep_duration= [KNL]
|
||||
Duration of each verbose-printk() sleep in jiffies.
|
||||
|
||||
tp720= [HW,PS2]
|
||||
|
||||
tpm_suspend_pcr=[HW,TPM]
|
||||
|
||||
@@ -111,6 +111,8 @@ static inline void cpu_maps_update_done(void)
|
||||
#endif /* CONFIG_SMP */
|
||||
extern struct bus_type cpu_subsys;
|
||||
|
||||
extern int lockdep_is_cpus_held(void);
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
extern void cpus_write_lock(void);
|
||||
extern void cpus_write_unlock(void);
|
||||
|
||||
@@ -3169,5 +3169,7 @@ unsigned long wp_shared_mapping_range(struct address_space *mapping,
|
||||
|
||||
extern int sysctl_nr_trim_pages;
|
||||
|
||||
void mem_dump_obj(void *object);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* _LINUX_MM_H */
|
||||
|
||||
@@ -63,6 +63,122 @@ struct rcu_cblist {
|
||||
#define RCU_NEXT_TAIL 3
|
||||
#define RCU_CBLIST_NSEGS 4
|
||||
|
||||
|
||||
/*
|
||||
* ==NOCB Offloading state machine==
|
||||
*
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
* | SEGCBLIST_SOFTIRQ_ONLY |
|
||||
* | |
|
||||
* | Callbacks processed by rcu_core() from softirqs or local |
|
||||
* | rcuc kthread, without holding nocb_lock. |
|
||||
* ----------------------------------------------------------------------------
|
||||
* |
|
||||
* v
|
||||
* ----------------------------------------------------------------------------
|
||||
* | SEGCBLIST_OFFLOADED |
|
||||
* | |
|
||||
* | Callbacks processed by rcu_core() from softirqs or local |
|
||||
* | rcuc kthread, while holding nocb_lock. Waking up CB and GP kthreads, |
|
||||
* | allowing nocb_timer to be armed. |
|
||||
* ----------------------------------------------------------------------------
|
||||
* |
|
||||
* v
|
||||
* -----------------------------------
|
||||
* | |
|
||||
* v v
|
||||
* --------------------------------------- ----------------------------------|
|
||||
* | SEGCBLIST_OFFLOADED | | | SEGCBLIST_OFFLOADED | |
|
||||
* | SEGCBLIST_KTHREAD_CB | | SEGCBLIST_KTHREAD_GP |
|
||||
* | | | |
|
||||
* | | | |
|
||||
* | CB kthread woke up and | | GP kthread woke up and |
|
||||
* | acknowledged SEGCBLIST_OFFLOADED. | | acknowledged SEGCBLIST_OFFLOADED|
|
||||
* | Processes callbacks concurrently | | |
|
||||
* | with rcu_core(), holding | | |
|
||||
* | nocb_lock. | | |
|
||||
* --------------------------------------- -----------------------------------
|
||||
* | |
|
||||
* -----------------------------------
|
||||
* |
|
||||
* v
|
||||
* |--------------------------------------------------------------------------|
|
||||
* | SEGCBLIST_OFFLOADED | |
|
||||
* | SEGCBLIST_KTHREAD_CB | |
|
||||
* | SEGCBLIST_KTHREAD_GP |
|
||||
* | |
|
||||
* | Kthreads handle callbacks holding nocb_lock, local rcu_core() stops |
|
||||
* | handling callbacks. |
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ==NOCB De-Offloading state machine==
|
||||
*
|
||||
*
|
||||
* |--------------------------------------------------------------------------|
|
||||
* | SEGCBLIST_OFFLOADED | |
|
||||
* | SEGCBLIST_KTHREAD_CB | |
|
||||
* | SEGCBLIST_KTHREAD_GP |
|
||||
* | |
|
||||
* | CB/GP kthreads handle callbacks holding nocb_lock, local rcu_core() |
|
||||
* | ignores callbacks. |
|
||||
* ----------------------------------------------------------------------------
|
||||
* |
|
||||
* v
|
||||
* |--------------------------------------------------------------------------|
|
||||
* | SEGCBLIST_KTHREAD_CB | |
|
||||
* | SEGCBLIST_KTHREAD_GP |
|
||||
* | |
|
||||
* | CB/GP kthreads and local rcu_core() handle callbacks concurrently |
|
||||
* | holding nocb_lock. Wake up CB and GP kthreads if necessary. |
|
||||
* ----------------------------------------------------------------------------
|
||||
* |
|
||||
* v
|
||||
* -----------------------------------
|
||||
* | |
|
||||
* v v
|
||||
* ---------------------------------------------------------------------------|
|
||||
* | |
|
||||
* | SEGCBLIST_KTHREAD_CB | SEGCBLIST_KTHREAD_GP |
|
||||
* | | |
|
||||
* | GP kthread woke up and | CB kthread woke up and |
|
||||
* | acknowledged the fact that | acknowledged the fact that |
|
||||
* | SEGCBLIST_OFFLOADED got cleared. | SEGCBLIST_OFFLOADED got cleared. |
|
||||
* | | The CB kthread goes to sleep |
|
||||
* | The callbacks from the target CPU | until it ever gets re-offloaded. |
|
||||
* | will be ignored from the GP kthread | |
|
||||
* | loop. | |
|
||||
* ----------------------------------------------------------------------------
|
||||
* | |
|
||||
* -----------------------------------
|
||||
* |
|
||||
* v
|
||||
* ----------------------------------------------------------------------------
|
||||
* | 0 |
|
||||
* | |
|
||||
* | Callbacks processed by rcu_core() from softirqs or local |
|
||||
* | rcuc kthread, while holding nocb_lock. Forbid nocb_timer to be armed. |
|
||||
* | Flush pending nocb_timer. Flush nocb bypass callbacks. |
|
||||
* ----------------------------------------------------------------------------
|
||||
* |
|
||||
* v
|
||||
* ----------------------------------------------------------------------------
|
||||
* | SEGCBLIST_SOFTIRQ_ONLY |
|
||||
* | |
|
||||
* | Callbacks processed by rcu_core() from softirqs or local |
|
||||
* | rcuc kthread, without holding nocb_lock. |
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
#define SEGCBLIST_ENABLED BIT(0)
|
||||
#define SEGCBLIST_SOFTIRQ_ONLY BIT(1)
|
||||
#define SEGCBLIST_KTHREAD_CB BIT(2)
|
||||
#define SEGCBLIST_KTHREAD_GP BIT(3)
|
||||
#define SEGCBLIST_OFFLOADED BIT(4)
|
||||
|
||||
struct rcu_segcblist {
|
||||
struct rcu_head *head;
|
||||
struct rcu_head **tails[RCU_CBLIST_NSEGS];
|
||||
@@ -72,8 +188,8 @@ struct rcu_segcblist {
|
||||
#else
|
||||
long len;
|
||||
#endif
|
||||
u8 enabled;
|
||||
u8 offloaded;
|
||||
long seglen[RCU_CBLIST_NSEGS];
|
||||
u8 flags;
|
||||
};
|
||||
|
||||
#define RCU_SEGCBLIST_INITIALIZER(n) \
|
||||
|
||||
+28
-20
@@ -33,6 +33,8 @@
|
||||
#define ULONG_CMP_GE(a, b) (ULONG_MAX / 2 >= (a) - (b))
|
||||
#define ULONG_CMP_LT(a, b) (ULONG_MAX / 2 < (a) - (b))
|
||||
#define ulong2long(a) (*(long *)(&(a)))
|
||||
#define USHORT_CMP_GE(a, b) (USHRT_MAX / 2 >= (unsigned short)((a) - (b)))
|
||||
#define USHORT_CMP_LT(a, b) (USHRT_MAX / 2 < (unsigned short)((a) - (b)))
|
||||
|
||||
/* Exported common interfaces */
|
||||
void call_rcu(struct rcu_head *head, rcu_callback_t func);
|
||||
@@ -86,6 +88,12 @@ void rcu_sched_clock_irq(int user);
|
||||
void rcu_report_dead(unsigned int cpu);
|
||||
void rcutree_migrate_callbacks(int cpu);
|
||||
|
||||
#ifdef CONFIG_TASKS_RCU_GENERIC
|
||||
void rcu_init_tasks_generic(void);
|
||||
#else
|
||||
static inline void rcu_init_tasks_generic(void) { }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RCU_STALL_COMMON
|
||||
void rcu_sysrq_start(void);
|
||||
void rcu_sysrq_end(void);
|
||||
@@ -104,8 +112,12 @@ static inline void rcu_user_exit(void) { }
|
||||
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
void rcu_init_nohz(void);
|
||||
int rcu_nocb_cpu_offload(int cpu);
|
||||
int rcu_nocb_cpu_deoffload(int cpu);
|
||||
#else /* #ifdef CONFIG_RCU_NOCB_CPU */
|
||||
static inline void rcu_init_nohz(void) { }
|
||||
static inline int rcu_nocb_cpu_offload(int cpu) { return -EINVAL; }
|
||||
static inline int rcu_nocb_cpu_deoffload(int cpu) { return 0; }
|
||||
#endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */
|
||||
|
||||
/**
|
||||
@@ -840,19 +852,11 @@ static inline notrace void rcu_read_unlock_sched_notrace(void)
|
||||
*/
|
||||
#define __is_kvfree_rcu_offset(offset) ((offset) < 4096)
|
||||
|
||||
/*
|
||||
* Helper macro for kfree_rcu() to prevent argument-expansion eyestrain.
|
||||
*/
|
||||
#define __kvfree_rcu(head, offset) \
|
||||
do { \
|
||||
BUILD_BUG_ON(!__is_kvfree_rcu_offset(offset)); \
|
||||
kvfree_call_rcu(head, (rcu_callback_t)(unsigned long)(offset)); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* kfree_rcu() - kfree an object after a grace period.
|
||||
* @ptr: pointer to kfree
|
||||
* @rhf: the name of the struct rcu_head within the type of @ptr.
|
||||
* @ptr: pointer to kfree for both single- and double-argument invocations.
|
||||
* @rhf: the name of the struct rcu_head within the type of @ptr,
|
||||
* but only for double-argument invocations.
|
||||
*
|
||||
* Many rcu callbacks functions just call kfree() on the base structure.
|
||||
* These functions are trivial, but their size adds up, and furthermore
|
||||
@@ -865,7 +869,7 @@ static inline notrace void rcu_read_unlock_sched_notrace(void)
|
||||
* Because the functions are not allowed in the low-order 4096 bytes of
|
||||
* kernel virtual memory, offsets up to 4095 bytes can be accommodated.
|
||||
* If the offset is larger than 4095 bytes, a compile-time error will
|
||||
* be generated in __kvfree_rcu(). If this error is triggered, you can
|
||||
* be generated in kvfree_rcu_arg_2(). If this error is triggered, you can
|
||||
* either fall back to use of call_rcu() or rearrange the structure to
|
||||
* position the rcu_head structure into the first 4096 bytes.
|
||||
*
|
||||
@@ -875,13 +879,7 @@ static inline notrace void rcu_read_unlock_sched_notrace(void)
|
||||
* The BUILD_BUG_ON check must not involve any function calls, hence the
|
||||
* checks are done in macros here.
|
||||
*/
|
||||
#define kfree_rcu(ptr, rhf) \
|
||||
do { \
|
||||
typeof (ptr) ___p = (ptr); \
|
||||
\
|
||||
if (___p) \
|
||||
__kvfree_rcu(&((___p)->rhf), offsetof(typeof(*(ptr)), rhf)); \
|
||||
} while (0)
|
||||
#define kfree_rcu kvfree_rcu
|
||||
|
||||
/**
|
||||
* kvfree_rcu() - kvfree an object after a grace period.
|
||||
@@ -913,7 +911,17 @@ do { \
|
||||
kvfree_rcu_arg_2, kvfree_rcu_arg_1)(__VA_ARGS__)
|
||||
|
||||
#define KVFREE_GET_MACRO(_1, _2, NAME, ...) NAME
|
||||
#define kvfree_rcu_arg_2(ptr, rhf) kfree_rcu(ptr, rhf)
|
||||
#define kvfree_rcu_arg_2(ptr, rhf) \
|
||||
do { \
|
||||
typeof (ptr) ___p = (ptr); \
|
||||
\
|
||||
if (___p) { \
|
||||
BUILD_BUG_ON(!__is_kvfree_rcu_offset(offsetof(typeof(*(ptr)), rhf))); \
|
||||
kvfree_call_rcu(&((___p)->rhf), (rcu_callback_t)(unsigned long) \
|
||||
(offsetof(typeof(*(ptr)), rhf))); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define kvfree_rcu_arg_1(ptr) \
|
||||
do { \
|
||||
typeof(ptr) ___p = (ptr); \
|
||||
|
||||
@@ -186,6 +186,8 @@ void kfree(const void *);
|
||||
void kfree_sensitive(const void *);
|
||||
size_t __ksize(const void *);
|
||||
size_t ksize(const void *);
|
||||
bool kmem_valid_obj(void *object);
|
||||
void kmem_dump_obj(void *object);
|
||||
|
||||
#ifdef CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR
|
||||
void __check_heap_object(const void *ptr, unsigned long n, struct page *page,
|
||||
|
||||
@@ -60,6 +60,9 @@ void cleanup_srcu_struct(struct srcu_struct *ssp);
|
||||
int __srcu_read_lock(struct srcu_struct *ssp) __acquires(ssp);
|
||||
void __srcu_read_unlock(struct srcu_struct *ssp, int idx) __releases(ssp);
|
||||
void synchronize_srcu(struct srcu_struct *ssp);
|
||||
unsigned long get_state_synchronize_srcu(struct srcu_struct *ssp);
|
||||
unsigned long start_poll_synchronize_srcu(struct srcu_struct *ssp);
|
||||
bool poll_state_synchronize_srcu(struct srcu_struct *ssp, unsigned long cookie);
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
|
||||
struct srcu_struct {
|
||||
short srcu_lock_nesting[2]; /* srcu_read_lock() nesting depth. */
|
||||
short srcu_idx; /* Current reader array element. */
|
||||
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_waiting; /* GP waiting for readers? */
|
||||
struct swait_queue_head srcu_wq;
|
||||
@@ -59,7 +60,7 @@ static inline int __srcu_read_lock(struct srcu_struct *ssp)
|
||||
{
|
||||
int idx;
|
||||
|
||||
idx = READ_ONCE(ssp->srcu_idx);
|
||||
idx = ((READ_ONCE(ssp->srcu_idx) + 1) & 0x2) >> 1;
|
||||
WRITE_ONCE(ssp->srcu_lock_nesting[idx], ssp->srcu_lock_nesting[idx] + 1);
|
||||
return idx;
|
||||
}
|
||||
@@ -80,7 +81,7 @@ static inline void srcu_torture_stats_print(struct srcu_struct *ssp,
|
||||
{
|
||||
int idx;
|
||||
|
||||
idx = READ_ONCE(ssp->srcu_idx) & 0x1;
|
||||
idx = ((READ_ONCE(ssp->srcu_idx) + 1) & 0x2) >> 1;
|
||||
pr_alert("%s%s Tiny SRCU per-CPU(idx=%d): (%hd,%hd)\n",
|
||||
tt, tf, idx,
|
||||
READ_ONCE(ssp->srcu_lock_nesting[!idx]),
|
||||
|
||||
@@ -192,6 +192,8 @@ extern int try_to_del_timer_sync(struct timer_list *timer);
|
||||
|
||||
#define del_singleshot_timer_sync(t) del_timer_sync(t)
|
||||
|
||||
extern bool timer_curr_running(struct timer_list *timer);
|
||||
|
||||
extern void init_timers(void);
|
||||
struct hrtimer;
|
||||
extern enum hrtimer_restart it_real_fn(struct hrtimer *);
|
||||
|
||||
+25
-2
@@ -32,11 +32,27 @@
|
||||
#define TOROUT_STRING(s) \
|
||||
pr_alert("%s" TORTURE_FLAG " %s\n", torture_type, s)
|
||||
#define VERBOSE_TOROUT_STRING(s) \
|
||||
do { if (verbose) pr_alert("%s" TORTURE_FLAG " %s\n", torture_type, s); } while (0)
|
||||
do { \
|
||||
if (verbose) { \
|
||||
verbose_torout_sleep(); \
|
||||
pr_alert("%s" TORTURE_FLAG " %s\n", torture_type, s); \
|
||||
} \
|
||||
} while (0)
|
||||
#define VERBOSE_TOROUT_ERRSTRING(s) \
|
||||
do { if (verbose) pr_alert("%s" TORTURE_FLAG "!!! %s\n", torture_type, s); } while (0)
|
||||
do { \
|
||||
if (verbose) { \
|
||||
verbose_torout_sleep(); \
|
||||
pr_alert("%s" TORTURE_FLAG "!!! %s\n", torture_type, s); \
|
||||
} \
|
||||
} while (0)
|
||||
void verbose_torout_sleep(void);
|
||||
|
||||
/* Definitions for online/offline exerciser. */
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
int torture_num_online_cpus(void);
|
||||
#else /* #ifdef CONFIG_HOTPLUG_CPU */
|
||||
static inline int torture_num_online_cpus(void) { return 1; }
|
||||
#endif /* #else #ifdef CONFIG_HOTPLUG_CPU */
|
||||
typedef void torture_ofl_func(void);
|
||||
bool torture_offline(int cpu, long *n_onl_attempts, long *n_onl_successes,
|
||||
unsigned long *sum_offl, int *min_onl, int *max_onl);
|
||||
@@ -61,6 +77,13 @@ static inline void torture_random_init(struct torture_random_state *trsp)
|
||||
trsp->trs_count = 0;
|
||||
}
|
||||
|
||||
/* Definitions for high-resolution-timer sleeps. */
|
||||
int torture_hrtimeout_ns(ktime_t baset_ns, u32 fuzzt_ns, struct torture_random_state *trsp);
|
||||
int torture_hrtimeout_us(u32 baset_us, u32 fuzzt_ns, struct torture_random_state *trsp);
|
||||
int torture_hrtimeout_ms(u32 baset_ms, u32 fuzzt_us, struct torture_random_state *trsp);
|
||||
int torture_hrtimeout_jiffies(u32 baset_j, struct torture_random_state *trsp);
|
||||
int torture_hrtimeout_s(u32 baset_s, u32 fuzzt_ms, struct torture_random_state *trsp);
|
||||
|
||||
/* Task shuffler, which causes CPUs to occasionally go idle. */
|
||||
void torture_shuffle_task_register(struct task_struct *tp);
|
||||
int torture_shuffle_init(long shuffint);
|
||||
|
||||
@@ -246,4 +246,10 @@ pcpu_free_vm_areas(struct vm_struct **vms, int nr_vms)
|
||||
int register_vmap_purge_notifier(struct notifier_block *nb);
|
||||
int unregister_vmap_purge_notifier(struct notifier_block *nb);
|
||||
|
||||
#ifdef CONFIG_MMU
|
||||
bool vmalloc_dump_obj(void *object);
|
||||
#else
|
||||
static inline bool vmalloc_dump_obj(void *object) { return false; }
|
||||
#endif
|
||||
|
||||
#endif /* _LINUX_VMALLOC_H */
|
||||
|
||||
@@ -505,6 +505,32 @@ TRACE_EVENT_RCU(rcu_callback,
|
||||
__entry->qlen)
|
||||
);
|
||||
|
||||
TRACE_EVENT_RCU(rcu_segcb_stats,
|
||||
|
||||
TP_PROTO(struct rcu_segcblist *rs, const char *ctx),
|
||||
|
||||
TP_ARGS(rs, ctx),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(const char *, ctx)
|
||||
__array(unsigned long, gp_seq, RCU_CBLIST_NSEGS)
|
||||
__array(long, seglen, RCU_CBLIST_NSEGS)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__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));
|
||||
|
||||
),
|
||||
|
||||
TP_printk("%s seglen: (DONE=%ld, WAIT=%ld, NEXT_READY=%ld, NEXT=%ld) "
|
||||
"gp_seq: (DONE=%lu, WAIT=%lu, NEXT_READY=%lu, NEXT=%lu)", __entry->ctx,
|
||||
__entry->seglen[0], __entry->seglen[1], __entry->seglen[2], __entry->seglen[3],
|
||||
__entry->gp_seq[0], __entry->gp_seq[1], __entry->gp_seq[2], __entry->gp_seq[3])
|
||||
|
||||
);
|
||||
|
||||
/*
|
||||
* Tracepoint for the registration of a single RCU callback of the special
|
||||
* kvfree() form. The first argument is the RCU type, the second argument
|
||||
|
||||
@@ -1518,6 +1518,7 @@ static noinline void __init kernel_init_freeable(void)
|
||||
|
||||
init_mm_internals();
|
||||
|
||||
rcu_init_tasks_generic();
|
||||
do_pre_smp_initcalls();
|
||||
lockup_detector_init();
|
||||
|
||||
|
||||
@@ -330,6 +330,13 @@ void lockdep_assert_cpus_held(void)
|
||||
percpu_rwsem_assert_held(&cpu_hotplug_lock);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
int lockdep_is_cpus_held(void)
|
||||
{
|
||||
return percpu_rwsem_is_held(&cpu_hotplug_lock);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void lockdep_acquire_cpus_lock(void)
|
||||
{
|
||||
rwsem_acquire(&cpu_hotplug_lock.dep_map, 0, 0, _THIS_IP_);
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/percpu-rwsem.h>
|
||||
#include <linux/torture.h>
|
||||
#include <linux/reboot.h>
|
||||
|
||||
|
||||
+3
-2
@@ -95,6 +95,7 @@ config TASKS_RUDE_RCU
|
||||
|
||||
config TASKS_TRACE_RCU
|
||||
def_bool 0
|
||||
select IRQ_WORK
|
||||
help
|
||||
This option enables a task-based RCU implementation that uses
|
||||
explicit rcu_read_lock_trace() read-side markers, and allows
|
||||
@@ -188,8 +189,8 @@ config RCU_FAST_NO_HZ
|
||||
|
||||
config RCU_BOOST
|
||||
bool "Enable RCU priority boosting"
|
||||
depends on RT_MUTEXES && PREEMPT_RCU && RCU_EXPERT
|
||||
default n
|
||||
depends on (RT_MUTEXES && PREEMPT_RCU && RCU_EXPERT) || PREEMPT_RT
|
||||
default y if PREEMPT_RT
|
||||
help
|
||||
This option boosts the priority of preempted RCU readers that
|
||||
block the current preemptible RCU grace period for too long.
|
||||
|
||||
+13
-3
@@ -378,7 +378,11 @@ do { \
|
||||
smp_mb__after_unlock_lock(); \
|
||||
} while (0)
|
||||
|
||||
#define raw_spin_unlock_rcu_node(p) raw_spin_unlock(&ACCESS_PRIVATE(p, lock))
|
||||
#define raw_spin_unlock_rcu_node(p) \
|
||||
do { \
|
||||
lockdep_assert_irqs_disabled(); \
|
||||
raw_spin_unlock(&ACCESS_PRIVATE(p, lock)); \
|
||||
} while (0)
|
||||
|
||||
#define raw_spin_lock_irq_rcu_node(p) \
|
||||
do { \
|
||||
@@ -387,7 +391,10 @@ do { \
|
||||
} while (0)
|
||||
|
||||
#define raw_spin_unlock_irq_rcu_node(p) \
|
||||
raw_spin_unlock_irq(&ACCESS_PRIVATE(p, lock))
|
||||
do { \
|
||||
lockdep_assert_irqs_disabled(); \
|
||||
raw_spin_unlock_irq(&ACCESS_PRIVATE(p, lock)); \
|
||||
} while (0)
|
||||
|
||||
#define raw_spin_lock_irqsave_rcu_node(p, flags) \
|
||||
do { \
|
||||
@@ -396,7 +403,10 @@ do { \
|
||||
} while (0)
|
||||
|
||||
#define raw_spin_unlock_irqrestore_rcu_node(p, flags) \
|
||||
raw_spin_unlock_irqrestore(&ACCESS_PRIVATE(p, lock), flags)
|
||||
do { \
|
||||
lockdep_assert_irqs_disabled(); \
|
||||
raw_spin_unlock_irqrestore(&ACCESS_PRIVATE(p, lock), flags); \
|
||||
} while (0)
|
||||
|
||||
#define raw_spin_trylock_rcu_node(p) \
|
||||
({ \
|
||||
|
||||
+163
-53
@@ -7,10 +7,10 @@
|
||||
* Authors: Paul E. McKenney <paulmck@linux.ibm.com>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "rcu_segcblist.h"
|
||||
|
||||
@@ -88,23 +88,135 @@ static void rcu_segcblist_set_len(struct rcu_segcblist *rsclp, long v)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Get the length of a segment of the rcu_segcblist structure. */
|
||||
static long rcu_segcblist_get_seglen(struct rcu_segcblist *rsclp, int seg)
|
||||
{
|
||||
return READ_ONCE(rsclp->seglen[seg]);
|
||||
}
|
||||
|
||||
/* Return number of callbacks in segmented callback list by summing seglen. */
|
||||
long rcu_segcblist_n_segment_cbs(struct rcu_segcblist *rsclp)
|
||||
{
|
||||
long len = 0;
|
||||
int i;
|
||||
|
||||
for (i = RCU_DONE_TAIL; i < RCU_CBLIST_NSEGS; i++)
|
||||
len += rcu_segcblist_get_seglen(rsclp, i);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Set the length of a segment of the rcu_segcblist structure. */
|
||||
static void rcu_segcblist_set_seglen(struct rcu_segcblist *rsclp, int seg, long v)
|
||||
{
|
||||
WRITE_ONCE(rsclp->seglen[seg], v);
|
||||
}
|
||||
|
||||
/* Increase the numeric length of a segment by a specified amount. */
|
||||
static void rcu_segcblist_add_seglen(struct rcu_segcblist *rsclp, int seg, long v)
|
||||
{
|
||||
WRITE_ONCE(rsclp->seglen[seg], rsclp->seglen[seg] + v);
|
||||
}
|
||||
|
||||
/* Move from's segment length to to's segment. */
|
||||
static void rcu_segcblist_move_seglen(struct rcu_segcblist *rsclp, int from, int to)
|
||||
{
|
||||
long len;
|
||||
|
||||
if (from == to)
|
||||
return;
|
||||
|
||||
len = rcu_segcblist_get_seglen(rsclp, from);
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
rcu_segcblist_add_seglen(rsclp, to, len);
|
||||
rcu_segcblist_set_seglen(rsclp, from, 0);
|
||||
}
|
||||
|
||||
/* Increment segment's length. */
|
||||
static void rcu_segcblist_inc_seglen(struct rcu_segcblist *rsclp, int seg)
|
||||
{
|
||||
rcu_segcblist_add_seglen(rsclp, seg, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Increase the numeric length of an rcu_segcblist structure by the
|
||||
* specified amount, which can be negative. This can cause the ->len
|
||||
* field to disagree with the actual number of callbacks on the structure.
|
||||
* This increase is fully ordered with respect to the callers accesses
|
||||
* both before and after.
|
||||
*
|
||||
* So why on earth is a memory barrier required both before and after
|
||||
* the update to the ->len field???
|
||||
*
|
||||
* The reason is that rcu_barrier() locklessly samples each CPU's ->len
|
||||
* field, and if a given CPU's field is zero, avoids IPIing that CPU.
|
||||
* This can of course race with both queuing and invoking of callbacks.
|
||||
* Failing to correctly handle either of these races could result in
|
||||
* rcu_barrier() failing to IPI a CPU that actually had callbacks queued
|
||||
* which rcu_barrier() was obligated to wait on. And if rcu_barrier()
|
||||
* failed to wait on such a callback, unloading certain kernel modules
|
||||
* would result in calls to functions whose code was no longer present in
|
||||
* the kernel, for but one example.
|
||||
*
|
||||
* Therefore, ->len transitions from 1->0 and 0->1 have to be carefully
|
||||
* ordered with respect with both list modifications and the rcu_barrier().
|
||||
*
|
||||
* The queuing case is CASE 1 and the invoking case is CASE 2.
|
||||
*
|
||||
* CASE 1: Suppose that CPU 0 has no callbacks queued, but invokes
|
||||
* call_rcu() just as CPU 1 invokes rcu_barrier(). CPU 0's ->len field
|
||||
* will transition from 0->1, which is one of the transitions that must
|
||||
* be handled carefully. Without the full memory barriers after the ->len
|
||||
* update and at the beginning of rcu_barrier(), the following could happen:
|
||||
*
|
||||
* CPU 0 CPU 1
|
||||
*
|
||||
* call_rcu().
|
||||
* rcu_barrier() sees ->len as 0.
|
||||
* set ->len = 1.
|
||||
* rcu_barrier() does nothing.
|
||||
* module is unloaded.
|
||||
* callback invokes unloaded function!
|
||||
*
|
||||
* With the full barriers, any case where rcu_barrier() sees ->len as 0 will
|
||||
* have unambiguously preceded the return from the racing call_rcu(), which
|
||||
* means that this call_rcu() invocation is OK to not wait on. After all,
|
||||
* you are supposed to make sure that any problematic call_rcu() invocations
|
||||
* happen before the rcu_barrier().
|
||||
*
|
||||
*
|
||||
* CASE 2: Suppose that CPU 0 is invoking its last callback just as
|
||||
* CPU 1 invokes rcu_barrier(). CPU 0's ->len field will transition from
|
||||
* 1->0, which is one of the transitions that must be handled carefully.
|
||||
* Without the full memory barriers before the ->len update and at the
|
||||
* end of rcu_barrier(), the following could happen:
|
||||
*
|
||||
* CPU 0 CPU 1
|
||||
*
|
||||
* start invoking last callback
|
||||
* set ->len = 0 (reordered)
|
||||
* rcu_barrier() sees ->len as 0
|
||||
* rcu_barrier() does nothing.
|
||||
* module is unloaded
|
||||
* callback executing after unloaded!
|
||||
*
|
||||
* With the full barriers, any case where rcu_barrier() sees ->len as 0
|
||||
* will be fully ordered after the completion of the callback function,
|
||||
* so that the module unloading operation is completely safe.
|
||||
*
|
||||
*/
|
||||
static void rcu_segcblist_add_len(struct rcu_segcblist *rsclp, long v)
|
||||
void rcu_segcblist_add_len(struct rcu_segcblist *rsclp, long v)
|
||||
{
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
smp_mb__before_atomic(); /* Up to the caller! */
|
||||
smp_mb__before_atomic(); // Read header comment above.
|
||||
atomic_long_add(v, &rsclp->len);
|
||||
smp_mb__after_atomic(); /* Up to the caller! */
|
||||
smp_mb__after_atomic(); // Read header comment above.
|
||||
#else
|
||||
smp_mb(); /* Up to the caller! */
|
||||
smp_mb(); // Read header comment above.
|
||||
WRITE_ONCE(rsclp->len, rsclp->len + v);
|
||||
smp_mb(); /* Up to the caller! */
|
||||
smp_mb(); // Read header comment above.
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -119,26 +231,6 @@ void rcu_segcblist_inc_len(struct rcu_segcblist *rsclp)
|
||||
rcu_segcblist_add_len(rsclp, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Exchange the numeric length of the specified rcu_segcblist structure
|
||||
* with the specified value. This can cause the ->len field to disagree
|
||||
* with the actual number of callbacks on the structure. This exchange is
|
||||
* fully ordered with respect to the callers accesses both before and after.
|
||||
*/
|
||||
static long rcu_segcblist_xchg_len(struct rcu_segcblist *rsclp, long v)
|
||||
{
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
return atomic_long_xchg(&rsclp->len, v);
|
||||
#else
|
||||
long ret = rsclp->len;
|
||||
|
||||
smp_mb(); /* Up to the caller! */
|
||||
WRITE_ONCE(rsclp->len, v);
|
||||
smp_mb(); /* Up to the caller! */
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize an rcu_segcblist structure.
|
||||
*/
|
||||
@@ -149,10 +241,12 @@ void rcu_segcblist_init(struct rcu_segcblist *rsclp)
|
||||
BUILD_BUG_ON(RCU_NEXT_TAIL + 1 != ARRAY_SIZE(rsclp->gp_seq));
|
||||
BUILD_BUG_ON(ARRAY_SIZE(rsclp->tails) != ARRAY_SIZE(rsclp->gp_seq));
|
||||
rsclp->head = NULL;
|
||||
for (i = 0; i < RCU_CBLIST_NSEGS; i++)
|
||||
for (i = 0; i < RCU_CBLIST_NSEGS; i++) {
|
||||
rsclp->tails[i] = &rsclp->head;
|
||||
rcu_segcblist_set_seglen(rsclp, i, 0);
|
||||
}
|
||||
rcu_segcblist_set_len(rsclp, 0);
|
||||
rsclp->enabled = 1;
|
||||
rcu_segcblist_set_flags(rsclp, SEGCBLIST_ENABLED);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -163,16 +257,21 @@ void rcu_segcblist_disable(struct rcu_segcblist *rsclp)
|
||||
{
|
||||
WARN_ON_ONCE(!rcu_segcblist_empty(rsclp));
|
||||
WARN_ON_ONCE(rcu_segcblist_n_cbs(rsclp));
|
||||
rsclp->enabled = 0;
|
||||
rcu_segcblist_clear_flags(rsclp, SEGCBLIST_ENABLED);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark the specified rcu_segcblist structure as offloaded. This
|
||||
* structure must be empty.
|
||||
*/
|
||||
void rcu_segcblist_offload(struct rcu_segcblist *rsclp)
|
||||
void rcu_segcblist_offload(struct rcu_segcblist *rsclp, bool offload)
|
||||
{
|
||||
rsclp->offloaded = 1;
|
||||
if (offload) {
|
||||
rcu_segcblist_clear_flags(rsclp, SEGCBLIST_SOFTIRQ_ONLY);
|
||||
rcu_segcblist_set_flags(rsclp, SEGCBLIST_OFFLOADED);
|
||||
} else {
|
||||
rcu_segcblist_clear_flags(rsclp, SEGCBLIST_OFFLOADED);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -245,7 +344,7 @@ void rcu_segcblist_enqueue(struct rcu_segcblist *rsclp,
|
||||
struct rcu_head *rhp)
|
||||
{
|
||||
rcu_segcblist_inc_len(rsclp);
|
||||
smp_mb(); /* Ensure counts are updated before callback is enqueued. */
|
||||
rcu_segcblist_inc_seglen(rsclp, RCU_NEXT_TAIL);
|
||||
rhp->next = NULL;
|
||||
WRITE_ONCE(*rsclp->tails[RCU_NEXT_TAIL], rhp);
|
||||
WRITE_ONCE(rsclp->tails[RCU_NEXT_TAIL], &rhp->next);
|
||||
@@ -274,27 +373,13 @@ bool rcu_segcblist_entrain(struct rcu_segcblist *rsclp,
|
||||
for (i = RCU_NEXT_TAIL; i > RCU_DONE_TAIL; i--)
|
||||
if (rsclp->tails[i] != rsclp->tails[i - 1])
|
||||
break;
|
||||
rcu_segcblist_inc_seglen(rsclp, i);
|
||||
WRITE_ONCE(*rsclp->tails[i], rhp);
|
||||
for (; i <= RCU_NEXT_TAIL; i++)
|
||||
WRITE_ONCE(rsclp->tails[i], &rhp->next);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract only the counts from the specified rcu_segcblist structure,
|
||||
* and place them in the specified rcu_cblist structure. This function
|
||||
* supports both callback orphaning and invocation, hence the separation
|
||||
* of counts and callbacks. (Callbacks ready for invocation must be
|
||||
* orphaned and adopted separately from pending callbacks, but counts
|
||||
* apply to all callbacks. Locking must be used to make sure that
|
||||
* both orphaned-callbacks lists are consistent.)
|
||||
*/
|
||||
void rcu_segcblist_extract_count(struct rcu_segcblist *rsclp,
|
||||
struct rcu_cblist *rclp)
|
||||
{
|
||||
rclp->len = rcu_segcblist_xchg_len(rsclp, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract only those callbacks ready to be invoked from the specified
|
||||
* rcu_segcblist structure and place them in the specified rcu_cblist
|
||||
@@ -307,6 +392,7 @@ void rcu_segcblist_extract_done_cbs(struct rcu_segcblist *rsclp,
|
||||
|
||||
if (!rcu_segcblist_ready_cbs(rsclp))
|
||||
return; /* Nothing to do. */
|
||||
rclp->len = rcu_segcblist_get_seglen(rsclp, RCU_DONE_TAIL);
|
||||
*rclp->tail = rsclp->head;
|
||||
WRITE_ONCE(rsclp->head, *rsclp->tails[RCU_DONE_TAIL]);
|
||||
WRITE_ONCE(*rsclp->tails[RCU_DONE_TAIL], NULL);
|
||||
@@ -314,6 +400,7 @@ void rcu_segcblist_extract_done_cbs(struct rcu_segcblist *rsclp,
|
||||
for (i = RCU_CBLIST_NSEGS - 1; i >= RCU_DONE_TAIL; i--)
|
||||
if (rsclp->tails[i] == rsclp->tails[RCU_DONE_TAIL])
|
||||
WRITE_ONCE(rsclp->tails[i], &rsclp->head);
|
||||
rcu_segcblist_set_seglen(rsclp, RCU_DONE_TAIL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -330,11 +417,15 @@ void rcu_segcblist_extract_pend_cbs(struct rcu_segcblist *rsclp,
|
||||
|
||||
if (!rcu_segcblist_pend_cbs(rsclp))
|
||||
return; /* Nothing to do. */
|
||||
rclp->len = 0;
|
||||
*rclp->tail = *rsclp->tails[RCU_DONE_TAIL];
|
||||
rclp->tail = rsclp->tails[RCU_NEXT_TAIL];
|
||||
WRITE_ONCE(*rsclp->tails[RCU_DONE_TAIL], NULL);
|
||||
for (i = RCU_DONE_TAIL + 1; i < RCU_CBLIST_NSEGS; i++)
|
||||
for (i = RCU_DONE_TAIL + 1; i < RCU_CBLIST_NSEGS; i++) {
|
||||
rclp->len += rcu_segcblist_get_seglen(rsclp, i);
|
||||
WRITE_ONCE(rsclp->tails[i], rsclp->tails[RCU_DONE_TAIL]);
|
||||
rcu_segcblist_set_seglen(rsclp, i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -345,7 +436,6 @@ void rcu_segcblist_insert_count(struct rcu_segcblist *rsclp,
|
||||
struct rcu_cblist *rclp)
|
||||
{
|
||||
rcu_segcblist_add_len(rsclp, rclp->len);
|
||||
rclp->len = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -359,6 +449,7 @@ void rcu_segcblist_insert_done_cbs(struct rcu_segcblist *rsclp,
|
||||
|
||||
if (!rclp->head)
|
||||
return; /* No callbacks to move. */
|
||||
rcu_segcblist_add_seglen(rsclp, RCU_DONE_TAIL, rclp->len);
|
||||
*rclp->tail = rsclp->head;
|
||||
WRITE_ONCE(rsclp->head, rclp->head);
|
||||
for (i = RCU_DONE_TAIL; i < RCU_CBLIST_NSEGS; i++)
|
||||
@@ -379,6 +470,8 @@ void rcu_segcblist_insert_pend_cbs(struct rcu_segcblist *rsclp,
|
||||
{
|
||||
if (!rclp->head)
|
||||
return; /* Nothing to do. */
|
||||
|
||||
rcu_segcblist_add_seglen(rsclp, RCU_NEXT_TAIL, rclp->len);
|
||||
WRITE_ONCE(*rsclp->tails[RCU_NEXT_TAIL], rclp->head);
|
||||
WRITE_ONCE(rsclp->tails[RCU_NEXT_TAIL], rclp->tail);
|
||||
}
|
||||
@@ -403,6 +496,7 @@ void rcu_segcblist_advance(struct rcu_segcblist *rsclp, unsigned long seq)
|
||||
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. */
|
||||
@@ -423,6 +517,7 @@ void rcu_segcblist_advance(struct rcu_segcblist *rsclp, unsigned long seq)
|
||||
if (rsclp->tails[j] == rsclp->tails[RCU_NEXT_TAIL])
|
||||
break; /* No more callbacks. */
|
||||
WRITE_ONCE(rsclp->tails[j], rsclp->tails[i]);
|
||||
rcu_segcblist_move_seglen(rsclp, i, j);
|
||||
rsclp->gp_seq[j] = rsclp->gp_seq[i];
|
||||
}
|
||||
}
|
||||
@@ -444,7 +539,7 @@ void rcu_segcblist_advance(struct rcu_segcblist *rsclp, unsigned long seq)
|
||||
*/
|
||||
bool rcu_segcblist_accelerate(struct rcu_segcblist *rsclp, unsigned long seq)
|
||||
{
|
||||
int i;
|
||||
int i, j;
|
||||
|
||||
WARN_ON_ONCE(!rcu_segcblist_is_enabled(rsclp));
|
||||
if (rcu_segcblist_restempty(rsclp, RCU_DONE_TAIL))
|
||||
@@ -487,6 +582,10 @@ bool rcu_segcblist_accelerate(struct rcu_segcblist *rsclp, unsigned long seq)
|
||||
if (rcu_segcblist_restempty(rsclp, i) || ++i >= RCU_NEXT_TAIL)
|
||||
return false;
|
||||
|
||||
/* Accounting: everything below i is about to get merged into i. */
|
||||
for (j = i + 1; j <= RCU_NEXT_TAIL; j++)
|
||||
rcu_segcblist_move_seglen(rsclp, j, i);
|
||||
|
||||
/*
|
||||
* Merge all later callbacks, including newly arrived callbacks,
|
||||
* into the segment located by the for-loop above. Assign "seq"
|
||||
@@ -514,13 +613,24 @@ void rcu_segcblist_merge(struct rcu_segcblist *dst_rsclp,
|
||||
struct rcu_cblist donecbs;
|
||||
struct rcu_cblist pendcbs;
|
||||
|
||||
lockdep_assert_cpus_held();
|
||||
|
||||
rcu_cblist_init(&donecbs);
|
||||
rcu_cblist_init(&pendcbs);
|
||||
rcu_segcblist_extract_count(src_rsclp, &donecbs);
|
||||
|
||||
rcu_segcblist_extract_done_cbs(src_rsclp, &donecbs);
|
||||
rcu_segcblist_extract_pend_cbs(src_rsclp, &pendcbs);
|
||||
|
||||
/*
|
||||
* No need smp_mb() before setting length to 0, because CPU hotplug
|
||||
* lock excludes rcu_barrier.
|
||||
*/
|
||||
rcu_segcblist_set_len(src_rsclp, 0);
|
||||
|
||||
rcu_segcblist_insert_count(dst_rsclp, &donecbs);
|
||||
rcu_segcblist_insert_count(dst_rsclp, &pendcbs);
|
||||
rcu_segcblist_insert_done_cbs(dst_rsclp, &donecbs);
|
||||
rcu_segcblist_insert_pend_cbs(dst_rsclp, &pendcbs);
|
||||
|
||||
rcu_segcblist_init(src_rsclp);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user