Merge branches 'rcu/torture', 'rcu/fixes', 'rcu/docs', 'rcu/refscale', 'rcu/tasks' and 'rcu/stall' into rcu/next
rcu/torture: RCU torture, locktorture and generic torture infrastructure rcu/fixes: Generic and misc fixes rcu/docs: RCU documentation updates rcu/refscale: RCU reference scalability test updates rcu/tasks: RCU tasks updates rcu/stall: Stall detection updates
|  | @ -181,7 +181,7 @@ operations is carried out at several levels: | |||
|    of this wait (or series of waits, as the case may be) is to permit a | ||||
|    concurrent CPU-hotplug operation to complete. | ||||
| #. In the case of RCU-sched, one of the last acts of an outgoing CPU is | ||||
|    to invoke ``rcu_report_dead()``, which reports a quiescent state for | ||||
|    to invoke ``rcutree_report_cpu_dead()``, which reports a quiescent state for | ||||
|    that CPU. However, this is likely paranoia-induced redundancy. | ||||
| 
 | ||||
| +-----------------------------------------------------------------------+ | ||||
|  |  | |||
|  | @ -564,15 +564,6 @@ | |||
|        font-size="192" | ||||
|        id="text202-7-9-6" | ||||
|        style="font-size:192px;font-style:normal;font-weight:bold;text-anchor:start;fill:#000000;stroke-width:0.025in;font-family:Courier">rcutree_migrate_callbacks()</text> | ||||
|     <text | ||||
|        xml:space="preserve" | ||||
|        x="8335.4873" | ||||
|        y="5357.1006" | ||||
|        font-style="normal" | ||||
|        font-weight="bold" | ||||
|        font-size="192" | ||||
|        id="text202-7-9-6-0" | ||||
|        style="font-size:192px;font-style:normal;font-weight:bold;text-anchor:start;fill:#000000;stroke-width:0.025in;font-family:Courier">rcu_migrate_callbacks()</text> | ||||
|     <text | ||||
|        xml:space="preserve" | ||||
|        x="8768.4678" | ||||
|  |  | |||
| Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB | 
|  | @ -1135,7 +1135,7 @@ | |||
|        font-weight="bold" | ||||
|        font-size="192" | ||||
|        id="text202-7-5-3-27-6-5" | ||||
|        style="font-size:192px;font-style:normal;font-weight:bold;text-anchor:start;fill:#000000;stroke-width:0.025in;font-family:Courier">rcu_report_dead()</text> | ||||
|        style="font-size:192px;font-style:normal;font-weight:bold;text-anchor:start;fill:#000000;stroke-width:0.025in;font-family:Courier">rcutree_report_cpu_dead()</text> | ||||
|     <text | ||||
|        xml:space="preserve" | ||||
|        x="3745.7725" | ||||
|  | @ -1256,7 +1256,7 @@ | |||
|        font-style="normal" | ||||
|        y="3679.27" | ||||
|        x="-3804.9949" | ||||
|        xml:space="preserve">rcu_cpu_starting()</text> | ||||
|        xml:space="preserve">rcutree_report_cpu_starting()</text> | ||||
|     <g | ||||
|        style="fill:none;stroke-width:0.025in" | ||||
|        id="g3107-7-5-0" | ||||
|  |  | |||
| Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB | 
|  | @ -1446,15 +1446,6 @@ | |||
|        font-size="192" | ||||
|        id="text202-7-9-6" | ||||
|        style="font-size:192px;font-style:normal;font-weight:bold;text-anchor:start;fill:#000000;stroke-width:0.025in;font-family:Courier">rcutree_migrate_callbacks()</text> | ||||
|     <text | ||||
|        xml:space="preserve" | ||||
|        x="8335.4873" | ||||
|        y="5357.1006" | ||||
|        font-style="normal" | ||||
|        font-weight="bold" | ||||
|        font-size="192" | ||||
|        id="text202-7-9-6-0" | ||||
|        style="font-size:192px;font-style:normal;font-weight:bold;text-anchor:start;fill:#000000;stroke-width:0.025in;font-family:Courier">rcu_migrate_callbacks()</text> | ||||
|     <text | ||||
|        xml:space="preserve" | ||||
|        x="8768.4678" | ||||
|  | @ -3274,7 +3265,7 @@ | |||
|          font-weight="bold" | ||||
|          font-size="192" | ||||
|          id="text202-7-5-3-27-6-5" | ||||
|          style="font-size:192px;font-style:normal;font-weight:bold;text-anchor:start;fill:#000000;stroke-width:0.025in;font-family:Courier">rcu_report_dead()</text> | ||||
|          style="font-size:192px;font-style:normal;font-weight:bold;text-anchor:start;fill:#000000;stroke-width:0.025in;font-family:Courier">rcutree_report_cpu_dead()</text> | ||||
|       <text | ||||
|          xml:space="preserve" | ||||
|          x="3745.7725" | ||||
|  | @ -3395,7 +3386,7 @@ | |||
|          font-style="normal" | ||||
|          y="3679.27" | ||||
|          x="-3804.9949" | ||||
|          xml:space="preserve">rcu_cpu_starting()</text> | ||||
|          xml:space="preserve">rcutree_report_cpu_starting()</text> | ||||
|       <g | ||||
|          style="fill:none;stroke-width:0.025in" | ||||
|          id="g3107-7-5-0" | ||||
|  |  | |||
| Before Width: | Height: | Size: 209 KiB After Width: | Height: | Size: 208 KiB | 
|  | @ -607,7 +607,7 @@ | |||
|        font-weight="bold" | ||||
|        font-size="192" | ||||
|        id="text202-7-5-3-27-6" | ||||
|        style="font-size:192px;font-style:normal;font-weight:bold;text-anchor:start;fill:#000000;stroke-width:0.025in;font-family:Courier">rcu_report_dead()</text> | ||||
|        style="font-size:192px;font-style:normal;font-weight:bold;text-anchor:start;fill:#000000;stroke-width:0.025in;font-family:Courier">rcutree_report_cpu_dead()</text> | ||||
|     <text | ||||
|        xml:space="preserve" | ||||
|        x="3745.7725" | ||||
|  | @ -728,7 +728,7 @@ | |||
|        font-style="normal" | ||||
|        y="3679.27" | ||||
|        x="-3804.9949" | ||||
|        xml:space="preserve">rcu_cpu_starting()</text> | ||||
|        xml:space="preserve">rcutree_report_cpu_starting()</text> | ||||
|     <g | ||||
|        style="fill:none;stroke-width:0.025in" | ||||
|        id="g3107-7-5-0" | ||||
|  |  | |||
| Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB | 
|  | @ -1955,12 +1955,12 @@ if offline CPUs block an RCU grace period for too long. | |||
| 
 | ||||
| An offline CPU's quiescent state will be reported either: | ||||
| 
 | ||||
| 1.  As the CPU goes offline using RCU's hotplug notifier (rcu_report_dead()). | ||||
| 1.  As the CPU goes offline using RCU's hotplug notifier (rcutree_report_cpu_dead()). | ||||
| 2.  When grace period initialization (rcu_gp_init()) detects a | ||||
|     race either with CPU offlining or with a task unblocking on a leaf | ||||
|     ``rcu_node`` structure whose CPUs are all offline. | ||||
| 
 | ||||
| The CPU-online path (rcu_cpu_starting()) should never need to report | ||||
| The CPU-online path (rcutree_report_cpu_starting()) should never need to report | ||||
| a quiescent state for an offline CPU.  However, as a debugging measure, | ||||
| it does emit a warning if a quiescent state was not already reported | ||||
| for that CPU. | ||||
|  |  | |||
|  | @ -8,6 +8,15 @@ One of the most common uses of RCU is protecting read-mostly linked lists | |||
| that all of the required memory ordering is provided by the list macros. | ||||
| This document describes several list-based RCU use cases. | ||||
| 
 | ||||
| When iterating a list while holding the rcu_read_lock(), writers may | ||||
| modify the list.  The reader is guaranteed to see all of the elements | ||||
| which were added to the list before they acquired the rcu_read_lock() | ||||
| and are still on the list when they drop the rcu_read_unlock(). | ||||
| Elements which are added to, or removed from the list may or may not | ||||
| be seen.  If the writer calls list_replace_rcu(), the reader may see | ||||
| either the old element or the new element; they will not see both, | ||||
| nor will they see neither. | ||||
| 
 | ||||
| 
 | ||||
| Example 1: Read-mostly list: Deferred Destruction | ||||
| ------------------------------------------------- | ||||
|  |  | |||
|  | @ -59,8 +59,8 @@ experiment with should focus on Section 2.  People who prefer to start | |||
| with example uses should focus on Sections 3 and 4.  People who need to | ||||
| understand the RCU implementation should focus on Section 5, then dive | ||||
| into the kernel source code.  People who reason best by analogy should | ||||
| focus on Section 6.  Section 7 serves as an index to the docbook API | ||||
| documentation, and Section 8 is the traditional answer key. | ||||
| focus on Section 6 and 7.  Section 8 serves as an index to the docbook | ||||
| API documentation, and Section 9 is the traditional answer key. | ||||
| 
 | ||||
| So, start with the section that makes the most sense to you and your | ||||
| preferred method of learning.  If you need to know everything about | ||||
|  |  | |||
|  | @ -4820,6 +4820,13 @@ | |||
| 			Set maximum number of finished RCU callbacks to | ||||
| 			process in one batch. | ||||
| 
 | ||||
| 	rcutree.do_rcu_barrier=	[KNL] | ||||
| 			Request a call to rcu_barrier().  This is | ||||
| 			throttled so that userspace tests can safely | ||||
| 			hammer on the sysfs variable if they so choose. | ||||
| 			If triggered before the RCU grace-period machinery | ||||
| 			is fully active, this will error out with EAGAIN. | ||||
| 
 | ||||
| 	rcutree.dump_tree=	[KNL] | ||||
| 			Dump the structure of the rcu_node combining tree | ||||
| 			out at early boot.  This is used for diagnostic | ||||
|  | @ -5473,6 +5480,12 @@ | |||
| 			test until boot completes in order to avoid | ||||
| 			interference. | ||||
| 
 | ||||
| 	refscale.lookup_instances= [KNL] | ||||
| 			Number of data elements to use for the forms of | ||||
| 			SLAB_TYPESAFE_BY_RCU testing.  A negative number | ||||
| 			is negated and multiplied by nr_cpu_ids, while | ||||
| 			zero specifies nr_cpu_ids. | ||||
| 
 | ||||
| 	refscale.loops= [KNL] | ||||
| 			Set the number of loops over the synchronization | ||||
| 			primitive under test.  Increasing this number | ||||
|  |  | |||
|  | @ -215,7 +215,7 @@ asmlinkage notrace void secondary_start_kernel(void) | |||
| 	if (system_uses_irq_prio_masking()) | ||||
| 		init_gic_priority_masking(); | ||||
| 
 | ||||
| 	rcu_cpu_starting(cpu); | ||||
| 	rcutree_report_cpu_starting(cpu); | ||||
| 	trace_hardirqs_off(); | ||||
| 
 | ||||
| 	/*
 | ||||
|  | @ -401,7 +401,7 @@ void __noreturn cpu_die_early(void) | |||
| 
 | ||||
| 	/* Mark this CPU absent */ | ||||
| 	set_cpu_present(cpu, 0); | ||||
| 	rcu_report_dead(cpu); | ||||
| 	rcutree_report_cpu_dead(); | ||||
| 
 | ||||
| 	if (IS_ENABLED(CONFIG_HOTPLUG_CPU)) { | ||||
| 		update_cpu_boot_status(CPU_KILL_ME); | ||||
|  |  | |||
|  | @ -1629,7 +1629,7 @@ void start_secondary(void *unused) | |||
| 
 | ||||
| 	smp_store_cpu_info(cpu); | ||||
| 	set_dec(tb_ticks_per_jiffy); | ||||
| 	rcu_cpu_starting(cpu); | ||||
| 	rcutree_report_cpu_starting(cpu); | ||||
| 	cpu_callin_map[cpu] = 1; | ||||
| 
 | ||||
| 	if (smp_ops->setup_cpu) | ||||
|  |  | |||
|  | @ -898,7 +898,7 @@ static void smp_start_secondary(void *cpuvoid) | |||
| 	S390_lowcore.restart_flags = 0; | ||||
| 	restore_access_regs(S390_lowcore.access_regs_save_area); | ||||
| 	cpu_init(); | ||||
| 	rcu_cpu_starting(cpu); | ||||
| 	rcutree_report_cpu_starting(cpu); | ||||
| 	init_cpu_timer(); | ||||
| 	vtime_init(); | ||||
| 	vdso_getcpu_init(); | ||||
|  |  | |||
|  | @ -288,7 +288,7 @@ static void notrace start_secondary(void *unused) | |||
| 
 | ||||
| 	cpu_init(); | ||||
| 	fpu__init_cpu(); | ||||
| 	rcu_cpu_starting(raw_smp_processor_id()); | ||||
| 	rcutree_report_cpu_starting(raw_smp_processor_id()); | ||||
| 	x86_cpuinit.early_percpu_clock_init(); | ||||
| 
 | ||||
| 	ap_starting(); | ||||
|  |  | |||
|  | @ -566,7 +566,7 @@ enum | |||
|  * | ||||
|  * _ RCU: | ||||
|  * 	1) rcutree_migrate_callbacks() migrates the queue. | ||||
|  * 	2) rcu_report_dead() reports the final quiescent states. | ||||
|  * 	2) rcutree_report_cpu_dead() reports the final quiescent states. | ||||
|  * | ||||
|  * _ IRQ_POLL: irq_poll_cpu_dead() migrates the queue | ||||
|  */ | ||||
|  |  | |||
							
								
								
									
										32
									
								
								include/linux/rcu_notifier.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,32 @@ | |||
| /* SPDX-License-Identifier: GPL-2.0+ */ | ||||
| /*
 | ||||
|  * Read-Copy Update notifiers, initially RCU CPU stall notifier. | ||||
|  * Separate from rcupdate.h to avoid #include loops. | ||||
|  * | ||||
|  * Copyright (C) 2023 Paul E. McKenney. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __LINUX_RCU_NOTIFIER_H | ||||
| #define __LINUX_RCU_NOTIFIER_H | ||||
| 
 | ||||
| // Actions for RCU CPU stall notifier calls.
 | ||||
| #define RCU_STALL_NOTIFY_NORM	1 | ||||
| #define RCU_STALL_NOTIFY_EXP	2 | ||||
| 
 | ||||
| #ifdef CONFIG_RCU_STALL_COMMON | ||||
| 
 | ||||
| #include <linux/notifier.h> | ||||
| #include <linux/types.h> | ||||
| 
 | ||||
| int rcu_stall_chain_notifier_register(struct notifier_block *n); | ||||
| int rcu_stall_chain_notifier_unregister(struct notifier_block *n); | ||||
| 
 | ||||
| #else // #ifdef CONFIG_RCU_STALL_COMMON
 | ||||
| 
 | ||||
| // No RCU CPU stall warnings in Tiny RCU.
 | ||||
| static inline int rcu_stall_chain_notifier_register(struct notifier_block *n) { return -EEXIST; } | ||||
| static inline int rcu_stall_chain_notifier_unregister(struct notifier_block *n) { return -ENOENT; } | ||||
| 
 | ||||
| #endif // #else // #ifdef CONFIG_RCU_STALL_COMMON
 | ||||
| 
 | ||||
| #endif /* __LINUX_RCU_NOTIFIER_H */ | ||||
|  | @ -122,8 +122,6 @@ static inline void call_rcu_hurry(struct rcu_head *head, rcu_callback_t func) | |||
| void rcu_init(void); | ||||
| extern int rcu_scheduler_active; | ||||
| 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); | ||||
|  |  | |||
|  | @ -171,6 +171,6 @@ static inline void rcu_all_qs(void) { barrier(); } | |||
| #define rcutree_offline_cpu      NULL | ||||
| #define rcutree_dead_cpu         NULL | ||||
| #define rcutree_dying_cpu        NULL | ||||
| static inline void rcu_cpu_starting(unsigned int cpu) { } | ||||
| static inline void rcutree_report_cpu_starting(unsigned int cpu) { } | ||||
| 
 | ||||
| #endif /* __LINUX_RCUTINY_H */ | ||||
|  |  | |||
|  | @ -37,7 +37,6 @@ void synchronize_rcu_expedited(void); | |||
| void kvfree_call_rcu(struct rcu_head *head, void *ptr); | ||||
| 
 | ||||
| void rcu_barrier(void); | ||||
| bool rcu_eqs_special_set(int cpu); | ||||
| void rcu_momentary_dyntick_idle(void); | ||||
| void kfree_rcu_scheduler_running(void); | ||||
| bool rcu_gp_might_be_stalled(void); | ||||
|  | @ -111,9 +110,21 @@ void rcu_all_qs(void); | |||
| /* RCUtree hotplug events */ | ||||
| int rcutree_prepare_cpu(unsigned int cpu); | ||||
| int rcutree_online_cpu(unsigned int cpu); | ||||
| int rcutree_offline_cpu(unsigned int cpu); | ||||
| void rcutree_report_cpu_starting(unsigned int cpu); | ||||
| 
 | ||||
| #ifdef CONFIG_HOTPLUG_CPU | ||||
| int rcutree_dead_cpu(unsigned int cpu); | ||||
| int rcutree_dying_cpu(unsigned int cpu); | ||||
| void rcu_cpu_starting(unsigned int cpu); | ||||
| int rcutree_offline_cpu(unsigned int cpu); | ||||
| #else | ||||
| #define rcutree_dead_cpu NULL | ||||
| #define rcutree_dying_cpu NULL | ||||
| #define rcutree_offline_cpu NULL | ||||
| #endif | ||||
| 
 | ||||
| void rcutree_migrate_callbacks(int cpu); | ||||
| 
 | ||||
| /* Called from hotplug and also arm64 early secondary boot failure */ | ||||
| void rcutree_report_cpu_dead(void); | ||||
| 
 | ||||
| #endif /* __LINUX_RCUTREE_H */ | ||||
|  |  | |||
|  | @ -245,8 +245,9 @@ DEFINE_FREE(kfree, void *, if (_T) kfree(_T)) | |||
| size_t ksize(const void *objp); | ||||
| 
 | ||||
| #ifdef CONFIG_PRINTK | ||||
| bool kmem_valid_obj(void *object); | ||||
| void kmem_dump_obj(void *object); | ||||
| bool kmem_dump_obj(void *object); | ||||
| #else | ||||
| static inline bool kmem_dump_obj(void *object) { return false; } | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  |  | |||
							
								
								
									
										13
									
								
								kernel/cpu.c
									
									
									
									
									
								
							
							
						
						|  | @ -1372,7 +1372,14 @@ static int takedown_cpu(unsigned int cpu) | |||
| 	cpuhp_bp_sync_dead(cpu); | ||||
| 
 | ||||
| 	tick_cleanup_dead_cpu(cpu); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Callbacks must be re-integrated right away to the RCU state machine. | ||||
| 	 * Otherwise an RCU callback could block a further teardown function | ||||
| 	 * waiting for its completion. | ||||
| 	 */ | ||||
| 	rcutree_migrate_callbacks(cpu); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -1388,10 +1395,10 @@ void cpuhp_report_idle_dead(void) | |||
| 	struct cpuhp_cpu_state *st = this_cpu_ptr(&cpuhp_state); | ||||
| 
 | ||||
| 	BUG_ON(st->state != CPUHP_AP_OFFLINE); | ||||
| 	rcu_report_dead(smp_processor_id()); | ||||
| 	rcutree_report_cpu_dead(); | ||||
| 	st->state = CPUHP_AP_IDLE_DEAD; | ||||
| 	/*
 | ||||
| 	 * We cannot call complete after rcu_report_dead() so we delegate it | ||||
| 	 * We cannot call complete after rcutree_report_cpu_dead() so we delegate it | ||||
| 	 * to an online cpu. | ||||
| 	 */ | ||||
| 	smp_call_function_single(cpumask_first(cpu_online_mask), | ||||
|  | @ -1617,7 +1624,7 @@ void notify_cpu_starting(unsigned int cpu) | |||
| 	struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); | ||||
| 	enum cpuhp_state target = min((int)st->target, CPUHP_AP_ONLINE); | ||||
| 
 | ||||
| 	rcu_cpu_starting(cpu);	/* Enables RCU usage on this CPU. */ | ||||
| 	rcutree_report_cpu_starting(cpu);	/* Enables RCU usage on this CPU. */ | ||||
| 	cpumask_set_cpu(cpu, &cpus_booted_once_mask); | ||||
| 
 | ||||
| 	/*
 | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ | |||
| #ifndef __LINUX_RCU_H | ||||
| #define __LINUX_RCU_H | ||||
| 
 | ||||
| #include <linux/slab.h> | ||||
| #include <trace/events/rcu.h> | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -248,6 +249,12 @@ static inline void debug_rcu_head_unqueue(struct rcu_head *head) | |||
| } | ||||
| #endif	/* #else !CONFIG_DEBUG_OBJECTS_RCU_HEAD */ | ||||
| 
 | ||||
| static inline void debug_rcu_head_callback(struct rcu_head *rhp) | ||||
| { | ||||
| 	if (unlikely(!rhp->func)) | ||||
| 		kmem_dump_obj(rhp); | ||||
| } | ||||
| 
 | ||||
| extern int rcu_cpu_stall_suppress_at_boot; | ||||
| 
 | ||||
| static inline bool rcu_stall_is_suppressed_at_boot(void) | ||||
|  | @ -650,4 +657,10 @@ static inline bool rcu_cpu_beenfullyonline(int cpu) { return true; } | |||
| bool rcu_cpu_beenfullyonline(int cpu); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_RCU_STALL_COMMON | ||||
| int rcu_stall_notifier_call_chain(unsigned long val, void *v); | ||||
| #else // #ifdef CONFIG_RCU_STALL_COMMON
 | ||||
| static inline int rcu_stall_notifier_call_chain(unsigned long val, void *v) { return NOTIFY_DONE; } | ||||
| #endif // #else // #ifdef CONFIG_RCU_STALL_COMMON
 | ||||
| 
 | ||||
| #endif /* __LINUX_RCU_H */ | ||||
|  |  | |||
|  | @ -368,7 +368,7 @@ bool rcu_segcblist_entrain(struct rcu_segcblist *rsclp, | |||
| 	smp_mb(); /* Ensure counts are updated before callback is entrained. */ | ||||
| 	rhp->next = NULL; | ||||
| 	for (i = RCU_NEXT_TAIL; i > RCU_DONE_TAIL; i--) | ||||
| 		if (rsclp->tails[i] != rsclp->tails[i - 1]) | ||||
| 		if (!rcu_segcblist_segempty(rsclp, i)) | ||||
| 			break; | ||||
| 	rcu_segcblist_inc_seglen(rsclp, i); | ||||
| 	WRITE_ONCE(*rsclp->tails[i], rhp); | ||||
|  | @ -551,7 +551,7 @@ bool rcu_segcblist_accelerate(struct rcu_segcblist *rsclp, unsigned long seq) | |||
| 	 * as their ->gp_seq[] grace-period completion sequence number. | ||||
| 	 */ | ||||
| 	for (i = RCU_NEXT_READY_TAIL; i > RCU_DONE_TAIL; i--) | ||||
| 		if (rsclp->tails[i] != rsclp->tails[i - 1] && | ||||
| 		if (!rcu_segcblist_segempty(rsclp, i) && | ||||
| 		    ULONG_CMP_LT(rsclp->gp_seq[i], seq)) | ||||
| 			break; | ||||
| 
 | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ | |||
| #include <linux/spinlock.h> | ||||
| #include <linux/smp.h> | ||||
| #include <linux/rcupdate_wait.h> | ||||
| #include <linux/rcu_notifier.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/sched/signal.h> | ||||
| #include <uapi/linux/sched/types.h> | ||||
|  | @ -2428,6 +2429,16 @@ static int rcutorture_booster_init(unsigned int cpu) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int rcu_torture_stall_nf(struct notifier_block *nb, unsigned long v, void *ptr) | ||||
| { | ||||
| 	pr_info("%s: v=%lu, duration=%lu.\n", __func__, v, (unsigned long)ptr); | ||||
| 	return NOTIFY_OK; | ||||
| } | ||||
| 
 | ||||
| static struct notifier_block rcu_torture_stall_block = { | ||||
| 	.notifier_call = rcu_torture_stall_nf, | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * CPU-stall kthread.  It waits as specified by stall_cpu_holdoff, then | ||||
|  * induces a CPU stall for the time specified by stall_cpu. | ||||
|  | @ -2435,9 +2446,14 @@ static int rcutorture_booster_init(unsigned int cpu) | |||
| static int rcu_torture_stall(void *args) | ||||
| { | ||||
| 	int idx; | ||||
| 	int ret; | ||||
| 	unsigned long stop_at; | ||||
| 
 | ||||
| 	VERBOSE_TOROUT_STRING("rcu_torture_stall task started"); | ||||
| 	ret = rcu_stall_chain_notifier_register(&rcu_torture_stall_block); | ||||
| 	if (ret) | ||||
| 		pr_info("%s: rcu_stall_chain_notifier_register() returned %d, %sexpected.\n", | ||||
| 			__func__, ret, !IS_ENABLED(CONFIG_RCU_STALL_COMMON) ? "un" : ""); | ||||
| 	if (stall_cpu_holdoff > 0) { | ||||
| 		VERBOSE_TOROUT_STRING("rcu_torture_stall begin holdoff"); | ||||
| 		schedule_timeout_interruptible(stall_cpu_holdoff * HZ); | ||||
|  | @ -2481,6 +2497,11 @@ static int rcu_torture_stall(void *args) | |||
| 		cur_ops->readunlock(idx); | ||||
| 	} | ||||
| 	pr_alert("%s end.\n", __func__); | ||||
| 	if (!ret) { | ||||
| 		ret = rcu_stall_chain_notifier_unregister(&rcu_torture_stall_block); | ||||
| 		if (ret) | ||||
| 			pr_info("%s: rcu_stall_chain_notifier_unregister() returned %d.\n", __func__, ret); | ||||
| 	} | ||||
| 	torture_shutdown_absorb("rcu_torture_stall"); | ||||
| 	while (!kthread_should_stop()) | ||||
| 		schedule_timeout_interruptible(10 * HZ); | ||||
|  |  | |||
|  | @ -655,12 +655,12 @@ static void typesafe_delay_section(const int nloops, const int udl, const int nd | |||
| 			goto retry; | ||||
| 		} | ||||
| 		un_delay(udl, ndl); | ||||
| 		b = READ_ONCE(rtsp->a); | ||||
| 		// Remember, seqlock read-side release can fail.
 | ||||
| 		if (!rts_release(rtsp, start)) { | ||||
| 			rcu_read_unlock(); | ||||
| 			goto retry; | ||||
| 		} | ||||
| 		b = READ_ONCE(rtsp->a); | ||||
| 		WARN_ONCE(a != b, "Re-read of ->a changed from %u to %u.\n", a, b); | ||||
| 		b = rtsp->b; | ||||
| 		rcu_read_unlock(); | ||||
|  | @ -1025,8 +1025,8 @@ static void | |||
| ref_scale_print_module_parms(struct ref_scale_ops *cur_ops, const char *tag) | ||||
| { | ||||
| 	pr_alert("%s" SCALE_FLAG | ||||
| 		 "--- %s:  verbose=%d shutdown=%d holdoff=%d loops=%ld nreaders=%d nruns=%d readdelay=%d\n", scale_type, tag, | ||||
| 		 verbose, shutdown, holdoff, loops, nreaders, nruns, readdelay); | ||||
| 		 "--- %s:  verbose=%d verbose_batched=%d shutdown=%d holdoff=%d lookup_instances=%ld loops=%ld nreaders=%d nruns=%d readdelay=%d\n", scale_type, tag, | ||||
| 		 verbose, verbose_batched, shutdown, holdoff, lookup_instances, loops, nreaders, nruns, readdelay); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
|  |  | |||
|  | @ -138,6 +138,7 @@ void srcu_drive_gp(struct work_struct *wp) | |||
| 	while (lh) { | ||||
| 		rhp = lh; | ||||
| 		lh = lh->next; | ||||
| 		debug_rcu_head_callback(rhp); | ||||
| 		local_bh_disable(); | ||||
| 		rhp->func(rhp); | ||||
| 		local_bh_enable(); | ||||
|  |  | |||
|  | @ -223,7 +223,7 @@ static bool init_srcu_struct_nodes(struct srcu_struct *ssp, gfp_t gfp_flags) | |||
| 				snp->grplo = cpu; | ||||
| 			snp->grphi = cpu; | ||||
| 		} | ||||
| 		sdp->grpmask = 1 << (cpu - sdp->mynode->grplo); | ||||
| 		sdp->grpmask = 1UL << (cpu - sdp->mynode->grplo); | ||||
| 	} | ||||
| 	smp_store_release(&ssp->srcu_sup->srcu_size_state, SRCU_SIZE_WAIT_BARRIER); | ||||
| 	return true; | ||||
|  | @ -255,29 +255,31 @@ static int init_srcu_struct_fields(struct srcu_struct *ssp, bool is_static) | |||
| 	ssp->srcu_sup->sda_is_static = is_static; | ||||
| 	if (!is_static) | ||||
| 		ssp->sda = alloc_percpu(struct srcu_data); | ||||
| 	if (!ssp->sda) { | ||||
| 		if (!is_static) | ||||
| 			kfree(ssp->srcu_sup); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	if (!ssp->sda) | ||||
| 		goto err_free_sup; | ||||
| 	init_srcu_struct_data(ssp); | ||||
| 	ssp->srcu_sup->srcu_gp_seq_needed_exp = 0; | ||||
| 	ssp->srcu_sup->srcu_last_gp_end = ktime_get_mono_fast_ns(); | ||||
| 	if (READ_ONCE(ssp->srcu_sup->srcu_size_state) == SRCU_SIZE_SMALL && SRCU_SIZING_IS_INIT()) { | ||||
| 		if (!init_srcu_struct_nodes(ssp, GFP_ATOMIC)) { | ||||
| 			if (!ssp->srcu_sup->sda_is_static) { | ||||
| 				free_percpu(ssp->sda); | ||||
| 				ssp->sda = NULL; | ||||
| 				kfree(ssp->srcu_sup); | ||||
| 				return -ENOMEM; | ||||
| 			} | ||||
| 		} else { | ||||
| 			WRITE_ONCE(ssp->srcu_sup->srcu_size_state, SRCU_SIZE_BIG); | ||||
| 		} | ||||
| 		if (!init_srcu_struct_nodes(ssp, GFP_ATOMIC)) | ||||
| 			goto err_free_sda; | ||||
| 		WRITE_ONCE(ssp->srcu_sup->srcu_size_state, SRCU_SIZE_BIG); | ||||
| 	} | ||||
| 	ssp->srcu_sup->srcu_ssp = ssp; | ||||
| 	smp_store_release(&ssp->srcu_sup->srcu_gp_seq_needed, 0); /* Init done. */ | ||||
| 	return 0; | ||||
| 
 | ||||
| err_free_sda: | ||||
| 	if (!is_static) { | ||||
| 		free_percpu(ssp->sda); | ||||
| 		ssp->sda = NULL; | ||||
| 	} | ||||
| err_free_sup: | ||||
| 	if (!is_static) { | ||||
| 		kfree(ssp->srcu_sup); | ||||
| 		ssp->srcu_sup = NULL; | ||||
| 	} | ||||
| 	return -ENOMEM; | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_DEBUG_LOCK_ALLOC | ||||
|  | @ -782,8 +784,7 @@ static void srcu_gp_start(struct srcu_struct *ssp) | |||
| 	spin_lock_rcu_node(sdp);  /* Interrupts already disabled. */ | ||||
| 	rcu_segcblist_advance(&sdp->srcu_cblist, | ||||
| 			      rcu_seq_current(&ssp->srcu_sup->srcu_gp_seq)); | ||||
| 	(void)rcu_segcblist_accelerate(&sdp->srcu_cblist, | ||||
| 				       rcu_seq_snap(&ssp->srcu_sup->srcu_gp_seq)); | ||||
| 	WARN_ON_ONCE(!rcu_segcblist_segempty(&sdp->srcu_cblist, RCU_NEXT_TAIL)); | ||||
| 	spin_unlock_rcu_node(sdp);  /* Interrupts remain disabled. */ | ||||
| 	WRITE_ONCE(ssp->srcu_sup->srcu_gp_start, jiffies); | ||||
| 	WRITE_ONCE(ssp->srcu_sup->srcu_n_exp_nodelay, 0); | ||||
|  | @ -833,7 +834,7 @@ static void srcu_schedule_cbs_snp(struct srcu_struct *ssp, struct srcu_node *snp | |||
| 	int cpu; | ||||
| 
 | ||||
| 	for (cpu = snp->grplo; cpu <= snp->grphi; cpu++) { | ||||
| 		if (!(mask & (1 << (cpu - snp->grplo)))) | ||||
| 		if (!(mask & (1UL << (cpu - snp->grplo)))) | ||||
| 			continue; | ||||
| 		srcu_schedule_cbs_sdp(per_cpu_ptr(ssp->sda, cpu), delay); | ||||
| 	} | ||||
|  | @ -1242,10 +1243,37 @@ static unsigned long srcu_gp_start_if_needed(struct srcu_struct *ssp, | |||
| 	spin_lock_irqsave_sdp_contention(sdp, &flags); | ||||
| 	if (rhp) | ||||
| 		rcu_segcblist_enqueue(&sdp->srcu_cblist, rhp); | ||||
| 	/*
 | ||||
| 	 * The snapshot for acceleration must be taken _before_ the read of the | ||||
| 	 * current gp sequence used for advancing, otherwise advancing may fail | ||||
| 	 * and acceleration may then fail too. | ||||
| 	 * | ||||
| 	 * This could happen if: | ||||
| 	 * | ||||
| 	 *  1) The RCU_WAIT_TAIL segment has callbacks (gp_num = X + 4) and the | ||||
| 	 *     RCU_NEXT_READY_TAIL also has callbacks (gp_num = X + 8). | ||||
| 	 * | ||||
| 	 *  2) The grace period for RCU_WAIT_TAIL is seen as started but not | ||||
| 	 *     completed so rcu_seq_current() returns X + SRCU_STATE_SCAN1. | ||||
| 	 * | ||||
| 	 *  3) This value is passed to rcu_segcblist_advance() which can't move | ||||
| 	 *     any segment forward and fails. | ||||
| 	 * | ||||
| 	 *  4) srcu_gp_start_if_needed() still proceeds with callback acceleration. | ||||
| 	 *     But then the call to rcu_seq_snap() observes the grace period for the | ||||
| 	 *     RCU_WAIT_TAIL segment as completed and the subsequent one for the | ||||
| 	 *     RCU_NEXT_READY_TAIL segment as started (ie: X + 4 + SRCU_STATE_SCAN1) | ||||
| 	 *     so it returns a snapshot of the next grace period, which is X + 12. | ||||
| 	 * | ||||
| 	 *  5) The value of X + 12 is passed to rcu_segcblist_accelerate() but the | ||||
| 	 *     freshly enqueued callback in RCU_NEXT_TAIL can't move to | ||||
| 	 *     RCU_NEXT_READY_TAIL which already has callbacks for a previous grace | ||||
| 	 *     period (gp_num = X + 8). So acceleration fails. | ||||
| 	 */ | ||||
| 	s = rcu_seq_snap(&ssp->srcu_sup->srcu_gp_seq); | ||||
| 	rcu_segcblist_advance(&sdp->srcu_cblist, | ||||
| 			      rcu_seq_current(&ssp->srcu_sup->srcu_gp_seq)); | ||||
| 	s = rcu_seq_snap(&ssp->srcu_sup->srcu_gp_seq); | ||||
| 	(void)rcu_segcblist_accelerate(&sdp->srcu_cblist, s); | ||||
| 	WARN_ON_ONCE(!rcu_segcblist_accelerate(&sdp->srcu_cblist, s) && rhp); | ||||
| 	if (ULONG_CMP_LT(sdp->srcu_gp_seq_needed, s)) { | ||||
| 		sdp->srcu_gp_seq_needed = s; | ||||
| 		needgp = true; | ||||
|  | @ -1692,6 +1720,7 @@ static void srcu_invoke_callbacks(struct work_struct *work) | |||
| 	ssp = sdp->ssp; | ||||
| 	rcu_cblist_init(&ready_cbs); | ||||
| 	spin_lock_irq_rcu_node(sdp); | ||||
| 	WARN_ON_ONCE(!rcu_segcblist_segempty(&sdp->srcu_cblist, RCU_NEXT_TAIL)); | ||||
| 	rcu_segcblist_advance(&sdp->srcu_cblist, | ||||
| 			      rcu_seq_current(&ssp->srcu_sup->srcu_gp_seq)); | ||||
| 	if (sdp->srcu_cblist_invoking || | ||||
|  | @ -1708,6 +1737,7 @@ static void srcu_invoke_callbacks(struct work_struct *work) | |||
| 	rhp = rcu_cblist_dequeue(&ready_cbs); | ||||
| 	for (; rhp != NULL; rhp = rcu_cblist_dequeue(&ready_cbs)) { | ||||
| 		debug_rcu_head_unqueue(rhp); | ||||
| 		debug_rcu_head_callback(rhp); | ||||
| 		local_bh_disable(); | ||||
| 		rhp->func(rhp); | ||||
| 		local_bh_enable(); | ||||
|  | @ -1720,8 +1750,6 @@ static void srcu_invoke_callbacks(struct work_struct *work) | |||
| 	 */ | ||||
| 	spin_lock_irq_rcu_node(sdp); | ||||
| 	rcu_segcblist_add_len(&sdp->srcu_cblist, -len); | ||||
| 	(void)rcu_segcblist_accelerate(&sdp->srcu_cblist, | ||||
| 				       rcu_seq_snap(&ssp->srcu_sup->srcu_gp_seq)); | ||||
| 	sdp->srcu_cblist_invoking = false; | ||||
| 	more = rcu_segcblist_ready_cbs(&sdp->srcu_cblist); | ||||
| 	spin_unlock_irq_rcu_node(sdp); | ||||
|  |  | |||
|  | @ -432,6 +432,7 @@ static void rcu_barrier_tasks_generic(struct rcu_tasks *rtp) | |||
| static int rcu_tasks_need_gpcb(struct rcu_tasks *rtp) | ||||
| { | ||||
| 	int cpu; | ||||
| 	int dequeue_limit; | ||||
| 	unsigned long flags; | ||||
| 	bool gpdone = poll_state_synchronize_rcu(rtp->percpu_dequeue_gpseq); | ||||
| 	long n; | ||||
|  | @ -439,7 +440,8 @@ static int rcu_tasks_need_gpcb(struct rcu_tasks *rtp) | |||
| 	long ncbsnz = 0; | ||||
| 	int needgpcb = 0; | ||||
| 
 | ||||
| 	for (cpu = 0; cpu < smp_load_acquire(&rtp->percpu_dequeue_lim); cpu++) { | ||||
| 	dequeue_limit = smp_load_acquire(&rtp->percpu_dequeue_lim); | ||||
| 	for (cpu = 0; cpu < dequeue_limit; cpu++) { | ||||
| 		struct rcu_tasks_percpu *rtpcp = per_cpu_ptr(rtp->rtpcpu, cpu); | ||||
| 
 | ||||
| 		/* Advance and accelerate any new callbacks. */ | ||||
|  | @ -538,6 +540,7 @@ static void rcu_tasks_invoke_cbs(struct rcu_tasks *rtp, struct rcu_tasks_percpu | |||
| 	raw_spin_unlock_irqrestore_rcu_node(rtpcp, flags); | ||||
| 	len = rcl.len; | ||||
| 	for (rhp = rcu_cblist_dequeue(&rcl); rhp; rhp = rcu_cblist_dequeue(&rcl)) { | ||||
| 		debug_rcu_head_callback(rhp); | ||||
| 		local_bh_disable(); | ||||
| 		rhp->func(rhp); | ||||
| 		local_bh_enable(); | ||||
|  | @ -1084,7 +1087,7 @@ void rcu_barrier_tasks(void) | |||
| } | ||||
| EXPORT_SYMBOL_GPL(rcu_barrier_tasks); | ||||
| 
 | ||||
| int rcu_tasks_lazy_ms = -1; | ||||
| static int rcu_tasks_lazy_ms = -1; | ||||
| module_param(rcu_tasks_lazy_ms, int, 0444); | ||||
| 
 | ||||
| static int __init rcu_spawn_tasks_kthread(void) | ||||
|  | @ -1979,20 +1982,22 @@ static void test_rcu_tasks_callback(struct rcu_head *rhp) | |||
| 
 | ||||
| static void rcu_tasks_initiate_self_tests(void) | ||||
| { | ||||
| 	pr_info("Running RCU-tasks wait API self tests\n"); | ||||
| #ifdef CONFIG_TASKS_RCU | ||||
| 	pr_info("Running RCU Tasks wait API self tests\n"); | ||||
| 	tests[0].runstart = jiffies; | ||||
| 	synchronize_rcu_tasks(); | ||||
| 	call_rcu_tasks(&tests[0].rh, test_rcu_tasks_callback); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_TASKS_RUDE_RCU | ||||
| 	pr_info("Running RCU Tasks Rude wait API self tests\n"); | ||||
| 	tests[1].runstart = jiffies; | ||||
| 	synchronize_rcu_tasks_rude(); | ||||
| 	call_rcu_tasks_rude(&tests[1].rh, test_rcu_tasks_callback); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_TASKS_TRACE_RCU | ||||
| 	pr_info("Running RCU Tasks Trace wait API self tests\n"); | ||||
| 	tests[2].runstart = jiffies; | ||||
| 	synchronize_rcu_tasks_trace(); | ||||
| 	call_rcu_tasks_trace(&tests[2].rh, test_rcu_tasks_callback); | ||||
|  |  | |||
|  | @ -97,6 +97,7 @@ static inline bool rcu_reclaim_tiny(struct rcu_head *head) | |||
| 
 | ||||
| 	trace_rcu_invoke_callback("", head); | ||||
| 	f = head->func; | ||||
| 	debug_rcu_head_callback(head); | ||||
| 	WRITE_ONCE(head->func, (rcu_callback_t)0L); | ||||
| 	f(head); | ||||
| 	rcu_lock_release(&rcu_callback_map); | ||||
|  |  | |||
|  | @ -31,6 +31,7 @@ | |||
| #include <linux/bitops.h> | ||||
| #include <linux/export.h> | ||||
| #include <linux/completion.h> | ||||
| #include <linux/kmemleak.h> | ||||
| #include <linux/moduleparam.h> | ||||
| #include <linux/panic.h> | ||||
| #include <linux/panic_notifier.h> | ||||
|  | @ -1260,7 +1261,7 @@ EXPORT_SYMBOL_GPL(rcu_gp_slow_register); | |||
| /* Unregister a counter, with NULL for not caring which. */ | ||||
| void rcu_gp_slow_unregister(atomic_t *rgssp) | ||||
| { | ||||
| 	WARN_ON_ONCE(rgssp && rgssp != rcu_gp_slow_suppress); | ||||
| 	WARN_ON_ONCE(rgssp && rgssp != rcu_gp_slow_suppress && rcu_gp_slow_suppress != NULL); | ||||
| 
 | ||||
| 	WRITE_ONCE(rcu_gp_slow_suppress, NULL); | ||||
| } | ||||
|  | @ -1556,10 +1557,22 @@ static bool rcu_gp_fqs_check_wake(int *gfp) | |||
|  */ | ||||
| static void rcu_gp_fqs(bool first_time) | ||||
| { | ||||
| 	int nr_fqs = READ_ONCE(rcu_state.nr_fqs_jiffies_stall); | ||||
| 	struct rcu_node *rnp = rcu_get_root(); | ||||
| 
 | ||||
| 	WRITE_ONCE(rcu_state.gp_activity, jiffies); | ||||
| 	WRITE_ONCE(rcu_state.n_force_qs, rcu_state.n_force_qs + 1); | ||||
| 
 | ||||
| 	WARN_ON_ONCE(nr_fqs > 3); | ||||
| 	/* Only countdown nr_fqs for stall purposes if jiffies moves. */ | ||||
| 	if (nr_fqs) { | ||||
| 		if (nr_fqs == 1) { | ||||
| 			WRITE_ONCE(rcu_state.jiffies_stall, | ||||
| 				   jiffies + rcu_jiffies_till_stall_check()); | ||||
| 		} | ||||
| 		WRITE_ONCE(rcu_state.nr_fqs_jiffies_stall, --nr_fqs); | ||||
| 	} | ||||
| 
 | ||||
| 	if (first_time) { | ||||
| 		/* Collect dyntick-idle snapshots. */ | ||||
| 		force_qs_rnp(dyntick_save_progress_counter); | ||||
|  | @ -2135,6 +2148,7 @@ static void rcu_do_batch(struct rcu_data *rdp) | |||
| 		trace_rcu_invoke_callback(rcu_state.name, rhp); | ||||
| 
 | ||||
| 		f = rhp->func; | ||||
| 		debug_rcu_head_callback(rhp); | ||||
| 		WRITE_ONCE(rhp->func, (rcu_callback_t)0L); | ||||
| 		f(rhp); | ||||
| 
 | ||||
|  | @ -2713,7 +2727,7 @@ __call_rcu_common(struct rcu_head *head, rcu_callback_t func, bool lazy_in) | |||
|  */ | ||||
| void call_rcu_hurry(struct rcu_head *head, rcu_callback_t func) | ||||
| { | ||||
| 	return __call_rcu_common(head, func, false); | ||||
| 	__call_rcu_common(head, func, false); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(call_rcu_hurry); | ||||
| #endif | ||||
|  | @ -2764,7 +2778,7 @@ EXPORT_SYMBOL_GPL(call_rcu_hurry); | |||
|  */ | ||||
| void call_rcu(struct rcu_head *head, rcu_callback_t func) | ||||
| { | ||||
| 	return __call_rcu_common(head, func, IS_ENABLED(CONFIG_RCU_LAZY)); | ||||
| 	__call_rcu_common(head, func, IS_ENABLED(CONFIG_RCU_LAZY)); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(call_rcu); | ||||
| 
 | ||||
|  | @ -3388,6 +3402,14 @@ void kvfree_call_rcu(struct rcu_head *head, void *ptr) | |||
| 		success = true; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The kvfree_rcu() caller considers the pointer freed at this point | ||||
| 	 * and likely removes any references to it. Since the actual slab | ||||
| 	 * freeing (and kmemleak_free()) is deferred, tell kmemleak to ignore | ||||
| 	 * this object (no scanning or false positives reporting). | ||||
| 	 */ | ||||
| 	kmemleak_ignore(ptr); | ||||
| 
 | ||||
| 	// Set timer to drain after KFREE_DRAIN_JIFFIES.
 | ||||
| 	if (rcu_scheduler_active == RCU_SCHEDULER_RUNNING) | ||||
| 		schedule_delayed_monitor_work(krcp); | ||||
|  | @ -4083,6 +4105,82 @@ void rcu_barrier(void) | |||
| } | ||||
| EXPORT_SYMBOL_GPL(rcu_barrier); | ||||
| 
 | ||||
| static unsigned long rcu_barrier_last_throttle; | ||||
| 
 | ||||
| /**
 | ||||
|  * rcu_barrier_throttled - Do rcu_barrier(), but limit to one per second | ||||
|  * | ||||
|  * This can be thought of as guard rails around rcu_barrier() that | ||||
|  * permits unrestricted userspace use, at least assuming the hardware's | ||||
|  * try_cmpxchg() is robust.  There will be at most one call per second to | ||||
|  * rcu_barrier() system-wide from use of this function, which means that | ||||
|  * callers might needlessly wait a second or three. | ||||
|  * | ||||
|  * This is intended for use by test suites to avoid OOM by flushing RCU | ||||
|  * callbacks from the previous test before starting the next.  See the | ||||
|  * rcutree.do_rcu_barrier module parameter for more information. | ||||
|  * | ||||
|  * Why not simply make rcu_barrier() more scalable?  That might be | ||||
|  * the eventual endpoint, but let's keep it simple for the time being. | ||||
|  * Note that the module parameter infrastructure serializes calls to a | ||||
|  * given .set() function, but should concurrent .set() invocation ever be | ||||
|  * possible, we are ready! | ||||
|  */ | ||||
| static void rcu_barrier_throttled(void) | ||||
| { | ||||
| 	unsigned long j = jiffies; | ||||
| 	unsigned long old = READ_ONCE(rcu_barrier_last_throttle); | ||||
| 	unsigned long s = rcu_seq_snap(&rcu_state.barrier_sequence); | ||||
| 
 | ||||
| 	while (time_in_range(j, old, old + HZ / 16) || | ||||
| 	       !try_cmpxchg(&rcu_barrier_last_throttle, &old, j)) { | ||||
| 		schedule_timeout_idle(HZ / 16); | ||||
| 		if (rcu_seq_done(&rcu_state.barrier_sequence, s)) { | ||||
| 			smp_mb(); /* caller's subsequent code after above check. */ | ||||
| 			return; | ||||
| 		} | ||||
| 		j = jiffies; | ||||
| 		old = READ_ONCE(rcu_barrier_last_throttle); | ||||
| 	} | ||||
| 	rcu_barrier(); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Invoke rcu_barrier_throttled() when a rcutree.do_rcu_barrier | ||||
|  * request arrives.  We insist on a true value to allow for possible | ||||
|  * future expansion. | ||||
|  */ | ||||
| static int param_set_do_rcu_barrier(const char *val, const struct kernel_param *kp) | ||||
| { | ||||
| 	bool b; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (rcu_scheduler_active != RCU_SCHEDULER_RUNNING) | ||||
| 		return -EAGAIN; | ||||
| 	ret = kstrtobool(val, &b); | ||||
| 	if (!ret && b) { | ||||
| 		atomic_inc((atomic_t *)kp->arg); | ||||
| 		rcu_barrier_throttled(); | ||||
| 		atomic_dec((atomic_t *)kp->arg); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Output the number of outstanding rcutree.do_rcu_barrier requests. | ||||
|  */ | ||||
| static int param_get_do_rcu_barrier(char *buffer, const struct kernel_param *kp) | ||||
| { | ||||
| 	return sprintf(buffer, "%d\n", atomic_read((atomic_t *)kp->arg)); | ||||
| } | ||||
| 
 | ||||
| static const struct kernel_param_ops do_rcu_barrier_ops = { | ||||
| 	.set = param_set_do_rcu_barrier, | ||||
| 	.get = param_get_do_rcu_barrier, | ||||
| }; | ||||
| static atomic_t do_rcu_barrier; | ||||
| module_param_cb(do_rcu_barrier, &do_rcu_barrier_ops, &do_rcu_barrier, 0644); | ||||
| 
 | ||||
| /*
 | ||||
|  * Compute the mask of online CPUs for the specified rcu_node structure. | ||||
|  * This will not be stable unless the rcu_node structure's ->lock is | ||||
|  | @ -4130,7 +4228,7 @@ bool rcu_lockdep_current_cpu_online(void) | |||
| 	rdp = this_cpu_ptr(&rcu_data); | ||||
| 	/*
 | ||||
| 	 * Strictly, we care here about the case where the current CPU is | ||||
| 	 * in rcu_cpu_starting() and thus has an excuse for rdp->grpmask | ||||
| 	 * in rcutree_report_cpu_starting() and thus has an excuse for rdp->grpmask | ||||
| 	 * not being up to date. So arch_spin_is_locked() might have a | ||||
| 	 * false positive if it's held by some *other* CPU, but that's | ||||
| 	 * OK because that just means a false *negative* on the warning. | ||||
|  | @ -4151,25 +4249,6 @@ static bool rcu_init_invoked(void) | |||
| 	return !!rcu_state.n_online_cpus; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Near the end of the offline process.  Trace the fact that this CPU | ||||
|  * is going offline. | ||||
|  */ | ||||
| int rcutree_dying_cpu(unsigned int cpu) | ||||
| { | ||||
| 	bool blkd; | ||||
| 	struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu); | ||||
| 	struct rcu_node *rnp = rdp->mynode; | ||||
| 
 | ||||
| 	if (!IS_ENABLED(CONFIG_HOTPLUG_CPU)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	blkd = !!(READ_ONCE(rnp->qsmask) & rdp->grpmask); | ||||
| 	trace_rcu_grace_period(rcu_state.name, READ_ONCE(rnp->gp_seq), | ||||
| 			       blkd ? TPS("cpuofl-bgp") : TPS("cpuofl")); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * All CPUs for the specified rcu_node structure have gone offline, | ||||
|  * and all tasks that were preempted within an RCU read-side critical | ||||
|  | @ -4215,23 +4294,6 @@ static void rcu_cleanup_dead_rnp(struct rcu_node *rnp_leaf) | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * The CPU has been completely removed, and some other CPU is reporting | ||||
|  * this fact from process context.  Do the remainder of the cleanup. | ||||
|  * There can only be one CPU hotplug operation at a time, so no need for | ||||
|  * explicit locking. | ||||
|  */ | ||||
| int rcutree_dead_cpu(unsigned int cpu) | ||||
| { | ||||
| 	if (!IS_ENABLED(CONFIG_HOTPLUG_CPU)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	WRITE_ONCE(rcu_state.n_online_cpus, rcu_state.n_online_cpus - 1); | ||||
| 	// Stop-machine done, so allow nohz_full to disable tick.
 | ||||
| 	tick_dep_clear(TICK_DEP_BIT_RCU); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Propagate ->qsinitmask bits up the rcu_node tree to account for the | ||||
|  * first CPU in a given leaf rcu_node structure coming online.  The caller | ||||
|  | @ -4384,29 +4446,6 @@ int rcutree_online_cpu(unsigned int cpu) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Near the beginning of the process.  The CPU is still very much alive | ||||
|  * with pretty much all services enabled. | ||||
|  */ | ||||
| int rcutree_offline_cpu(unsigned int cpu) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	struct rcu_data *rdp; | ||||
| 	struct rcu_node *rnp; | ||||
| 
 | ||||
| 	rdp = per_cpu_ptr(&rcu_data, cpu); | ||||
| 	rnp = rdp->mynode; | ||||
| 	raw_spin_lock_irqsave_rcu_node(rnp, flags); | ||||
| 	rnp->ffmask &= ~rdp->grpmask; | ||||
| 	raw_spin_unlock_irqrestore_rcu_node(rnp, flags); | ||||
| 
 | ||||
| 	rcutree_affinity_setting(cpu, cpu); | ||||
| 
 | ||||
| 	// nohz_full CPUs need the tick for stop-machine to work quickly
 | ||||
| 	tick_dep_set(TICK_DEP_BIT_RCU); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Mark the specified CPU as being online so that subsequent grace periods | ||||
|  * (both expedited and normal) will wait on it.  Note that this means that | ||||
|  | @ -4418,8 +4457,10 @@ int rcutree_offline_cpu(unsigned int cpu) | |||
|  * from the incoming CPU rather than from the cpuhp_step mechanism. | ||||
|  * This is because this function must be invoked at a precise location. | ||||
|  * This incoming CPU must not have enabled interrupts yet. | ||||
|  * | ||||
|  * This mirrors the effects of rcutree_report_cpu_dead(). | ||||
|  */ | ||||
| void rcu_cpu_starting(unsigned int cpu) | ||||
| void rcutree_report_cpu_starting(unsigned int cpu) | ||||
| { | ||||
| 	unsigned long mask; | ||||
| 	struct rcu_data *rdp; | ||||
|  | @ -4473,14 +4514,21 @@ void rcu_cpu_starting(unsigned int cpu) | |||
|  * Note that this function is special in that it is invoked directly | ||||
|  * from the outgoing CPU rather than from the cpuhp_step mechanism. | ||||
|  * This is because this function must be invoked at a precise location. | ||||
|  * | ||||
|  * This mirrors the effect of rcutree_report_cpu_starting(). | ||||
|  */ | ||||
| void rcu_report_dead(unsigned int cpu) | ||||
| void rcutree_report_cpu_dead(void) | ||||
| { | ||||
| 	unsigned long flags, seq_flags; | ||||
| 	unsigned long flags; | ||||
| 	unsigned long mask; | ||||
| 	struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu); | ||||
| 	struct rcu_data *rdp = this_cpu_ptr(&rcu_data); | ||||
| 	struct rcu_node *rnp = rdp->mynode;  /* Outgoing CPU's rdp & rnp. */ | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * IRQS must be disabled from now on and until the CPU dies, or an interrupt | ||||
| 	 * may introduce a new READ-side while it is actually off the QS masks. | ||||
| 	 */ | ||||
| 	lockdep_assert_irqs_disabled(); | ||||
| 	// Do any dangling deferred wakeups.
 | ||||
| 	do_nocb_deferred_wakeup(rdp); | ||||
| 
 | ||||
|  | @ -4488,7 +4536,6 @@ void rcu_report_dead(unsigned int cpu) | |||
| 
 | ||||
| 	/* Remove outgoing CPU from mask in the leaf rcu_node structure. */ | ||||
| 	mask = rdp->grpmask; | ||||
| 	local_irq_save(seq_flags); | ||||
| 	arch_spin_lock(&rcu_state.ofl_lock); | ||||
| 	raw_spin_lock_irqsave_rcu_node(rnp, flags); /* Enforce GP memory-order guarantee. */ | ||||
| 	rdp->rcu_ofl_gp_seq = READ_ONCE(rcu_state.gp_seq); | ||||
|  | @ -4502,8 +4549,6 @@ void rcu_report_dead(unsigned int cpu) | |||
| 	WRITE_ONCE(rnp->qsmaskinitnext, rnp->qsmaskinitnext & ~mask); | ||||
| 	raw_spin_unlock_irqrestore_rcu_node(rnp, flags); | ||||
| 	arch_spin_unlock(&rcu_state.ofl_lock); | ||||
| 	local_irq_restore(seq_flags); | ||||
| 
 | ||||
| 	rdp->cpu_started = false; | ||||
| } | ||||
| 
 | ||||
|  | @ -4558,7 +4603,60 @@ void rcutree_migrate_callbacks(int cpu) | |||
| 		  cpu, rcu_segcblist_n_cbs(&rdp->cblist), | ||||
| 		  rcu_segcblist_first_cb(&rdp->cblist)); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * The CPU has been completely removed, and some other CPU is reporting | ||||
|  * this fact from process context.  Do the remainder of the cleanup. | ||||
|  * There can only be one CPU hotplug operation at a time, so no need for | ||||
|  * explicit locking. | ||||
|  */ | ||||
| int rcutree_dead_cpu(unsigned int cpu) | ||||
| { | ||||
| 	WRITE_ONCE(rcu_state.n_online_cpus, rcu_state.n_online_cpus - 1); | ||||
| 	// Stop-machine done, so allow nohz_full to disable tick.
 | ||||
| 	tick_dep_clear(TICK_DEP_BIT_RCU); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Near the end of the offline process.  Trace the fact that this CPU | ||||
|  * is going offline. | ||||
|  */ | ||||
| int rcutree_dying_cpu(unsigned int cpu) | ||||
| { | ||||
| 	bool blkd; | ||||
| 	struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu); | ||||
| 	struct rcu_node *rnp = rdp->mynode; | ||||
| 
 | ||||
| 	blkd = !!(READ_ONCE(rnp->qsmask) & rdp->grpmask); | ||||
| 	trace_rcu_grace_period(rcu_state.name, READ_ONCE(rnp->gp_seq), | ||||
| 			       blkd ? TPS("cpuofl-bgp") : TPS("cpuofl")); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Near the beginning of the process.  The CPU is still very much alive | ||||
|  * with pretty much all services enabled. | ||||
|  */ | ||||
| int rcutree_offline_cpu(unsigned int cpu) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	struct rcu_data *rdp; | ||||
| 	struct rcu_node *rnp; | ||||
| 
 | ||||
| 	rdp = per_cpu_ptr(&rcu_data, cpu); | ||||
| 	rnp = rdp->mynode; | ||||
| 	raw_spin_lock_irqsave_rcu_node(rnp, flags); | ||||
| 	rnp->ffmask &= ~rdp->grpmask; | ||||
| 	raw_spin_unlock_irqrestore_rcu_node(rnp, flags); | ||||
| 
 | ||||
| 	rcutree_affinity_setting(cpu, cpu); | ||||
| 
 | ||||
| 	// nohz_full CPUs need the tick for stop-machine to work quickly
 | ||||
| 	tick_dep_set(TICK_DEP_BIT_RCU); | ||||
| 	return 0; | ||||
| } | ||||
| #endif /* #ifdef CONFIG_HOTPLUG_CPU */ | ||||
| 
 | ||||
| /*
 | ||||
|  * On non-huge systems, use expedited RCU grace periods to make suspend | ||||
|  | @ -4990,7 +5088,7 @@ void __init rcu_init(void) | |||
| 	pm_notifier(rcu_pm_notify, 0); | ||||
| 	WARN_ON(num_online_cpus() > 1); // Only one CPU this early in boot.
 | ||||
| 	rcutree_prepare_cpu(cpu); | ||||
| 	rcu_cpu_starting(cpu); | ||||
| 	rcutree_report_cpu_starting(cpu); | ||||
| 	rcutree_online_cpu(cpu); | ||||
| 
 | ||||
| 	/* Create workqueue for Tree SRCU and for expedited GPs. */ | ||||
|  |  | |||
|  | @ -386,6 +386,10 @@ struct rcu_state { | |||
| 						/*  in jiffies. */ | ||||
| 	unsigned long jiffies_stall;		/* Time at which to check */ | ||||
| 						/*  for CPU stalls. */ | ||||
| 	int nr_fqs_jiffies_stall;		/* Number of fqs loops after
 | ||||
| 						 * which read jiffies and set | ||||
| 						 * jiffies_stall. Stall | ||||
| 						 * warnings disabled if !0. */ | ||||
| 	unsigned long jiffies_resched;		/* Time at which to resched */ | ||||
| 						/*  a reluctant CPU. */ | ||||
| 	unsigned long n_force_qs_gpstart;	/* Snapshot of n_force_qs at */ | ||||
|  |  | |||
|  | @ -621,10 +621,14 @@ static void synchronize_rcu_expedited_wait(void) | |||
| 	} | ||||
| 
 | ||||
| 	for (;;) { | ||||
| 		unsigned long j; | ||||
| 
 | ||||
| 		if (synchronize_rcu_expedited_wait_once(jiffies_stall)) | ||||
| 			return; | ||||
| 		if (rcu_stall_is_suppressed()) | ||||
| 			continue; | ||||
| 		j = jiffies; | ||||
| 		rcu_stall_notifier_call_chain(RCU_STALL_NOTIFY_EXP, (void *)(j - jiffies_start)); | ||||
| 		trace_rcu_stall_warning(rcu_state.name, TPS("ExpeditedStall")); | ||||
| 		pr_err("INFO: %s detected expedited stalls on CPUs/tasks: {", | ||||
| 		       rcu_state.name); | ||||
|  | @ -647,7 +651,7 @@ static void synchronize_rcu_expedited_wait(void) | |||
| 			} | ||||
| 		} | ||||
| 		pr_cont(" } %lu jiffies s: %lu root: %#lx/%c\n", | ||||
| 			jiffies - jiffies_start, rcu_state.expedited_sequence, | ||||
| 			j - jiffies_start, rcu_state.expedited_sequence, | ||||
| 			data_race(rnp_root->expmask), | ||||
| 			".T"[!!data_race(rnp_root->exp_tasks)]); | ||||
| 		if (ndetected) { | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ | |||
|  */ | ||||
| 
 | ||||
| #include <linux/kvm_para.h> | ||||
| #include <linux/rcu_notifier.h> | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////////////
 | ||||
| //
 | ||||
|  | @ -149,12 +150,17 @@ static void panic_on_rcu_stall(void) | |||
| /**
 | ||||
|  * rcu_cpu_stall_reset - restart stall-warning timeout for current grace period | ||||
|  * | ||||
|  * To perform the reset request from the caller, disable stall detection until | ||||
|  * 3 fqs loops have passed. This is required to ensure a fresh jiffies is | ||||
|  * loaded.  It should be safe to do from the fqs loop as enough timer | ||||
|  * interrupts and context switches should have passed. | ||||
|  * | ||||
|  * The caller must disable hard irqs. | ||||
|  */ | ||||
| void rcu_cpu_stall_reset(void) | ||||
| { | ||||
| 	WRITE_ONCE(rcu_state.jiffies_stall, | ||||
| 		   jiffies + rcu_jiffies_till_stall_check()); | ||||
| 	WRITE_ONCE(rcu_state.nr_fqs_jiffies_stall, 3); | ||||
| 	WRITE_ONCE(rcu_state.jiffies_stall, ULONG_MAX); | ||||
| } | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////////////
 | ||||
|  | @ -170,6 +176,7 @@ static void record_gp_stall_check_time(void) | |||
| 	WRITE_ONCE(rcu_state.gp_start, j); | ||||
| 	j1 = rcu_jiffies_till_stall_check(); | ||||
| 	smp_mb(); // ->gp_start before ->jiffies_stall and caller's ->gp_seq.
 | ||||
| 	WRITE_ONCE(rcu_state.nr_fqs_jiffies_stall, 0); | ||||
| 	WRITE_ONCE(rcu_state.jiffies_stall, j + j1); | ||||
| 	rcu_state.jiffies_resched = j + j1 / 2; | ||||
| 	rcu_state.n_force_qs_gpstart = READ_ONCE(rcu_state.n_force_qs); | ||||
|  | @ -534,16 +541,16 @@ static void rcu_check_gp_kthread_starvation(void) | |||
| 		       data_race(READ_ONCE(rcu_state.gp_state)), | ||||
| 		       gpk ? data_race(READ_ONCE(gpk->__state)) : ~0, cpu); | ||||
| 		if (gpk) { | ||||
| 			struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu); | ||||
| 
 | ||||
| 			pr_err("\tUnless %s kthread gets sufficient CPU time, OOM is now expected behavior.\n", rcu_state.name); | ||||
| 			pr_err("RCU grace-period kthread stack dump:\n"); | ||||
| 			sched_show_task(gpk); | ||||
| 			if (cpu >= 0) { | ||||
| 				if (cpu_is_offline(cpu)) { | ||||
| 					pr_err("RCU GP kthread last ran on offline CPU %d.\n", cpu); | ||||
| 				} else  { | ||||
| 					pr_err("Stack dump where RCU GP kthread last ran:\n"); | ||||
| 					dump_cpu_task(cpu); | ||||
| 				} | ||||
| 			if (cpu_is_offline(cpu)) { | ||||
| 				pr_err("RCU GP kthread last ran on offline CPU %d.\n", cpu); | ||||
| 			} else if (!(data_race(READ_ONCE(rdp->mynode->qsmask)) & rdp->grpmask)) { | ||||
| 				pr_err("Stack dump where RCU GP kthread last ran:\n"); | ||||
| 				dump_cpu_task(cpu); | ||||
| 			} | ||||
| 			wake_up_process(gpk); | ||||
| 		} | ||||
|  | @ -711,7 +718,7 @@ static void print_cpu_stall(unsigned long gps) | |||
| 
 | ||||
| static void check_cpu_stall(struct rcu_data *rdp) | ||||
| { | ||||
| 	bool didstall = false; | ||||
| 	bool self_detected; | ||||
| 	unsigned long gs1; | ||||
| 	unsigned long gs2; | ||||
| 	unsigned long gps; | ||||
|  | @ -725,6 +732,16 @@ static void check_cpu_stall(struct rcu_data *rdp) | |||
| 	    !rcu_gp_in_progress()) | ||||
| 		return; | ||||
| 	rcu_stall_kick_kthreads(); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Check if it was requested (via rcu_cpu_stall_reset()) that the FQS | ||||
| 	 * loop has to set jiffies to ensure a non-stale jiffies value. This | ||||
| 	 * is required to have good jiffies value after coming out of long | ||||
| 	 * breaks of jiffies updates. Not doing so can cause false positives. | ||||
| 	 */ | ||||
| 	if (READ_ONCE(rcu_state.nr_fqs_jiffies_stall) > 0) | ||||
| 		return; | ||||
| 
 | ||||
| 	j = jiffies; | ||||
| 
 | ||||
| 	/*
 | ||||
|  | @ -758,10 +775,10 @@ static void check_cpu_stall(struct rcu_data *rdp) | |||
| 		return; /* No stall or GP completed since entering function. */ | ||||
| 	rnp = rdp->mynode; | ||||
| 	jn = jiffies + ULONG_MAX / 2; | ||||
| 	self_detected = READ_ONCE(rnp->qsmask) & rdp->grpmask; | ||||
| 	if (rcu_gp_in_progress() && | ||||
| 	    (READ_ONCE(rnp->qsmask) & rdp->grpmask) && | ||||
| 	    (self_detected || ULONG_CMP_GE(j, js + RCU_STALL_RAT_DELAY)) && | ||||
| 	    cmpxchg(&rcu_state.jiffies_stall, js, jn) == js) { | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * If a virtual machine is stopped by the host it can look to | ||||
| 		 * the watchdog like an RCU stall. Check to see if the host | ||||
|  | @ -770,39 +787,28 @@ static void check_cpu_stall(struct rcu_data *rdp) | |||
| 		if (kvm_check_and_clear_guest_paused()) | ||||
| 			return; | ||||
| 
 | ||||
| 		/* We haven't checked in, so go dump stack. */ | ||||
| 		print_cpu_stall(gps); | ||||
| 		rcu_stall_notifier_call_chain(RCU_STALL_NOTIFY_NORM, (void *)j - gps); | ||||
| 		if (self_detected) { | ||||
| 			/* We haven't checked in, so go dump stack. */ | ||||
| 			print_cpu_stall(gps); | ||||
| 		} else { | ||||
| 			/* They had a few time units to dump stack, so complain. */ | ||||
| 			print_other_cpu_stall(gs2, gps); | ||||
| 		} | ||||
| 
 | ||||
| 		if (READ_ONCE(rcu_cpu_stall_ftrace_dump)) | ||||
| 			rcu_ftrace_dump(DUMP_ALL); | ||||
| 		didstall = true; | ||||
| 
 | ||||
| 	} else if (rcu_gp_in_progress() && | ||||
| 		   ULONG_CMP_GE(j, js + RCU_STALL_RAT_DELAY) && | ||||
| 		   cmpxchg(&rcu_state.jiffies_stall, js, jn) == js) { | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * If a virtual machine is stopped by the host it can look to | ||||
| 		 * the watchdog like an RCU stall. Check to see if the host | ||||
| 		 * stopped the vm. | ||||
| 		 */ | ||||
| 		if (kvm_check_and_clear_guest_paused()) | ||||
| 			return; | ||||
| 
 | ||||
| 		/* They had a few time units to dump stack, so complain. */ | ||||
| 		print_other_cpu_stall(gs2, gps); | ||||
| 		if (READ_ONCE(rcu_cpu_stall_ftrace_dump)) | ||||
| 			rcu_ftrace_dump(DUMP_ALL); | ||||
| 		didstall = true; | ||||
| 	} | ||||
| 	if (didstall && READ_ONCE(rcu_state.jiffies_stall) == jn) { | ||||
| 		jn = jiffies + 3 * rcu_jiffies_till_stall_check() + 3; | ||||
| 		WRITE_ONCE(rcu_state.jiffies_stall, jn); | ||||
| 		if (READ_ONCE(rcu_state.jiffies_stall) == jn) { | ||||
| 			jn = jiffies + 3 * rcu_jiffies_till_stall_check() + 3; | ||||
| 			WRITE_ONCE(rcu_state.jiffies_stall, jn); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////////////
 | ||||
| //
 | ||||
| // RCU forward-progress mechanisms, including of callback invocation.
 | ||||
| // RCU forward-progress mechanisms, including for callback invocation.
 | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -1054,3 +1060,58 @@ static int __init rcu_sysrq_init(void) | |||
| 	return 0; | ||||
| } | ||||
| early_initcall(rcu_sysrq_init); | ||||
| 
 | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////////////
 | ||||
| //
 | ||||
| // RCU CPU stall-warning notifiers
 | ||||
| 
 | ||||
| static ATOMIC_NOTIFIER_HEAD(rcu_cpu_stall_notifier_list); | ||||
| 
 | ||||
| /**
 | ||||
|  * rcu_stall_chain_notifier_register - Add an RCU CPU stall notifier | ||||
|  * @n: Entry to add. | ||||
|  * | ||||
|  * Adds an RCU CPU stall notifier to an atomic notifier chain. | ||||
|  * The @action passed to a notifier will be @RCU_STALL_NOTIFY_NORM or | ||||
|  * friends.  The @data will be the duration of the stalled grace period, | ||||
|  * in jiffies, coerced to a void* pointer. | ||||
|  * | ||||
|  * Returns 0 on success, %-EEXIST on error. | ||||
|  */ | ||||
| int rcu_stall_chain_notifier_register(struct notifier_block *n) | ||||
| { | ||||
| 	return atomic_notifier_chain_register(&rcu_cpu_stall_notifier_list, n); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(rcu_stall_chain_notifier_register); | ||||
| 
 | ||||
| /**
 | ||||
|  * rcu_stall_chain_notifier_unregister - Remove an RCU CPU stall notifier | ||||
|  * @n: Entry to add. | ||||
|  * | ||||
|  * Removes an RCU CPU stall notifier from an atomic notifier chain. | ||||
|  * | ||||
|  * Returns zero on success, %-ENOENT on failure. | ||||
|  */ | ||||
| int rcu_stall_chain_notifier_unregister(struct notifier_block *n) | ||||
| { | ||||
| 	return atomic_notifier_chain_unregister(&rcu_cpu_stall_notifier_list, n); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(rcu_stall_chain_notifier_unregister); | ||||
| 
 | ||||
| /*
 | ||||
|  * rcu_stall_notifier_call_chain - Call functions in an RCU CPU stall notifier chain | ||||
|  * @val: Value passed unmodified to notifier function | ||||
|  * @v: Pointer passed unmodified to notifier function | ||||
|  * | ||||
|  * Calls each function in the RCU CPU stall notifier chain in turn, which | ||||
|  * is an atomic call chain.  See atomic_notifier_call_chain() for more | ||||
|  * information. | ||||
|  * | ||||
|  * This is for use within RCU, hence the omission of the extra asterisk | ||||
|  * to indicate a non-kerneldoc format header comment. | ||||
|  */ | ||||
| int rcu_stall_notifier_call_chain(unsigned long val, void *v) | ||||
| { | ||||
| 	return atomic_notifier_call_chain(&rcu_cpu_stall_notifier_list, val, v); | ||||
| } | ||||
|  |  | |||
|  | @ -528,26 +528,6 @@ bool slab_is_available(void) | |||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_PRINTK | ||||
| /**
 | ||||
|  * kmem_valid_obj - does the pointer reference a valid slab object? | ||||
|  * @object: pointer to query. | ||||
|  * | ||||
|  * Return: %true if the pointer is to a not-yet-freed object from | ||||
|  * kmalloc() or kmem_cache_alloc(), either %true or %false if the pointer | ||||
|  * is to an already-freed object, and %false otherwise. | ||||
|  */ | ||||
| bool kmem_valid_obj(void *object) | ||||
| { | ||||
| 	struct folio *folio; | ||||
| 
 | ||||
| 	/* Some arches consider ZERO_SIZE_PTR to be a valid address. */ | ||||
| 	if (object < (void *)PAGE_SIZE || !virt_addr_valid(object)) | ||||
| 		return false; | ||||
| 	folio = virt_to_folio(object); | ||||
| 	return folio_test_slab(folio); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(kmem_valid_obj); | ||||
| 
 | ||||
| static void kmem_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *slab) | ||||
| { | ||||
| 	if (__kfence_obj_info(kpp, object, slab)) | ||||
|  | @ -566,11 +546,11 @@ static void kmem_obj_info(struct kmem_obj_info *kpp, void *object, struct slab * | |||
|  * and, if available, the slab name, return address, and stack trace from | ||||
|  * the allocation and last free path of that object. | ||||
|  * | ||||
|  * This function will splat if passed a pointer to a non-slab object. | ||||
|  * If you are not sure what type of object you have, you should instead | ||||
|  * use mem_dump_obj(). | ||||
|  * Return: %true if the pointer is to a not-yet-freed object from | ||||
|  * kmalloc() or kmem_cache_alloc(), either %true or %false if the pointer | ||||
|  * is to an already-freed object, and %false otherwise. | ||||
|  */ | ||||
| void kmem_dump_obj(void *object) | ||||
| bool kmem_dump_obj(void *object) | ||||
| { | ||||
| 	char *cp = IS_ENABLED(CONFIG_MMU) ? "" : "/vmalloc"; | ||||
| 	int i; | ||||
|  | @ -578,13 +558,13 @@ void kmem_dump_obj(void *object) | |||
| 	unsigned long ptroffset; | ||||
| 	struct kmem_obj_info kp = { }; | ||||
| 
 | ||||
| 	if (WARN_ON_ONCE(!virt_addr_valid(object))) | ||||
| 		return; | ||||
| 	/* Some arches consider ZERO_SIZE_PTR to be a valid address. */ | ||||
| 	if (object < (void *)PAGE_SIZE || !virt_addr_valid(object)) | ||||
| 		return false; | ||||
| 	slab = virt_to_slab(object); | ||||
| 	if (WARN_ON_ONCE(!slab)) { | ||||
| 		pr_cont(" non-slab memory.\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 	if (!slab) | ||||
| 		return false; | ||||
| 
 | ||||
| 	kmem_obj_info(&kp, object, slab); | ||||
| 	if (kp.kp_slab_cache) | ||||
| 		pr_cont(" slab%s %s", cp, kp.kp_slab_cache->name); | ||||
|  | @ -621,6 +601,7 @@ void kmem_dump_obj(void *object) | |||
| 		pr_info("    %pS\n", kp.kp_free_stack[i]); | ||||
| 	} | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(kmem_dump_obj); | ||||
| #endif | ||||
|  |  | |||
|  | @ -1060,10 +1060,8 @@ void mem_dump_obj(void *object) | |||
| { | ||||
| 	const char *type; | ||||
| 
 | ||||
| 	if (kmem_valid_obj(object)) { | ||||
| 		kmem_dump_obj(object); | ||||
| 	if (kmem_dump_obj(object)) | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (vmalloc_dump_obj(object)) | ||||
| 		return; | ||||
|  |  | |||
|  | @ -6427,15 +6427,6 @@ sub process { | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| # check for soon-to-be-deprecated single-argument k[v]free_rcu() API | ||||
| 		if ($line =~ /\bk[v]?free_rcu\s*\([^(]+\)/) { | ||||
| 			if ($line =~ /\bk[v]?free_rcu\s*\([^,]+\)/) { | ||||
| 				ERROR("DEPRECATED_API", | ||||
| 				      "Single-argument k[v]free_rcu() API is deprecated, please pass rcu_head object or call k[v]free_rcu_mightsleep()." . $herecurr); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 
 | ||||
| # check for unnecessary "Out of Memory" messages | ||||
| 		if ($line =~ /^\+.*\b$logFunctions\s*\(/ && | ||||
| 		    $prevline =~ /^[ \+]\s*if\s*\(\s*(\!\s*|NULL\s*==\s*)?($Lval)(\s*==\s*NULL\s*)?\s*\)/ && | ||||
|  |  | |||
 Frederic Weisbecker
						Frederic Weisbecker