You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'core-rcu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull RCU updates from Ingo Molnar: "The main changes in this cycle were: - changes related to No-CBs CPUs and NO_HZ_FULL - RCU-tasks implementation - torture-test updates - miscellaneous fixes - locktorture updates - RCU documentation updates" * 'core-rcu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (81 commits) workqueue: Use cond_resched_rcu_qs macro workqueue: Add quiescent state between work items locktorture: Cleanup header usage locktorture: Cannot hold read and write lock locktorture: Fix __acquire annotation for spinlock irq locktorture: Support rwlocks rcu: Eliminate deadlock between CPU hotplug and expedited grace periods locktorture: Document boot/module parameters rcutorture: Rename rcutorture_runnable parameter locktorture: Add test scenario for rwsem_lock locktorture: Add test scenario for mutex_lock locktorture: Make torture scripting account for new _runnable name locktorture: Introduce torture context locktorture: Support rwsems locktorture: Add infrastructure for torturing read locks torture: Address race in module cleanup locktorture: Make statistics generic locktorture: Teach about lock debugging locktorture: Support mutexes locktorture: Add documentation ...
This commit is contained in:
@@ -56,8 +56,20 @@ RCU_STALL_RAT_DELAY
|
||||
two jiffies. (This is a cpp macro, not a kernel configuration
|
||||
parameter.)
|
||||
|
||||
When a CPU detects that it is stalling, it will print a message similar
|
||||
to the following:
|
||||
rcupdate.rcu_task_stall_timeout
|
||||
|
||||
This boot/sysfs parameter controls the RCU-tasks stall warning
|
||||
interval. A value of zero or less suppresses RCU-tasks stall
|
||||
warnings. A positive value sets the stall-warning interval
|
||||
in jiffies. An RCU-tasks stall warning starts wtih the line:
|
||||
|
||||
INFO: rcu_tasks detected stalls on tasks:
|
||||
|
||||
And continues with the output of sched_show_task() for each
|
||||
task stalling the current RCU-tasks grace period.
|
||||
|
||||
For non-RCU-tasks flavors of RCU, when a CPU detects that it is stalling,
|
||||
it will print a message similar to the following:
|
||||
|
||||
INFO: rcu_sched_state detected stall on CPU 5 (t=2500 jiffies)
|
||||
|
||||
@@ -174,8 +186,12 @@ o A CPU looping with preemption disabled. This condition can
|
||||
o A CPU looping with bottom halves disabled. This condition can
|
||||
result in RCU-sched and RCU-bh stalls.
|
||||
|
||||
o For !CONFIG_PREEMPT kernels, a CPU looping anywhere in the kernel
|
||||
without invoking schedule().
|
||||
o For !CONFIG_PREEMPT kernels, a CPU looping anywhere in the
|
||||
kernel without invoking schedule(). Note that cond_resched()
|
||||
does not necessarily prevent RCU CPU stall warnings. Therefore,
|
||||
if the looping in the kernel is really expected and desirable
|
||||
behavior, you might need to replace some of the cond_resched()
|
||||
calls with calls to cond_resched_rcu_qs().
|
||||
|
||||
o A CPU-bound real-time task in a CONFIG_PREEMPT kernel, which might
|
||||
happen to preempt a low-priority task in the middle of an RCU
|
||||
@@ -208,11 +224,10 @@ o A hardware failure. This is quite unlikely, but has occurred
|
||||
This resulted in a series of RCU CPU stall warnings, eventually
|
||||
leading the realization that the CPU had failed.
|
||||
|
||||
The RCU, RCU-sched, and RCU-bh implementations have CPU stall warning.
|
||||
SRCU does not have its own CPU stall warnings, but its calls to
|
||||
synchronize_sched() will result in RCU-sched detecting RCU-sched-related
|
||||
CPU stalls. Please note that RCU only detects CPU stalls when there is
|
||||
a grace period in progress. No grace period, no CPU stall warnings.
|
||||
The RCU, RCU-sched, RCU-bh, and RCU-tasks implementations have CPU stall
|
||||
warning. Note that SRCU does -not- have CPU stall warnings. Please note
|
||||
that RCU only detects CPU stalls when there is a grace period in progress.
|
||||
No grace period, no CPU stall warnings.
|
||||
|
||||
To diagnose the cause of the stall, inspect the stack traces.
|
||||
The offending function will usually be near the top of the stack.
|
||||
|
||||
@@ -1723,6 +1723,49 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
||||
lockd.nlm_udpport=M [NFS] Assign UDP port.
|
||||
Format: <integer>
|
||||
|
||||
locktorture.nreaders_stress= [KNL]
|
||||
Set the number of locking read-acquisition kthreads.
|
||||
Defaults to being automatically set based on the
|
||||
number of online CPUs.
|
||||
|
||||
locktorture.nwriters_stress= [KNL]
|
||||
Set the number of locking write-acquisition kthreads.
|
||||
|
||||
locktorture.onoff_holdoff= [KNL]
|
||||
Set time (s) after boot for CPU-hotplug testing.
|
||||
|
||||
locktorture.onoff_interval= [KNL]
|
||||
Set time (s) between CPU-hotplug operations, or
|
||||
zero to disable CPU-hotplug testing.
|
||||
|
||||
locktorture.shuffle_interval= [KNL]
|
||||
Set task-shuffle interval (jiffies). Shuffling
|
||||
tasks allows some CPUs to go into dyntick-idle
|
||||
mode during the locktorture test.
|
||||
|
||||
locktorture.shutdown_secs= [KNL]
|
||||
Set time (s) after boot system shutdown. This
|
||||
is useful for hands-off automated testing.
|
||||
|
||||
locktorture.stat_interval= [KNL]
|
||||
Time (s) between statistics printk()s.
|
||||
|
||||
locktorture.stutter= [KNL]
|
||||
Time (s) to stutter testing, for example,
|
||||
specifying five seconds causes the test to run for
|
||||
five seconds, wait for five seconds, and so on.
|
||||
This tests the locking primitive's ability to
|
||||
transition abruptly to and from idle.
|
||||
|
||||
locktorture.torture_runnable= [BOOT]
|
||||
Start locktorture running at boot time.
|
||||
|
||||
locktorture.torture_type= [KNL]
|
||||
Specify the locking implementation to test.
|
||||
|
||||
locktorture.verbose= [KNL]
|
||||
Enable additional printk() statements.
|
||||
|
||||
logibm.irq= [HW,MOUSE] Logitech Bus Mouse Driver
|
||||
Format: <irq>
|
||||
|
||||
@@ -2900,6 +2943,24 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
||||
Lazy RCU callbacks are those which RCU can
|
||||
prove do nothing more than free memory.
|
||||
|
||||
rcutorture.cbflood_inter_holdoff= [KNL]
|
||||
Set holdoff time (jiffies) between successive
|
||||
callback-flood tests.
|
||||
|
||||
rcutorture.cbflood_intra_holdoff= [KNL]
|
||||
Set holdoff time (jiffies) between successive
|
||||
bursts of callbacks within a given callback-flood
|
||||
test.
|
||||
|
||||
rcutorture.cbflood_n_burst= [KNL]
|
||||
Set the number of bursts making up a given
|
||||
callback-flood test. Set this to zero to
|
||||
disable callback-flood testing.
|
||||
|
||||
rcutorture.cbflood_n_per_burst= [KNL]
|
||||
Set the number of callbacks to be registered
|
||||
in a given burst of a callback-flood test.
|
||||
|
||||
rcutorture.fqs_duration= [KNL]
|
||||
Set duration of force_quiescent_state bursts.
|
||||
|
||||
@@ -2939,7 +3000,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
||||
Set time (s) between CPU-hotplug operations, or
|
||||
zero to disable CPU-hotplug testing.
|
||||
|
||||
rcutorture.rcutorture_runnable= [BOOT]
|
||||
rcutorture.torture_runnable= [BOOT]
|
||||
Start rcutorture running at boot time.
|
||||
|
||||
rcutorture.shuffle_interval= [KNL]
|
||||
@@ -3001,6 +3062,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
||||
rcupdate.rcu_cpu_stall_timeout= [KNL]
|
||||
Set timeout for RCU CPU stall warning messages.
|
||||
|
||||
rcupdate.rcu_task_stall_timeout= [KNL]
|
||||
Set timeout in jiffies for RCU task stall warning
|
||||
messages. Disable with a value less than or equal
|
||||
to zero.
|
||||
|
||||
rdinit= [KNL]
|
||||
Format: <full_path>
|
||||
Run specified binary instead of /init from the ramdisk,
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
Kernel Lock Torture Test Operation
|
||||
|
||||
CONFIG_LOCK_TORTURE_TEST
|
||||
|
||||
The CONFIG LOCK_TORTURE_TEST config option provides a kernel module
|
||||
that runs torture tests on core kernel locking primitives. The kernel
|
||||
module, 'locktorture', may be built after the fact on the running
|
||||
kernel to be tested, if desired. The tests periodically output status
|
||||
messages via printk(), which can be examined via the dmesg (perhaps
|
||||
grepping for "torture"). The test is started when the module is loaded,
|
||||
and stops when the module is unloaded. This program is based on how RCU
|
||||
is tortured, via rcutorture.
|
||||
|
||||
This torture test consists of creating a number of kernel threads which
|
||||
acquire the lock and hold it for specific amount of time, thus simulating
|
||||
different critical region behaviors. The amount of contention on the lock
|
||||
can be simulated by either enlarging this critical region hold time and/or
|
||||
creating more kthreads.
|
||||
|
||||
|
||||
MODULE PARAMETERS
|
||||
|
||||
This module has the following parameters:
|
||||
|
||||
|
||||
** Locktorture-specific **
|
||||
|
||||
nwriters_stress Number of kernel threads that will stress exclusive lock
|
||||
ownership (writers). The default value is twice the number
|
||||
of online CPUs.
|
||||
|
||||
nreaders_stress Number of kernel threads that will stress shared lock
|
||||
ownership (readers). The default is the same amount of writer
|
||||
locks. If the user did not specify nwriters_stress, then
|
||||
both readers and writers be the amount of online CPUs.
|
||||
|
||||
torture_type Type of lock to torture. By default, only spinlocks will
|
||||
be tortured. This module can torture the following locks,
|
||||
with string values as follows:
|
||||
|
||||
o "lock_busted": Simulates a buggy lock implementation.
|
||||
|
||||
o "spin_lock": spin_lock() and spin_unlock() pairs.
|
||||
|
||||
o "spin_lock_irq": spin_lock_irq() and spin_unlock_irq()
|
||||
pairs.
|
||||
|
||||
o "rw_lock": read/write lock() and unlock() rwlock pairs.
|
||||
|
||||
o "rw_lock_irq": read/write lock_irq() and unlock_irq()
|
||||
rwlock pairs.
|
||||
|
||||
o "mutex_lock": mutex_lock() and mutex_unlock() pairs.
|
||||
|
||||
o "rwsem_lock": read/write down() and up() semaphore pairs.
|
||||
|
||||
torture_runnable Start locktorture at boot time in the case where the
|
||||
module is built into the kernel, otherwise wait for
|
||||
torture_runnable to be set via sysfs before starting.
|
||||
By default it will begin once the module is loaded.
|
||||
|
||||
|
||||
** Torture-framework (RCU + locking) **
|
||||
|
||||
shutdown_secs The number of seconds to run the test before terminating
|
||||
the test and powering off the system. The default is
|
||||
zero, which disables test termination and system shutdown.
|
||||
This capability is useful for automated testing.
|
||||
|
||||
onoff_interval The number of seconds between each attempt to execute a
|
||||
randomly selected CPU-hotplug operation. Defaults
|
||||
to zero, which disables CPU hotplugging. In
|
||||
CONFIG_HOTPLUG_CPU=n kernels, locktorture will silently
|
||||
refuse to do any CPU-hotplug operations regardless of
|
||||
what value is specified for onoff_interval.
|
||||
|
||||
onoff_holdoff The number of seconds to wait until starting CPU-hotplug
|
||||
operations. This would normally only be used when
|
||||
locktorture was built into the kernel and started
|
||||
automatically at boot time, in which case it is useful
|
||||
in order to avoid confusing boot-time code with CPUs
|
||||
coming and going. This parameter is only useful if
|
||||
CONFIG_HOTPLUG_CPU is enabled.
|
||||
|
||||
stat_interval Number of seconds between statistics-related printk()s.
|
||||
By default, locktorture will report stats every 60 seconds.
|
||||
Setting the interval to zero causes the statistics to
|
||||
be printed -only- when the module is unloaded, and this
|
||||
is the default.
|
||||
|
||||
stutter The length of time to run the test before pausing for this
|
||||
same period of time. Defaults to "stutter=5", so as
|
||||
to run and pause for (roughly) five-second intervals.
|
||||
Specifying "stutter=0" causes the test to run continuously
|
||||
without pausing, which is the old default behavior.
|
||||
|
||||
shuffle_interval The number of seconds to keep the test threads affinitied
|
||||
to a particular subset of the CPUs, defaults to 3 seconds.
|
||||
Used in conjunction with test_no_idle_hz.
|
||||
|
||||
verbose Enable verbose debugging printing, via printk(). Enabled
|
||||
by default. This extra information is mostly related to
|
||||
high-level errors and reports from the main 'torture'
|
||||
framework.
|
||||
|
||||
|
||||
STATISTICS
|
||||
|
||||
Statistics are printed in the following format:
|
||||
|
||||
spin_lock-torture: Writes: Total: 93746064 Max/Min: 0/0 Fail: 0
|
||||
(A) (B) (C) (D) (E)
|
||||
|
||||
(A): Lock type that is being tortured -- torture_type parameter.
|
||||
|
||||
(B): Number of writer lock acquisitions. If dealing with a read/write primitive
|
||||
a second "Reads" statistics line is printed.
|
||||
|
||||
(C): Number of times the lock was acquired.
|
||||
|
||||
(D): Min and max number of times threads failed to acquire the lock.
|
||||
|
||||
(E): true/false values if there were errors acquiring the lock. This should
|
||||
-only- be positive if there is a bug in the locking primitive's
|
||||
implementation. Otherwise a lock should never fail (i.e., spin_lock()).
|
||||
Of course, the same applies for (C), above. A dummy example of this is
|
||||
the "lock_busted" type.
|
||||
|
||||
USAGE
|
||||
|
||||
The following script may be used to torture locks:
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
modprobe locktorture
|
||||
sleep 3600
|
||||
rmmod locktorture
|
||||
dmesg | grep torture:
|
||||
|
||||
The output can be manually inspected for the error flag of "!!!".
|
||||
One could of course create a more elaborate script that automatically
|
||||
checked for such errors. The "rmmod" command forces a "SUCCESS",
|
||||
"FAILURE", or "RCU_HOTPLUG" indication to be printk()ed. The first
|
||||
two are self-explanatory, while the last indicates that while there
|
||||
were no locking failures, CPU-hotplug problems were detected.
|
||||
|
||||
Also see: Documentation/RCU/torture.txt
|
||||
@@ -574,30 +574,14 @@ However, stores are not speculated. This means that ordering -is- provided
|
||||
in the following example:
|
||||
|
||||
q = ACCESS_ONCE(a);
|
||||
if (ACCESS_ONCE(q)) {
|
||||
if (q) {
|
||||
ACCESS_ONCE(b) = p;
|
||||
}
|
||||
|
||||
Please note that ACCESS_ONCE() is not optional! Without the ACCESS_ONCE(),
|
||||
the compiler is within its rights to transform this example:
|
||||
|
||||
q = a;
|
||||
if (q) {
|
||||
b = p; /* BUG: Compiler can reorder!!! */
|
||||
do_something();
|
||||
} else {
|
||||
b = p; /* BUG: Compiler can reorder!!! */
|
||||
do_something_else();
|
||||
}
|
||||
|
||||
into this, which of course defeats the ordering:
|
||||
|
||||
b = p;
|
||||
q = a;
|
||||
if (q)
|
||||
do_something();
|
||||
else
|
||||
do_something_else();
|
||||
Please note that ACCESS_ONCE() is not optional! Without the
|
||||
ACCESS_ONCE(), might combine the load from 'a' with other loads from
|
||||
'a', and the store to 'b' with other stores to 'b', with possible highly
|
||||
counterintuitive effects on ordering.
|
||||
|
||||
Worse yet, if the compiler is able to prove (say) that the value of
|
||||
variable 'a' is always non-zero, it would be well within its rights
|
||||
@@ -605,11 +589,12 @@ to optimize the original example by eliminating the "if" statement
|
||||
as follows:
|
||||
|
||||
q = a;
|
||||
b = p; /* BUG: Compiler can reorder!!! */
|
||||
do_something();
|
||||
b = p; /* BUG: Compiler and CPU can both reorder!!! */
|
||||
|
||||
The solution is again ACCESS_ONCE() and barrier(), which preserves the
|
||||
ordering between the load from variable 'a' and the store to variable 'b':
|
||||
So don't leave out the ACCESS_ONCE().
|
||||
|
||||
It is tempting to try to enforce ordering on identical stores on both
|
||||
branches of the "if" statement as follows:
|
||||
|
||||
q = ACCESS_ONCE(a);
|
||||
if (q) {
|
||||
@@ -622,18 +607,11 @@ ordering between the load from variable 'a' and the store to variable 'b':
|
||||
do_something_else();
|
||||
}
|
||||
|
||||
The initial ACCESS_ONCE() is required to prevent the compiler from
|
||||
proving the value of 'a', and the pair of barrier() invocations are
|
||||
required to prevent the compiler from pulling the two identical stores
|
||||
to 'b' out from the legs of the "if" statement.
|
||||
|
||||
It is important to note that control dependencies absolutely require a
|
||||
a conditional. For example, the following "optimized" version of
|
||||
the above example breaks ordering, which is why the barrier() invocations
|
||||
are absolutely required if you have identical stores in both legs of
|
||||
the "if" statement:
|
||||
Unfortunately, current compilers will transform this as follows at high
|
||||
optimization levels:
|
||||
|
||||
q = ACCESS_ONCE(a);
|
||||
barrier();
|
||||
ACCESS_ONCE(b) = p; /* BUG: No ordering vs. load from a!!! */
|
||||
if (q) {
|
||||
/* ACCESS_ONCE(b) = p; -- moved up, BUG!!! */
|
||||
@@ -643,21 +621,36 @@ the "if" statement:
|
||||
do_something_else();
|
||||
}
|
||||
|
||||
It is of course legal for the prior load to be part of the conditional,
|
||||
for example, as follows:
|
||||
Now there is no conditional between the load from 'a' and the store to
|
||||
'b', which means that the CPU is within its rights to reorder them:
|
||||
The conditional is absolutely required, and must be present in the
|
||||
assembly code even after all compiler optimizations have been applied.
|
||||
Therefore, if you need ordering in this example, you need explicit
|
||||
memory barriers, for example, smp_store_release():
|
||||
|
||||
if (ACCESS_ONCE(a) > 0) {
|
||||
barrier();
|
||||
ACCESS_ONCE(b) = q / 2;
|
||||
q = ACCESS_ONCE(a);
|
||||
if (q) {
|
||||
smp_store_release(&b, p);
|
||||
do_something();
|
||||
} else {
|
||||
barrier();
|
||||
ACCESS_ONCE(b) = q / 3;
|
||||
smp_store_release(&b, p);
|
||||
do_something_else();
|
||||
}
|
||||
|
||||
This will again ensure that the load from variable 'a' is ordered before the
|
||||
stores to variable 'b'.
|
||||
In contrast, without explicit memory barriers, two-legged-if control
|
||||
ordering is guaranteed only when the stores differ, for example:
|
||||
|
||||
q = ACCESS_ONCE(a);
|
||||
if (q) {
|
||||
ACCESS_ONCE(b) = p;
|
||||
do_something();
|
||||
} else {
|
||||
ACCESS_ONCE(b) = r;
|
||||
do_something_else();
|
||||
}
|
||||
|
||||
The initial ACCESS_ONCE() is still required to prevent the compiler from
|
||||
proving the value of 'a'.
|
||||
|
||||
In addition, you need to be careful what you do with the local variable 'q',
|
||||
otherwise the compiler might be able to guess the value and again remove
|
||||
@@ -665,12 +658,10 @@ the needed conditional. For example:
|
||||
|
||||
q = ACCESS_ONCE(a);
|
||||
if (q % MAX) {
|
||||
barrier();
|
||||
ACCESS_ONCE(b) = p;
|
||||
do_something();
|
||||
} else {
|
||||
barrier();
|
||||
ACCESS_ONCE(b) = p;
|
||||
ACCESS_ONCE(b) = r;
|
||||
do_something_else();
|
||||
}
|
||||
|
||||
@@ -682,9 +673,12 @@ transform the above code into the following:
|
||||
ACCESS_ONCE(b) = p;
|
||||
do_something_else();
|
||||
|
||||
This transformation loses the ordering between the load from variable 'a'
|
||||
and the store to variable 'b'. If you are relying on this ordering, you
|
||||
should do something like the following:
|
||||
Given this transformation, the CPU is not required to respect the ordering
|
||||
between the load from variable 'a' and the store to variable 'b'. It is
|
||||
tempting to add a barrier(), but this does not help. The conditional
|
||||
is gone, and the barrier won't bring it back. Therefore, if you are
|
||||
relying on this ordering, you should make sure that MAX is greater than
|
||||
one, perhaps as follows:
|
||||
|
||||
q = ACCESS_ONCE(a);
|
||||
BUILD_BUG_ON(MAX <= 1); /* Order load from a with store to b. */
|
||||
@@ -692,35 +686,45 @@ should do something like the following:
|
||||
ACCESS_ONCE(b) = p;
|
||||
do_something();
|
||||
} else {
|
||||
ACCESS_ONCE(b) = p;
|
||||
ACCESS_ONCE(b) = r;
|
||||
do_something_else();
|
||||
}
|
||||
|
||||
Please note once again that the stores to 'b' differ. If they were
|
||||
identical, as noted earlier, the compiler could pull this store outside
|
||||
of the 'if' statement.
|
||||
|
||||
Finally, control dependencies do -not- provide transitivity. This is
|
||||
demonstrated by two related examples:
|
||||
demonstrated by two related examples, with the initial values of
|
||||
x and y both being zero:
|
||||
|
||||
CPU 0 CPU 1
|
||||
===================== =====================
|
||||
r1 = ACCESS_ONCE(x); r2 = ACCESS_ONCE(y);
|
||||
if (r1 >= 0) if (r2 >= 0)
|
||||
if (r1 > 0) if (r2 > 0)
|
||||
ACCESS_ONCE(y) = 1; ACCESS_ONCE(x) = 1;
|
||||
|
||||
assert(!(r1 == 1 && r2 == 1));
|
||||
|
||||
The above two-CPU example will never trigger the assert(). However,
|
||||
if control dependencies guaranteed transitivity (which they do not),
|
||||
then adding the following two CPUs would guarantee a related assertion:
|
||||
then adding the following CPU would guarantee a related assertion:
|
||||
|
||||
CPU 2 CPU 3
|
||||
===================== =====================
|
||||
ACCESS_ONCE(x) = 2; ACCESS_ONCE(y) = 2;
|
||||
CPU 2
|
||||
=====================
|
||||
ACCESS_ONCE(x) = 2;
|
||||
|
||||
assert(!(r1 == 2 && r2 == 2 && x == 1 && y == 1)); /* FAILS!!! */
|
||||
assert(!(r1 == 2 && r2 == 1 && x == 2)); /* FAILS!!! */
|
||||
|
||||
But because control dependencies do -not- provide transitivity, the
|
||||
above assertion can fail after the combined four-CPU example completes.
|
||||
If you need the four-CPU example to provide ordering, you will need
|
||||
smp_mb() between the loads and stores in the CPU 0 and CPU 1 code fragments.
|
||||
But because control dependencies do -not- provide transitivity, the above
|
||||
assertion can fail after the combined three-CPU example completes. If you
|
||||
need the three-CPU example to provide ordering, you will need smp_mb()
|
||||
between the loads and stores in the CPU 0 and CPU 1 code fragments,
|
||||
that is, just before or just after the "if" statements.
|
||||
|
||||
These two examples are the LB and WWC litmus tests from this paper:
|
||||
http://www.cl.cam.ac.uk/users/pes20/ppc-supplemental/test6.pdf and this
|
||||
site: https://www.cl.cam.ac.uk/~pes20/ppcmem/index.html.
|
||||
|
||||
In summary:
|
||||
|
||||
|
||||
@@ -367,7 +367,7 @@ static struct fdtable *close_files(struct files_struct * files)
|
||||
struct file * file = xchg(&fdt->fd[i], NULL);
|
||||
if (file) {
|
||||
filp_close(file, files);
|
||||
cond_resched();
|
||||
cond_resched_rcu_qs();
|
||||
}
|
||||
}
|
||||
i++;
|
||||
|
||||
@@ -213,6 +213,7 @@ extern struct bus_type cpu_subsys;
|
||||
extern void cpu_hotplug_begin(void);
|
||||
extern void cpu_hotplug_done(void);
|
||||
extern void get_online_cpus(void);
|
||||
extern bool try_get_online_cpus(void);
|
||||
extern void put_online_cpus(void);
|
||||
extern void cpu_hotplug_disable(void);
|
||||
extern void cpu_hotplug_enable(void);
|
||||
@@ -230,6 +231,7 @@ int cpu_down(unsigned int cpu);
|
||||
static inline void cpu_hotplug_begin(void) {}
|
||||
static inline void cpu_hotplug_done(void) {}
|
||||
#define get_online_cpus() do { } while (0)
|
||||
#define try_get_online_cpus() true
|
||||
#define put_online_cpus() do { } while (0)
|
||||
#define cpu_hotplug_disable() do { } while (0)
|
||||
#define cpu_hotplug_enable() do { } while (0)
|
||||
|
||||
@@ -111,12 +111,21 @@ extern struct group_info init_groups;
|
||||
#ifdef CONFIG_PREEMPT_RCU
|
||||
#define INIT_TASK_RCU_PREEMPT(tsk) \
|
||||
.rcu_read_lock_nesting = 0, \
|
||||
.rcu_read_unlock_special = 0, \
|
||||
.rcu_read_unlock_special.s = 0, \
|
||||
.rcu_node_entry = LIST_HEAD_INIT(tsk.rcu_node_entry), \
|
||||
INIT_TASK_RCU_TREE_PREEMPT()
|
||||
#else
|
||||
#define INIT_TASK_RCU_PREEMPT(tsk)
|
||||
#endif
|
||||
#ifdef CONFIG_TASKS_RCU
|
||||
#define INIT_TASK_RCU_TASKS(tsk) \
|
||||
.rcu_tasks_holdout = false, \
|
||||
.rcu_tasks_holdout_list = \
|
||||
LIST_HEAD_INIT(tsk.rcu_tasks_holdout_list), \
|
||||
.rcu_tasks_idle_cpu = -1,
|
||||
#else
|
||||
#define INIT_TASK_RCU_TASKS(tsk)
|
||||
#endif
|
||||
|
||||
extern struct cred init_cred;
|
||||
|
||||
@@ -224,6 +233,7 @@ extern struct task_group root_task_group;
|
||||
INIT_FTRACE_GRAPH \
|
||||
INIT_TRACE_RECURSION \
|
||||
INIT_TASK_RCU_PREEMPT(tsk) \
|
||||
INIT_TASK_RCU_TASKS(tsk) \
|
||||
INIT_CPUSET_SEQ(tsk) \
|
||||
INIT_RT_MUTEXES(tsk) \
|
||||
INIT_VTIME(tsk) \
|
||||
|
||||
@@ -510,6 +510,7 @@ static inline void print_irqtrace_events(struct task_struct *curr)
|
||||
|
||||
#define lock_map_acquire(l) lock_acquire_exclusive(l, 0, 0, NULL, _THIS_IP_)
|
||||
#define lock_map_acquire_read(l) lock_acquire_shared_recursive(l, 0, 0, NULL, _THIS_IP_)
|
||||
#define lock_map_acquire_tryread(l) lock_acquire_shared_recursive(l, 0, 1, NULL, _THIS_IP_)
|
||||
#define lock_map_release(l) lock_release(l, 1, _THIS_IP_)
|
||||
|
||||
#ifdef CONFIG_PROVE_LOCKING
|
||||
|
||||
+65
-41
@@ -47,14 +47,12 @@
|
||||
#include <asm/barrier.h>
|
||||
|
||||
extern int rcu_expedited; /* for sysctl */
|
||||
#ifdef CONFIG_RCU_TORTURE_TEST
|
||||
extern int rcutorture_runnable; /* for sysctl */
|
||||
#endif /* #ifdef CONFIG_RCU_TORTURE_TEST */
|
||||
|
||||
enum rcutorture_type {
|
||||
RCU_FLAVOR,
|
||||
RCU_BH_FLAVOR,
|
||||
RCU_SCHED_FLAVOR,
|
||||
RCU_TASKS_FLAVOR,
|
||||
SRCU_FLAVOR,
|
||||
INVALID_RCU_FLAVOR
|
||||
};
|
||||
@@ -197,6 +195,28 @@ void call_rcu_sched(struct rcu_head *head,
|
||||
|
||||
void synchronize_sched(void);
|
||||
|
||||
/**
|
||||
* call_rcu_tasks() - Queue an RCU for invocation task-based grace period
|
||||
* @head: structure to be used for queueing the RCU updates.
|
||||
* @func: actual callback function to be invoked after the grace period
|
||||
*
|
||||
* The callback function will be invoked some time after a full grace
|
||||
* period elapses, in other words after all currently executing RCU
|
||||
* read-side critical sections have completed. call_rcu_tasks() assumes
|
||||
* that the read-side critical sections end at a voluntary context
|
||||
* switch (not a preemption!), entry into idle, or transition to usermode
|
||||
* execution. As such, there are no read-side primitives analogous to
|
||||
* rcu_read_lock() and rcu_read_unlock() because this primitive is intended
|
||||
* to determine that all tasks have passed through a safe state, not so
|
||||
* much for data-strcuture synchronization.
|
||||
*
|
||||
* See the description of call_rcu() for more detailed information on
|
||||
* memory ordering guarantees.
|
||||
*/
|
||||
void call_rcu_tasks(struct rcu_head *head, void (*func)(struct rcu_head *head));
|
||||
void synchronize_rcu_tasks(void);
|
||||
void rcu_barrier_tasks(void);
|
||||
|
||||
#ifdef CONFIG_PREEMPT_RCU
|
||||
|
||||
void __rcu_read_lock(void);
|
||||
@@ -238,8 +258,8 @@ static inline int rcu_preempt_depth(void)
|
||||
|
||||
/* Internal to kernel */
|
||||
void rcu_init(void);
|
||||
void rcu_sched_qs(int cpu);
|
||||
void rcu_bh_qs(int cpu);
|
||||
void rcu_sched_qs(void);
|
||||
void rcu_bh_qs(void);
|
||||
void rcu_check_callbacks(int cpu, int user);
|
||||
struct notifier_block;
|
||||
void rcu_idle_enter(void);
|
||||
@@ -269,6 +289,14 @@ static inline void rcu_user_hooks_switch(struct task_struct *prev,
|
||||
struct task_struct *next) { }
|
||||
#endif /* CONFIG_RCU_USER_QS */
|
||||
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
void rcu_init_nohz(void);
|
||||
#else /* #ifdef CONFIG_RCU_NOCB_CPU */
|
||||
static inline void rcu_init_nohz(void)
|
||||
{
|
||||
}
|
||||
#endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */
|
||||
|
||||
/**
|
||||
* RCU_NONIDLE - Indicate idle-loop code that needs RCU readers
|
||||
* @a: Code that RCU needs to pay attention to.
|
||||
@@ -294,6 +322,36 @@ static inline void rcu_user_hooks_switch(struct task_struct *prev,
|
||||
rcu_irq_exit(); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Note a voluntary context switch for RCU-tasks benefit. This is a
|
||||
* macro rather than an inline function to avoid #include hell.
|
||||
*/
|
||||
#ifdef CONFIG_TASKS_RCU
|
||||
#define TASKS_RCU(x) x
|
||||
extern struct srcu_struct tasks_rcu_exit_srcu;
|
||||
#define rcu_note_voluntary_context_switch(t) \
|
||||
do { \
|
||||
if (ACCESS_ONCE((t)->rcu_tasks_holdout)) \
|
||||
ACCESS_ONCE((t)->rcu_tasks_holdout) = false; \
|
||||
} while (0)
|
||||
#else /* #ifdef CONFIG_TASKS_RCU */
|
||||
#define TASKS_RCU(x) do { } while (0)
|
||||
#define rcu_note_voluntary_context_switch(t) do { } while (0)
|
||||
#endif /* #else #ifdef CONFIG_TASKS_RCU */
|
||||
|
||||
/**
|
||||
* cond_resched_rcu_qs - Report potential quiescent states to RCU
|
||||
*
|
||||
* This macro resembles cond_resched(), except that it is defined to
|
||||
* report potential quiescent states to RCU-tasks even if the cond_resched()
|
||||
* machinery were to be shut off, as some advocate for PREEMPT kernels.
|
||||
*/
|
||||
#define cond_resched_rcu_qs() \
|
||||
do { \
|
||||
rcu_note_voluntary_context_switch(current); \
|
||||
cond_resched(); \
|
||||
} while (0)
|
||||
|
||||
#if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) || defined(CONFIG_SMP)
|
||||
bool __rcu_is_watching(void);
|
||||
#endif /* #if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) || defined(CONFIG_SMP) */
|
||||
@@ -349,7 +407,7 @@ bool rcu_lockdep_current_cpu_online(void);
|
||||
#else /* #if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PROVE_RCU) */
|
||||
static inline bool rcu_lockdep_current_cpu_online(void)
|
||||
{
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
#endif /* #else #if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PROVE_RCU) */
|
||||
|
||||
@@ -371,41 +429,7 @@ extern struct lockdep_map rcu_sched_lock_map;
|
||||
extern struct lockdep_map rcu_callback_map;
|
||||
int debug_lockdep_rcu_enabled(void);
|
||||
|
||||
/**
|
||||
* rcu_read_lock_held() - might we be in RCU read-side critical section?
|
||||
*
|
||||
* If CONFIG_DEBUG_LOCK_ALLOC is selected, returns nonzero iff in an RCU
|
||||
* read-side critical section. In absence of CONFIG_DEBUG_LOCK_ALLOC,
|
||||
* this assumes we are in an RCU read-side critical section unless it can
|
||||
* prove otherwise. This is useful for debug checks in functions that
|
||||
* require that they be called within an RCU read-side critical section.
|
||||
*
|
||||
* Checks debug_lockdep_rcu_enabled() to prevent false positives during boot
|
||||
* and while lockdep is disabled.
|
||||
*
|
||||
* Note that rcu_read_lock() and the matching rcu_read_unlock() must
|
||||
* occur in the same context, for example, it is illegal to invoke
|
||||
* rcu_read_unlock() in process context if the matching rcu_read_lock()
|
||||
* was invoked from within an irq handler.
|
||||
*
|
||||
* Note that rcu_read_lock() is disallowed if the CPU is either idle or
|
||||
* offline from an RCU perspective, so check for those as well.
|
||||
*/
|
||||
static inline int rcu_read_lock_held(void)
|
||||
{
|
||||
if (!debug_lockdep_rcu_enabled())
|
||||
return 1;
|
||||
if (!rcu_is_watching())
|
||||
return 0;
|
||||
if (!rcu_lockdep_current_cpu_online())
|
||||
return 0;
|
||||
return lock_is_held(&rcu_lock_map);
|
||||
}
|
||||
|
||||
/*
|
||||
* rcu_read_lock_bh_held() is defined out of line to avoid #include-file
|
||||
* hell.
|
||||
*/
|
||||
int rcu_read_lock_held(void);
|
||||
int rcu_read_lock_bh_held(void);
|
||||
|
||||
/**
|
||||
|
||||
@@ -80,7 +80,7 @@ static inline void kfree_call_rcu(struct rcu_head *head,
|
||||
|
||||
static inline void rcu_note_context_switch(int cpu)
|
||||
{
|
||||
rcu_sched_qs(cpu);
|
||||
rcu_sched_qs();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+23
-18
@@ -1213,6 +1213,13 @@ struct sched_dl_entity {
|
||||
struct hrtimer dl_timer;
|
||||
};
|
||||
|
||||
union rcu_special {
|
||||
struct {
|
||||
bool blocked;
|
||||
bool need_qs;
|
||||
} b;
|
||||
short s;
|
||||
};
|
||||
struct rcu_node;
|
||||
|
||||
enum perf_event_task_context {
|
||||
@@ -1265,12 +1272,18 @@ struct task_struct {
|
||||
|
||||
#ifdef CONFIG_PREEMPT_RCU
|
||||
int rcu_read_lock_nesting;
|
||||
char rcu_read_unlock_special;
|
||||
union rcu_special rcu_read_unlock_special;
|
||||
struct list_head rcu_node_entry;
|
||||
#endif /* #ifdef CONFIG_PREEMPT_RCU */
|
||||
#ifdef CONFIG_TREE_PREEMPT_RCU
|
||||
struct rcu_node *rcu_blocked_node;
|
||||
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
|
||||
#ifdef CONFIG_TASKS_RCU
|
||||
unsigned long rcu_tasks_nvcsw;
|
||||
bool rcu_tasks_holdout;
|
||||
struct list_head rcu_tasks_holdout_list;
|
||||
int rcu_tasks_idle_cpu;
|
||||
#endif /* #ifdef CONFIG_TASKS_RCU */
|
||||
|
||||
#if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT)
|
||||
struct sched_info sched_info;
|
||||
@@ -2014,29 +2027,21 @@ extern void task_clear_jobctl_trapping(struct task_struct *task);
|
||||
extern void task_clear_jobctl_pending(struct task_struct *task,
|
||||
unsigned int mask);
|
||||
|
||||
static inline void rcu_copy_process(struct task_struct *p)
|
||||
{
|
||||
#ifdef CONFIG_PREEMPT_RCU
|
||||
|
||||
#define RCU_READ_UNLOCK_BLOCKED (1 << 0) /* blocked while in RCU read-side. */
|
||||
#define RCU_READ_UNLOCK_NEED_QS (1 << 1) /* RCU core needs CPU response. */
|
||||
|
||||
static inline void rcu_copy_process(struct task_struct *p)
|
||||
{
|
||||
p->rcu_read_lock_nesting = 0;
|
||||
p->rcu_read_unlock_special = 0;
|
||||
#ifdef CONFIG_TREE_PREEMPT_RCU
|
||||
p->rcu_read_unlock_special.s = 0;
|
||||
p->rcu_blocked_node = NULL;
|
||||
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
|
||||
INIT_LIST_HEAD(&p->rcu_node_entry);
|
||||
#endif /* #ifdef CONFIG_PREEMPT_RCU */
|
||||
#ifdef CONFIG_TASKS_RCU
|
||||
p->rcu_tasks_holdout = false;
|
||||
INIT_LIST_HEAD(&p->rcu_tasks_holdout_list);
|
||||
p->rcu_tasks_idle_cpu = -1;
|
||||
#endif /* #ifdef CONFIG_TASKS_RCU */
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline void rcu_copy_process(struct task_struct *p)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static inline void tsk_restore_flags(struct task_struct *task,
|
||||
unsigned long orig_flags, unsigned long flags)
|
||||
{
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
/* Definitions for online/offline exerciser. */
|
||||
int torture_onoff_init(long ooholdoff, long oointerval);
|
||||
char *torture_onoff_stats(char *page);
|
||||
void torture_onoff_stats(void);
|
||||
bool torture_onoff_failures(void);
|
||||
|
||||
/* Low-rider random number generator. */
|
||||
@@ -77,7 +77,8 @@ int torture_stutter_init(int s);
|
||||
/* Initialization and cleanup. */
|
||||
bool torture_init_begin(char *ttype, bool v, int *runnable);
|
||||
void torture_init_end(void);
|
||||
bool torture_cleanup(void);
|
||||
bool torture_cleanup_begin(void);
|
||||
void torture_cleanup_end(void);
|
||||
bool torture_must_stop(void);
|
||||
bool torture_must_stop_irq(void);
|
||||
void torture_kthread_stopping(char *title);
|
||||
|
||||
@@ -180,9 +180,12 @@ TRACE_EVENT(rcu_grace_period_init,
|
||||
* argument is a string as follows:
|
||||
*
|
||||
* "WakeEmpty": Wake rcuo kthread, first CB to empty list.
|
||||
* "WakeEmptyIsDeferred": Wake rcuo kthread later, first CB to empty list.
|
||||
* "WakeOvf": Wake rcuo kthread, CB list is huge.
|
||||
* "WakeOvfIsDeferred": Wake rcuo kthread later, CB list is huge.
|
||||
* "WakeNot": Don't wake rcuo kthread.
|
||||
* "WakeNotPoll": Don't wake rcuo kthread because it is polling.
|
||||
* "DeferredWake": Carried out the "IsDeferred" wakeup.
|
||||
* "Poll": Start of new polling cycle for rcu_nocb_poll.
|
||||
* "Sleep": Sleep waiting for CBs for !rcu_nocb_poll.
|
||||
* "WokeEmpty": rcuo kthread woke to find empty list.
|
||||
|
||||
+12
-2
@@ -507,6 +507,16 @@ config PREEMPT_RCU
|
||||
This option enables preemptible-RCU code that is common between
|
||||
TREE_PREEMPT_RCU and, in the old days, TINY_PREEMPT_RCU.
|
||||
|
||||
config TASKS_RCU
|
||||
bool "Task_based RCU implementation using voluntary context switch"
|
||||
default n
|
||||
help
|
||||
This option enables a task-based RCU implementation that uses
|
||||
only voluntary context switch (not preemption!), idle, and
|
||||
user-mode execution as quiescent states.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config RCU_STALL_COMMON
|
||||
def_bool ( TREE_RCU || TREE_PREEMPT_RCU || RCU_TRACE )
|
||||
help
|
||||
@@ -737,7 +747,7 @@ choice
|
||||
|
||||
config RCU_NOCB_CPU_NONE
|
||||
bool "No build_forced no-CBs CPUs"
|
||||
depends on RCU_NOCB_CPU && !NO_HZ_FULL_ALL
|
||||
depends on RCU_NOCB_CPU
|
||||
help
|
||||
This option does not force any of the CPUs to be no-CBs CPUs.
|
||||
Only CPUs designated by the rcu_nocbs= boot parameter will be
|
||||
@@ -751,7 +761,7 @@ config RCU_NOCB_CPU_NONE
|
||||
|
||||
config RCU_NOCB_CPU_ZERO
|
||||
bool "CPU 0 is a build_forced no-CBs CPU"
|
||||
depends on RCU_NOCB_CPU && !NO_HZ_FULL_ALL
|
||||
depends on RCU_NOCB_CPU
|
||||
help
|
||||
This option forces CPU 0 to be a no-CBs CPU, so that its RCU
|
||||
callbacks are invoked by a per-CPU kthread whose name begins
|
||||
|
||||
@@ -583,6 +583,7 @@ asmlinkage __visible void __init start_kernel(void)
|
||||
early_irq_init();
|
||||
init_IRQ();
|
||||
tick_init();
|
||||
rcu_init_nohz();
|
||||
init_timers();
|
||||
hrtimers_init();
|
||||
softirq_init();
|
||||
|
||||
+15
-1
@@ -79,6 +79,8 @@ static struct {
|
||||
|
||||
/* Lockdep annotations for get/put_online_cpus() and cpu_hotplug_begin/end() */
|
||||
#define cpuhp_lock_acquire_read() lock_map_acquire_read(&cpu_hotplug.dep_map)
|
||||
#define cpuhp_lock_acquire_tryread() \
|
||||
lock_map_acquire_tryread(&cpu_hotplug.dep_map)
|
||||
#define cpuhp_lock_acquire() lock_map_acquire(&cpu_hotplug.dep_map)
|
||||
#define cpuhp_lock_release() lock_map_release(&cpu_hotplug.dep_map)
|
||||
|
||||
@@ -91,10 +93,22 @@ void get_online_cpus(void)
|
||||
mutex_lock(&cpu_hotplug.lock);
|
||||
cpu_hotplug.refcount++;
|
||||
mutex_unlock(&cpu_hotplug.lock);
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(get_online_cpus);
|
||||
|
||||
bool try_get_online_cpus(void)
|
||||
{
|
||||
if (cpu_hotplug.active_writer == current)
|
||||
return true;
|
||||
if (!mutex_trylock(&cpu_hotplug.lock))
|
||||
return false;
|
||||
cpuhp_lock_acquire_tryread();
|
||||
cpu_hotplug.refcount++;
|
||||
mutex_unlock(&cpu_hotplug.lock);
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(try_get_online_cpus);
|
||||
|
||||
void put_online_cpus(void)
|
||||
{
|
||||
if (cpu_hotplug.active_writer == current)
|
||||
|
||||
@@ -667,6 +667,7 @@ void do_exit(long code)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
int group_dead;
|
||||
TASKS_RCU(int tasks_rcu_i);
|
||||
|
||||
profile_task_exit(tsk);
|
||||
|
||||
@@ -775,6 +776,7 @@ void do_exit(long code)
|
||||
*/
|
||||
flush_ptrace_hw_breakpoint(tsk);
|
||||
|
||||
TASKS_RCU(tasks_rcu_i = __srcu_read_lock(&tasks_rcu_exit_srcu));
|
||||
exit_notify(tsk, group_dead);
|
||||
proc_exit_connector(tsk);
|
||||
#ifdef CONFIG_NUMA
|
||||
@@ -814,6 +816,7 @@ void do_exit(long code)
|
||||
if (tsk->nr_dirtied)
|
||||
__this_cpu_add(dirty_throttle_leaks, tsk->nr_dirtied);
|
||||
exit_rcu();
|
||||
TASKS_RCU(__srcu_read_unlock(&tasks_rcu_exit_srcu, tasks_rcu_i));
|
||||
|
||||
/*
|
||||
* The setting of TASK_RUNNING by try_to_wake_up() may be delayed
|
||||
|
||||
+445
-84
File diff suppressed because it is too large
Load Diff
+198
-80
@@ -49,11 +49,19 @@
|
||||
#include <linux/trace_clock.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/torture.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Paul E. McKenney <paulmck@us.ibm.com> and Josh Triplett <josh@joshtriplett.org>");
|
||||
|
||||
|
||||
torture_param(int, cbflood_inter_holdoff, HZ,
|
||||
"Holdoff between floods (jiffies)");
|
||||
torture_param(int, cbflood_intra_holdoff, 1,
|
||||
"Holdoff between bursts (jiffies)");
|
||||
torture_param(int, cbflood_n_burst, 3, "# bursts in flood, zero to disable");
|
||||
torture_param(int, cbflood_n_per_burst, 20000,
|
||||
"# callbacks per burst in flood");
|
||||
torture_param(int, fqs_duration, 0,
|
||||
"Duration of fqs bursts (us), 0 to disable");
|
||||
torture_param(int, fqs_holdoff, 0, "Holdoff time within fqs bursts (us)");
|
||||
@@ -96,10 +104,12 @@ module_param(torture_type, charp, 0444);
|
||||
MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh, ...)");
|
||||
|
||||
static int nrealreaders;
|
||||
static int ncbflooders;
|
||||
static struct task_struct *writer_task;
|
||||
static struct task_struct **fakewriter_tasks;
|
||||
static struct task_struct **reader_tasks;
|
||||
static struct task_struct *stats_task;
|
||||
static struct task_struct **cbflood_task;
|
||||
static struct task_struct *fqs_task;
|
||||
static struct task_struct *boost_tasks[NR_CPUS];
|
||||
static struct task_struct *stall_task;
|
||||
@@ -138,6 +148,7 @@ static long n_rcu_torture_boosts;
|
||||
static long n_rcu_torture_timers;
|
||||
static long n_barrier_attempts;
|
||||
static long n_barrier_successes;
|
||||
static atomic_long_t n_cbfloods;
|
||||
static struct list_head rcu_torture_removed;
|
||||
|
||||
static int rcu_torture_writer_state;
|
||||
@@ -157,9 +168,9 @@ static int rcu_torture_writer_state;
|
||||
#else
|
||||
#define RCUTORTURE_RUNNABLE_INIT 0
|
||||
#endif
|
||||
int rcutorture_runnable = RCUTORTURE_RUNNABLE_INIT;
|
||||
module_param(rcutorture_runnable, int, 0444);
|
||||
MODULE_PARM_DESC(rcutorture_runnable, "Start rcutorture at boot");
|
||||
static int torture_runnable = RCUTORTURE_RUNNABLE_INIT;
|
||||
module_param(torture_runnable, int, 0444);
|
||||
MODULE_PARM_DESC(torture_runnable, "Start rcutorture at boot");
|
||||
|
||||
#if defined(CONFIG_RCU_BOOST) && !defined(CONFIG_HOTPLUG_CPU)
|
||||
#define rcu_can_boost() 1
|
||||
@@ -182,7 +193,7 @@ static u64 notrace rcu_trace_clock_local(void)
|
||||
#endif /* #else #ifdef CONFIG_RCU_TRACE */
|
||||
|
||||
static unsigned long boost_starttime; /* jiffies of next boost test start. */
|
||||
DEFINE_MUTEX(boost_mutex); /* protect setting boost_starttime */
|
||||
static DEFINE_MUTEX(boost_mutex); /* protect setting boost_starttime */
|
||||
/* and boost task create/destroy. */
|
||||
static atomic_t barrier_cbs_count; /* Barrier callbacks registered. */
|
||||
static bool barrier_phase; /* Test phase. */
|
||||
@@ -242,7 +253,7 @@ struct rcu_torture_ops {
|
||||
void (*call)(struct rcu_head *head, void (*func)(struct rcu_head *rcu));
|
||||
void (*cb_barrier)(void);
|
||||
void (*fqs)(void);
|
||||
void (*stats)(char *page);
|
||||
void (*stats)(void);
|
||||
int irq_capable;
|
||||
int can_boost;
|
||||
const char *name;
|
||||
@@ -525,21 +536,21 @@ static void srcu_torture_barrier(void)
|
||||
srcu_barrier(&srcu_ctl);
|
||||
}
|
||||
|
||||
static void srcu_torture_stats(char *page)
|
||||
static void srcu_torture_stats(void)
|
||||
{
|
||||
int cpu;
|
||||
int idx = srcu_ctl.completed & 0x1;
|
||||
|
||||
page += sprintf(page, "%s%s per-CPU(idx=%d):",
|
||||
torture_type, TORTURE_FLAG, idx);
|
||||
pr_alert("%s%s per-CPU(idx=%d):",
|
||||
torture_type, TORTURE_FLAG, idx);
|
||||
for_each_possible_cpu(cpu) {
|
||||
long c0, c1;
|
||||
|
||||
c0 = (long)per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[!idx];
|
||||
c1 = (long)per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[idx];
|
||||
page += sprintf(page, " %d(%ld,%ld)", cpu, c0, c1);
|
||||
pr_cont(" %d(%ld,%ld)", cpu, c0, c1);
|
||||
}
|
||||
sprintf(page, "\n");
|
||||
pr_cont("\n");
|
||||
}
|
||||
|
||||
static void srcu_torture_synchronize_expedited(void)
|
||||
@@ -601,6 +612,52 @@ static struct rcu_torture_ops sched_ops = {
|
||||
.name = "sched"
|
||||
};
|
||||
|
||||
#ifdef CONFIG_TASKS_RCU
|
||||
|
||||
/*
|
||||
* Definitions for RCU-tasks torture testing.
|
||||
*/
|
||||
|
||||
static int tasks_torture_read_lock(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tasks_torture_read_unlock(int idx)
|
||||
{
|
||||
}
|
||||
|
||||
static void rcu_tasks_torture_deferred_free(struct rcu_torture *p)
|
||||
{
|
||||
call_rcu_tasks(&p->rtort_rcu, rcu_torture_cb);
|
||||
}
|
||||
|
||||
static struct rcu_torture_ops tasks_ops = {
|
||||
.ttype = RCU_TASKS_FLAVOR,
|
||||
.init = rcu_sync_torture_init,
|
||||
.readlock = tasks_torture_read_lock,
|
||||
.read_delay = rcu_read_delay, /* just reuse rcu's version. */
|
||||
.readunlock = tasks_torture_read_unlock,
|
||||
.completed = rcu_no_completed,
|
||||
.deferred_free = rcu_tasks_torture_deferred_free,
|
||||
.sync = synchronize_rcu_tasks,
|
||||
.exp_sync = synchronize_rcu_tasks,
|
||||
.call = call_rcu_tasks,
|
||||
.cb_barrier = rcu_barrier_tasks,
|
||||
.fqs = NULL,
|
||||
.stats = NULL,
|
||||
.irq_capable = 1,
|
||||
.name = "tasks"
|
||||
};
|
||||
|
||||
#define RCUTORTURE_TASKS_OPS &tasks_ops,
|
||||
|
||||
#else /* #ifdef CONFIG_TASKS_RCU */
|
||||
|
||||
#define RCUTORTURE_TASKS_OPS
|
||||
|
||||
#endif /* #else #ifdef CONFIG_TASKS_RCU */
|
||||
|
||||
/*
|
||||
* RCU torture priority-boost testing. Runs one real-time thread per
|
||||
* CPU for moderate bursts, repeatedly registering RCU callbacks and
|
||||
@@ -667,7 +724,7 @@ static int rcu_torture_boost(void *arg)
|
||||
}
|
||||
call_rcu_time = jiffies;
|
||||
}
|
||||
cond_resched();
|
||||
cond_resched_rcu_qs();
|
||||
stutter_wait("rcu_torture_boost");
|
||||
if (torture_must_stop())
|
||||
goto checkwait;
|
||||
@@ -707,6 +764,58 @@ checkwait: stutter_wait("rcu_torture_boost");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rcu_torture_cbflood_cb(struct rcu_head *rhp)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* RCU torture callback-flood kthread. Repeatedly induces bursts of calls
|
||||
* to call_rcu() or analogous, increasing the probability of occurrence
|
||||
* of callback-overflow corner cases.
|
||||
*/
|
||||
static int
|
||||
rcu_torture_cbflood(void *arg)
|
||||
{
|
||||
int err = 1;
|
||||
int i;
|
||||
int j;
|
||||
struct rcu_head *rhp;
|
||||
|
||||
if (cbflood_n_per_burst > 0 &&
|
||||
cbflood_inter_holdoff > 0 &&
|
||||
cbflood_intra_holdoff > 0 &&
|
||||
cur_ops->call &&
|
||||
cur_ops->cb_barrier) {
|
||||
rhp = vmalloc(sizeof(*rhp) *
|
||||
cbflood_n_burst * cbflood_n_per_burst);
|
||||
err = !rhp;
|
||||
}
|
||||
if (err) {
|
||||
VERBOSE_TOROUT_STRING("rcu_torture_cbflood disabled: Bad args or OOM");
|
||||
while (!torture_must_stop())
|
||||
schedule_timeout_interruptible(HZ);
|
||||
return 0;
|
||||
}
|
||||
VERBOSE_TOROUT_STRING("rcu_torture_cbflood task started");
|
||||
do {
|
||||
schedule_timeout_interruptible(cbflood_inter_holdoff);
|
||||
atomic_long_inc(&n_cbfloods);
|
||||
WARN_ON(signal_pending(current));
|
||||
for (i = 0; i < cbflood_n_burst; i++) {
|
||||
for (j = 0; j < cbflood_n_per_burst; j++) {
|
||||
cur_ops->call(&rhp[i * cbflood_n_per_burst + j],
|
||||
rcu_torture_cbflood_cb);
|
||||
}
|
||||
schedule_timeout_interruptible(cbflood_intra_holdoff);
|
||||
WARN_ON(signal_pending(current));
|
||||
}
|
||||
cur_ops->cb_barrier();
|
||||
stutter_wait("rcu_torture_cbflood");
|
||||
} while (!torture_must_stop());
|
||||
torture_kthread_stopping("rcu_torture_cbflood");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* RCU torture force-quiescent-state kthread. Repeatedly induces
|
||||
* bursts of calls to force_quiescent_state(), increasing the probability
|
||||
@@ -1019,7 +1128,7 @@ rcu_torture_reader(void *arg)
|
||||
__this_cpu_inc(rcu_torture_batch[completed]);
|
||||
preempt_enable();
|
||||
cur_ops->readunlock(idx);
|
||||
cond_resched();
|
||||
cond_resched_rcu_qs();
|
||||
stutter_wait("rcu_torture_reader");
|
||||
} while (!torture_must_stop());
|
||||
if (irqreader && cur_ops->irq_capable) {
|
||||
@@ -1031,10 +1140,15 @@ rcu_torture_reader(void *arg)
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an RCU-torture statistics message in the specified buffer.
|
||||
* Print torture statistics. Caller must ensure that there is only
|
||||
* one call to this function at a given time!!! This is normally
|
||||
* accomplished by relying on the module system to only have one copy
|
||||
* of the module loaded, and then by giving the rcu_torture_stats
|
||||
* kthread full control (or the init/cleanup functions when rcu_torture_stats
|
||||
* thread is not running).
|
||||
*/
|
||||
static void
|
||||
rcu_torture_printk(char *page)
|
||||
rcu_torture_stats_print(void)
|
||||
{
|
||||
int cpu;
|
||||
int i;
|
||||
@@ -1052,55 +1166,61 @@ rcu_torture_printk(char *page)
|
||||
if (pipesummary[i] != 0)
|
||||
break;
|
||||
}
|
||||
page += sprintf(page, "%s%s ", torture_type, TORTURE_FLAG);
|
||||
page += sprintf(page,
|
||||
"rtc: %p ver: %lu tfle: %d rta: %d rtaf: %d rtf: %d ",
|
||||
rcu_torture_current,
|
||||
rcu_torture_current_version,
|
||||
list_empty(&rcu_torture_freelist),
|
||||
atomic_read(&n_rcu_torture_alloc),
|
||||
atomic_read(&n_rcu_torture_alloc_fail),
|
||||
atomic_read(&n_rcu_torture_free));
|
||||
page += sprintf(page, "rtmbe: %d rtbke: %ld rtbre: %ld ",
|
||||
atomic_read(&n_rcu_torture_mberror),
|
||||
n_rcu_torture_boost_ktrerror,
|
||||
n_rcu_torture_boost_rterror);
|
||||
page += sprintf(page, "rtbf: %ld rtb: %ld nt: %ld ",
|
||||
n_rcu_torture_boost_failure,
|
||||
n_rcu_torture_boosts,
|
||||
n_rcu_torture_timers);
|
||||
page = torture_onoff_stats(page);
|
||||
page += sprintf(page, "barrier: %ld/%ld:%ld",
|
||||
n_barrier_successes,
|
||||
n_barrier_attempts,
|
||||
n_rcu_torture_barrier_error);
|
||||
page += sprintf(page, "\n%s%s ", torture_type, TORTURE_FLAG);
|
||||
|
||||
pr_alert("%s%s ", torture_type, TORTURE_FLAG);
|
||||
pr_cont("rtc: %p ver: %lu tfle: %d rta: %d rtaf: %d rtf: %d ",
|
||||
rcu_torture_current,
|
||||
rcu_torture_current_version,
|
||||
list_empty(&rcu_torture_freelist),
|
||||
atomic_read(&n_rcu_torture_alloc),
|
||||
atomic_read(&n_rcu_torture_alloc_fail),
|
||||
atomic_read(&n_rcu_torture_free));
|
||||
pr_cont("rtmbe: %d rtbke: %ld rtbre: %ld ",
|
||||
atomic_read(&n_rcu_torture_mberror),
|
||||
n_rcu_torture_boost_ktrerror,
|
||||
n_rcu_torture_boost_rterror);
|
||||
pr_cont("rtbf: %ld rtb: %ld nt: %ld ",
|
||||
n_rcu_torture_boost_failure,
|
||||
n_rcu_torture_boosts,
|
||||
n_rcu_torture_timers);
|
||||
torture_onoff_stats();
|
||||
pr_cont("barrier: %ld/%ld:%ld ",
|
||||
n_barrier_successes,
|
||||
n_barrier_attempts,
|
||||
n_rcu_torture_barrier_error);
|
||||
pr_cont("cbflood: %ld\n", atomic_long_read(&n_cbfloods));
|
||||
|
||||
pr_alert("%s%s ", torture_type, TORTURE_FLAG);
|
||||
if (atomic_read(&n_rcu_torture_mberror) != 0 ||
|
||||
n_rcu_torture_barrier_error != 0 ||
|
||||
n_rcu_torture_boost_ktrerror != 0 ||
|
||||
n_rcu_torture_boost_rterror != 0 ||
|
||||
n_rcu_torture_boost_failure != 0 ||
|
||||
i > 1) {
|
||||
page += sprintf(page, "!!! ");
|
||||
pr_cont("%s", "!!! ");
|
||||
atomic_inc(&n_rcu_torture_error);
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
page += sprintf(page, "Reader Pipe: ");
|
||||
pr_cont("Reader Pipe: ");
|
||||
for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++)
|
||||
page += sprintf(page, " %ld", pipesummary[i]);
|
||||
page += sprintf(page, "\n%s%s ", torture_type, TORTURE_FLAG);
|
||||
page += sprintf(page, "Reader Batch: ");
|
||||
pr_cont(" %ld", pipesummary[i]);
|
||||
pr_cont("\n");
|
||||
|
||||
pr_alert("%s%s ", torture_type, TORTURE_FLAG);
|
||||
pr_cont("Reader Batch: ");
|
||||
for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++)
|
||||
page += sprintf(page, " %ld", batchsummary[i]);
|
||||
page += sprintf(page, "\n%s%s ", torture_type, TORTURE_FLAG);
|
||||
page += sprintf(page, "Free-Block Circulation: ");
|
||||
pr_cont(" %ld", batchsummary[i]);
|
||||
pr_cont("\n");
|
||||
|
||||
pr_alert("%s%s ", torture_type, TORTURE_FLAG);
|
||||
pr_cont("Free-Block Circulation: ");
|
||||
for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) {
|
||||
page += sprintf(page, " %d",
|
||||
atomic_read(&rcu_torture_wcount[i]));
|
||||
pr_cont(" %d", atomic_read(&rcu_torture_wcount[i]));
|
||||
}
|
||||
page += sprintf(page, "\n");
|
||||
pr_cont("\n");
|
||||
|
||||
if (cur_ops->stats)
|
||||
cur_ops->stats(page);
|
||||
cur_ops->stats();
|
||||
if (rtcv_snap == rcu_torture_current_version &&
|
||||
rcu_torture_current != NULL) {
|
||||
int __maybe_unused flags;
|
||||
@@ -1109,40 +1229,15 @@ rcu_torture_printk(char *page)
|
||||
|
||||
rcutorture_get_gp_data(cur_ops->ttype,
|
||||
&flags, &gpnum, &completed);
|
||||
page += sprintf(page,
|
||||
"??? Writer stall state %d g%lu c%lu f%#x\n",
|
||||
rcu_torture_writer_state,
|
||||
gpnum, completed, flags);
|
||||
pr_alert("??? Writer stall state %d g%lu c%lu f%#x\n",
|
||||
rcu_torture_writer_state,
|
||||
gpnum, completed, flags);
|
||||
show_rcu_gp_kthreads();
|
||||
rcutorture_trace_dump();
|
||||
}
|
||||
rtcv_snap = rcu_torture_current_version;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print torture statistics. Caller must ensure that there is only
|
||||
* one call to this function at a given time!!! This is normally
|
||||
* accomplished by relying on the module system to only have one copy
|
||||
* of the module loaded, and then by giving the rcu_torture_stats
|
||||
* kthread full control (or the init/cleanup functions when rcu_torture_stats
|
||||
* thread is not running).
|
||||
*/
|
||||
static void
|
||||
rcu_torture_stats_print(void)
|
||||
{
|
||||
int size = nr_cpu_ids * 200 + 8192;
|
||||
char *buf;
|
||||
|
||||
buf = kmalloc(size, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
pr_err("rcu-torture: Out of memory, need: %d", size);
|
||||
return;
|
||||
}
|
||||
rcu_torture_printk(buf);
|
||||
pr_alert("%s", buf);
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Periodically prints torture statistics, if periodic statistics printing
|
||||
* was specified via the stat_interval module parameter.
|
||||
@@ -1295,7 +1390,8 @@ static int rcu_torture_barrier_cbs(void *arg)
|
||||
if (atomic_dec_and_test(&barrier_cbs_count))
|
||||
wake_up(&barrier_wq);
|
||||
} while (!torture_must_stop());
|
||||
cur_ops->cb_barrier();
|
||||
if (cur_ops->cb_barrier != NULL)
|
||||
cur_ops->cb_barrier();
|
||||
destroy_rcu_head_on_stack(&rcu);
|
||||
torture_kthread_stopping("rcu_torture_barrier_cbs");
|
||||
return 0;
|
||||
@@ -1418,7 +1514,7 @@ rcu_torture_cleanup(void)
|
||||
int i;
|
||||
|
||||
rcutorture_record_test_transition();
|
||||
if (torture_cleanup()) {
|
||||
if (torture_cleanup_begin()) {
|
||||
if (cur_ops->cb_barrier != NULL)
|
||||
cur_ops->cb_barrier();
|
||||
return;
|
||||
@@ -1447,6 +1543,8 @@ rcu_torture_cleanup(void)
|
||||
|
||||
torture_stop_kthread(rcu_torture_stats, stats_task);
|
||||
torture_stop_kthread(rcu_torture_fqs, fqs_task);
|
||||
for (i = 0; i < ncbflooders; i++)
|
||||
torture_stop_kthread(rcu_torture_cbflood, cbflood_task[i]);
|
||||
if ((test_boost == 1 && cur_ops->can_boost) ||
|
||||
test_boost == 2) {
|
||||
unregister_cpu_notifier(&rcutorture_cpu_nb);
|
||||
@@ -1468,6 +1566,7 @@ rcu_torture_cleanup(void)
|
||||
"End of test: RCU_HOTPLUG");
|
||||
else
|
||||
rcu_torture_print_module_parms(cur_ops, "End of test: SUCCESS");
|
||||
torture_cleanup_end();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD
|
||||
@@ -1534,9 +1633,10 @@ rcu_torture_init(void)
|
||||
int firsterr = 0;
|
||||
static struct rcu_torture_ops *torture_ops[] = {
|
||||
&rcu_ops, &rcu_bh_ops, &rcu_busted_ops, &srcu_ops, &sched_ops,
|
||||
RCUTORTURE_TASKS_OPS
|
||||
};
|
||||
|
||||
if (!torture_init_begin(torture_type, verbose, &rcutorture_runnable))
|
||||
if (!torture_init_begin(torture_type, verbose, &torture_runnable))
|
||||
return -EBUSY;
|
||||
|
||||
/* Process args and tell the world that the torturer is on the job. */
|
||||
@@ -1693,6 +1793,24 @@ rcu_torture_init(void)
|
||||
goto unwind;
|
||||
if (object_debug)
|
||||
rcu_test_debug_objects();
|
||||
if (cbflood_n_burst > 0) {
|
||||
/* Create the cbflood threads */
|
||||
ncbflooders = (num_online_cpus() + 3) / 4;
|
||||
cbflood_task = kcalloc(ncbflooders, sizeof(*cbflood_task),
|
||||
GFP_KERNEL);
|
||||
if (!cbflood_task) {
|
||||
VERBOSE_TOROUT_ERRSTRING("out of memory");
|
||||
firsterr = -ENOMEM;
|
||||
goto unwind;
|
||||
}
|
||||
for (i = 0; i < ncbflooders; i++) {
|
||||
firsterr = torture_create_kthread(rcu_torture_cbflood,
|
||||
NULL,
|
||||
cbflood_task[i]);
|
||||
if (firsterr)
|
||||
goto unwind;
|
||||
}
|
||||
}
|
||||
rcutorture_record_test_transition();
|
||||
torture_init_end();
|
||||
return 0;
|
||||
|
||||
+11
-9
@@ -51,7 +51,7 @@ static long long rcu_dynticks_nesting = DYNTICK_TASK_EXIT_IDLE;
|
||||
|
||||
#include "tiny_plugin.h"
|
||||
|
||||
/* Common code for rcu_idle_enter() and rcu_irq_exit(), see kernel/rcutree.c. */
|
||||
/* Common code for rcu_idle_enter() and rcu_irq_exit(), see kernel/rcu/tree.c. */
|
||||
static void rcu_idle_enter_common(long long newval)
|
||||
{
|
||||
if (newval) {
|
||||
@@ -62,7 +62,7 @@ static void rcu_idle_enter_common(long long newval)
|
||||
}
|
||||
RCU_TRACE(trace_rcu_dyntick(TPS("Start"),
|
||||
rcu_dynticks_nesting, newval));
|
||||
if (!is_idle_task(current)) {
|
||||
if (IS_ENABLED(CONFIG_RCU_TRACE) && !is_idle_task(current)) {
|
||||
struct task_struct *idle __maybe_unused = idle_task(smp_processor_id());
|
||||
|
||||
RCU_TRACE(trace_rcu_dyntick(TPS("Entry error: not idle task"),
|
||||
@@ -72,7 +72,7 @@ static void rcu_idle_enter_common(long long newval)
|
||||
current->pid, current->comm,
|
||||
idle->pid, idle->comm); /* must be idle task! */
|
||||
}
|
||||
rcu_sched_qs(0); /* implies rcu_bh_qsctr_inc(0) */
|
||||
rcu_sched_qs(); /* implies rcu_bh_inc() */
|
||||
barrier();
|
||||
rcu_dynticks_nesting = newval;
|
||||
}
|
||||
@@ -114,7 +114,7 @@ void rcu_irq_exit(void)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcu_irq_exit);
|
||||
|
||||
/* Common code for rcu_idle_exit() and rcu_irq_enter(), see kernel/rcutree.c. */
|
||||
/* Common code for rcu_idle_exit() and rcu_irq_enter(), see kernel/rcu/tree.c. */
|
||||
static void rcu_idle_exit_common(long long oldval)
|
||||
{
|
||||
if (oldval) {
|
||||
@@ -123,7 +123,7 @@ static void rcu_idle_exit_common(long long oldval)
|
||||
return;
|
||||
}
|
||||
RCU_TRACE(trace_rcu_dyntick(TPS("End"), oldval, rcu_dynticks_nesting));
|
||||
if (!is_idle_task(current)) {
|
||||
if (IS_ENABLED(CONFIG_RCU_TRACE) && !is_idle_task(current)) {
|
||||
struct task_struct *idle __maybe_unused = idle_task(smp_processor_id());
|
||||
|
||||
RCU_TRACE(trace_rcu_dyntick(TPS("Exit error: not idle task"),
|
||||
@@ -217,7 +217,7 @@ static int rcu_qsctr_help(struct rcu_ctrlblk *rcp)
|
||||
* are at it, given that any rcu quiescent state is also an rcu_bh
|
||||
* quiescent state. Use "+" instead of "||" to defeat short circuiting.
|
||||
*/
|
||||
void rcu_sched_qs(int cpu)
|
||||
void rcu_sched_qs(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
@@ -231,7 +231,7 @@ void rcu_sched_qs(int cpu)
|
||||
/*
|
||||
* Record an rcu_bh quiescent state.
|
||||
*/
|
||||
void rcu_bh_qs(int cpu)
|
||||
void rcu_bh_qs(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
@@ -251,9 +251,11 @@ void rcu_check_callbacks(int cpu, int user)
|
||||
{
|
||||
RCU_TRACE(check_cpu_stalls());
|
||||
if (user || rcu_is_cpu_rrupt_from_idle())
|
||||
rcu_sched_qs(cpu);
|
||||
rcu_sched_qs();
|
||||
else if (!in_softirq())
|
||||
rcu_bh_qs(cpu);
|
||||
rcu_bh_qs();
|
||||
if (user)
|
||||
rcu_note_voluntary_context_switch(current);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user