Merge 059c4a341d ("Merge tag 'pstore-v6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux") into android-mainline
Steps on the way to 6.2-rc1 Change-Id: If4d3bc3484cc4090a890c461814a55ba048a6a9d Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
@@ -1,165 +0,0 @@
|
||||
.. _array_rcu_doc:
|
||||
|
||||
Using RCU to Protect Read-Mostly Arrays
|
||||
=======================================
|
||||
|
||||
Although RCU is more commonly used to protect linked lists, it can
|
||||
also be used to protect arrays. Three situations are as follows:
|
||||
|
||||
1. :ref:`Hash Tables <hash_tables>`
|
||||
|
||||
2. :ref:`Static Arrays <static_arrays>`
|
||||
|
||||
3. :ref:`Resizable Arrays <resizable_arrays>`
|
||||
|
||||
Each of these three situations involves an RCU-protected pointer to an
|
||||
array that is separately indexed. It might be tempting to consider use
|
||||
of RCU to instead protect the index into an array, however, this use
|
||||
case is **not** supported. The problem with RCU-protected indexes into
|
||||
arrays is that compilers can play way too many optimization games with
|
||||
integers, which means that the rules governing handling of these indexes
|
||||
are far more trouble than they are worth. If RCU-protected indexes into
|
||||
arrays prove to be particularly valuable (which they have not thus far),
|
||||
explicit cooperation from the compiler will be required to permit them
|
||||
to be safely used.
|
||||
|
||||
That aside, each of the three RCU-protected pointer situations are
|
||||
described in the following sections.
|
||||
|
||||
.. _hash_tables:
|
||||
|
||||
Situation 1: Hash Tables
|
||||
------------------------
|
||||
|
||||
Hash tables are often implemented as an array, where each array entry
|
||||
has a linked-list hash chain. Each hash chain can be protected by RCU
|
||||
as described in listRCU.rst. This approach also applies to other
|
||||
array-of-list situations, such as radix trees.
|
||||
|
||||
.. _static_arrays:
|
||||
|
||||
Situation 2: Static Arrays
|
||||
--------------------------
|
||||
|
||||
Static arrays, where the data (rather than a pointer to the data) is
|
||||
located in each array element, and where the array is never resized,
|
||||
have not been used with RCU. Rik van Riel recommends using seqlock in
|
||||
this situation, which would also have minimal read-side overhead as long
|
||||
as updates are rare.
|
||||
|
||||
Quick Quiz:
|
||||
Why is it so important that updates be rare when using seqlock?
|
||||
|
||||
:ref:`Answer to Quick Quiz <answer_quick_quiz_seqlock>`
|
||||
|
||||
.. _resizable_arrays:
|
||||
|
||||
Situation 3: Resizable Arrays
|
||||
------------------------------
|
||||
|
||||
Use of RCU for resizable arrays is demonstrated by the grow_ary()
|
||||
function formerly used by the System V IPC code. The array is used
|
||||
to map from semaphore, message-queue, and shared-memory IDs to the data
|
||||
structure that represents the corresponding IPC construct. The grow_ary()
|
||||
function does not acquire any locks; instead its caller must hold the
|
||||
ids->sem semaphore.
|
||||
|
||||
The grow_ary() function, shown below, does some limit checks, allocates a
|
||||
new ipc_id_ary, copies the old to the new portion of the new, initializes
|
||||
the remainder of the new, updates the ids->entries pointer to point to
|
||||
the new array, and invokes ipc_rcu_putref() to free up the old array.
|
||||
Note that rcu_assign_pointer() is used to update the ids->entries pointer,
|
||||
which includes any memory barriers required on whatever architecture
|
||||
you are running on::
|
||||
|
||||
static int grow_ary(struct ipc_ids* ids, int newsize)
|
||||
{
|
||||
struct ipc_id_ary* new;
|
||||
struct ipc_id_ary* old;
|
||||
int i;
|
||||
int size = ids->entries->size;
|
||||
|
||||
if(newsize > IPCMNI)
|
||||
newsize = IPCMNI;
|
||||
if(newsize <= size)
|
||||
return newsize;
|
||||
|
||||
new = ipc_rcu_alloc(sizeof(struct kern_ipc_perm *)*newsize +
|
||||
sizeof(struct ipc_id_ary));
|
||||
if(new == NULL)
|
||||
return size;
|
||||
new->size = newsize;
|
||||
memcpy(new->p, ids->entries->p,
|
||||
sizeof(struct kern_ipc_perm *)*size +
|
||||
sizeof(struct ipc_id_ary));
|
||||
for(i=size;i<newsize;i++) {
|
||||
new->p[i] = NULL;
|
||||
}
|
||||
old = ids->entries;
|
||||
|
||||
/*
|
||||
* Use rcu_assign_pointer() to make sure the memcpyed
|
||||
* contents of the new array are visible before the new
|
||||
* array becomes visible.
|
||||
*/
|
||||
rcu_assign_pointer(ids->entries, new);
|
||||
|
||||
ipc_rcu_putref(old);
|
||||
return newsize;
|
||||
}
|
||||
|
||||
The ipc_rcu_putref() function decrements the array's reference count
|
||||
and then, if the reference count has dropped to zero, uses call_rcu()
|
||||
to free the array after a grace period has elapsed.
|
||||
|
||||
The array is traversed by the ipc_lock() function. This function
|
||||
indexes into the array under the protection of rcu_read_lock(),
|
||||
using rcu_dereference() to pick up the pointer to the array so
|
||||
that it may later safely be dereferenced -- memory barriers are
|
||||
required on the Alpha CPU. Since the size of the array is stored
|
||||
with the array itself, there can be no array-size mismatches, so
|
||||
a simple check suffices. The pointer to the structure corresponding
|
||||
to the desired IPC object is placed in "out", with NULL indicating
|
||||
a non-existent entry. After acquiring "out->lock", the "out->deleted"
|
||||
flag indicates whether the IPC object is in the process of being
|
||||
deleted, and, if not, the pointer is returned::
|
||||
|
||||
struct kern_ipc_perm* ipc_lock(struct ipc_ids* ids, int id)
|
||||
{
|
||||
struct kern_ipc_perm* out;
|
||||
int lid = id % SEQ_MULTIPLIER;
|
||||
struct ipc_id_ary* entries;
|
||||
|
||||
rcu_read_lock();
|
||||
entries = rcu_dereference(ids->entries);
|
||||
if(lid >= entries->size) {
|
||||
rcu_read_unlock();
|
||||
return NULL;
|
||||
}
|
||||
out = entries->p[lid];
|
||||
if(out == NULL) {
|
||||
rcu_read_unlock();
|
||||
return NULL;
|
||||
}
|
||||
spin_lock(&out->lock);
|
||||
|
||||
/* ipc_rmid() may have already freed the ID while ipc_lock
|
||||
* was spinning: here verify that the structure is still valid
|
||||
*/
|
||||
if (out->deleted) {
|
||||
spin_unlock(&out->lock);
|
||||
rcu_read_unlock();
|
||||
return NULL;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
.. _answer_quick_quiz_seqlock:
|
||||
|
||||
Answer to Quick Quiz:
|
||||
Why is it so important that updates be rare when using seqlock?
|
||||
|
||||
The reason that it is important that updates be rare when
|
||||
using seqlock is that frequent updates can livelock readers.
|
||||
One way to avoid this problem is to assign a seqlock for
|
||||
each array entry rather than to the entire array.
|
||||
+141
-99
@@ -32,8 +32,8 @@ over a rather long period of time, but improvements are always welcome!
|
||||
for lockless updates. This does result in the mildly
|
||||
counter-intuitive situation where rcu_read_lock() and
|
||||
rcu_read_unlock() are used to protect updates, however, this
|
||||
approach provides the same potential simplifications that garbage
|
||||
collectors do.
|
||||
approach can provide the same simplifications to certain types
|
||||
of lockless algorithms that garbage collectors do.
|
||||
|
||||
1. Does the update code have proper mutual exclusion?
|
||||
|
||||
@@ -49,12 +49,12 @@ over a rather long period of time, but improvements are always welcome!
|
||||
them -- even x86 allows later loads to be reordered to precede
|
||||
earlier stores), and be prepared to explain why this added
|
||||
complexity is worthwhile. If you choose #c, be prepared to
|
||||
explain how this single task does not become a major bottleneck on
|
||||
big multiprocessor machines (for example, if the task is updating
|
||||
information relating to itself that other tasks can read, there
|
||||
by definition can be no bottleneck). Note that the definition
|
||||
of "large" has changed significantly: Eight CPUs was "large"
|
||||
in the year 2000, but a hundred CPUs was unremarkable in 2017.
|
||||
explain how this single task does not become a major bottleneck
|
||||
on large systems (for example, if the task is updating information
|
||||
relating to itself that other tasks can read, there by definition
|
||||
can be no bottleneck). Note that the definition of "large" has
|
||||
changed significantly: Eight CPUs was "large" in the year 2000,
|
||||
but a hundred CPUs was unremarkable in 2017.
|
||||
|
||||
2. Do the RCU read-side critical sections make proper use of
|
||||
rcu_read_lock() and friends? These primitives are needed
|
||||
@@ -97,33 +97,38 @@ over a rather long period of time, but improvements are always welcome!
|
||||
|
||||
b. Proceed as in (a) above, but also maintain per-element
|
||||
locks (that are acquired by both readers and writers)
|
||||
that guard per-element state. Of course, fields that
|
||||
the readers refrain from accessing can be guarded by
|
||||
some other lock acquired only by updaters, if desired.
|
||||
that guard per-element state. Fields that the readers
|
||||
refrain from accessing can be guarded by some other lock
|
||||
acquired only by updaters, if desired.
|
||||
|
||||
This works quite well, also.
|
||||
This also works quite well.
|
||||
|
||||
c. Make updates appear atomic to readers. For example,
|
||||
pointer updates to properly aligned fields will
|
||||
appear atomic, as will individual atomic primitives.
|
||||
Sequences of operations performed under a lock will *not*
|
||||
appear to be atomic to RCU readers, nor will sequences
|
||||
of multiple atomic primitives.
|
||||
of multiple atomic primitives. One alternative is to
|
||||
move multiple individual fields to a separate structure,
|
||||
thus solving the multiple-field problem by imposing an
|
||||
additional level of indirection.
|
||||
|
||||
This can work, but is starting to get a bit tricky.
|
||||
|
||||
d. Carefully order the updates and the reads so that
|
||||
readers see valid data at all phases of the update.
|
||||
This is often more difficult than it sounds, especially
|
||||
given modern CPUs' tendency to reorder memory references.
|
||||
One must usually liberally sprinkle memory barriers
|
||||
(smp_wmb(), smp_rmb(), smp_mb()) through the code,
|
||||
making it difficult to understand and to test.
|
||||
d. Carefully order the updates and the reads so that readers
|
||||
see valid data at all phases of the update. This is often
|
||||
more difficult than it sounds, especially given modern
|
||||
CPUs' tendency to reorder memory references. One must
|
||||
usually liberally sprinkle memory-ordering operations
|
||||
through the code, making it difficult to understand and
|
||||
to test. Where it works, it is better to use things
|
||||
like smp_store_release() and smp_load_acquire(), but in
|
||||
some cases the smp_mb() full memory barrier is required.
|
||||
|
||||
It is usually better to group the changing data into
|
||||
a separate structure, so that the change may be made
|
||||
to appear atomic by updating a pointer to reference
|
||||
a new structure containing updated values.
|
||||
As noted earlier, it is usually better to group the
|
||||
changing data into a separate structure, so that the
|
||||
change may be made to appear atomic by updating a pointer
|
||||
to reference a new structure containing updated values.
|
||||
|
||||
4. Weakly ordered CPUs pose special challenges. Almost all CPUs
|
||||
are weakly ordered -- even x86 CPUs allow later loads to be
|
||||
@@ -188,26 +193,29 @@ over a rather long period of time, but improvements are always welcome!
|
||||
when publicizing a pointer to a structure that can
|
||||
be traversed by an RCU read-side critical section.
|
||||
|
||||
5. If call_rcu() or call_srcu() is used, the callback function will
|
||||
be called from softirq context. In particular, it cannot block.
|
||||
If you need the callback to block, run that code in a workqueue
|
||||
handler scheduled from the callback. The queue_rcu_work()
|
||||
function does this for you in the case of call_rcu().
|
||||
5. If any of call_rcu(), call_srcu(), call_rcu_tasks(),
|
||||
call_rcu_tasks_rude(), or call_rcu_tasks_trace() is used,
|
||||
the callback function may be invoked from softirq context,
|
||||
and in any case with bottom halves disabled. In particular,
|
||||
this callback function cannot block. If you need the callback
|
||||
to block, run that code in a workqueue handler scheduled from
|
||||
the callback. The queue_rcu_work() function does this for you
|
||||
in the case of call_rcu().
|
||||
|
||||
6. Since synchronize_rcu() can block, it cannot be called
|
||||
from any sort of irq context. The same rule applies
|
||||
for synchronize_srcu(), synchronize_rcu_expedited(), and
|
||||
synchronize_srcu_expedited().
|
||||
for synchronize_srcu(), synchronize_rcu_expedited(),
|
||||
synchronize_srcu_expedited(), synchronize_rcu_tasks(),
|
||||
synchronize_rcu_tasks_rude(), and synchronize_rcu_tasks_trace().
|
||||
|
||||
The expedited forms of these primitives have the same semantics
|
||||
as the non-expedited forms, but expediting is both expensive and
|
||||
(with the exception of synchronize_srcu_expedited()) unfriendly
|
||||
to real-time workloads. Use of the expedited primitives should
|
||||
be restricted to rare configuration-change operations that would
|
||||
not normally be undertaken while a real-time workload is running.
|
||||
However, real-time workloads can use rcupdate.rcu_normal kernel
|
||||
boot parameter to completely disable expedited grace periods,
|
||||
though this might have performance implications.
|
||||
as the non-expedited forms, but expediting is more CPU intensive.
|
||||
Use of the expedited primitives should be restricted to rare
|
||||
configuration-change operations that would not normally be
|
||||
undertaken while a real-time workload is running. Note that
|
||||
IPI-sensitive real-time workloads can use the rcupdate.rcu_normal
|
||||
kernel boot parameter to completely disable expedited grace
|
||||
periods, though this might have performance implications.
|
||||
|
||||
In particular, if you find yourself invoking one of the expedited
|
||||
primitives repeatedly in a loop, please do everyone a favor:
|
||||
@@ -215,8 +223,9 @@ over a rather long period of time, but improvements are always welcome!
|
||||
a single non-expedited primitive to cover the entire batch.
|
||||
This will very likely be faster than the loop containing the
|
||||
expedited primitive, and will be much much easier on the rest
|
||||
of the system, especially to real-time workloads running on
|
||||
the rest of the system.
|
||||
of the system, especially to real-time workloads running on the
|
||||
rest of the system. Alternatively, instead use asynchronous
|
||||
primitives such as call_rcu().
|
||||
|
||||
7. As of v4.20, a given kernel implements only one RCU flavor, which
|
||||
is RCU-sched for PREEMPTION=n and RCU-preempt for PREEMPTION=y.
|
||||
@@ -239,7 +248,8 @@ over a rather long period of time, but improvements are always welcome!
|
||||
the corresponding readers must use rcu_read_lock_trace() and
|
||||
rcu_read_unlock_trace(). If an updater uses call_rcu_tasks_rude()
|
||||
or synchronize_rcu_tasks_rude(), then the corresponding readers
|
||||
must use anything that disables interrupts.
|
||||
must use anything that disables preemption, for example,
|
||||
preempt_disable() and preempt_enable().
|
||||
|
||||
Mixing things up will result in confusion and broken kernels, and
|
||||
has even resulted in an exploitable security issue. Therefore,
|
||||
@@ -253,15 +263,16 @@ over a rather long period of time, but improvements are always welcome!
|
||||
that this usage is safe is that readers can use anything that
|
||||
disables BH when updaters use call_rcu() or synchronize_rcu().
|
||||
|
||||
8. Although synchronize_rcu() is slower than is call_rcu(), it
|
||||
usually results in simpler code. So, unless update performance is
|
||||
critically important, the updaters cannot block, or the latency of
|
||||
synchronize_rcu() is visible from userspace, synchronize_rcu()
|
||||
should be used in preference to call_rcu(). Furthermore,
|
||||
kfree_rcu() usually results in even simpler code than does
|
||||
synchronize_rcu() without synchronize_rcu()'s multi-millisecond
|
||||
latency. So please take advantage of kfree_rcu()'s "fire and
|
||||
forget" memory-freeing capabilities where it applies.
|
||||
8. Although synchronize_rcu() is slower than is call_rcu(),
|
||||
it usually results in simpler code. So, unless update
|
||||
performance is critically important, the updaters cannot block,
|
||||
or the latency of synchronize_rcu() is visible from userspace,
|
||||
synchronize_rcu() should be used in preference to call_rcu().
|
||||
Furthermore, kfree_rcu() and kvfree_rcu() usually result
|
||||
in even simpler code than does synchronize_rcu() without
|
||||
synchronize_rcu()'s multi-millisecond latency. So please take
|
||||
advantage of kfree_rcu()'s and kvfree_rcu()'s "fire and forget"
|
||||
memory-freeing capabilities where it applies.
|
||||
|
||||
An especially important property of the synchronize_rcu()
|
||||
primitive is that it automatically self-limits: if grace periods
|
||||
@@ -271,8 +282,8 @@ over a rather long period of time, but improvements are always welcome!
|
||||
cases where grace periods are delayed, as failing to do so can
|
||||
result in excessive realtime latencies or even OOM conditions.
|
||||
|
||||
Ways of gaining this self-limiting property when using call_rcu()
|
||||
include:
|
||||
Ways of gaining this self-limiting property when using call_rcu(),
|
||||
kfree_rcu(), or kvfree_rcu() include:
|
||||
|
||||
a. Keeping a count of the number of data-structure elements
|
||||
used by the RCU-protected data structure, including
|
||||
@@ -304,18 +315,21 @@ over a rather long period of time, but improvements are always welcome!
|
||||
here is that superuser already has lots of ways to crash
|
||||
the machine.
|
||||
|
||||
d. Periodically invoke synchronize_rcu(), permitting a limited
|
||||
number of updates per grace period. Better yet, periodically
|
||||
invoke rcu_barrier() to wait for all outstanding callbacks.
|
||||
d. Periodically invoke rcu_barrier(), permitting a limited
|
||||
number of updates per grace period.
|
||||
|
||||
The same cautions apply to call_srcu() and kfree_rcu().
|
||||
The same cautions apply to call_srcu(), call_rcu_tasks(),
|
||||
call_rcu_tasks_rude(), and call_rcu_tasks_trace(). This is
|
||||
why there is an srcu_barrier(), rcu_barrier_tasks(),
|
||||
rcu_barrier_tasks_rude(), and rcu_barrier_tasks_rude(),
|
||||
respectively.
|
||||
|
||||
Note that although these primitives do take action to avoid memory
|
||||
exhaustion when any given CPU has too many callbacks, a determined
|
||||
user could still exhaust memory. This is especially the case
|
||||
if a system with a large number of CPUs has been configured to
|
||||
offload all of its RCU callbacks onto a single CPU, or if the
|
||||
system has relatively little free memory.
|
||||
Note that although these primitives do take action to avoid
|
||||
memory exhaustion when any given CPU has too many callbacks,
|
||||
a determined user or administrator can still exhaust memory.
|
||||
This is especially the case if a system with a large number of
|
||||
CPUs has been configured to offload all of its RCU callbacks onto
|
||||
a single CPU, or if the system has relatively little free memory.
|
||||
|
||||
9. All RCU list-traversal primitives, which include
|
||||
rcu_dereference(), list_for_each_entry_rcu(), and
|
||||
@@ -344,14 +358,14 @@ over a rather long period of time, but improvements are always welcome!
|
||||
and you don't hold the appropriate update-side lock, you *must*
|
||||
use the "_rcu()" variants of the list macros. Failing to do so
|
||||
will break Alpha, cause aggressive compilers to generate bad code,
|
||||
and confuse people trying to read your code.
|
||||
and confuse people trying to understand your code.
|
||||
|
||||
11. Any lock acquired by an RCU callback must be acquired elsewhere
|
||||
with softirq disabled, e.g., via spin_lock_irqsave(),
|
||||
spin_lock_bh(), etc. Failing to disable softirq on a given
|
||||
acquisition of that lock will result in deadlock as soon as
|
||||
the RCU softirq handler happens to run your RCU callback while
|
||||
interrupting that acquisition's critical section.
|
||||
with softirq disabled, e.g., via spin_lock_bh(). Failing to
|
||||
disable softirq on a given acquisition of that lock will result
|
||||
in deadlock as soon as the RCU softirq handler happens to run
|
||||
your RCU callback while interrupting that acquisition's critical
|
||||
section.
|
||||
|
||||
12. RCU callbacks can be and are executed in parallel. In many cases,
|
||||
the callback code simply wrappers around kfree(), so that this
|
||||
@@ -372,7 +386,17 @@ over a rather long period of time, but improvements are always welcome!
|
||||
for some real-time workloads, this is the whole point of using
|
||||
the rcu_nocbs= kernel boot parameter.
|
||||
|
||||
13. Unlike other forms of RCU, it *is* permissible to block in an
|
||||
In addition, do not assume that callbacks queued in a given order
|
||||
will be invoked in that order, even if they all are queued on the
|
||||
same CPU. Furthermore, do not assume that same-CPU callbacks will
|
||||
be invoked serially. For example, in recent kernels, CPUs can be
|
||||
switched between offloaded and de-offloaded callback invocation,
|
||||
and while a given CPU is undergoing such a switch, its callbacks
|
||||
might be concurrently invoked by that CPU's softirq handler and
|
||||
that CPU's rcuo kthread. At such times, that CPU's callbacks
|
||||
might be executed both concurrently and out of order.
|
||||
|
||||
13. Unlike most flavors of RCU, it *is* permissible to block in an
|
||||
SRCU read-side critical section (demarked by srcu_read_lock()
|
||||
and srcu_read_unlock()), hence the "SRCU": "sleepable RCU".
|
||||
Please note that if you don't need to sleep in read-side critical
|
||||
@@ -412,6 +436,12 @@ over a rather long period of time, but improvements are always welcome!
|
||||
never sends IPIs to other CPUs, so it is easier on
|
||||
real-time workloads than is synchronize_rcu_expedited().
|
||||
|
||||
It is also permissible to sleep in RCU Tasks Trace read-side
|
||||
critical, which are delimited by rcu_read_lock_trace() and
|
||||
rcu_read_unlock_trace(). However, this is a specialized flavor
|
||||
of RCU, and you should not use it without first checking with
|
||||
its current users. In most cases, you should instead use SRCU.
|
||||
|
||||
Note that rcu_assign_pointer() relates to SRCU just as it does to
|
||||
other forms of RCU, but instead of rcu_dereference() you should
|
||||
use srcu_dereference() in order to avoid lockdep splats.
|
||||
@@ -442,50 +472,62 @@ over a rather long period of time, but improvements are always welcome!
|
||||
find problems as follows:
|
||||
|
||||
CONFIG_PROVE_LOCKING:
|
||||
check that accesses to RCU-protected data
|
||||
structures are carried out under the proper RCU
|
||||
read-side critical section, while holding the right
|
||||
combination of locks, or whatever other conditions
|
||||
are appropriate.
|
||||
check that accesses to RCU-protected data structures
|
||||
are carried out under the proper RCU read-side critical
|
||||
section, while holding the right combination of locks,
|
||||
or whatever other conditions are appropriate.
|
||||
|
||||
CONFIG_DEBUG_OBJECTS_RCU_HEAD:
|
||||
check that you don't pass the
|
||||
same object to call_rcu() (or friends) before an RCU
|
||||
grace period has elapsed since the last time that you
|
||||
passed that same object to call_rcu() (or friends).
|
||||
check that you don't pass the same object to call_rcu()
|
||||
(or friends) before an RCU grace period has elapsed
|
||||
since the last time that you passed that same object to
|
||||
call_rcu() (or friends).
|
||||
|
||||
__rcu sparse checks:
|
||||
tag the pointer to the RCU-protected data
|
||||
structure with __rcu, and sparse will warn you if you
|
||||
access that pointer without the services of one of the
|
||||
variants of rcu_dereference().
|
||||
tag the pointer to the RCU-protected data structure
|
||||
with __rcu, and sparse will warn you if you access that
|
||||
pointer without the services of one of the variants
|
||||
of rcu_dereference().
|
||||
|
||||
These debugging aids can help you find problems that are
|
||||
otherwise extremely difficult to spot.
|
||||
|
||||
17. If you register a callback using call_rcu() or call_srcu(), and
|
||||
pass in a function defined within a loadable module, then it in
|
||||
necessary to wait for all pending callbacks to be invoked after
|
||||
the last invocation and before unloading that module. Note that
|
||||
it is absolutely *not* sufficient to wait for a grace period!
|
||||
The current (say) synchronize_rcu() implementation is *not*
|
||||
guaranteed to wait for callbacks registered on other CPUs.
|
||||
Or even on the current CPU if that CPU recently went offline
|
||||
and came back online.
|
||||
17. If you pass a callback function defined within a module to one of
|
||||
call_rcu(), call_srcu(), call_rcu_tasks(), call_rcu_tasks_rude(),
|
||||
or call_rcu_tasks_trace(), then it is necessary to wait for all
|
||||
pending callbacks to be invoked before unloading that module.
|
||||
Note that it is absolutely *not* sufficient to wait for a grace
|
||||
period! For example, synchronize_rcu() implementation is *not*
|
||||
guaranteed to wait for callbacks registered on other CPUs via
|
||||
call_rcu(). Or even on the current CPU if that CPU recently
|
||||
went offline and came back online.
|
||||
|
||||
You instead need to use one of the barrier functions:
|
||||
|
||||
- call_rcu() -> rcu_barrier()
|
||||
- call_srcu() -> srcu_barrier()
|
||||
- call_rcu_tasks() -> rcu_barrier_tasks()
|
||||
- call_rcu_tasks_rude() -> rcu_barrier_tasks_rude()
|
||||
- call_rcu_tasks_trace() -> rcu_barrier_tasks_trace()
|
||||
|
||||
However, these barrier functions are absolutely *not* guaranteed
|
||||
to wait for a grace period. In fact, if there are no call_rcu()
|
||||
callbacks waiting anywhere in the system, rcu_barrier() is within
|
||||
its rights to return immediately.
|
||||
to wait for a grace period. For example, if there are no
|
||||
call_rcu() callbacks queued anywhere in the system, rcu_barrier()
|
||||
can and will return immediately.
|
||||
|
||||
So if you need to wait for both an RCU grace period and for
|
||||
all pre-existing call_rcu() callbacks, you will need to execute
|
||||
both rcu_barrier() and synchronize_rcu(), if necessary, using
|
||||
something like workqueues to execute them concurrently.
|
||||
So if you need to wait for both a grace period and for all
|
||||
pre-existing callbacks, you will need to invoke both functions,
|
||||
with the pair depending on the flavor of RCU:
|
||||
|
||||
- Either synchronize_rcu() or synchronize_rcu_expedited(),
|
||||
together with rcu_barrier()
|
||||
- Either synchronize_srcu() or synchronize_srcu_expedited(),
|
||||
together with and srcu_barrier()
|
||||
- synchronize_rcu_tasks() and rcu_barrier_tasks()
|
||||
- synchronize_tasks_rude() and rcu_barrier_tasks_rude()
|
||||
- synchronize_tasks_trace() and rcu_barrier_tasks_trace()
|
||||
|
||||
If necessary, you can use something like workqueues to execute
|
||||
the requisite pair of functions concurrently.
|
||||
|
||||
See rcubarrier.rst for more information.
|
||||
|
||||
@@ -9,7 +9,6 @@ RCU concepts
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
arrayRCU
|
||||
checklist
|
||||
lockdep
|
||||
lockdep-splat
|
||||
|
||||
+102
-70
@@ -3,11 +3,10 @@
|
||||
Using RCU to Protect Read-Mostly Linked Lists
|
||||
=============================================
|
||||
|
||||
One of the best applications of RCU is to protect read-mostly linked lists
|
||||
(``struct list_head`` in list.h). One big advantage of this approach
|
||||
is that all of the required memory barriers are included for you in
|
||||
the list macros. This document describes several applications of RCU,
|
||||
with the best fits first.
|
||||
One of the most common uses of RCU is protecting read-mostly linked lists
|
||||
(``struct list_head`` in list.h). One big advantage of this approach is
|
||||
that all of the required memory ordering is provided by the list macros.
|
||||
This document describes several list-based RCU use cases.
|
||||
|
||||
|
||||
Example 1: Read-mostly list: Deferred Destruction
|
||||
@@ -35,7 +34,8 @@ The code traversing the list of all processes typically looks like::
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
The simplified code for removing a process from a task list is::
|
||||
The simplified and heavily inlined code for removing a process from a
|
||||
task list is::
|
||||
|
||||
void release_task(struct task_struct *p)
|
||||
{
|
||||
@@ -45,39 +45,48 @@ The simplified code for removing a process from a task list is::
|
||||
call_rcu(&p->rcu, delayed_put_task_struct);
|
||||
}
|
||||
|
||||
When a process exits, ``release_task()`` calls ``list_del_rcu(&p->tasks)`` under
|
||||
``tasklist_lock`` writer lock protection, to remove the task from the list of
|
||||
all tasks. The ``tasklist_lock`` prevents concurrent list additions/removals
|
||||
from corrupting the list. Readers using ``for_each_process()`` are not protected
|
||||
with the ``tasklist_lock``. To prevent readers from noticing changes in the list
|
||||
pointers, the ``task_struct`` object is freed only after one or more grace
|
||||
periods elapse (with the help of call_rcu()). This deferring of destruction
|
||||
ensures that any readers traversing the list will see valid ``p->tasks.next``
|
||||
pointers and deletion/freeing can happen in parallel with traversal of the list.
|
||||
This pattern is also called an **existence lock**, since RCU pins the object in
|
||||
memory until all existing readers finish.
|
||||
When a process exits, ``release_task()`` calls ``list_del_rcu(&p->tasks)``
|
||||
via __exit_signal() and __unhash_process() under ``tasklist_lock``
|
||||
writer lock protection. The list_del_rcu() invocation removes
|
||||
the task from the list of all tasks. The ``tasklist_lock``
|
||||
prevents concurrent list additions/removals from corrupting the
|
||||
list. Readers using ``for_each_process()`` are not protected with the
|
||||
``tasklist_lock``. To prevent readers from noticing changes in the list
|
||||
pointers, the ``task_struct`` object is freed only after one or more
|
||||
grace periods elapse, with the help of call_rcu(), which is invoked via
|
||||
put_task_struct_rcu_user(). This deferring of destruction ensures that
|
||||
any readers traversing the list will see valid ``p->tasks.next`` pointers
|
||||
and deletion/freeing can happen in parallel with traversal of the list.
|
||||
This pattern is also called an **existence lock**, since RCU refrains
|
||||
from invoking the delayed_put_task_struct() callback function until
|
||||
all existing readers finish, which guarantees that the ``task_struct``
|
||||
object in question will remain in existence until after the completion
|
||||
of all RCU readers that might possibly have a reference to that object.
|
||||
|
||||
|
||||
Example 2: Read-Side Action Taken Outside of Lock: No In-Place Updates
|
||||
----------------------------------------------------------------------
|
||||
|
||||
The best applications are cases where, if reader-writer locking were
|
||||
used, the read-side lock would be dropped before taking any action
|
||||
based on the results of the search. The most celebrated example is
|
||||
the routing table. Because the routing table is tracking the state of
|
||||
equipment outside of the computer, it will at times contain stale data.
|
||||
Therefore, once the route has been computed, there is no need to hold
|
||||
the routing table static during transmission of the packet. After all,
|
||||
you can hold the routing table static all you want, but that won't keep
|
||||
the external Internet from changing, and it is the state of the external
|
||||
Internet that really matters. In addition, routing entries are typically
|
||||
added or deleted, rather than being modified in place.
|
||||
Some reader-writer locking use cases compute a value while holding
|
||||
the read-side lock, but continue to use that value after that lock is
|
||||
released. These use cases are often good candidates for conversion
|
||||
to RCU. One prominent example involves network packet routing.
|
||||
Because the packet-routing data tracks the state of equipment outside
|
||||
of the computer, it will at times contain stale data. Therefore, once
|
||||
the route has been computed, there is no need to hold the routing table
|
||||
static during transmission of the packet. After all, you can hold the
|
||||
routing table static all you want, but that won't keep the external
|
||||
Internet from changing, and it is the state of the external Internet
|
||||
that really matters. In addition, routing entries are typically added
|
||||
or deleted, rather than being modified in place. This is a rare example
|
||||
of the finite speed of light and the non-zero size of atoms actually
|
||||
helping make synchronization be lighter weight.
|
||||
|
||||
A straightforward example of this use of RCU may be found in the
|
||||
system-call auditing support. For example, a reader-writer locked
|
||||
A straightforward example of this type of RCU use case may be found in
|
||||
the system-call auditing support. For example, a reader-writer locked
|
||||
implementation of ``audit_filter_task()`` might be as follows::
|
||||
|
||||
static enum audit_state audit_filter_task(struct task_struct *tsk)
|
||||
static enum audit_state audit_filter_task(struct task_struct *tsk, char **key)
|
||||
{
|
||||
struct audit_entry *e;
|
||||
enum audit_state state;
|
||||
@@ -86,6 +95,8 @@ implementation of ``audit_filter_task()`` might be as follows::
|
||||
/* Note: audit_filter_mutex held by caller. */
|
||||
list_for_each_entry(e, &audit_tsklist, list) {
|
||||
if (audit_filter_rules(tsk, &e->rule, NULL, &state)) {
|
||||
if (state == AUDIT_STATE_RECORD)
|
||||
*key = kstrdup(e->rule.filterkey, GFP_ATOMIC);
|
||||
read_unlock(&auditsc_lock);
|
||||
return state;
|
||||
}
|
||||
@@ -101,7 +112,7 @@ you are turning auditing off, it is OK to audit a few extra system calls.
|
||||
|
||||
This means that RCU can be easily applied to the read side, as follows::
|
||||
|
||||
static enum audit_state audit_filter_task(struct task_struct *tsk)
|
||||
static enum audit_state audit_filter_task(struct task_struct *tsk, char **key)
|
||||
{
|
||||
struct audit_entry *e;
|
||||
enum audit_state state;
|
||||
@@ -110,6 +121,8 @@ This means that RCU can be easily applied to the read side, as follows::
|
||||
/* Note: audit_filter_mutex held by caller. */
|
||||
list_for_each_entry_rcu(e, &audit_tsklist, list) {
|
||||
if (audit_filter_rules(tsk, &e->rule, NULL, &state)) {
|
||||
if (state == AUDIT_STATE_RECORD)
|
||||
*key = kstrdup(e->rule.filterkey, GFP_ATOMIC);
|
||||
rcu_read_unlock();
|
||||
return state;
|
||||
}
|
||||
@@ -118,13 +131,15 @@ This means that RCU can be easily applied to the read side, as follows::
|
||||
return AUDIT_BUILD_CONTEXT;
|
||||
}
|
||||
|
||||
The ``read_lock()`` and ``read_unlock()`` calls have become rcu_read_lock()
|
||||
and rcu_read_unlock(), respectively, and the list_for_each_entry() has
|
||||
become list_for_each_entry_rcu(). The **_rcu()** list-traversal primitives
|
||||
insert the read-side memory barriers that are required on DEC Alpha CPUs.
|
||||
The read_lock() and read_unlock() calls have become rcu_read_lock()
|
||||
and rcu_read_unlock(), respectively, and the list_for_each_entry()
|
||||
has become list_for_each_entry_rcu(). The **_rcu()** list-traversal
|
||||
primitives add READ_ONCE() and diagnostic checks for incorrect use
|
||||
outside of an RCU read-side critical section.
|
||||
|
||||
The changes to the update side are also straightforward. A reader-writer lock
|
||||
might be used as follows for deletion and insertion::
|
||||
might be used as follows for deletion and insertion in these simplified
|
||||
versions of audit_del_rule() and audit_add_rule()::
|
||||
|
||||
static inline int audit_del_rule(struct audit_rule *rule,
|
||||
struct list_head *list)
|
||||
@@ -188,16 +203,16 @@ Following are the RCU equivalents for these two functions::
|
||||
return 0;
|
||||
}
|
||||
|
||||
Normally, the ``write_lock()`` and ``write_unlock()`` would be replaced by a
|
||||
Normally, the write_lock() and write_unlock() would be replaced by a
|
||||
spin_lock() and a spin_unlock(). But in this case, all callers hold
|
||||
``audit_filter_mutex``, so no additional locking is required. The
|
||||
``auditsc_lock`` can therefore be eliminated, since use of RCU eliminates the
|
||||
auditsc_lock can therefore be eliminated, since use of RCU eliminates the
|
||||
need for writers to exclude readers.
|
||||
|
||||
The list_del(), list_add(), and list_add_tail() primitives have been
|
||||
replaced by list_del_rcu(), list_add_rcu(), and list_add_tail_rcu().
|
||||
The **_rcu()** list-manipulation primitives add memory barriers that are needed on
|
||||
weakly ordered CPUs (most of them!). The list_del_rcu() primitive omits the
|
||||
The **_rcu()** list-manipulation primitives add memory barriers that are
|
||||
needed on weakly ordered CPUs. The list_del_rcu() primitive omits the
|
||||
pointer poisoning debug-assist code that would otherwise cause concurrent
|
||||
readers to fail spectacularly.
|
||||
|
||||
@@ -238,7 +253,9 @@ need to be filled in)::
|
||||
The RCU version creates a copy, updates the copy, then replaces the old
|
||||
entry with the newly updated entry. This sequence of actions, allowing
|
||||
concurrent reads while making a copy to perform an update, is what gives
|
||||
RCU (*read-copy update*) its name. The RCU code is as follows::
|
||||
RCU (*read-copy update*) its name.
|
||||
|
||||
The RCU version of audit_upd_rule() is as follows::
|
||||
|
||||
static inline int audit_upd_rule(struct audit_rule *rule,
|
||||
struct list_head *list,
|
||||
@@ -267,6 +284,9 @@ RCU (*read-copy update*) its name. The RCU code is as follows::
|
||||
Again, this assumes that the caller holds ``audit_filter_mutex``. Normally, the
|
||||
writer lock would become a spinlock in this sort of code.
|
||||
|
||||
The update_lsm_rule() does something very similar, for those who would
|
||||
prefer to look at real Linux-kernel code.
|
||||
|
||||
Another use of this pattern can be found in the openswitch driver's *connection
|
||||
tracking table* code in ``ct_limit_set()``. The table holds connection tracking
|
||||
entries and has a limit on the maximum entries. There is one such table
|
||||
@@ -281,9 +301,10 @@ Example 4: Eliminating Stale Data
|
||||
---------------------------------
|
||||
|
||||
The auditing example above tolerates stale data, as do most algorithms
|
||||
that are tracking external state. Because there is a delay from the
|
||||
time the external state changes before Linux becomes aware of the change,
|
||||
additional RCU-induced staleness is generally not a problem.
|
||||
that are tracking external state. After all, given there is a delay
|
||||
from the time the external state changes before Linux becomes aware
|
||||
of the change, and so as noted earlier, a small quantity of additional
|
||||
RCU-induced staleness is generally not a problem.
|
||||
|
||||
However, there are many examples where stale data cannot be tolerated.
|
||||
One example in the Linux kernel is the System V IPC (see the shm_lock()
|
||||
@@ -302,7 +323,7 @@ Quick Quiz:
|
||||
|
||||
If the system-call audit module were to ever need to reject stale data, one way
|
||||
to accomplish this would be to add a ``deleted`` flag and a ``lock`` spinlock to the
|
||||
audit_entry structure, and modify ``audit_filter_task()`` as follows::
|
||||
``audit_entry`` structure, and modify audit_filter_task() as follows::
|
||||
|
||||
static enum audit_state audit_filter_task(struct task_struct *tsk)
|
||||
{
|
||||
@@ -319,6 +340,8 @@ audit_entry structure, and modify ``audit_filter_task()`` as follows::
|
||||
return AUDIT_BUILD_CONTEXT;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
if (state == AUDIT_STATE_RECORD)
|
||||
*key = kstrdup(e->rule.filterkey, GFP_ATOMIC);
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -326,12 +349,6 @@ audit_entry structure, and modify ``audit_filter_task()`` as follows::
|
||||
return AUDIT_BUILD_CONTEXT;
|
||||
}
|
||||
|
||||
Note that this example assumes that entries are only added and deleted.
|
||||
Additional mechanism is required to deal correctly with the update-in-place
|
||||
performed by ``audit_upd_rule()``. For one thing, ``audit_upd_rule()`` would
|
||||
need additional memory barriers to ensure that the list_add_rcu() was really
|
||||
executed before the list_del_rcu().
|
||||
|
||||
The ``audit_del_rule()`` function would need to set the ``deleted`` flag under the
|
||||
spinlock as follows::
|
||||
|
||||
@@ -357,24 +374,32 @@ spinlock as follows::
|
||||
|
||||
This too assumes that the caller holds ``audit_filter_mutex``.
|
||||
|
||||
Note that this example assumes that entries are only added and deleted.
|
||||
Additional mechanism is required to deal correctly with the update-in-place
|
||||
performed by audit_upd_rule(). For one thing, audit_upd_rule() would
|
||||
need to hold the locks of both the old ``audit_entry`` and its replacement
|
||||
while executing the list_replace_rcu().
|
||||
|
||||
|
||||
Example 5: Skipping Stale Objects
|
||||
---------------------------------
|
||||
|
||||
For some usecases, reader performance can be improved by skipping stale objects
|
||||
during read-side list traversal if the object in concern is pending destruction
|
||||
after one or more grace periods. One such example can be found in the timerfd
|
||||
subsystem. When a ``CLOCK_REALTIME`` clock is reprogrammed - for example due to
|
||||
setting of the system time, then all programmed timerfds that depend on this
|
||||
clock get triggered and processes waiting on them to expire are woken up in
|
||||
advance of their scheduled expiry. To facilitate this, all such timers are added
|
||||
to an RCU-managed ``cancel_list`` when they are setup in
|
||||
For some use cases, reader performance can be improved by skipping
|
||||
stale objects during read-side list traversal, where stale objects
|
||||
are those that will be removed and destroyed after one or more grace
|
||||
periods. One such example can be found in the timerfd subsystem. When a
|
||||
``CLOCK_REALTIME`` clock is reprogrammed (for example due to setting
|
||||
of the system time) then all programmed ``timerfds`` that depend on
|
||||
this clock get triggered and processes waiting on them are awakened in
|
||||
advance of their scheduled expiry. To facilitate this, all such timers
|
||||
are added to an RCU-managed ``cancel_list`` when they are setup in
|
||||
``timerfd_setup_cancel()``::
|
||||
|
||||
static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags)
|
||||
{
|
||||
spin_lock(&ctx->cancel_lock);
|
||||
if ((ctx->clockid == CLOCK_REALTIME &&
|
||||
if ((ctx->clockid == CLOCK_REALTIME ||
|
||||
ctx->clockid == CLOCK_REALTIME_ALARM) &&
|
||||
(flags & TFD_TIMER_ABSTIME) && (flags & TFD_TIMER_CANCEL_ON_SET)) {
|
||||
if (!ctx->might_cancel) {
|
||||
ctx->might_cancel = true;
|
||||
@@ -382,13 +407,16 @@ to an RCU-managed ``cancel_list`` when they are setup in
|
||||
list_add_rcu(&ctx->clist, &cancel_list);
|
||||
spin_unlock(&cancel_lock);
|
||||
}
|
||||
} else {
|
||||
__timerfd_remove_cancel(ctx);
|
||||
}
|
||||
spin_unlock(&ctx->cancel_lock);
|
||||
}
|
||||
|
||||
When a timerfd is freed (fd is closed), then the ``might_cancel`` flag of the
|
||||
timerfd object is cleared, the object removed from the ``cancel_list`` and
|
||||
destroyed::
|
||||
When a timerfd is freed (fd is closed), then the ``might_cancel``
|
||||
flag of the timerfd object is cleared, the object removed from the
|
||||
``cancel_list`` and destroyed, as shown in this simplified and inlined
|
||||
version of timerfd_release()::
|
||||
|
||||
int timerfd_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
@@ -403,7 +431,10 @@ destroyed::
|
||||
}
|
||||
spin_unlock(&ctx->cancel_lock);
|
||||
|
||||
hrtimer_cancel(&ctx->t.tmr);
|
||||
if (isalarm(ctx))
|
||||
alarm_cancel(&ctx->t.alarm);
|
||||
else
|
||||
hrtimer_cancel(&ctx->t.tmr);
|
||||
kfree_rcu(ctx, rcu);
|
||||
return 0;
|
||||
}
|
||||
@@ -416,6 +447,7 @@ objects::
|
||||
|
||||
void timerfd_clock_was_set(void)
|
||||
{
|
||||
ktime_t moffs = ktime_mono_to_real(0);
|
||||
struct timerfd_ctx *ctx;
|
||||
unsigned long flags;
|
||||
|
||||
@@ -424,7 +456,7 @@ objects::
|
||||
if (!ctx->might_cancel)
|
||||
continue;
|
||||
spin_lock_irqsave(&ctx->wqh.lock, flags);
|
||||
if (ctx->moffs != ktime_mono_to_real(0)) {
|
||||
if (ctx->moffs != moffs) {
|
||||
ctx->moffs = KTIME_MAX;
|
||||
ctx->ticks++;
|
||||
wake_up_locked_poll(&ctx->wqh, EPOLLIN);
|
||||
@@ -434,10 +466,10 @@ objects::
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
The key point here is, because RCU-traversal of the ``cancel_list`` happens
|
||||
while objects are being added and removed to the list, sometimes the traversal
|
||||
can step on an object that has been removed from the list. In this example, it
|
||||
is seen that it is better to skip such objects using a flag.
|
||||
The key point is that because RCU-protected traversal of the
|
||||
``cancel_list`` happens concurrently with object addition and removal,
|
||||
sometimes the traversal can access an object that has been removed from
|
||||
the list. In this example, a flag is used to skip such objects.
|
||||
|
||||
|
||||
Summary
|
||||
|
||||
@@ -17,7 +17,9 @@ state::
|
||||
rcu_read_lock_held() for normal RCU.
|
||||
rcu_read_lock_bh_held() for RCU-bh.
|
||||
rcu_read_lock_sched_held() for RCU-sched.
|
||||
rcu_read_lock_any_held() for any of normal RCU, RCU-bh, and RCU-sched.
|
||||
srcu_read_lock_held() for SRCU.
|
||||
rcu_read_lock_trace_held() for RCU Tasks Trace.
|
||||
|
||||
These functions are conservative, and will therefore return 1 if they
|
||||
aren't certain (for example, if CONFIG_DEBUG_LOCK_ALLOC is not set).
|
||||
@@ -53,6 +55,8 @@ checking of rcu_dereference() primitives:
|
||||
is invoked by both SRCU readers and updaters.
|
||||
rcu_dereference_raw(p):
|
||||
Don't check. (Use sparingly, if at all.)
|
||||
rcu_dereference_raw_check(p):
|
||||
Don't do lockdep at all. (Use sparingly, if at all.)
|
||||
rcu_dereference_protected(p, c):
|
||||
Use explicit check expression "c", and omit all barriers
|
||||
and compiler constraints. This is useful when the data
|
||||
|
||||
@@ -1966,7 +1966,7 @@ There are some more advanced barrier functions:
|
||||
(*) io_stop_wc();
|
||||
|
||||
For memory accesses with write-combining attributes (e.g. those returned
|
||||
by ioremap_wc(), the CPU may wait for prior accesses to be merged with
|
||||
by ioremap_wc()), the CPU may wait for prior accesses to be merged with
|
||||
subsequent ones. io_stop_wc() can be used to prevent the merging of
|
||||
write-combining memory accesses before this macro with those after it when
|
||||
such wait has performance implications.
|
||||
|
||||
@@ -80,7 +80,7 @@ Documentation/memory-barriers.txt
|
||||
|
||||
- 메모리 배리어의 종류.
|
||||
- 메모리 배리어에 대해 가정해선 안될 것.
|
||||
- 데이터 의존성 배리어 (역사적).
|
||||
- 주소 데이터 의존성 배리어 (역사적).
|
||||
- 컨트롤 의존성.
|
||||
- SMP 배리어 짝맞추기.
|
||||
- 메모리 배리어 시퀀스의 예.
|
||||
@@ -217,7 +217,7 @@ Documentation/memory-barriers.txt
|
||||
P = &B D = *Q;
|
||||
|
||||
D 로 읽혀지는 값은 CPU 2 에서 P 로부터 읽혀진 주소값에 의존적이기 때문에 여기엔
|
||||
분명한 데이터 의존성이 있습니다. 하지만 이 이벤트들의 실행 결과로는 아래의
|
||||
분명한 주소 의존성이 있습니다. 하지만 이 이벤트들의 실행 결과로는 아래의
|
||||
결과들이 모두 나타날 수 있습니다:
|
||||
|
||||
(Q == &A) and (D == 1)
|
||||
@@ -416,19 +416,19 @@ CPU 에게 기대할 수 있는 최소한의 보장사항 몇가지가 있습니
|
||||
하나씩 요청해 집어넣습니다. 쓰기 배리어 앞의 모든 스토어 오퍼레이션들은
|
||||
쓰기 배리어 뒤의 모든 스토어 오퍼레이션들보다 _앞서_ 수행될 겁니다.
|
||||
|
||||
[!] 쓰기 배리어들은 읽기 또는 데이터 의존성 배리어와 함께 짝을 맞춰
|
||||
[!] 쓰기 배리어들은 읽기 또는 주소 의존성 배리어와 함께 짝을 맞춰
|
||||
사용되어야만 함을 알아두세요; "SMP 배리어 짝맞추기" 서브섹션을 참고하세요.
|
||||
|
||||
|
||||
(2) 데이터 의존성 배리어.
|
||||
(2) 주소 의존성 배리어 (역사적).
|
||||
|
||||
데이터 의존성 배리어는 읽기 배리어의 보다 완화된 형태입니다. 두개의 로드
|
||||
주소 의존성 배리어는 읽기 배리어의 보다 완화된 형태입니다. 두개의 로드
|
||||
오퍼레이션이 있고 두번째 것이 첫번째 것의 결과에 의존하고 있을 때(예:
|
||||
두번째 로드가 참조할 주소를 첫번째 로드가 읽는 경우), 두번째 로드가 읽어올
|
||||
데이터는 첫번째 로드에 의해 그 주소가 얻어진 뒤에 업데이트 됨을 보장하기
|
||||
위해서 데이터 의존성 배리어가 필요할 수 있습니다.
|
||||
위해서 주소 의존성 배리어가 필요할 수 있습니다.
|
||||
|
||||
데이터 의존성 배리어는 상호 의존적인 로드 오퍼레이션들 사이의 부분적 순서
|
||||
주소 의존성 배리어는 상호 의존적인 로드 오퍼레이션들 사이의 부분적 순서
|
||||
세우기입니다; 스토어 오퍼레이션들이나 독립적인 로드들, 또는 중복되는
|
||||
로드들에 대해서는 어떤 영향도 끼치지 않습니다.
|
||||
|
||||
@@ -436,37 +436,41 @@ CPU 에게 기대할 수 있는 최소한의 보장사항 몇가지가 있습니
|
||||
오퍼레이션들을 던져 넣고 있으며, 거기에 관심이 있는 다른 CPU 는 그
|
||||
오퍼레이션들을 메모리 시스템이 실행한 결과를 인지할 수 있습니다. 이처럼
|
||||
다른 CPU 의 스토어 오퍼레이션의 결과에 관심을 두고 있는 CPU 가 수행 요청한
|
||||
데이터 의존성 배리어는, 배리어 앞의 어떤 로드 오퍼레이션이 다른 CPU 에서
|
||||
주소 의존성 배리어는, 배리어 앞의 어떤 로드 오퍼레이션이 다른 CPU 에서
|
||||
던져 넣은 스토어 오퍼레이션과 같은 영역을 향했다면, 그런 스토어
|
||||
오퍼레이션들이 만들어내는 결과가 데이터 의존성 배리어 뒤의 로드
|
||||
오퍼레이션들이 만들어내는 결과가 주소 의존성 배리어 뒤의 로드
|
||||
오퍼레이션들에게는 보일 것을 보장합니다.
|
||||
|
||||
이 순서 세우기 제약에 대한 그림을 보기 위해선 "메모리 배리어 시퀀스의 예"
|
||||
서브섹션을 참고하시기 바랍니다.
|
||||
|
||||
[!] 첫번째 로드는 반드시 _데이터_ 의존성을 가져야지 컨트롤 의존성을 가져야
|
||||
[!] 첫번째 로드는 반드시 _주소_ 의존성을 가져야지 컨트롤 의존성을 가져야
|
||||
하는게 아님을 알아두십시오. 만약 두번째 로드를 위한 주소가 첫번째 로드에
|
||||
의존적이지만 그 의존성은 조건적이지 그 주소 자체를 가져오는게 아니라면,
|
||||
그것은 _컨트롤_ 의존성이고, 이 경우에는 읽기 배리어나 그보다 강력한
|
||||
무언가가 필요합니다. 더 자세한 내용을 위해서는 "컨트롤 의존성" 서브섹션을
|
||||
참고하시기 바랍니다.
|
||||
|
||||
[!] 데이터 의존성 배리어는 보통 쓰기 배리어들과 함께 짝을 맞춰 사용되어야
|
||||
[!] 주소 의존성 배리어는 보통 쓰기 배리어들과 함께 짝을 맞춰 사용되어야
|
||||
합니다; "SMP 배리어 짝맞추기" 서브섹션을 참고하세요.
|
||||
|
||||
[!] 커널 v5.9 릴리즈에서 명시적 주소 의존성 배리어를 위한 커널 API 들이
|
||||
삭제되었습니다. 오늘날에는 공유된 변수들의 로드를 표시하는 READ_ONCE() 나
|
||||
rcu_dereference() 와 같은 API 들은 묵시적으로 주소 의존성 배리어를 제공합니다.
|
||||
|
||||
|
||||
(3) 읽기 (또는 로드) 메모리 배리어.
|
||||
|
||||
읽기 배리어는 데이터 의존성 배리어 기능의 보장사항에 더해서 배리어보다
|
||||
앞서 명시된 모든 LOAD 오퍼레이션들이 배리어 뒤에 명시되는 모든 LOAD
|
||||
읽기 배리어는 주소 의존성 배리어 기능의 보장사항에 더해서 배리어보다 앞서
|
||||
명시된 모든 LOAD 오퍼레이션들이 배리어 뒤에 명시되는 모든 LOAD
|
||||
오퍼레이션들보다 먼저 행해진 것으로 시스템의 다른 컴포넌트들에 보여질 것을
|
||||
보장합니다.
|
||||
|
||||
읽기 배리어는 로드 오퍼레이션에 행해지는 부분적 순서 세우기입니다; 스토어
|
||||
오퍼레이션에 대해서는 어떤 영향도 끼치지 않습니다.
|
||||
|
||||
읽기 메모리 배리어는 데이터 의존성 배리어를 내장하므로 데이터 의존성
|
||||
배리어를 대신할 수 있습니다.
|
||||
읽기 메모리 배리어는 주소 의존성 배리어를 내장하므로 주소 의존성 배리어를
|
||||
대신할 수 있습니다.
|
||||
|
||||
[!] 읽기 배리어는 일반적으로 쓰기 배리어들과 함께 짝을 맞춰 사용되어야
|
||||
합니다; "SMP 배리어 짝맞추기" 서브섹션을 참고하세요.
|
||||
@@ -571,16 +575,20 @@ ACQUIRE 는 해당 오퍼레이션의 로드 부분에만 적용되고 RELEASE
|
||||
Documentation/core-api/dma-api.rst
|
||||
|
||||
|
||||
데이터 의존성 배리어 (역사적)
|
||||
-----------------------------
|
||||
주소 의존성 배리어 (역사적)
|
||||
---------------------------
|
||||
|
||||
리눅스 커널 v4.15 기준으로, smp_mb() 가 DEC Alpha 용 READ_ONCE() 코드에
|
||||
추가되었는데, 이는 이 섹션에 주의를 기울여야 하는 사람들은 DEC Alpha 아키텍쳐
|
||||
전용 코드를 만드는 사람들과 READ_ONCE() 자체를 만드는 사람들 뿐임을 의미합니다.
|
||||
그런 분들을 위해, 그리고 역사에 관심 있는 분들을 위해, 여기 데이터 의존성
|
||||
그런 분들을 위해, 그리고 역사에 관심 있는 분들을 위해, 여기 주소 의존성
|
||||
배리어에 대한 이야기를 적습니다.
|
||||
|
||||
데이터 의존성 배리어의 사용에 있어 지켜야 하는 사항들은 약간 미묘하고, 데이터
|
||||
[!] 주소 의존성은 로드에서 로드로와 로드에서 스토어로의 관계들 모두에서
|
||||
나타나지만, 주소 의존성 배리어는 로드에서 스토어로의 상황에서는 필요하지
|
||||
않습니다.
|
||||
|
||||
주소 의존성 배리어의 사용에 있어 지켜야 하는 사항들은 약간 미묘하고, 데이터
|
||||
의존성 배리어가 사용되어야 하는 상황도 항상 명백하지는 않습니다. 설명을 위해
|
||||
다음의 이벤트 시퀀스를 생각해 봅시다:
|
||||
|
||||
@@ -590,10 +598,13 @@ ACQUIRE 는 해당 오퍼레이션의 로드 부분에만 적용되고 RELEASE
|
||||
B = 4;
|
||||
<쓰기 배리어>
|
||||
WRITE_ONCE(P, &B)
|
||||
Q = READ_ONCE(P);
|
||||
Q = READ_ONCE_OLD(P);
|
||||
D = *Q;
|
||||
|
||||
여기엔 분명한 데이터 의존성이 존재하므로, 이 시퀀스가 끝났을 때 Q 는 &A 또는 &B
|
||||
[!] READ_ONCE_OLD() 는 4.15 커널 전의 버전에서의, 주소 의존성 배리어를 내포하지
|
||||
않는 READ_ONCE() 에 해당합니다.
|
||||
|
||||
여기엔 분명한 주소 의존성이 존재하므로, 이 시퀀스가 끝났을 때 Q 는 &A 또는 &B
|
||||
일 것이고, 따라서:
|
||||
|
||||
(Q == &A) 는 (D == 1) 를,
|
||||
@@ -608,8 +619,8 @@ ACQUIRE 는 해당 오퍼레이션의 로드 부분에만 적용되고 RELEASE
|
||||
그렇지 않습니다, 그리고 이 현상은 (DEC Alpha 와 같은) 여러 CPU 에서 실제로
|
||||
발견될 수 있습니다.
|
||||
|
||||
이 문제 상황을 제대로 해결하기 위해, 데이터 의존성 배리어나 그보다 강화된
|
||||
무언가가 주소를 읽어올 때와 데이터를 읽어올 때 사이에 추가되어야만 합니다:
|
||||
이 문제 상황을 제대로 해결하기 위해, READ_ONCE() 는 커널 v4.15 릴리즈 부터
|
||||
묵시적 주소 의존성 배리어를 제공합니다:
|
||||
|
||||
CPU 1 CPU 2
|
||||
=============== ===============
|
||||
@@ -618,7 +629,7 @@ ACQUIRE 는 해당 오퍼레이션의 로드 부분에만 적용되고 RELEASE
|
||||
<쓰기 배리어>
|
||||
WRITE_ONCE(P, &B);
|
||||
Q = READ_ONCE(P);
|
||||
<데이터 의존성 배리어>
|
||||
<묵시적 주소 의존성 배리어>
|
||||
D = *Q;
|
||||
|
||||
이 변경은 앞의 처음 두가지 결과 중 하나만이 발생할 수 있고, 세번째의 결과는
|
||||
@@ -634,7 +645,7 @@ P 는 짝수 번호 캐시 라인에 저장되어 있고, 변수 B 는 홀수
|
||||
중이라면 포인터 P (&B) 의 새로운 값과 변수 B 의 기존 값 (2) 를 볼 수 있습니다.
|
||||
|
||||
|
||||
의존적 쓰기들의 순서를 맞추는데에는 데이터 의존성 배리어가 필요치 않은데, 이는
|
||||
의존적 쓰기들의 순서를 맞추는데에는 주소 의존성 배리어가 필요치 않은데, 이는
|
||||
리눅스 커널이 지원하는 CPU 들은 (1) 쓰기가 정말로 일어날지, (2) 쓰기가 어디에
|
||||
이루어질지, 그리고 (3) 쓰여질 값을 확실히 알기 전까지는 쓰기를 수행하지 않기
|
||||
때문입니다. 하지만 "컨트롤 의존성" 섹션과
|
||||
@@ -647,12 +658,12 @@ Documentation/RCU/rcu_dereference.rst 파일을 주의 깊게 읽어 주시기
|
||||
B = 4;
|
||||
<쓰기 배리어>
|
||||
WRITE_ONCE(P, &B);
|
||||
Q = READ_ONCE(P);
|
||||
Q = READ_ONCE_OLD(P);
|
||||
WRITE_ONCE(*Q, 5);
|
||||
|
||||
따라서, Q 로의 읽기와 *Q 로의 쓰기 사이에는 데이터 종속성 배리어가 필요치
|
||||
않습니다. 달리 말하면, 데이터 종속성 배리어가 없더라도 다음 결과는 생기지
|
||||
않습니다:
|
||||
따라서, Q 로의 읽기와 *Q 로의 쓰기 사이에는 주소 의존성 배리어가 필요치
|
||||
않습니다. 달리 말하면, 오늘날의 READ_ONCE() 의 묵시적 주소 의존성 배리어가
|
||||
없더라도 다음 결과는 생기지 않습니다:
|
||||
|
||||
(Q == &B) && (B == 4)
|
||||
|
||||
@@ -663,16 +674,16 @@ Documentation/RCU/rcu_dereference.rst 파일을 주의 깊게 읽어 주시기
|
||||
해줍니다.
|
||||
|
||||
|
||||
데이터 의존성에 의해 제공되는 이 순서규칙은 이를 포함하고 있는 CPU 에
|
||||
주소 의존성에 의해 제공되는 이 순서규칙은 이를 포함하고 있는 CPU 에
|
||||
지역적임을 알아두시기 바랍니다. 더 많은 정보를 위해선 "Multicopy 원자성"
|
||||
섹션을 참고하세요.
|
||||
|
||||
|
||||
데이터 의존성 배리어는 매우 중요한데, 예를 들어 RCU 시스템에서 그렇습니다.
|
||||
주소 의존성 배리어는 매우 중요한데, 예를 들어 RCU 시스템에서 그렇습니다.
|
||||
include/linux/rcupdate.h 의 rcu_assign_pointer() 와 rcu_dereference() 를
|
||||
참고하세요. 여기서 데이터 의존성 배리어는 RCU 로 관리되는 포인터의 타겟을 현재
|
||||
타겟에서 수정된 새로운 타겟으로 바꾸는 작업에서 새로 수정된 타겟이 초기화가
|
||||
완료되지 않은 채로 보여지는 일이 일어나지 않게 해줍니다.
|
||||
참고하세요. 이것들은 RCU 로 관리되는 포인터의 타겟을 현재 타겟에서 수정된
|
||||
새로운 타겟으로 바꾸는 작업에서 새로 수정된 타겟이 초기화가 완료되지 않은 채로
|
||||
보여지는 일이 일어나지 않게 해줍니다.
|
||||
|
||||
더 많은 예를 위해선 "캐시 일관성" 서브섹션을 참고하세요.
|
||||
|
||||
@@ -684,16 +695,17 @@ include/linux/rcupdate.h 의 rcu_assign_pointer() 와 rcu_dereference() 를
|
||||
약간 다루기 어려울 수 있습니다. 이 섹션의 목적은 여러분이 컴파일러의 무시로
|
||||
인해 여러분의 코드가 망가지는 걸 막을 수 있도록 돕는겁니다.
|
||||
|
||||
로드-로드 컨트롤 의존성은 데이터 의존성 배리어만으로는 정확히 동작할 수가
|
||||
없어서 읽기 메모리 배리어를 필요로 합니다. 아래의 코드를 봅시다:
|
||||
로드-로드 컨트롤 의존성은 (묵시적인) 주소 의존성 배리어만으로는 정확히 동작할
|
||||
수가 없어서 읽기 메모리 배리어를 필요로 합니다. 아래의 코드를 봅시다:
|
||||
|
||||
q = READ_ONCE(a);
|
||||
<묵시적 주소 의존성 배리어>
|
||||
if (q) {
|
||||
<데이터 의존성 배리어> /* BUG: No data dependency!!! */
|
||||
/* BUG: No address dependency!!! */
|
||||
p = READ_ONCE(b);
|
||||
}
|
||||
|
||||
이 코드는 원하는 대로의 효과를 내지 못할 수 있는데, 이 코드에는 데이터 의존성이
|
||||
이 코드는 원하는 대로의 효과를 내지 못할 수 있는데, 이 코드에는 주소 의존성이
|
||||
아니라 컨트롤 의존성이 존재하기 때문으로, 이런 상황에서 CPU 는 실행 속도를 더
|
||||
빠르게 하기 위해 분기 조건의 결과를 예측하고 코드를 재배치 할 수 있어서 다른
|
||||
CPU 는 b 로부터의 로드 오퍼레이션이 a 로부터의 로드 오퍼레이션보다 먼저 발생한
|
||||
@@ -930,9 +942,9 @@ CPU 간 상호작용을 다룰 때에 일부 타입의 메모리 배리어는
|
||||
범용 배리어들은 범용 배리어끼리도 짝을 맞추지만 multicopy 원자성이 없는
|
||||
대부분의 다른 타입의 배리어들과도 짝을 맞춥니다. ACQUIRE 배리어는 RELEASE
|
||||
배리어와 짝을 맞춥니다만, 둘 다 범용 배리어를 포함해 다른 배리어들과도 짝을
|
||||
맞출 수 있습니다. 쓰기 배리어는 데이터 의존성 배리어나 컨트롤 의존성, ACQUIRE
|
||||
맞출 수 있습니다. 쓰기 배리어는 주소 의존성 배리어나 컨트롤 의존성, ACQUIRE
|
||||
배리어, RELEASE 배리어, 읽기 배리어, 또는 범용 배리어와 짝을 맞춥니다.
|
||||
비슷하게 읽기 배리어나 컨트롤 의존성, 또는 데이터 의존성 배리어는 쓰기 배리어나
|
||||
비슷하게 읽기 배리어나 컨트롤 의존성, 또는 주소 의존성 배리어는 쓰기 배리어나
|
||||
ACQUIRE 배리어, RELEASE 배리어, 또는 범용 배리어와 짝을 맞추는데, 다음과
|
||||
같습니다:
|
||||
|
||||
@@ -951,7 +963,7 @@ ACQUIRE 배리어, RELEASE 배리어, 또는 범용 배리어와 짝을 맞추
|
||||
a = 1;
|
||||
<쓰기 배리어>
|
||||
WRITE_ONCE(b, &a); x = READ_ONCE(b);
|
||||
<데이터 의존성 배리어>
|
||||
<묵시적 주소 의존성 배리어>
|
||||
y = *x;
|
||||
|
||||
또는:
|
||||
@@ -970,8 +982,8 @@ ACQUIRE 배리어, RELEASE 배리어, 또는 범용 배리어와 짝을 맞추
|
||||
기본적으로, 여기서의 읽기 배리어는 "더 완화된" 타입일 순 있어도 항상 존재해야
|
||||
합니다.
|
||||
|
||||
[!] 쓰기 배리어 앞의 스토어 오퍼레이션은 일반적으로 읽기 배리어나 데이터
|
||||
의존성 배리어 뒤의 로드 오퍼레이션과 매치될 것이고, 반대도 마찬가지입니다:
|
||||
[!] 쓰기 배리어 앞의 스토어 오퍼레이션은 일반적으로 읽기 배리어나 주소 의존성
|
||||
배리어 뒤의 로드 오퍼레이션과 매치될 것이고, 반대도 마찬가지입니다:
|
||||
|
||||
CPU 1 CPU 2
|
||||
=================== ===================
|
||||
@@ -1023,7 +1035,7 @@ ACQUIRE 배리어, RELEASE 배리어, 또는 범용 배리어와 짝을 맞추
|
||||
V
|
||||
|
||||
|
||||
둘째, 데이터 의존성 배리어는 데이터 의존적 로드 오퍼레이션들의 부분적 순서
|
||||
둘째, 주소 의존성 배리어는 데이터 의존적 로드 오퍼레이션들의 부분적 순서
|
||||
세우기로 동작합니다. 다음 일련의 이벤트들을 보세요:
|
||||
|
||||
CPU 1 CPU 2
|
||||
@@ -1069,7 +1081,7 @@ ACQUIRE 배리어, RELEASE 배리어, 또는 범용 배리어와 짝을 맞추
|
||||
앞의 예에서, CPU 2 는 (B 의 값이 될) *C 의 값 읽기가 C 의 LOAD 뒤에 이어짐에도
|
||||
B 가 7 이라는 결과를 얻습니다.
|
||||
|
||||
하지만, 만약 데이터 의존성 배리어가 C 의 로드와 *C (즉, B) 의 로드 사이에
|
||||
하지만, 만약 주소 의존성 배리어가 C 의 로드와 *C (즉, B) 의 로드 사이에
|
||||
있었다면:
|
||||
|
||||
CPU 1 CPU 2
|
||||
@@ -1080,7 +1092,7 @@ B 가 7 이라는 결과를 얻습니다.
|
||||
<쓰기 배리어>
|
||||
STORE C = &B LOAD X
|
||||
STORE D = 4 LOAD C (gets &B)
|
||||
<데이터 의존성 배리어>
|
||||
<주소 의존성 배리어>
|
||||
LOAD *C (reads B)
|
||||
|
||||
다음과 같이 됩니다:
|
||||
@@ -1103,7 +1115,7 @@ B 가 7 이라는 결과를 얻습니다.
|
||||
| +-------+ | |
|
||||
| | X->9 |------>| |
|
||||
| +-------+ | |
|
||||
C 로의 스토어 앞의 ---> \ ddddddddddddddddd | |
|
||||
C 로의 스토어 앞의 ---> \ aaaaaaaaaaaaaaaaa | |
|
||||
모든 이벤트 결과가 \ +-------+ | |
|
||||
뒤의 로드에게 ----->| B->2 |------>| |
|
||||
보이게 강제한다 +-------+ | |
|
||||
@@ -1291,7 +1303,7 @@ A 의 로드 두개가 모두 B 의 로드 뒤에 있지만, 서로 다른 값
|
||||
즉각 완료한다 : : +-------+
|
||||
|
||||
|
||||
읽기 배리어나 데이터 의존성 배리어를 두번째 로드 직전에 놓는다면:
|
||||
읽기 배리어나 주소 의존성 배리어를 두번째 로드 직전에 놓는다면:
|
||||
|
||||
CPU 1 CPU 2
|
||||
======================= =======================
|
||||
@@ -1785,21 +1797,20 @@ READ_ONCE(jiffies) 라고 할 필요가 없습니다. READ_ONCE() 와 WRITE_ONC
|
||||
CPU 메모리 배리어
|
||||
-----------------
|
||||
|
||||
리눅스 커널은 다음의 여덟개 기본 CPU 메모리 배리어를 가지고 있습니다:
|
||||
리눅스 커널은 다음의 일곱개 기본 CPU 메모리 배리어를 가지고 있습니다:
|
||||
|
||||
TYPE MANDATORY SMP CONDITIONAL
|
||||
=============== ======================= ===========================
|
||||
=============== ======================= ===============
|
||||
범용 mb() smp_mb()
|
||||
쓰기 wmb() smp_wmb()
|
||||
읽기 rmb() smp_rmb()
|
||||
데이터 의존성 READ_ONCE()
|
||||
주소 의존성 READ_ONCE()
|
||||
|
||||
|
||||
데이터 의존성 배리어를 제외한 모든 메모리 배리어는 컴파일러 배리어를
|
||||
포함합니다. 데이터 의존성은 컴파일러에의 추가적인 순서 보장을 포함하지
|
||||
않습니다.
|
||||
주소 의존성 배리어를 제외한 모든 메모리 배리어는 컴파일러 배리어를 포함합니다.
|
||||
주소 의존성은 컴파일러에의 추가적인 순서 보장을 포함하지 않습니다.
|
||||
|
||||
방백: 데이터 의존성이 있는 경우, 컴파일러는 해당 로드를 올바른 순서로 일으킬
|
||||
방백: 주소 의존성이 있는 경우, 컴파일러는 해당 로드를 올바른 순서로 일으킬
|
||||
것으로 (예: `a[b]` 는 a[b] 를 로드 하기 전에 b 의 값을 먼저 로드한다)
|
||||
기대되지만, C 언어 사양에는 컴파일러가 b 의 값을 추측 (예: 1 과 같음) 해서
|
||||
b 로드 전에 a 로드를 하는 코드 (예: tmp = a[1]; if (b != 1) tmp = a[b]; ) 를
|
||||
@@ -1863,6 +1874,7 @@ Mandatory 배리어들은 SMP 시스템에서도 UP 시스템에서도 SMP 효
|
||||
|
||||
(*) dma_wmb();
|
||||
(*) dma_rmb();
|
||||
(*) dma_mb();
|
||||
|
||||
이것들은 CPU 와 DMA 가능한 디바이스에서 모두 액세스 가능한 공유 메모리의
|
||||
읽기, 쓰기 작업들의 순서를 보장하기 위해 consistent memory 에서 사용하기
|
||||
@@ -1893,12 +1905,13 @@ Mandatory 배리어들은 SMP 시스템에서도 UP 시스템에서도 SMP 효
|
||||
|
||||
dma_rmb() 는 디스크립터로부터 데이터를 읽어오기 전에 디바이스가 소유권을
|
||||
내려놓았을 것을 보장하고, dma_wmb() 는 디바이스가 자신이 소유권을 다시
|
||||
가졌음을 보기 전에 디스크립터에 데이터가 쓰였을 것을 보장합니다. 참고로,
|
||||
writel() 을 사용하면 캐시 일관성이 있는 메모리 (cache coherent memory)
|
||||
쓰기가 MMIO 영역에의 쓰기 전에 완료되었을 것을 보장하므로 writel() 앞에
|
||||
wmb() 를 실행할 필요가 없음을 알아두시기 바랍니다. writel() 보다 비용이
|
||||
저렴한 writel_relaxed() 는 이런 보장을 제공하지 않으므로 여기선 사용되지
|
||||
않아야 합니다.
|
||||
가졌음을 보기 전에 디스크립터에 데이터가 쓰였을 것을 보장합니다. dma_mb()
|
||||
는 dma_rmb() 와 dma_wmb() 를 모두 내포합니다. 참고로, writel() 을
|
||||
사용하면 캐시 일관성이 있는 메모리 (cache coherent memory) 쓰기가 MMIO
|
||||
영역에의 쓰기 전에 완료되었을 것을 보장하므로 writel() 앞에 wmb() 를
|
||||
실행할 필요가 없음을 알아두시기 바랍니다. writel() 보다 비용이 저렴한
|
||||
writel_relaxed() 는 이런 보장을 제공하지 않으므로 여기선 사용되지 않아야
|
||||
합니다.
|
||||
|
||||
writel_relaxed() 와 같은 완화된 I/O 접근자들에 대한 자세한 내용을 위해서는
|
||||
"커널 I/O 배리어의 효과" 섹션을, consistent memory 에 대한 자세한 내용을
|
||||
@@ -1918,6 +1931,14 @@ Mandatory 배리어들은 SMP 시스템에서도 UP 시스템에서도 SMP 효
|
||||
Persistent memory 에서의 로드를 위해선 현재의 읽기 메모리 배리어로도 읽기
|
||||
순서를 보장하는데 충분합니다.
|
||||
|
||||
(*) io_stop_wc();
|
||||
|
||||
쓰기와 결합된 특성을 갖는 메모리 액세스의 경우 (예: ioremap_wc() 에 의해
|
||||
리턴되는 것들), CPU 는 앞의 액세스들이 뒤따르는 것들과 병합되게끔 기다릴
|
||||
수 있습니다. io_stop_wc() 는 그런 기다림이 성능에 영향을 끼칠 수 있을 때,
|
||||
이 매크로 앞의 쓰기-결합된 메모리 액세스들이 매크로 뒤의 것들과 병합되는
|
||||
것을 방지하기 위해 사용될 수 있습니다.
|
||||
|
||||
=========================
|
||||
암묵적 커널 메모리 배리어
|
||||
=========================
|
||||
@@ -2827,9 +2848,9 @@ ld.acq 와 stl.rel 인스트럭션을 각각 만들어 내도록 합니다.
|
||||
DEC Alpha CPU 는 가장 완화된 메모리 순서의 CPU 중 하나입니다. 뿐만 아니라,
|
||||
Alpha CPU 의 일부 버전은 분할된 데이터 캐시를 가지고 있어서, 의미적으로
|
||||
관계되어 있는 두개의 캐시 라인이 서로 다른 시간에 업데이트 되는게 가능합니다.
|
||||
이게 데이터 의존성 배리어가 정말 필요해지는 부분인데, 데이터 의존성 배리어는
|
||||
메모리 일관성 시스템과 함께 두개의 캐시를 동기화 시켜서, 포인터 변경과 새로운
|
||||
데이터의 발견을 올바른 순서로 일어나게 하기 때문입니다.
|
||||
이게 주소 의존성 배리어가 정말 필요해지는 부분인데, 주소 의존성 배리어는 메모리
|
||||
일관성 시스템과 함께 두개의 캐시를 동기화 시켜서, 포인터 변경과 새로운 데이터의
|
||||
발견을 올바른 순서로 일어나게 하기 때문입니다.
|
||||
|
||||
리눅스 커널의 메모리 배리어 모델은 Alpha 에 기초해서 정의되었습니다만, v4.15
|
||||
부터는 Alpha 용 READ_ONCE() 코드 내에 smp_mb() 가 추가되어서 메모리 모델로의
|
||||
|
||||
+4
-4
@@ -16668,10 +16668,10 @@ F: net/psample
|
||||
|
||||
PSTORE FILESYSTEM
|
||||
M: Kees Cook <keescook@chromium.org>
|
||||
M: Anton Vorontsov <anton@enomsg.org>
|
||||
M: Colin Cross <ccross@android.com>
|
||||
M: Tony Luck <tony.luck@intel.com>
|
||||
S: Maintained
|
||||
R: Tony Luck <tony.luck@intel.com>
|
||||
R: Guilherme G. Piccoli <gpiccoli@igalia.com>
|
||||
L: linux-hardening@vger.kernel.org
|
||||
S: Supported
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/pstore
|
||||
F: Documentation/admin-guide/ramoops.rst
|
||||
F: Documentation/admin-guide/pstore-blk.rst
|
||||
|
||||
@@ -580,7 +580,7 @@ KBUILD_AFLAGS := -D__ASSEMBLY__ -fno-PIE
|
||||
KBUILD_CFLAGS := -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs \
|
||||
-fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE \
|
||||
-Werror=implicit-function-declaration -Werror=implicit-int \
|
||||
-Werror=return-type -Wno-format-security \
|
||||
-Werror=return-type -Wno-format-security -funsigned-char \
|
||||
-std=gnu11
|
||||
KBUILD_CPPFLAGS := -D__KERNEL__
|
||||
KBUILD_RUSTFLAGS := $(rust_common_flags) \
|
||||
|
||||
@@ -468,6 +468,9 @@ config ARCH_WANT_IRQS_OFF_ACTIVATE_MM
|
||||
config ARCH_HAVE_NMI_SAFE_CMPXCHG
|
||||
bool
|
||||
|
||||
config ARCH_HAS_NMI_SAFE_THIS_CPU_OPS
|
||||
bool
|
||||
|
||||
config HAVE_ALIGNED_STRUCT_PAGE
|
||||
bool
|
||||
help
|
||||
|
||||
@@ -31,6 +31,7 @@ config ARM64
|
||||
select ARCH_HAS_KCOV
|
||||
select ARCH_HAS_KEEPINITRD
|
||||
select ARCH_HAS_MEMBARRIER_SYNC_CORE
|
||||
select ARCH_HAS_NMI_SAFE_THIS_CPU_OPS
|
||||
select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
|
||||
select ARCH_HAS_PTE_DEVMAP
|
||||
select ARCH_HAS_PTE_SPECIAL
|
||||
|
||||
@@ -10,6 +10,7 @@ config LOONGARCH
|
||||
select ARCH_ENABLE_MEMORY_HOTPLUG
|
||||
select ARCH_ENABLE_MEMORY_HOTREMOVE
|
||||
select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI
|
||||
select ARCH_HAS_NMI_SAFE_THIS_CPU_OPS
|
||||
select ARCH_HAS_PTE_SPECIAL
|
||||
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
|
||||
select ARCH_INLINE_READ_LOCK if !PREEMPTION
|
||||
|
||||
@@ -73,6 +73,7 @@ config S390
|
||||
select ARCH_HAS_GIGANTIC_PAGE
|
||||
select ARCH_HAS_KCOV
|
||||
select ARCH_HAS_MEM_ENCRYPT
|
||||
select ARCH_HAS_NMI_SAFE_THIS_CPU_OPS
|
||||
select ARCH_HAS_PTE_SPECIAL
|
||||
select ARCH_HAS_SCALED_CPUTIME
|
||||
select ARCH_HAS_SET_MEMORY
|
||||
|
||||
@@ -81,6 +81,7 @@ config X86
|
||||
select ARCH_HAS_KCOV if X86_64
|
||||
select ARCH_HAS_MEM_ENCRYPT
|
||||
select ARCH_HAS_MEMBARRIER_SYNC_CORE
|
||||
select ARCH_HAS_NMI_SAFE_THIS_CPU_OPS
|
||||
select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
|
||||
select ARCH_HAS_PMEM_API if X86_64
|
||||
select ARCH_HAS_PTE_DEVMAP if X86_64
|
||||
|
||||
@@ -24,7 +24,7 @@ struct p4_event_bind {
|
||||
unsigned int escr_msr[2]; /* ESCR MSR for this event */
|
||||
unsigned int escr_emask; /* valid ESCR EventMask bits */
|
||||
unsigned int shared; /* event is shared across threads */
|
||||
char cntr[2][P4_CNTR_LIMIT]; /* counter index (offset), -1 on absence */
|
||||
signed char cntr[2][P4_CNTR_LIMIT]; /* counter index (offset), -1 on absence */
|
||||
};
|
||||
|
||||
struct p4_pebs_bind {
|
||||
|
||||
@@ -207,7 +207,7 @@ static int efi_pstore_erase(struct pstore_record *record)
|
||||
|
||||
static struct pstore_info efi_pstore_info = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "efi",
|
||||
.name = KBUILD_MODNAME,
|
||||
.flags = PSTORE_FLAGS_DMESG,
|
||||
.open = efi_pstore_open,
|
||||
.close = efi_pstore_close,
|
||||
|
||||
@@ -440,9 +440,8 @@ static int stv0288_set_frontend(struct dvb_frontend *fe)
|
||||
struct stv0288_state *state = fe->demodulator_priv;
|
||||
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
||||
|
||||
char tm;
|
||||
unsigned char tda[3];
|
||||
u8 reg, time_out = 0;
|
||||
u8 tda[3], reg, time_out = 0;
|
||||
s8 tm;
|
||||
|
||||
dprintk("%s : FE_SET_FRONTEND\n", __func__);
|
||||
|
||||
|
||||
@@ -363,8 +363,8 @@ static int envctrl_read_cpu_info(int cpu, struct i2c_child_t *pchild,
|
||||
char mon_type, unsigned char *bufdata)
|
||||
{
|
||||
unsigned char data;
|
||||
int i;
|
||||
char *tbl, j = -1;
|
||||
int i, j = -1;
|
||||
char *tbl;
|
||||
|
||||
/* Find the right monitor type and channel. */
|
||||
for (i = 0; i < PCF8584_MAX_CHANNELS; i++) {
|
||||
|
||||
@@ -312,7 +312,7 @@ void scsi_eh_scmd_add(struct scsi_cmnd *scmd)
|
||||
* Ensure that all tasks observe the host state change before the
|
||||
* host_failed change.
|
||||
*/
|
||||
call_rcu(&scmd->rcu, scsi_eh_inc_host_failed);
|
||||
call_rcu_hurry(&scmd->rcu, scsi_eh_inc_host_failed);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,7 +42,7 @@ typedef unsigned int hive_bool;
|
||||
#define hive_false 0
|
||||
#define hive_true 1
|
||||
|
||||
typedef char hive_int8;
|
||||
typedef signed char hive_int8;
|
||||
typedef short hive_int16;
|
||||
typedef int hive_int32;
|
||||
typedef long long hive_int64;
|
||||
|
||||
+20
-5
@@ -89,6 +89,11 @@ static char *compress =
|
||||
module_param(compress, charp, 0444);
|
||||
MODULE_PARM_DESC(compress, "compression to use");
|
||||
|
||||
/* How much of the kernel log to snapshot */
|
||||
unsigned long kmsg_bytes = CONFIG_PSTORE_DEFAULT_KMSG_BYTES;
|
||||
module_param(kmsg_bytes, ulong, 0444);
|
||||
MODULE_PARM_DESC(kmsg_bytes, "amount of kernel log to snapshot (in bytes)");
|
||||
|
||||
/* Compression parameters */
|
||||
static struct crypto_comp *tfm;
|
||||
|
||||
@@ -100,9 +105,6 @@ struct pstore_zbackend {
|
||||
static char *big_oops_buf;
|
||||
static size_t big_oops_buf_sz;
|
||||
|
||||
/* How much of the console log to snapshot */
|
||||
unsigned long kmsg_bytes = CONFIG_PSTORE_DEFAULT_KMSG_BYTES;
|
||||
|
||||
void pstore_set_kmsg_bytes(int bytes)
|
||||
{
|
||||
kmsg_bytes = bytes;
|
||||
@@ -391,6 +393,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
|
||||
const char *why;
|
||||
unsigned int part = 1;
|
||||
unsigned long flags = 0;
|
||||
int saved_ret = 0;
|
||||
int ret;
|
||||
|
||||
why = kmsg_dump_reason_str(reason);
|
||||
@@ -461,12 +464,21 @@ static void pstore_dump(struct kmsg_dumper *dumper,
|
||||
if (ret == 0 && reason == KMSG_DUMP_OOPS) {
|
||||
pstore_new_entry = 1;
|
||||
pstore_timer_kick();
|
||||
} else {
|
||||
/* Preserve only the first non-zero returned value. */
|
||||
if (!saved_ret)
|
||||
saved_ret = ret;
|
||||
}
|
||||
|
||||
total += record.size;
|
||||
part++;
|
||||
}
|
||||
spin_unlock_irqrestore(&psinfo->buf_lock, flags);
|
||||
|
||||
if (saved_ret) {
|
||||
pr_err_once("backend (%s) writing error (%d)\n", psinfo->name,
|
||||
saved_ret);
|
||||
}
|
||||
}
|
||||
|
||||
static struct kmsg_dumper pstore_dumper = {
|
||||
@@ -562,8 +574,9 @@ out:
|
||||
int pstore_register(struct pstore_info *psi)
|
||||
{
|
||||
if (backend && strcmp(backend, psi->name)) {
|
||||
pr_warn("ignoring unexpected backend '%s'\n", psi->name);
|
||||
return -EPERM;
|
||||
pr_warn("backend '%s' already in use: ignoring '%s'\n",
|
||||
backend, psi->name);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Sanity check flags. */
|
||||
@@ -662,6 +675,8 @@ void pstore_unregister(struct pstore_info *psi)
|
||||
psinfo = NULL;
|
||||
kfree(backend);
|
||||
backend = NULL;
|
||||
|
||||
pr_info("Unregistered %s as persistent store backend\n", psi->name);
|
||||
mutex_unlock(&psinfo_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pstore_unregister);
|
||||
|
||||
+25
-19
@@ -18,10 +18,11 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/pstore_ram.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "ram_internal.h"
|
||||
|
||||
#define RAMOOPS_KERNMSG_HDR "===="
|
||||
#define MIN_MEM_SIZE 4096UL
|
||||
@@ -451,20 +452,28 @@ static void ramoops_free_przs(struct ramoops_context *cxt)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Free pmsg PRZ */
|
||||
persistent_ram_free(&cxt->mprz);
|
||||
|
||||
/* Free console PRZ */
|
||||
persistent_ram_free(&cxt->cprz);
|
||||
|
||||
/* Free dump PRZs */
|
||||
if (cxt->dprzs) {
|
||||
for (i = 0; i < cxt->max_dump_cnt; i++)
|
||||
persistent_ram_free(cxt->dprzs[i]);
|
||||
persistent_ram_free(&cxt->dprzs[i]);
|
||||
|
||||
kfree(cxt->dprzs);
|
||||
cxt->dprzs = NULL;
|
||||
cxt->max_dump_cnt = 0;
|
||||
}
|
||||
|
||||
/* Free ftrace PRZs */
|
||||
if (cxt->fprzs) {
|
||||
for (i = 0; i < cxt->max_ftrace_cnt; i++)
|
||||
persistent_ram_free(cxt->fprzs[i]);
|
||||
persistent_ram_free(&cxt->fprzs[i]);
|
||||
kfree(cxt->fprzs);
|
||||
cxt->fprzs = NULL;
|
||||
cxt->max_ftrace_cnt = 0;
|
||||
}
|
||||
}
|
||||
@@ -548,9 +557,10 @@ static int ramoops_init_przs(const char *name,
|
||||
|
||||
while (i > 0) {
|
||||
i--;
|
||||
persistent_ram_free(prz_ar[i]);
|
||||
persistent_ram_free(&prz_ar[i]);
|
||||
}
|
||||
kfree(prz_ar);
|
||||
prz_ar = NULL;
|
||||
goto fail;
|
||||
}
|
||||
*paddr += zone_sz;
|
||||
@@ -735,6 +745,7 @@ static int ramoops_probe(struct platform_device *pdev)
|
||||
/* Make sure we didn't get bogus platform data pointer. */
|
||||
if (!pdata) {
|
||||
pr_err("NULL platform data\n");
|
||||
err = -EINVAL;
|
||||
goto fail_out;
|
||||
}
|
||||
|
||||
@@ -742,6 +753,7 @@ static int ramoops_probe(struct platform_device *pdev)
|
||||
!pdata->ftrace_size && !pdata->pmsg_size)) {
|
||||
pr_err("The memory size and the record/console size must be "
|
||||
"non-zero\n");
|
||||
err = -EINVAL;
|
||||
goto fail_out;
|
||||
}
|
||||
|
||||
@@ -772,12 +784,17 @@ static int ramoops_probe(struct platform_device *pdev)
|
||||
dump_mem_sz, cxt->record_size,
|
||||
&cxt->max_dump_cnt, 0, 0);
|
||||
if (err)
|
||||
goto fail_out;
|
||||
goto fail_init;
|
||||
|
||||
err = ramoops_init_prz("console", dev, cxt, &cxt->cprz, &paddr,
|
||||
cxt->console_size, 0);
|
||||
if (err)
|
||||
goto fail_init_cprz;
|
||||
goto fail_init;
|
||||
|
||||
err = ramoops_init_prz("pmsg", dev, cxt, &cxt->mprz, &paddr,
|
||||
cxt->pmsg_size, 0);
|
||||
if (err)
|
||||
goto fail_init;
|
||||
|
||||
cxt->max_ftrace_cnt = (cxt->flags & RAMOOPS_FLAG_FTRACE_PER_CPU)
|
||||
? nr_cpu_ids
|
||||
@@ -788,12 +805,7 @@ static int ramoops_probe(struct platform_device *pdev)
|
||||
(cxt->flags & RAMOOPS_FLAG_FTRACE_PER_CPU)
|
||||
? PRZ_FLAG_NO_LOCK : 0);
|
||||
if (err)
|
||||
goto fail_init_fprz;
|
||||
|
||||
err = ramoops_init_prz("pmsg", dev, cxt, &cxt->mprz, &paddr,
|
||||
cxt->pmsg_size, 0);
|
||||
if (err)
|
||||
goto fail_init_mprz;
|
||||
goto fail_init;
|
||||
|
||||
cxt->pstore.data = cxt;
|
||||
/*
|
||||
@@ -857,11 +869,7 @@ fail_buf:
|
||||
kfree(cxt->pstore.buf);
|
||||
fail_clear:
|
||||
cxt->pstore.bufsize = 0;
|
||||
persistent_ram_free(cxt->mprz);
|
||||
fail_init_mprz:
|
||||
fail_init_fprz:
|
||||
persistent_ram_free(cxt->cprz);
|
||||
fail_init_cprz:
|
||||
fail_init:
|
||||
ramoops_free_przs(cxt);
|
||||
fail_out:
|
||||
return err;
|
||||
@@ -876,8 +884,6 @@ static int ramoops_remove(struct platform_device *pdev)
|
||||
kfree(cxt->pstore.buf);
|
||||
cxt->pstore.bufsize = 0;
|
||||
|
||||
persistent_ram_free(cxt->mprz);
|
||||
persistent_ram_free(cxt->cprz);
|
||||
ramoops_free_przs(cxt);
|
||||
|
||||
return 0;
|
||||
|
||||
+16
-4
@@ -13,13 +13,14 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/pstore_ram.h>
|
||||
#include <linux/rslib.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
#include "ram_internal.h"
|
||||
|
||||
/**
|
||||
* struct persistent_ram_buffer - persistent circular RAM buffer
|
||||
*
|
||||
@@ -439,7 +440,11 @@ static void *persistent_ram_vmap(phys_addr_t start, size_t size,
|
||||
phys_addr_t addr = page_start + i * PAGE_SIZE;
|
||||
pages[i] = pfn_to_page(addr >> PAGE_SHIFT);
|
||||
}
|
||||
vaddr = vmap(pages, page_count, VM_MAP, prot);
|
||||
/*
|
||||
* VM_IOREMAP used here to bypass this region during vread()
|
||||
* and kmap_atomic() (i.e. kcore) to avoid __va() failures.
|
||||
*/
|
||||
vaddr = vmap(pages, page_count, VM_MAP | VM_IOREMAP, prot);
|
||||
kfree(pages);
|
||||
|
||||
/*
|
||||
@@ -543,8 +548,14 @@ static int persistent_ram_post_init(struct persistent_ram_zone *prz, u32 sig,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void persistent_ram_free(struct persistent_ram_zone *prz)
|
||||
void persistent_ram_free(struct persistent_ram_zone **_prz)
|
||||
{
|
||||
struct persistent_ram_zone *prz;
|
||||
|
||||
if (!_prz)
|
||||
return;
|
||||
|
||||
prz = *_prz;
|
||||
if (!prz)
|
||||
return;
|
||||
|
||||
@@ -568,6 +579,7 @@ void persistent_ram_free(struct persistent_ram_zone *prz)
|
||||
persistent_ram_free_old(prz);
|
||||
kfree(prz->label);
|
||||
kfree(prz);
|
||||
*_prz = NULL;
|
||||
}
|
||||
|
||||
struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
|
||||
@@ -604,6 +616,6 @@ struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
|
||||
|
||||
return prz;
|
||||
err:
|
||||
persistent_ram_free(prz);
|
||||
persistent_ram_free(&prz);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2010 Marco Stornelli <marco.stornelli@gmail.com>
|
||||
* Copyright (C) 2011 Kees Cook <keescook@chromium.org>
|
||||
* Copyright (C) 2011 Google, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/pstore_ram.h>
|
||||
|
||||
/*
|
||||
* Choose whether access to the RAM zone requires locking or not. If a zone
|
||||
* can be written to from different CPUs like with ftrace for example, then
|
||||
* PRZ_FLAG_NO_LOCK is used. For all other cases, locking is required.
|
||||
*/
|
||||
#define PRZ_FLAG_NO_LOCK BIT(0)
|
||||
/*
|
||||
* If a PRZ should only have a single-boot lifetime, this marks it as
|
||||
* getting wiped after its contents get copied out after boot.
|
||||
*/
|
||||
#define PRZ_FLAG_ZAP_OLD BIT(1)
|
||||
|
||||
/**
|
||||
* struct persistent_ram_zone - Details of a persistent RAM zone (PRZ)
|
||||
* used as a pstore backend
|
||||
*
|
||||
* @paddr: physical address of the mapped RAM area
|
||||
* @size: size of mapping
|
||||
* @label: unique name of this PRZ
|
||||
* @type: frontend type for this PRZ
|
||||
* @flags: holds PRZ_FLAGS_* bits
|
||||
*
|
||||
* @buffer_lock:
|
||||
* locks access to @buffer "size" bytes and "start" offset
|
||||
* @buffer:
|
||||
* pointer to actual RAM area managed by this PRZ
|
||||
* @buffer_size:
|
||||
* bytes in @buffer->data (not including any trailing ECC bytes)
|
||||
*
|
||||
* @par_buffer:
|
||||
* pointer into @buffer->data containing ECC bytes for @buffer->data
|
||||
* @par_header:
|
||||
* pointer into @buffer->data containing ECC bytes for @buffer header
|
||||
* (i.e. all fields up to @data)
|
||||
* @rs_decoder:
|
||||
* RSLIB instance for doing ECC calculations
|
||||
* @corrected_bytes:
|
||||
* ECC corrected bytes accounting since boot
|
||||
* @bad_blocks:
|
||||
* ECC uncorrectable bytes accounting since boot
|
||||
* @ecc_info:
|
||||
* ECC configuration details
|
||||
*
|
||||
* @old_log:
|
||||
* saved copy of @buffer->data prior to most recent wipe
|
||||
* @old_log_size:
|
||||
* bytes contained in @old_log
|
||||
*
|
||||
*/
|
||||
struct persistent_ram_zone {
|
||||
phys_addr_t paddr;
|
||||
size_t size;
|
||||
void *vaddr;
|
||||
char *label;
|
||||
enum pstore_type_id type;
|
||||
u32 flags;
|
||||
|
||||
raw_spinlock_t buffer_lock;
|
||||
struct persistent_ram_buffer *buffer;
|
||||
size_t buffer_size;
|
||||
|
||||
char *par_buffer;
|
||||
char *par_header;
|
||||
struct rs_control *rs_decoder;
|
||||
int corrected_bytes;
|
||||
int bad_blocks;
|
||||
struct persistent_ram_ecc_info ecc_info;
|
||||
|
||||
char *old_log;
|
||||
size_t old_log_size;
|
||||
};
|
||||
|
||||
struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
|
||||
u32 sig, struct persistent_ram_ecc_info *ecc_info,
|
||||
unsigned int memtype, u32 flags, char *label);
|
||||
void persistent_ram_free(struct persistent_ram_zone **_prz);
|
||||
void persistent_ram_zap(struct persistent_ram_zone *prz);
|
||||
|
||||
int persistent_ram_write(struct persistent_ram_zone *prz, const void *s,
|
||||
unsigned int count);
|
||||
int persistent_ram_write_user(struct persistent_ram_zone *prz,
|
||||
const void __user *s, unsigned int count);
|
||||
|
||||
void persistent_ram_save_old(struct persistent_ram_zone *prz);
|
||||
size_t persistent_ram_old_size(struct persistent_ram_zone *prz);
|
||||
void *persistent_ram_old(struct persistent_ram_zone *prz);
|
||||
void persistent_ram_free_old(struct persistent_ram_zone *prz);
|
||||
ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
|
||||
char *str, size_t len);
|
||||
+1
-1
@@ -761,7 +761,7 @@ static inline int notrace psz_kmsg_write_record(struct psz_context *cxt,
|
||||
/* avoid destroying old data, allocate a new one */
|
||||
len = zone->buffer_size + sizeof(*zone->buffer);
|
||||
zone->oldbuf = zone->buffer;
|
||||
zone->buffer = kzalloc(len, GFP_KERNEL);
|
||||
zone->buffer = kzalloc(len, GFP_ATOMIC);
|
||||
if (!zone->buffer) {
|
||||
zone->buffer = zone->oldbuf;
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -416,7 +416,7 @@ static __always_inline void guest_context_enter_irqoff(void)
|
||||
*/
|
||||
if (!context_tracking_guest_enter()) {
|
||||
instrumentation_begin();
|
||||
rcu_virt_note_context_switch(smp_processor_id());
|
||||
rcu_virt_note_context_switch();
|
||||
instrumentation_end();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,28 +8,7 @@
|
||||
#ifndef __LINUX_PSTORE_RAM_H__
|
||||
#define __LINUX_PSTORE_RAM_H__
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/pstore.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* Choose whether access to the RAM zone requires locking or not. If a zone
|
||||
* can be written to from different CPUs like with ftrace for example, then
|
||||
* PRZ_FLAG_NO_LOCK is used. For all other cases, locking is required.
|
||||
*/
|
||||
#define PRZ_FLAG_NO_LOCK BIT(0)
|
||||
/*
|
||||
* If a PRZ should only have a single-boot lifetime, this marks it as
|
||||
* getting wiped after its contents get copied out after boot.
|
||||
*/
|
||||
#define PRZ_FLAG_ZAP_OLD BIT(1)
|
||||
|
||||
struct persistent_ram_buffer;
|
||||
struct rs_control;
|
||||
|
||||
struct persistent_ram_ecc_info {
|
||||
int block_size;
|
||||
@@ -39,84 +18,6 @@ struct persistent_ram_ecc_info {
|
||||
uint16_t *par;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct persistent_ram_zone - Details of a persistent RAM zone (PRZ)
|
||||
* used as a pstore backend
|
||||
*
|
||||
* @paddr: physical address of the mapped RAM area
|
||||
* @size: size of mapping
|
||||
* @label: unique name of this PRZ
|
||||
* @type: frontend type for this PRZ
|
||||
* @flags: holds PRZ_FLAGS_* bits
|
||||
*
|
||||
* @buffer_lock:
|
||||
* locks access to @buffer "size" bytes and "start" offset
|
||||
* @buffer:
|
||||
* pointer to actual RAM area managed by this PRZ
|
||||
* @buffer_size:
|
||||
* bytes in @buffer->data (not including any trailing ECC bytes)
|
||||
*
|
||||
* @par_buffer:
|
||||
* pointer into @buffer->data containing ECC bytes for @buffer->data
|
||||
* @par_header:
|
||||
* pointer into @buffer->data containing ECC bytes for @buffer header
|
||||
* (i.e. all fields up to @data)
|
||||
* @rs_decoder:
|
||||
* RSLIB instance for doing ECC calculations
|
||||
* @corrected_bytes:
|
||||
* ECC corrected bytes accounting since boot
|
||||
* @bad_blocks:
|
||||
* ECC uncorrectable bytes accounting since boot
|
||||
* @ecc_info:
|
||||
* ECC configuration details
|
||||
*
|
||||
* @old_log:
|
||||
* saved copy of @buffer->data prior to most recent wipe
|
||||
* @old_log_size:
|
||||
* bytes contained in @old_log
|
||||
*
|
||||
*/
|
||||
struct persistent_ram_zone {
|
||||
phys_addr_t paddr;
|
||||
size_t size;
|
||||
void *vaddr;
|
||||
char *label;
|
||||
enum pstore_type_id type;
|
||||
u32 flags;
|
||||
|
||||
raw_spinlock_t buffer_lock;
|
||||
struct persistent_ram_buffer *buffer;
|
||||
size_t buffer_size;
|
||||
|
||||
char *par_buffer;
|
||||
char *par_header;
|
||||
struct rs_control *rs_decoder;
|
||||
int corrected_bytes;
|
||||
int bad_blocks;
|
||||
struct persistent_ram_ecc_info ecc_info;
|
||||
|
||||
char *old_log;
|
||||
size_t old_log_size;
|
||||
};
|
||||
|
||||
struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
|
||||
u32 sig, struct persistent_ram_ecc_info *ecc_info,
|
||||
unsigned int memtype, u32 flags, char *label);
|
||||
void persistent_ram_free(struct persistent_ram_zone *prz);
|
||||
void persistent_ram_zap(struct persistent_ram_zone *prz);
|
||||
|
||||
int persistent_ram_write(struct persistent_ram_zone *prz, const void *s,
|
||||
unsigned int count);
|
||||
int persistent_ram_write_user(struct persistent_ram_zone *prz,
|
||||
const void __user *s, unsigned int count);
|
||||
|
||||
void persistent_ram_save_old(struct persistent_ram_zone *prz);
|
||||
size_t persistent_ram_old_size(struct persistent_ram_zone *prz);
|
||||
void *persistent_ram_old(struct persistent_ram_zone *prz);
|
||||
void persistent_ram_free_old(struct persistent_ram_zone *prz);
|
||||
ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
|
||||
char *str, size_t len);
|
||||
|
||||
/*
|
||||
* Ramoops platform data
|
||||
* @mem_size memory size for ramoops
|
||||
|
||||
@@ -108,6 +108,15 @@ static inline int rcu_preempt_depth(void)
|
||||
|
||||
#endif /* #else #ifdef CONFIG_PREEMPT_RCU */
|
||||
|
||||
#ifdef CONFIG_RCU_LAZY
|
||||
void call_rcu_hurry(struct rcu_head *head, rcu_callback_t func);
|
||||
#else
|
||||
static inline void call_rcu_hurry(struct rcu_head *head, rcu_callback_t func)
|
||||
{
|
||||
call_rcu(head, func);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Internal to kernel */
|
||||
void rcu_init(void);
|
||||
extern int rcu_scheduler_active;
|
||||
@@ -340,6 +349,11 @@ static inline int rcu_read_lock_any_held(void)
|
||||
return !preemptible();
|
||||
}
|
||||
|
||||
static inline int debug_lockdep_rcu_enabled(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
|
||||
|
||||
#ifdef CONFIG_PROVE_RCU
|
||||
|
||||
@@ -142,12 +142,10 @@ static inline int rcu_needs_cpu(void)
|
||||
* Take advantage of the fact that there is only one CPU, which
|
||||
* allows us to ignore virtualization-based context switches.
|
||||
*/
|
||||
static inline void rcu_virt_note_context_switch(int cpu) { }
|
||||
static inline void rcu_virt_note_context_switch(void) { }
|
||||
static inline void rcu_cpu_stall_reset(void) { }
|
||||
static inline int rcu_jiffies_till_stall_check(void) { return 21 * HZ; }
|
||||
static inline void rcu_irq_exit_check_preempt(void) { }
|
||||
#define rcu_is_idle_cpu(cpu) \
|
||||
(is_idle_task(current) && !in_nmi() && !in_hardirq() && !in_serving_softirq())
|
||||
static inline void exit_rcu(void) { }
|
||||
static inline bool rcu_preempt_need_deferred_qs(struct task_struct *t)
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@ void rcu_cpu_stall_reset(void);
|
||||
* wrapper around rcu_note_context_switch(), which allows TINY_RCU
|
||||
* to save a few bytes. The caller must have disabled interrupts.
|
||||
*/
|
||||
static inline void rcu_virt_note_context_switch(int cpu)
|
||||
static inline void rcu_virt_note_context_switch(void)
|
||||
{
|
||||
rcu_note_context_switch(false);
|
||||
}
|
||||
@@ -87,8 +87,6 @@ bool poll_state_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp);
|
||||
void cond_synchronize_rcu(unsigned long oldstate);
|
||||
void cond_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp);
|
||||
|
||||
bool rcu_is_idle_cpu(int cpu);
|
||||
|
||||
#ifdef CONFIG_PROVE_RCU
|
||||
void rcu_irq_exit_check_preempt(void);
|
||||
#else
|
||||
|
||||
@@ -76,6 +76,17 @@
|
||||
* rcu_read_lock before reading the address, then rcu_read_unlock after
|
||||
* taking the spinlock within the structure expected at that address.
|
||||
*
|
||||
* Note that it is not possible to acquire a lock within a structure
|
||||
* allocated with SLAB_TYPESAFE_BY_RCU without first acquiring a reference
|
||||
* as described above. The reason is that SLAB_TYPESAFE_BY_RCU pages
|
||||
* are not zeroed before being given to the slab, which means that any
|
||||
* locks must be initialized after each and every kmem_struct_alloc().
|
||||
* Alternatively, make the ctor passed to kmem_cache_create() initialize
|
||||
* the locks at page-allocation time, as is done in __i915_request_ctor(),
|
||||
* sighand_ctor(), and anon_vma_ctor(). Such a ctor permits readers
|
||||
* to safely acquire those ctor-initialized locks under rcu_read_lock()
|
||||
* protection.
|
||||
*
|
||||
* Note that SLAB_TYPESAFE_BY_RCU was originally named SLAB_DESTROY_BY_RCU.
|
||||
*/
|
||||
/* Defer freeing slabs to RCU */
|
||||
|
||||
@@ -64,6 +64,20 @@ unsigned long get_state_synchronize_srcu(struct srcu_struct *ssp);
|
||||
unsigned long start_poll_synchronize_srcu(struct srcu_struct *ssp);
|
||||
bool poll_state_synchronize_srcu(struct srcu_struct *ssp, unsigned long cookie);
|
||||
|
||||
#ifdef CONFIG_NEED_SRCU_NMI_SAFE
|
||||
int __srcu_read_lock_nmisafe(struct srcu_struct *ssp) __acquires(ssp);
|
||||
void __srcu_read_unlock_nmisafe(struct srcu_struct *ssp, int idx) __releases(ssp);
|
||||
#else
|
||||
static inline int __srcu_read_lock_nmisafe(struct srcu_struct *ssp)
|
||||
{
|
||||
return __srcu_read_lock(ssp);
|
||||
}
|
||||
static inline void __srcu_read_unlock_nmisafe(struct srcu_struct *ssp, int idx)
|
||||
{
|
||||
__srcu_read_unlock(ssp, idx);
|
||||
}
|
||||
#endif /* CONFIG_NEED_SRCU_NMI_SAFE */
|
||||
|
||||
#ifdef CONFIG_SRCU
|
||||
void srcu_init(void);
|
||||
#else /* #ifdef CONFIG_SRCU */
|
||||
@@ -104,6 +118,18 @@ static inline int srcu_read_lock_held(const struct srcu_struct *ssp)
|
||||
|
||||
#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
|
||||
|
||||
#define SRCU_NMI_UNKNOWN 0x0
|
||||
#define SRCU_NMI_UNSAFE 0x1
|
||||
#define SRCU_NMI_SAFE 0x2
|
||||
|
||||
#if defined(CONFIG_PROVE_RCU) && defined(CONFIG_TREE_SRCU)
|
||||
void srcu_check_nmi_safety(struct srcu_struct *ssp, bool nmi_safe);
|
||||
#else
|
||||
static inline void srcu_check_nmi_safety(struct srcu_struct *ssp,
|
||||
bool nmi_safe) { }
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* srcu_dereference_check - fetch SRCU-protected pointer for later dereferencing
|
||||
* @p: the pointer to fetch and protect for later dereferencing
|
||||
@@ -161,17 +187,36 @@ static inline int srcu_read_lock(struct srcu_struct *ssp) __acquires(ssp)
|
||||
{
|
||||
int retval;
|
||||
|
||||
srcu_check_nmi_safety(ssp, false);
|
||||
retval = __srcu_read_lock(ssp);
|
||||
rcu_lock_acquire(&(ssp)->dep_map);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* srcu_read_lock_nmisafe - register a new reader for an SRCU-protected structure.
|
||||
* @ssp: srcu_struct in which to register the new reader.
|
||||
*
|
||||
* Enter an SRCU read-side critical section, but in an NMI-safe manner.
|
||||
* See srcu_read_lock() for more information.
|
||||
*/
|
||||
static inline int srcu_read_lock_nmisafe(struct srcu_struct *ssp) __acquires(ssp)
|
||||
{
|
||||
int retval;
|
||||
|
||||
srcu_check_nmi_safety(ssp, true);
|
||||
retval = __srcu_read_lock_nmisafe(ssp);
|
||||
rcu_lock_acquire(&(ssp)->dep_map);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Used by tracing, cannot be traced and cannot invoke lockdep. */
|
||||
static inline notrace int
|
||||
srcu_read_lock_notrace(struct srcu_struct *ssp) __acquires(ssp)
|
||||
{
|
||||
int retval;
|
||||
|
||||
srcu_check_nmi_safety(ssp, false);
|
||||
retval = __srcu_read_lock(ssp);
|
||||
return retval;
|
||||
}
|
||||
@@ -187,14 +232,32 @@ static inline void srcu_read_unlock(struct srcu_struct *ssp, int idx)
|
||||
__releases(ssp)
|
||||
{
|
||||
WARN_ON_ONCE(idx & ~0x1);
|
||||
srcu_check_nmi_safety(ssp, false);
|
||||
rcu_lock_release(&(ssp)->dep_map);
|
||||
__srcu_read_unlock(ssp, idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* srcu_read_unlock_nmisafe - unregister a old reader from an SRCU-protected structure.
|
||||
* @ssp: srcu_struct in which to unregister the old reader.
|
||||
* @idx: return value from corresponding srcu_read_lock().
|
||||
*
|
||||
* Exit an SRCU read-side critical section, but in an NMI-safe manner.
|
||||
*/
|
||||
static inline void srcu_read_unlock_nmisafe(struct srcu_struct *ssp, int idx)
|
||||
__releases(ssp)
|
||||
{
|
||||
WARN_ON_ONCE(idx & ~0x1);
|
||||
srcu_check_nmi_safety(ssp, true);
|
||||
rcu_lock_release(&(ssp)->dep_map);
|
||||
__srcu_read_unlock_nmisafe(ssp, idx);
|
||||
}
|
||||
|
||||
/* Used by tracing, cannot be traced and cannot call lockdep. */
|
||||
static inline notrace void
|
||||
srcu_read_unlock_notrace(struct srcu_struct *ssp, int idx) __releases(ssp)
|
||||
{
|
||||
srcu_check_nmi_safety(ssp, false);
|
||||
__srcu_read_unlock(ssp, idx);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,9 @@ struct srcu_struct;
|
||||
*/
|
||||
struct srcu_data {
|
||||
/* Read-side state. */
|
||||
unsigned long srcu_lock_count[2]; /* Locks per CPU. */
|
||||
unsigned long srcu_unlock_count[2]; /* Unlocks per CPU. */
|
||||
atomic_long_t srcu_lock_count[2]; /* Locks per CPU. */
|
||||
atomic_long_t srcu_unlock_count[2]; /* Unlocks per CPU. */
|
||||
int srcu_nmi_safety; /* NMI-safe srcu_struct structure? */
|
||||
|
||||
/* Update-side state. */
|
||||
spinlock_t __private lock ____cacheline_internodealigned_in_smp;
|
||||
|
||||
@@ -14,10 +14,12 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/preempt.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "encoding.h"
|
||||
@@ -1308,3 +1310,51 @@ noinline void __tsan_atomic_signal_fence(int memorder)
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(__tsan_atomic_signal_fence);
|
||||
|
||||
#ifdef __HAVE_ARCH_MEMSET
|
||||
void *__tsan_memset(void *s, int c, size_t count);
|
||||
noinline void *__tsan_memset(void *s, int c, size_t count)
|
||||
{
|
||||
/*
|
||||
* Instead of not setting up watchpoints where accessed size is greater
|
||||
* than MAX_ENCODABLE_SIZE, truncate checked size to MAX_ENCODABLE_SIZE.
|
||||
*/
|
||||
size_t check_len = min_t(size_t, count, MAX_ENCODABLE_SIZE);
|
||||
|
||||
check_access(s, check_len, KCSAN_ACCESS_WRITE, _RET_IP_);
|
||||
return memset(s, c, count);
|
||||
}
|
||||
#else
|
||||
void *__tsan_memset(void *s, int c, size_t count) __alias(memset);
|
||||
#endif
|
||||
EXPORT_SYMBOL(__tsan_memset);
|
||||
|
||||
#ifdef __HAVE_ARCH_MEMMOVE
|
||||
void *__tsan_memmove(void *dst, const void *src, size_t len);
|
||||
noinline void *__tsan_memmove(void *dst, const void *src, size_t len)
|
||||
{
|
||||
size_t check_len = min_t(size_t, len, MAX_ENCODABLE_SIZE);
|
||||
|
||||
check_access(dst, check_len, KCSAN_ACCESS_WRITE, _RET_IP_);
|
||||
check_access(src, check_len, 0, _RET_IP_);
|
||||
return memmove(dst, src, len);
|
||||
}
|
||||
#else
|
||||
void *__tsan_memmove(void *dst, const void *src, size_t len) __alias(memmove);
|
||||
#endif
|
||||
EXPORT_SYMBOL(__tsan_memmove);
|
||||
|
||||
#ifdef __HAVE_ARCH_MEMCPY
|
||||
void *__tsan_memcpy(void *dst, const void *src, size_t len);
|
||||
noinline void *__tsan_memcpy(void *dst, const void *src, size_t len)
|
||||
{
|
||||
size_t check_len = min_t(size_t, len, MAX_ENCODABLE_SIZE);
|
||||
|
||||
check_access(dst, check_len, KCSAN_ACCESS_WRITE, _RET_IP_);
|
||||
check_access(src, check_len, 0, _RET_IP_);
|
||||
return memcpy(dst, src, len);
|
||||
}
|
||||
#else
|
||||
void *__tsan_memcpy(void *dst, const void *src, size_t len) __alias(memcpy);
|
||||
#endif
|
||||
EXPORT_SYMBOL(__tsan_memcpy);
|
||||
|
||||
@@ -72,6 +72,9 @@ config TREE_SRCU
|
||||
help
|
||||
This option selects the full-fledged version of SRCU.
|
||||
|
||||
config NEED_SRCU_NMI_SAFE
|
||||
def_bool HAVE_NMI && !ARCH_HAS_NMI_SAFE_THIS_CPU_OPS && !TINY_SRCU
|
||||
|
||||
config TASKS_RCU_GENERIC
|
||||
def_bool TASKS_RCU || TASKS_RUDE_RCU || TASKS_TRACE_RCU
|
||||
select SRCU
|
||||
@@ -311,4 +314,12 @@ config TASKS_TRACE_RCU_READ_MB
|
||||
Say N here if you hate read-side memory barriers.
|
||||
Take the default if you are unsure.
|
||||
|
||||
config RCU_LAZY
|
||||
bool "RCU callback lazy invocation functionality"
|
||||
depends on RCU_NOCB_CPU
|
||||
default n
|
||||
help
|
||||
To save power, batch RCU callbacks and flush after delay, memory
|
||||
pressure, or callback list growing too big.
|
||||
|
||||
endmenu # "RCU Subsystem"
|
||||
|
||||
@@ -474,6 +474,14 @@ enum rcutorture_type {
|
||||
INVALID_RCU_FLAVOR
|
||||
};
|
||||
|
||||
#if defined(CONFIG_RCU_LAZY)
|
||||
unsigned long rcu_lazy_get_jiffies_till_flush(void);
|
||||
void rcu_lazy_set_jiffies_till_flush(unsigned long j);
|
||||
#else
|
||||
static inline unsigned long rcu_lazy_get_jiffies_till_flush(void) { return 0; }
|
||||
static inline void rcu_lazy_set_jiffies_till_flush(unsigned long j) { }
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_TREE_RCU)
|
||||
void rcutorture_get_gp_data(enum rcutorture_type test_type, int *flags,
|
||||
unsigned long *gp_seq);
|
||||
|
||||
+66
-3
@@ -95,6 +95,7 @@ torture_param(int, verbose, 1, "Enable verbose debugging printk()s");
|
||||
torture_param(int, writer_holdoff, 0, "Holdoff (us) between GPs, zero to disable");
|
||||
torture_param(int, kfree_rcu_test, 0, "Do we run a kfree_rcu() scale test?");
|
||||
torture_param(int, kfree_mult, 1, "Multiple of kfree_obj size to allocate.");
|
||||
torture_param(int, kfree_by_call_rcu, 0, "Use call_rcu() to emulate kfree_rcu()?");
|
||||
|
||||
static char *scale_type = "rcu";
|
||||
module_param(scale_type, charp, 0444);
|
||||
@@ -175,7 +176,7 @@ static struct rcu_scale_ops rcu_ops = {
|
||||
.get_gp_seq = rcu_get_gp_seq,
|
||||
.gp_diff = rcu_seq_diff,
|
||||
.exp_completed = rcu_exp_batches_completed,
|
||||
.async = call_rcu,
|
||||
.async = call_rcu_hurry,
|
||||
.gp_barrier = rcu_barrier,
|
||||
.sync = synchronize_rcu,
|
||||
.exp_sync = synchronize_rcu_expedited,
|
||||
@@ -659,6 +660,14 @@ struct kfree_obj {
|
||||
struct rcu_head rh;
|
||||
};
|
||||
|
||||
/* Used if doing RCU-kfree'ing via call_rcu(). */
|
||||
static void kfree_call_rcu(struct rcu_head *rh)
|
||||
{
|
||||
struct kfree_obj *obj = container_of(rh, struct kfree_obj, rh);
|
||||
|
||||
kfree(obj);
|
||||
}
|
||||
|
||||
static int
|
||||
kfree_scale_thread(void *arg)
|
||||
{
|
||||
@@ -696,6 +705,11 @@ kfree_scale_thread(void *arg)
|
||||
if (!alloc_ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
if (kfree_by_call_rcu) {
|
||||
call_rcu(&(alloc_ptr->rh), kfree_call_rcu);
|
||||
continue;
|
||||
}
|
||||
|
||||
// By default kfree_rcu_test_single and kfree_rcu_test_double are
|
||||
// initialized to false. If both have the same value (false or true)
|
||||
// both are randomly tested, otherwise only the one with value true
|
||||
@@ -767,11 +781,58 @@ kfree_scale_shutdown(void *arg)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
// Used if doing RCU-kfree'ing via call_rcu().
|
||||
static unsigned long jiffies_at_lazy_cb;
|
||||
static struct rcu_head lazy_test1_rh;
|
||||
static int rcu_lazy_test1_cb_called;
|
||||
static void call_rcu_lazy_test1(struct rcu_head *rh)
|
||||
{
|
||||
jiffies_at_lazy_cb = jiffies;
|
||||
WRITE_ONCE(rcu_lazy_test1_cb_called, 1);
|
||||
}
|
||||
|
||||
static int __init
|
||||
kfree_scale_init(void)
|
||||
{
|
||||
long i;
|
||||
int firsterr = 0;
|
||||
long i;
|
||||
unsigned long jif_start;
|
||||
unsigned long orig_jif;
|
||||
|
||||
// Also, do a quick self-test to ensure laziness is as much as
|
||||
// expected.
|
||||
if (kfree_by_call_rcu && !IS_ENABLED(CONFIG_RCU_LAZY)) {
|
||||
pr_alert("CONFIG_RCU_LAZY is disabled, falling back to kfree_rcu() for delayed RCU kfree'ing\n");
|
||||
kfree_by_call_rcu = 0;
|
||||
}
|
||||
|
||||
if (kfree_by_call_rcu) {
|
||||
/* do a test to check the timeout. */
|
||||
orig_jif = rcu_lazy_get_jiffies_till_flush();
|
||||
|
||||
rcu_lazy_set_jiffies_till_flush(2 * HZ);
|
||||
rcu_barrier();
|
||||
|
||||
jif_start = jiffies;
|
||||
jiffies_at_lazy_cb = 0;
|
||||
call_rcu(&lazy_test1_rh, call_rcu_lazy_test1);
|
||||
|
||||
smp_cond_load_relaxed(&rcu_lazy_test1_cb_called, VAL == 1);
|
||||
|
||||
rcu_lazy_set_jiffies_till_flush(orig_jif);
|
||||
|
||||
if (WARN_ON_ONCE(jiffies_at_lazy_cb - jif_start < 2 * HZ)) {
|
||||
pr_alert("ERROR: call_rcu() CBs are not being lazy as expected!\n");
|
||||
WARN_ON_ONCE(1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (WARN_ON_ONCE(jiffies_at_lazy_cb - jif_start > 3 * HZ)) {
|
||||
pr_alert("ERROR: call_rcu() CBs are being too lazy!\n");
|
||||
WARN_ON_ONCE(1);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
kfree_nrealthreads = compute_real(kfree_nthreads);
|
||||
/* Start up the kthreads. */
|
||||
@@ -784,7 +845,9 @@ kfree_scale_init(void)
|
||||
schedule_timeout_uninterruptible(1);
|
||||
}
|
||||
|
||||
pr_alert("kfree object size=%zu\n", kfree_mult * sizeof(struct kfree_obj));
|
||||
pr_alert("kfree object size=%zu, kfree_by_call_rcu=%d\n",
|
||||
kfree_mult * sizeof(struct kfree_obj),
|
||||
kfree_by_call_rcu);
|
||||
|
||||
kfree_reader_tasks = kcalloc(kfree_nrealthreads, sizeof(kfree_reader_tasks[0]),
|
||||
GFP_KERNEL);
|
||||
|
||||
+58
-14
@@ -357,6 +357,10 @@ struct rcu_torture_ops {
|
||||
bool (*poll_gp_state_exp)(unsigned long oldstate);
|
||||
void (*cond_sync_exp)(unsigned long oldstate);
|
||||
void (*cond_sync_exp_full)(struct rcu_gp_oldstate *rgosp);
|
||||
unsigned long (*get_comp_state)(void);
|
||||
void (*get_comp_state_full)(struct rcu_gp_oldstate *rgosp);
|
||||
bool (*same_gp_state)(unsigned long oldstate1, unsigned long oldstate2);
|
||||
bool (*same_gp_state_full)(struct rcu_gp_oldstate *rgosp1, struct rcu_gp_oldstate *rgosp2);
|
||||
unsigned long (*get_gp_state)(void);
|
||||
void (*get_gp_state_full)(struct rcu_gp_oldstate *rgosp);
|
||||
unsigned long (*get_gp_completed)(void);
|
||||
@@ -510,7 +514,7 @@ static unsigned long rcu_no_completed(void)
|
||||
|
||||
static void rcu_torture_deferred_free(struct rcu_torture *p)
|
||||
{
|
||||
call_rcu(&p->rtort_rcu, rcu_torture_cb);
|
||||
call_rcu_hurry(&p->rtort_rcu, rcu_torture_cb);
|
||||
}
|
||||
|
||||
static void rcu_sync_torture_init(void)
|
||||
@@ -535,6 +539,10 @@ static struct rcu_torture_ops rcu_ops = {
|
||||
.deferred_free = rcu_torture_deferred_free,
|
||||
.sync = synchronize_rcu,
|
||||
.exp_sync = synchronize_rcu_expedited,
|
||||
.same_gp_state = same_state_synchronize_rcu,
|
||||
.same_gp_state_full = same_state_synchronize_rcu_full,
|
||||
.get_comp_state = get_completed_synchronize_rcu,
|
||||
.get_comp_state_full = get_completed_synchronize_rcu_full,
|
||||
.get_gp_state = get_state_synchronize_rcu,
|
||||
.get_gp_state_full = get_state_synchronize_rcu_full,
|
||||
.get_gp_completed = get_completed_synchronize_rcu,
|
||||
@@ -551,7 +559,7 @@ static struct rcu_torture_ops rcu_ops = {
|
||||
.start_gp_poll_exp_full = start_poll_synchronize_rcu_expedited_full,
|
||||
.poll_gp_state_exp = poll_state_synchronize_rcu,
|
||||
.cond_sync_exp = cond_synchronize_rcu_expedited,
|
||||
.call = call_rcu,
|
||||
.call = call_rcu_hurry,
|
||||
.cb_barrier = rcu_barrier,
|
||||
.fqs = rcu_force_quiescent_state,
|
||||
.stats = NULL,
|
||||
@@ -615,10 +623,14 @@ static struct rcu_torture_ops rcu_busted_ops = {
|
||||
DEFINE_STATIC_SRCU(srcu_ctl);
|
||||
static struct srcu_struct srcu_ctld;
|
||||
static struct srcu_struct *srcu_ctlp = &srcu_ctl;
|
||||
static struct rcu_torture_ops srcud_ops;
|
||||
|
||||
static int srcu_torture_read_lock(void) __acquires(srcu_ctlp)
|
||||
{
|
||||
return srcu_read_lock(srcu_ctlp);
|
||||
if (cur_ops == &srcud_ops)
|
||||
return srcu_read_lock_nmisafe(srcu_ctlp);
|
||||
else
|
||||
return srcu_read_lock(srcu_ctlp);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -642,7 +654,10 @@ srcu_read_delay(struct torture_random_state *rrsp, struct rt_read_seg *rtrsp)
|
||||
|
||||
static void srcu_torture_read_unlock(int idx) __releases(srcu_ctlp)
|
||||
{
|
||||
srcu_read_unlock(srcu_ctlp, idx);
|
||||
if (cur_ops == &srcud_ops)
|
||||
srcu_read_unlock_nmisafe(srcu_ctlp, idx);
|
||||
else
|
||||
srcu_read_unlock(srcu_ctlp, idx);
|
||||
}
|
||||
|
||||
static int torture_srcu_read_lock_held(void)
|
||||
@@ -848,7 +863,7 @@ static void rcu_tasks_torture_deferred_free(struct rcu_torture *p)
|
||||
|
||||
static void synchronize_rcu_mult_test(void)
|
||||
{
|
||||
synchronize_rcu_mult(call_rcu_tasks, call_rcu);
|
||||
synchronize_rcu_mult(call_rcu_tasks, call_rcu_hurry);
|
||||
}
|
||||
|
||||
static struct rcu_torture_ops tasks_ops = {
|
||||
@@ -1258,13 +1273,15 @@ static void rcu_torture_write_types(void)
|
||||
} else if (gp_normal && !cur_ops->deferred_free) {
|
||||
pr_alert("%s: gp_normal without primitives.\n", __func__);
|
||||
}
|
||||
if (gp_poll1 && cur_ops->start_gp_poll && cur_ops->poll_gp_state) {
|
||||
if (gp_poll1 && cur_ops->get_comp_state && cur_ops->same_gp_state &&
|
||||
cur_ops->start_gp_poll && cur_ops->poll_gp_state) {
|
||||
synctype[nsynctypes++] = RTWS_POLL_GET;
|
||||
pr_info("%s: Testing polling GPs.\n", __func__);
|
||||
} else if (gp_poll && (!cur_ops->start_gp_poll || !cur_ops->poll_gp_state)) {
|
||||
pr_alert("%s: gp_poll without primitives.\n", __func__);
|
||||
}
|
||||
if (gp_poll_full1 && cur_ops->start_gp_poll_full && cur_ops->poll_gp_state_full) {
|
||||
if (gp_poll_full1 && cur_ops->get_comp_state_full && cur_ops->same_gp_state_full
|
||||
&& cur_ops->start_gp_poll_full && cur_ops->poll_gp_state_full) {
|
||||
synctype[nsynctypes++] = RTWS_POLL_GET_FULL;
|
||||
pr_info("%s: Testing polling full-state GPs.\n", __func__);
|
||||
} else if (gp_poll_full && (!cur_ops->start_gp_poll_full || !cur_ops->poll_gp_state_full)) {
|
||||
@@ -1339,14 +1356,18 @@ rcu_torture_writer(void *arg)
|
||||
struct rcu_gp_oldstate cookie_full;
|
||||
int expediting = 0;
|
||||
unsigned long gp_snap;
|
||||
unsigned long gp_snap1;
|
||||
struct rcu_gp_oldstate gp_snap_full;
|
||||
struct rcu_gp_oldstate gp_snap1_full;
|
||||
int i;
|
||||
int idx;
|
||||
int oldnice = task_nice(current);
|
||||
struct rcu_gp_oldstate rgo[NUM_ACTIVE_RCU_POLL_FULL_OLDSTATE];
|
||||
struct rcu_torture *rp;
|
||||
struct rcu_torture *old_rp;
|
||||
static DEFINE_TORTURE_RANDOM(rand);
|
||||
bool stutter_waited;
|
||||
unsigned long ulo[NUM_ACTIVE_RCU_POLL_OLDSTATE];
|
||||
|
||||
VERBOSE_TOROUT_STRING("rcu_torture_writer task started");
|
||||
if (!can_expedite)
|
||||
@@ -1463,20 +1484,43 @@ rcu_torture_writer(void *arg)
|
||||
break;
|
||||
case RTWS_POLL_GET:
|
||||
rcu_torture_writer_state = RTWS_POLL_GET;
|
||||
for (i = 0; i < ARRAY_SIZE(ulo); i++)
|
||||
ulo[i] = cur_ops->get_comp_state();
|
||||
gp_snap = cur_ops->start_gp_poll();
|
||||
rcu_torture_writer_state = RTWS_POLL_WAIT;
|
||||
while (!cur_ops->poll_gp_state(gp_snap))
|
||||
while (!cur_ops->poll_gp_state(gp_snap)) {
|
||||
gp_snap1 = cur_ops->get_gp_state();
|
||||
for (i = 0; i < ARRAY_SIZE(ulo); i++)
|
||||
if (cur_ops->poll_gp_state(ulo[i]) ||
|
||||
cur_ops->same_gp_state(ulo[i], gp_snap1)) {
|
||||
ulo[i] = gp_snap1;
|
||||
break;
|
||||
}
|
||||
WARN_ON_ONCE(i >= ARRAY_SIZE(ulo));
|
||||
torture_hrtimeout_jiffies(torture_random(&rand) % 16,
|
||||
&rand);
|
||||
}
|
||||
rcu_torture_pipe_update(old_rp);
|
||||
break;
|
||||
case RTWS_POLL_GET_FULL:
|
||||
rcu_torture_writer_state = RTWS_POLL_GET_FULL;
|
||||
for (i = 0; i < ARRAY_SIZE(rgo); i++)
|
||||
cur_ops->get_comp_state_full(&rgo[i]);
|
||||
cur_ops->start_gp_poll_full(&gp_snap_full);
|
||||
rcu_torture_writer_state = RTWS_POLL_WAIT_FULL;
|
||||
while (!cur_ops->poll_gp_state_full(&gp_snap_full))
|
||||
while (!cur_ops->poll_gp_state_full(&gp_snap_full)) {
|
||||
cur_ops->get_gp_state_full(&gp_snap1_full);
|
||||
for (i = 0; i < ARRAY_SIZE(rgo); i++)
|
||||
if (cur_ops->poll_gp_state_full(&rgo[i]) ||
|
||||
cur_ops->same_gp_state_full(&rgo[i],
|
||||
&gp_snap1_full)) {
|
||||
rgo[i] = gp_snap1_full;
|
||||
break;
|
||||
}
|
||||
WARN_ON_ONCE(i >= ARRAY_SIZE(rgo));
|
||||
torture_hrtimeout_jiffies(torture_random(&rand) % 16,
|
||||
&rand);
|
||||
}
|
||||
rcu_torture_pipe_update(old_rp);
|
||||
break;
|
||||
case RTWS_POLL_GET_EXP:
|
||||
@@ -3388,13 +3432,13 @@ static void rcu_test_debug_objects(void)
|
||||
/* Try to queue the rh2 pair of callbacks for the same grace period. */
|
||||
preempt_disable(); /* Prevent preemption from interrupting test. */
|
||||
rcu_read_lock(); /* Make it impossible to finish a grace period. */
|
||||
call_rcu(&rh1, rcu_torture_leak_cb); /* Start grace period. */
|
||||
call_rcu_hurry(&rh1, rcu_torture_leak_cb); /* Start grace period. */
|
||||
local_irq_disable(); /* Make it harder to start a new grace period. */
|
||||
call_rcu(&rh2, rcu_torture_leak_cb);
|
||||
call_rcu(&rh2, rcu_torture_err_cb); /* Duplicate callback. */
|
||||
call_rcu_hurry(&rh2, rcu_torture_leak_cb);
|
||||
call_rcu_hurry(&rh2, rcu_torture_err_cb); /* Duplicate callback. */
|
||||
if (rhp) {
|
||||
call_rcu(rhp, rcu_torture_leak_cb);
|
||||
call_rcu(rhp, rcu_torture_err_cb); /* Another duplicate callback. */
|
||||
call_rcu_hurry(rhp, rcu_torture_leak_cb);
|
||||
call_rcu_hurry(rhp, rcu_torture_err_cb); /* Another duplicate callback. */
|
||||
}
|
||||
local_irq_enable();
|
||||
rcu_read_unlock();
|
||||
|
||||
+84
-16
@@ -417,7 +417,7 @@ static unsigned long srcu_readers_lock_idx(struct srcu_struct *ssp, int idx)
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct srcu_data *cpuc = per_cpu_ptr(ssp->sda, cpu);
|
||||
|
||||
sum += READ_ONCE(cpuc->srcu_lock_count[idx]);
|
||||
sum += atomic_long_read(&cpuc->srcu_lock_count[idx]);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
@@ -429,13 +429,18 @@ static unsigned long srcu_readers_lock_idx(struct srcu_struct *ssp, int idx)
|
||||
static unsigned long srcu_readers_unlock_idx(struct srcu_struct *ssp, int idx)
|
||||
{
|
||||
int cpu;
|
||||
unsigned long mask = 0;
|
||||
unsigned long sum = 0;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct srcu_data *cpuc = per_cpu_ptr(ssp->sda, cpu);
|
||||
|
||||
sum += READ_ONCE(cpuc->srcu_unlock_count[idx]);
|
||||
sum += atomic_long_read(&cpuc->srcu_unlock_count[idx]);
|
||||
if (IS_ENABLED(CONFIG_PROVE_RCU))
|
||||
mask = mask | READ_ONCE(cpuc->srcu_nmi_safety);
|
||||
}
|
||||
WARN_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) && (mask & (mask >> 1)),
|
||||
"Mixed NMI-safe readers for srcu_struct at %ps.\n", ssp);
|
||||
return sum;
|
||||
}
|
||||
|
||||
@@ -503,10 +508,10 @@ static bool srcu_readers_active(struct srcu_struct *ssp)
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct srcu_data *cpuc = per_cpu_ptr(ssp->sda, cpu);
|
||||
|
||||
sum += READ_ONCE(cpuc->srcu_lock_count[0]);
|
||||
sum += READ_ONCE(cpuc->srcu_lock_count[1]);
|
||||
sum -= READ_ONCE(cpuc->srcu_unlock_count[0]);
|
||||
sum -= READ_ONCE(cpuc->srcu_unlock_count[1]);
|
||||
sum += atomic_long_read(&cpuc->srcu_lock_count[0]);
|
||||
sum += atomic_long_read(&cpuc->srcu_lock_count[1]);
|
||||
sum -= atomic_long_read(&cpuc->srcu_unlock_count[0]);
|
||||
sum -= atomic_long_read(&cpuc->srcu_unlock_count[1]);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
@@ -626,6 +631,29 @@ void cleanup_srcu_struct(struct srcu_struct *ssp)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cleanup_srcu_struct);
|
||||
|
||||
#ifdef CONFIG_PROVE_RCU
|
||||
/*
|
||||
* Check for consistent NMI safety.
|
||||
*/
|
||||
void srcu_check_nmi_safety(struct srcu_struct *ssp, bool nmi_safe)
|
||||
{
|
||||
int nmi_safe_mask = 1 << nmi_safe;
|
||||
int old_nmi_safe_mask;
|
||||
struct srcu_data *sdp;
|
||||
|
||||
/* NMI-unsafe use in NMI is a bad sign */
|
||||
WARN_ON_ONCE(!nmi_safe && in_nmi());
|
||||
sdp = raw_cpu_ptr(ssp->sda);
|
||||
old_nmi_safe_mask = READ_ONCE(sdp->srcu_nmi_safety);
|
||||
if (!old_nmi_safe_mask) {
|
||||
WRITE_ONCE(sdp->srcu_nmi_safety, nmi_safe_mask);
|
||||
return;
|
||||
}
|
||||
WARN_ONCE(old_nmi_safe_mask != nmi_safe_mask, "CPU %d old state %d new state %d\n", sdp->cpu, old_nmi_safe_mask, nmi_safe_mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(srcu_check_nmi_safety);
|
||||
#endif /* CONFIG_PROVE_RCU */
|
||||
|
||||
/*
|
||||
* Counts the new reader in the appropriate per-CPU element of the
|
||||
* srcu_struct.
|
||||
@@ -636,7 +664,7 @@ int __srcu_read_lock(struct srcu_struct *ssp)
|
||||
int idx;
|
||||
|
||||
idx = READ_ONCE(ssp->srcu_idx) & 0x1;
|
||||
this_cpu_inc(ssp->sda->srcu_lock_count[idx]);
|
||||
this_cpu_inc(ssp->sda->srcu_lock_count[idx].counter);
|
||||
smp_mb(); /* B */ /* Avoid leaking the critical section. */
|
||||
return idx;
|
||||
}
|
||||
@@ -650,10 +678,45 @@ EXPORT_SYMBOL_GPL(__srcu_read_lock);
|
||||
void __srcu_read_unlock(struct srcu_struct *ssp, int idx)
|
||||
{
|
||||
smp_mb(); /* C */ /* Avoid leaking the critical section. */
|
||||
this_cpu_inc(ssp->sda->srcu_unlock_count[idx]);
|
||||
this_cpu_inc(ssp->sda->srcu_unlock_count[idx].counter);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__srcu_read_unlock);
|
||||
|
||||
#ifdef CONFIG_NEED_SRCU_NMI_SAFE
|
||||
|
||||
/*
|
||||
* Counts the new reader in the appropriate per-CPU element of the
|
||||
* srcu_struct, but in an NMI-safe manner using RMW atomics.
|
||||
* Returns an index that must be passed to the matching srcu_read_unlock().
|
||||
*/
|
||||
int __srcu_read_lock_nmisafe(struct srcu_struct *ssp)
|
||||
{
|
||||
int idx;
|
||||
struct srcu_data *sdp = raw_cpu_ptr(ssp->sda);
|
||||
|
||||
idx = READ_ONCE(ssp->srcu_idx) & 0x1;
|
||||
atomic_long_inc(&sdp->srcu_lock_count[idx]);
|
||||
smp_mb__after_atomic(); /* B */ /* Avoid leaking the critical section. */
|
||||
return idx;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__srcu_read_lock_nmisafe);
|
||||
|
||||
/*
|
||||
* Removes the count for the old reader from the appropriate per-CPU
|
||||
* element of the srcu_struct. Note that this may well be a different
|
||||
* CPU than that which was incremented by the corresponding srcu_read_lock().
|
||||
*/
|
||||
void __srcu_read_unlock_nmisafe(struct srcu_struct *ssp, int idx)
|
||||
{
|
||||
struct srcu_data *sdp = raw_cpu_ptr(ssp->sda);
|
||||
|
||||
smp_mb__before_atomic(); /* C */ /* Avoid leaking the critical section. */
|
||||
atomic_long_inc(&sdp->srcu_unlock_count[idx]);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__srcu_read_unlock_nmisafe);
|
||||
|
||||
#endif // CONFIG_NEED_SRCU_NMI_SAFE
|
||||
|
||||
/*
|
||||
* Start an SRCU grace period.
|
||||
*/
|
||||
@@ -1090,7 +1153,12 @@ static unsigned long srcu_gp_start_if_needed(struct srcu_struct *ssp,
|
||||
int ss_state;
|
||||
|
||||
check_init_srcu_struct(ssp);
|
||||
idx = srcu_read_lock(ssp);
|
||||
/*
|
||||
* While starting a new grace period, make sure we are in an
|
||||
* SRCU read-side critical section so that the grace-period
|
||||
* sequence number cannot wrap around in the meantime.
|
||||
*/
|
||||
idx = __srcu_read_lock_nmisafe(ssp);
|
||||
ss_state = smp_load_acquire(&ssp->srcu_size_state);
|
||||
if (ss_state < SRCU_SIZE_WAIT_CALL)
|
||||
sdp = per_cpu_ptr(ssp->sda, 0);
|
||||
@@ -1123,7 +1191,7 @@ static unsigned long srcu_gp_start_if_needed(struct srcu_struct *ssp,
|
||||
srcu_funnel_gp_start(ssp, sdp, s, do_norm);
|
||||
else if (needexp)
|
||||
srcu_funnel_exp_start(ssp, sdp_mynode, s);
|
||||
srcu_read_unlock(ssp, idx);
|
||||
__srcu_read_unlock_nmisafe(ssp, idx);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1427,13 +1495,13 @@ void srcu_barrier(struct srcu_struct *ssp)
|
||||
/* Initial count prevents reaching zero until all CBs are posted. */
|
||||
atomic_set(&ssp->srcu_barrier_cpu_cnt, 1);
|
||||
|
||||
idx = srcu_read_lock(ssp);
|
||||
idx = __srcu_read_lock_nmisafe(ssp);
|
||||
if (smp_load_acquire(&ssp->srcu_size_state) < SRCU_SIZE_WAIT_BARRIER)
|
||||
srcu_barrier_one_cpu(ssp, per_cpu_ptr(ssp->sda, 0));
|
||||
else
|
||||
for_each_possible_cpu(cpu)
|
||||
srcu_barrier_one_cpu(ssp, per_cpu_ptr(ssp->sda, cpu));
|
||||
srcu_read_unlock(ssp, idx);
|
||||
__srcu_read_unlock_nmisafe(ssp, idx);
|
||||
|
||||
/* Remove the initial count, at which point reaching zero can happen. */
|
||||
if (atomic_dec_and_test(&ssp->srcu_barrier_cpu_cnt))
|
||||
@@ -1687,8 +1755,8 @@ void srcu_torture_stats_print(struct srcu_struct *ssp, char *tt, char *tf)
|
||||
struct srcu_data *sdp;
|
||||
|
||||
sdp = per_cpu_ptr(ssp->sda, cpu);
|
||||
u0 = data_race(sdp->srcu_unlock_count[!idx]);
|
||||
u1 = data_race(sdp->srcu_unlock_count[idx]);
|
||||
u0 = data_race(atomic_long_read(&sdp->srcu_unlock_count[!idx]));
|
||||
u1 = data_race(atomic_long_read(&sdp->srcu_unlock_count[idx]));
|
||||
|
||||
/*
|
||||
* Make sure that a lock is always counted if the corresponding
|
||||
@@ -1696,8 +1764,8 @@ void srcu_torture_stats_print(struct srcu_struct *ssp, char *tt, char *tf)
|
||||
*/
|
||||
smp_rmb();
|
||||
|
||||
l0 = data_race(sdp->srcu_lock_count[!idx]);
|
||||
l1 = data_race(sdp->srcu_lock_count[idx]);
|
||||
l0 = data_race(atomic_long_read(&sdp->srcu_lock_count[!idx]));
|
||||
l1 = data_race(atomic_long_read(&sdp->srcu_lock_count[idx]));
|
||||
|
||||
c0 = l0 - u0;
|
||||
c1 = l1 - u1;
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ static void rcu_sync_func(struct rcu_head *rhp);
|
||||
|
||||
static void rcu_sync_call(struct rcu_sync *rsp)
|
||||
{
|
||||
call_rcu(&rsp->cb_head, rcu_sync_func);
|
||||
call_rcu_hurry(&rsp->cb_head, rcu_sync_func);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -728,7 +728,7 @@ static void rcu_tasks_wait_gp(struct rcu_tasks *rtp)
|
||||
if (rtsi > 0 && !reported && time_after(j, lastinfo + rtsi)) {
|
||||
lastinfo = j;
|
||||
rtsi = rtsi * rcu_task_stall_info_mult;
|
||||
pr_info("%s: %s grace period %lu is %lu jiffies old.\n",
|
||||
pr_info("%s: %s grace period number %lu (since boot) is %lu jiffies old.\n",
|
||||
__func__, rtp->kname, rtp->tasks_gp_seq, j - rtp->gp_start);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ static struct rcu_ctrlblk rcu_ctrlblk = {
|
||||
|
||||
void rcu_barrier(void)
|
||||
{
|
||||
wait_rcu_gp(call_rcu);
|
||||
wait_rcu_gp(call_rcu_hurry);
|
||||
}
|
||||
EXPORT_SYMBOL(rcu_barrier);
|
||||
|
||||
|
||||
+96
-56
@@ -301,12 +301,6 @@ static bool rcu_dynticks_in_eqs(int snap)
|
||||
return !(snap & RCU_DYNTICKS_IDX);
|
||||
}
|
||||
|
||||
/* Return true if the specified CPU is currently idle from an RCU viewpoint. */
|
||||
bool rcu_is_idle_cpu(int cpu)
|
||||
{
|
||||
return rcu_dynticks_in_eqs(rcu_dynticks_snap(cpu));
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if the CPU corresponding to the specified rcu_data
|
||||
* structure has spent some time in an extended quiescent state since
|
||||
@@ -2108,7 +2102,7 @@ int rcutree_dying_cpu(unsigned int cpu)
|
||||
if (!IS_ENABLED(CONFIG_HOTPLUG_CPU))
|
||||
return 0;
|
||||
|
||||
blkd = !!(rnp->qsmask & rdp->grpmask);
|
||||
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;
|
||||
@@ -2418,7 +2412,7 @@ void rcu_force_quiescent_state(void)
|
||||
struct rcu_node *rnp_old = NULL;
|
||||
|
||||
/* Funnel through hierarchy to reduce memory contention. */
|
||||
rnp = __this_cpu_read(rcu_data.mynode);
|
||||
rnp = raw_cpu_read(rcu_data.mynode);
|
||||
for (; rnp != NULL; rnp = rnp->parent) {
|
||||
ret = (READ_ONCE(rcu_state.gp_flags) & RCU_GP_FLAG_FQS) ||
|
||||
!raw_spin_trylock(&rnp->fqslock);
|
||||
@@ -2730,47 +2724,8 @@ static void check_cb_ovld(struct rcu_data *rdp)
|
||||
raw_spin_unlock_rcu_node(rnp);
|
||||
}
|
||||
|
||||
/**
|
||||
* call_rcu() - Queue an RCU callback for invocation after a 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 pre-existing RCU read-side
|
||||
* critical sections have completed. However, the callback function
|
||||
* might well execute concurrently with RCU read-side critical sections
|
||||
* that started after call_rcu() was invoked.
|
||||
*
|
||||
* RCU read-side critical sections are delimited by rcu_read_lock()
|
||||
* and rcu_read_unlock(), and may be nested. In addition, but only in
|
||||
* v5.0 and later, regions of code across which interrupts, preemption,
|
||||
* or softirqs have been disabled also serve as RCU read-side critical
|
||||
* sections. This includes hardware interrupt handlers, softirq handlers,
|
||||
* and NMI handlers.
|
||||
*
|
||||
* Note that all CPUs must agree that the grace period extended beyond
|
||||
* all pre-existing RCU read-side critical section. On systems with more
|
||||
* than one CPU, this means that when "func()" is invoked, each CPU is
|
||||
* guaranteed to have executed a full memory barrier since the end of its
|
||||
* last RCU read-side critical section whose beginning preceded the call
|
||||
* to call_rcu(). It also means that each CPU executing an RCU read-side
|
||||
* critical section that continues beyond the start of "func()" must have
|
||||
* executed a memory barrier after the call_rcu() but before the beginning
|
||||
* of that RCU read-side critical section. Note that these guarantees
|
||||
* include CPUs that are offline, idle, or executing in user mode, as
|
||||
* well as CPUs that are executing in the kernel.
|
||||
*
|
||||
* Furthermore, if CPU A invoked call_rcu() and CPU B invoked the
|
||||
* resulting RCU callback function "func()", then both CPU A and CPU B are
|
||||
* guaranteed to execute a full memory barrier during the time interval
|
||||
* between the call to call_rcu() and the invocation of "func()" -- even
|
||||
* if CPU A and CPU B are the same CPU (but again only if the system has
|
||||
* more than one CPU).
|
||||
*
|
||||
* Implementation of these memory-ordering guarantees is described here:
|
||||
* Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst.
|
||||
*/
|
||||
void call_rcu(struct rcu_head *head, rcu_callback_t func)
|
||||
static void
|
||||
__call_rcu_common(struct rcu_head *head, rcu_callback_t func, bool lazy)
|
||||
{
|
||||
static atomic_t doublefrees;
|
||||
unsigned long flags;
|
||||
@@ -2811,7 +2766,7 @@ void call_rcu(struct rcu_head *head, rcu_callback_t func)
|
||||
}
|
||||
|
||||
check_cb_ovld(rdp);
|
||||
if (rcu_nocb_try_bypass(rdp, head, &was_alldone, flags))
|
||||
if (rcu_nocb_try_bypass(rdp, head, &was_alldone, flags, lazy))
|
||||
return; // Enqueued onto ->nocb_bypass, so just leave.
|
||||
// If no-CBs CPU gets here, rcu_nocb_try_bypass() acquired ->nocb_lock.
|
||||
rcu_segcblist_enqueue(&rdp->cblist, head);
|
||||
@@ -2833,8 +2788,84 @@ void call_rcu(struct rcu_head *head, rcu_callback_t func)
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(call_rcu);
|
||||
|
||||
#ifdef CONFIG_RCU_LAZY
|
||||
/**
|
||||
* call_rcu_hurry() - Queue RCU callback for invocation after grace period, and
|
||||
* flush all lazy callbacks (including the new one) to the main ->cblist while
|
||||
* doing so.
|
||||
*
|
||||
* @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 pre-existing RCU read-side
|
||||
* critical sections have completed.
|
||||
*
|
||||
* Use this API instead of call_rcu() if you don't want the callback to be
|
||||
* invoked after very long periods of time, which can happen on systems without
|
||||
* memory pressure and on systems which are lightly loaded or mostly idle.
|
||||
* This function will cause callbacks to be invoked sooner than later at the
|
||||
* expense of extra power. Other than that, this function is identical to, and
|
||||
* reuses call_rcu()'s logic. Refer to call_rcu() for more details about memory
|
||||
* ordering and other functionality.
|
||||
*/
|
||||
void call_rcu_hurry(struct rcu_head *head, rcu_callback_t func)
|
||||
{
|
||||
return __call_rcu_common(head, func, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(call_rcu_hurry);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* call_rcu() - Queue an RCU callback for invocation after a grace period.
|
||||
* By default the callbacks are 'lazy' and are kept hidden from the main
|
||||
* ->cblist to prevent starting of grace periods too soon.
|
||||
* If you desire grace periods to start very soon, use call_rcu_hurry().
|
||||
*
|
||||
* @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 pre-existing RCU read-side
|
||||
* critical sections have completed. However, the callback function
|
||||
* might well execute concurrently with RCU read-side critical sections
|
||||
* that started after call_rcu() was invoked.
|
||||
*
|
||||
* RCU read-side critical sections are delimited by rcu_read_lock()
|
||||
* and rcu_read_unlock(), and may be nested. In addition, but only in
|
||||
* v5.0 and later, regions of code across which interrupts, preemption,
|
||||
* or softirqs have been disabled also serve as RCU read-side critical
|
||||
* sections. This includes hardware interrupt handlers, softirq handlers,
|
||||
* and NMI handlers.
|
||||
*
|
||||
* Note that all CPUs must agree that the grace period extended beyond
|
||||
* all pre-existing RCU read-side critical section. On systems with more
|
||||
* than one CPU, this means that when "func()" is invoked, each CPU is
|
||||
* guaranteed to have executed a full memory barrier since the end of its
|
||||
* last RCU read-side critical section whose beginning preceded the call
|
||||
* to call_rcu(). It also means that each CPU executing an RCU read-side
|
||||
* critical section that continues beyond the start of "func()" must have
|
||||
* executed a memory barrier after the call_rcu() but before the beginning
|
||||
* of that RCU read-side critical section. Note that these guarantees
|
||||
* include CPUs that are offline, idle, or executing in user mode, as
|
||||
* well as CPUs that are executing in the kernel.
|
||||
*
|
||||
* Furthermore, if CPU A invoked call_rcu() and CPU B invoked the
|
||||
* resulting RCU callback function "func()", then both CPU A and CPU B are
|
||||
* guaranteed to execute a full memory barrier during the time interval
|
||||
* between the call to call_rcu() and the invocation of "func()" -- even
|
||||
* if CPU A and CPU B are the same CPU (but again only if the system has
|
||||
* more than one CPU).
|
||||
*
|
||||
* Implementation of these memory-ordering guarantees is described here:
|
||||
* Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst.
|
||||
*/
|
||||
void call_rcu(struct rcu_head *head, rcu_callback_t func)
|
||||
{
|
||||
return __call_rcu_common(head, func, IS_ENABLED(CONFIG_RCU_LAZY));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(call_rcu);
|
||||
|
||||
/* Maximum number of jiffies to wait before draining a batch. */
|
||||
#define KFREE_DRAIN_JIFFIES (5 * HZ)
|
||||
@@ -3509,7 +3540,7 @@ void synchronize_rcu(void)
|
||||
if (rcu_gp_is_expedited())
|
||||
synchronize_rcu_expedited();
|
||||
else
|
||||
wait_rcu_gp(call_rcu);
|
||||
wait_rcu_gp(call_rcu_hurry);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3896,6 +3927,8 @@ static void rcu_barrier_entrain(struct rcu_data *rdp)
|
||||
{
|
||||
unsigned long gseq = READ_ONCE(rcu_state.barrier_sequence);
|
||||
unsigned long lseq = READ_ONCE(rdp->barrier_seq_snap);
|
||||
bool wake_nocb = false;
|
||||
bool was_alldone = false;
|
||||
|
||||
lockdep_assert_held(&rcu_state.barrier_lock);
|
||||
if (rcu_seq_state(lseq) || !rcu_seq_state(gseq) || rcu_seq_ctr(lseq) != rcu_seq_ctr(gseq))
|
||||
@@ -3904,7 +3937,14 @@ static void rcu_barrier_entrain(struct rcu_data *rdp)
|
||||
rdp->barrier_head.func = rcu_barrier_callback;
|
||||
debug_rcu_head_queue(&rdp->barrier_head);
|
||||
rcu_nocb_lock(rdp);
|
||||
WARN_ON_ONCE(!rcu_nocb_flush_bypass(rdp, NULL, jiffies));
|
||||
/*
|
||||
* Flush bypass and wakeup rcuog if we add callbacks to an empty regular
|
||||
* queue. This way we don't wait for bypass timer that can reach seconds
|
||||
* if it's fully lazy.
|
||||
*/
|
||||
was_alldone = rcu_rdp_is_offloaded(rdp) && !rcu_segcblist_pend_cbs(&rdp->cblist);
|
||||
WARN_ON_ONCE(!rcu_nocb_flush_bypass(rdp, NULL, jiffies, false));
|
||||
wake_nocb = was_alldone && rcu_segcblist_pend_cbs(&rdp->cblist);
|
||||
if (rcu_segcblist_entrain(&rdp->cblist, &rdp->barrier_head)) {
|
||||
atomic_inc(&rcu_state.barrier_cpu_count);
|
||||
} else {
|
||||
@@ -3912,6 +3952,8 @@ static void rcu_barrier_entrain(struct rcu_data *rdp)
|
||||
rcu_barrier_trace(TPS("IRQNQ"), -1, rcu_state.barrier_sequence);
|
||||
}
|
||||
rcu_nocb_unlock(rdp);
|
||||
if (wake_nocb)
|
||||
wake_nocb_gp(rdp, false);
|
||||
smp_store_release(&rdp->barrier_seq_snap, gseq);
|
||||
}
|
||||
|
||||
@@ -4278,8 +4320,6 @@ void rcu_report_dead(unsigned int cpu)
|
||||
// Do any dangling deferred wakeups.
|
||||
do_nocb_deferred_wakeup(rdp);
|
||||
|
||||
/* QS for any half-done expedited grace period. */
|
||||
rcu_report_exp_rdp(rdp);
|
||||
rcu_preempt_deferred_qs(current);
|
||||
|
||||
/* Remove outgoing CPU from mask in the leaf rcu_node structure. */
|
||||
@@ -4327,7 +4367,7 @@ void rcutree_migrate_callbacks(int cpu)
|
||||
my_rdp = this_cpu_ptr(&rcu_data);
|
||||
my_rnp = my_rdp->mynode;
|
||||
rcu_nocb_lock(my_rdp); /* irqs already disabled. */
|
||||
WARN_ON_ONCE(!rcu_nocb_flush_bypass(my_rdp, NULL, jiffies));
|
||||
WARN_ON_ONCE(!rcu_nocb_flush_bypass(my_rdp, NULL, jiffies, false));
|
||||
raw_spin_lock_rcu_node(my_rnp); /* irqs already disabled. */
|
||||
/* Leverage recent GPs and set GP for new callbacks. */
|
||||
needwake = rcu_advance_cbs(my_rnp, rdp) ||
|
||||
|
||||
+8
-4
@@ -263,14 +263,16 @@ struct rcu_data {
|
||||
unsigned long last_fqs_resched; /* Time of last rcu_resched(). */
|
||||
unsigned long last_sched_clock; /* Jiffies of last rcu_sched_clock_irq(). */
|
||||
|
||||
long lazy_len; /* Length of buffered lazy callbacks. */
|
||||
int cpu;
|
||||
};
|
||||
|
||||
/* Values for nocb_defer_wakeup field in struct rcu_data. */
|
||||
#define RCU_NOCB_WAKE_NOT 0
|
||||
#define RCU_NOCB_WAKE_BYPASS 1
|
||||
#define RCU_NOCB_WAKE 2
|
||||
#define RCU_NOCB_WAKE_FORCE 3
|
||||
#define RCU_NOCB_WAKE_LAZY 2
|
||||
#define RCU_NOCB_WAKE 3
|
||||
#define RCU_NOCB_WAKE_FORCE 4
|
||||
|
||||
#define RCU_JIFFIES_TILL_FORCE_QS (1 + (HZ > 250) + (HZ > 500))
|
||||
/* For jiffies_till_first_fqs and */
|
||||
@@ -439,10 +441,12 @@ static void zero_cpu_stall_ticks(struct rcu_data *rdp);
|
||||
static struct swait_queue_head *rcu_nocb_gp_get(struct rcu_node *rnp);
|
||||
static void rcu_nocb_gp_cleanup(struct swait_queue_head *sq);
|
||||
static void rcu_init_one_nocb(struct rcu_node *rnp);
|
||||
static bool wake_nocb_gp(struct rcu_data *rdp, bool force);
|
||||
static bool rcu_nocb_flush_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
unsigned long j);
|
||||
unsigned long j, bool lazy);
|
||||
static bool rcu_nocb_try_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
bool *was_alldone, unsigned long flags);
|
||||
bool *was_alldone, unsigned long flags,
|
||||
bool lazy);
|
||||
static void __call_rcu_nocb_wake(struct rcu_data *rdp, bool was_empty,
|
||||
unsigned long flags);
|
||||
static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp, int level);
|
||||
|
||||
@@ -937,7 +937,7 @@ void synchronize_rcu_expedited(void)
|
||||
|
||||
/* If expedited grace periods are prohibited, fall back to normal. */
|
||||
if (rcu_gp_is_normal()) {
|
||||
wait_rcu_gp(call_rcu);
|
||||
wait_rcu_gp(call_rcu_hurry);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+205
-54
@@ -256,6 +256,31 @@ static bool wake_nocb_gp(struct rcu_data *rdp, bool force)
|
||||
return __wake_nocb_gp(rdp_gp, rdp, force, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* LAZY_FLUSH_JIFFIES decides the maximum amount of time that
|
||||
* can elapse before lazy callbacks are flushed. Lazy callbacks
|
||||
* could be flushed much earlier for a number of other reasons
|
||||
* however, LAZY_FLUSH_JIFFIES will ensure no lazy callbacks are
|
||||
* left unsubmitted to RCU after those many jiffies.
|
||||
*/
|
||||
#define LAZY_FLUSH_JIFFIES (10 * HZ)
|
||||
static unsigned long jiffies_till_flush = LAZY_FLUSH_JIFFIES;
|
||||
|
||||
#ifdef CONFIG_RCU_LAZY
|
||||
// To be called only from test code.
|
||||
void rcu_lazy_set_jiffies_till_flush(unsigned long jif)
|
||||
{
|
||||
jiffies_till_flush = jif;
|
||||
}
|
||||
EXPORT_SYMBOL(rcu_lazy_set_jiffies_till_flush);
|
||||
|
||||
unsigned long rcu_lazy_get_jiffies_till_flush(void)
|
||||
{
|
||||
return jiffies_till_flush;
|
||||
}
|
||||
EXPORT_SYMBOL(rcu_lazy_get_jiffies_till_flush);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Arrange to wake the GP kthread for this NOCB group at some future
|
||||
* time when it is safe to do so.
|
||||
@@ -269,10 +294,14 @@ static void wake_nocb_gp_defer(struct rcu_data *rdp, int waketype,
|
||||
raw_spin_lock_irqsave(&rdp_gp->nocb_gp_lock, flags);
|
||||
|
||||
/*
|
||||
* Bypass wakeup overrides previous deferments. In case
|
||||
* of callback storm, no need to wake up too early.
|
||||
* Bypass wakeup overrides previous deferments. In case of
|
||||
* callback storms, no need to wake up too early.
|
||||
*/
|
||||
if (waketype == RCU_NOCB_WAKE_BYPASS) {
|
||||
if (waketype == RCU_NOCB_WAKE_LAZY &&
|
||||
rdp->nocb_defer_wakeup == RCU_NOCB_WAKE_NOT) {
|
||||
mod_timer(&rdp_gp->nocb_timer, jiffies + jiffies_till_flush);
|
||||
WRITE_ONCE(rdp_gp->nocb_defer_wakeup, waketype);
|
||||
} else if (waketype == RCU_NOCB_WAKE_BYPASS) {
|
||||
mod_timer(&rdp_gp->nocb_timer, jiffies + 2);
|
||||
WRITE_ONCE(rdp_gp->nocb_defer_wakeup, waketype);
|
||||
} else {
|
||||
@@ -293,12 +322,16 @@ static void wake_nocb_gp_defer(struct rcu_data *rdp, int waketype,
|
||||
* proves to be initially empty, just return false because the no-CB GP
|
||||
* kthread may need to be awakened in this case.
|
||||
*
|
||||
* Return true if there was something to be flushed and it succeeded, otherwise
|
||||
* false.
|
||||
*
|
||||
* Note that this function always returns true if rhp is NULL.
|
||||
*/
|
||||
static bool rcu_nocb_do_flush_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
unsigned long j)
|
||||
static bool rcu_nocb_do_flush_bypass(struct rcu_data *rdp, struct rcu_head *rhp_in,
|
||||
unsigned long j, bool lazy)
|
||||
{
|
||||
struct rcu_cblist rcl;
|
||||
struct rcu_head *rhp = rhp_in;
|
||||
|
||||
WARN_ON_ONCE(!rcu_rdp_is_offloaded(rdp));
|
||||
rcu_lockdep_assert_cblist_protected(rdp);
|
||||
@@ -310,7 +343,20 @@ static bool rcu_nocb_do_flush_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
/* Note: ->cblist.len already accounts for ->nocb_bypass contents. */
|
||||
if (rhp)
|
||||
rcu_segcblist_inc_len(&rdp->cblist); /* Must precede enqueue. */
|
||||
|
||||
/*
|
||||
* If the new CB requested was a lazy one, queue it onto the main
|
||||
* ->cblist so that we can take advantage of the grace-period that will
|
||||
* happen regardless. But queue it onto the bypass list first so that
|
||||
* the lazy CB is ordered with the existing CBs in the bypass list.
|
||||
*/
|
||||
if (lazy && rhp) {
|
||||
rcu_cblist_enqueue(&rdp->nocb_bypass, rhp);
|
||||
rhp = NULL;
|
||||
}
|
||||
rcu_cblist_flush_enqueue(&rcl, &rdp->nocb_bypass, rhp);
|
||||
WRITE_ONCE(rdp->lazy_len, 0);
|
||||
|
||||
rcu_segcblist_insert_pend_cbs(&rdp->cblist, &rcl);
|
||||
WRITE_ONCE(rdp->nocb_bypass_first, j);
|
||||
rcu_nocb_bypass_unlock(rdp);
|
||||
@@ -326,13 +372,13 @@ static bool rcu_nocb_do_flush_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
* Note that this function always returns true if rhp is NULL.
|
||||
*/
|
||||
static bool rcu_nocb_flush_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
unsigned long j)
|
||||
unsigned long j, bool lazy)
|
||||
{
|
||||
if (!rcu_rdp_is_offloaded(rdp))
|
||||
return true;
|
||||
rcu_lockdep_assert_cblist_protected(rdp);
|
||||
rcu_nocb_bypass_lock(rdp);
|
||||
return rcu_nocb_do_flush_bypass(rdp, rhp, j);
|
||||
return rcu_nocb_do_flush_bypass(rdp, rhp, j, lazy);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -345,7 +391,7 @@ static void rcu_nocb_try_flush_bypass(struct rcu_data *rdp, unsigned long j)
|
||||
if (!rcu_rdp_is_offloaded(rdp) ||
|
||||
!rcu_nocb_bypass_trylock(rdp))
|
||||
return;
|
||||
WARN_ON_ONCE(!rcu_nocb_do_flush_bypass(rdp, NULL, j));
|
||||
WARN_ON_ONCE(!rcu_nocb_do_flush_bypass(rdp, NULL, j, false));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -367,12 +413,14 @@ static void rcu_nocb_try_flush_bypass(struct rcu_data *rdp, unsigned long j)
|
||||
* there is only one CPU in operation.
|
||||
*/
|
||||
static bool rcu_nocb_try_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
bool *was_alldone, unsigned long flags)
|
||||
bool *was_alldone, unsigned long flags,
|
||||
bool lazy)
|
||||
{
|
||||
unsigned long c;
|
||||
unsigned long cur_gp_seq;
|
||||
unsigned long j = jiffies;
|
||||
long ncbs = rcu_cblist_n_cbs(&rdp->nocb_bypass);
|
||||
bool bypass_is_lazy = (ncbs == READ_ONCE(rdp->lazy_len));
|
||||
|
||||
lockdep_assert_irqs_disabled();
|
||||
|
||||
@@ -417,24 +465,29 @@ static bool rcu_nocb_try_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
// If there hasn't yet been all that many ->cblist enqueues
|
||||
// this jiffy, tell the caller to enqueue onto ->cblist. But flush
|
||||
// ->nocb_bypass first.
|
||||
if (rdp->nocb_nobypass_count < nocb_nobypass_lim_per_jiffy) {
|
||||
// Lazy CBs throttle this back and do immediate bypass queuing.
|
||||
if (rdp->nocb_nobypass_count < nocb_nobypass_lim_per_jiffy && !lazy) {
|
||||
rcu_nocb_lock(rdp);
|
||||
*was_alldone = !rcu_segcblist_pend_cbs(&rdp->cblist);
|
||||
if (*was_alldone)
|
||||
trace_rcu_nocb_wake(rcu_state.name, rdp->cpu,
|
||||
TPS("FirstQ"));
|
||||
WARN_ON_ONCE(!rcu_nocb_flush_bypass(rdp, NULL, j));
|
||||
|
||||
WARN_ON_ONCE(!rcu_nocb_flush_bypass(rdp, NULL, j, false));
|
||||
WARN_ON_ONCE(rcu_cblist_n_cbs(&rdp->nocb_bypass));
|
||||
return false; // Caller must enqueue the callback.
|
||||
}
|
||||
|
||||
// If ->nocb_bypass has been used too long or is too full,
|
||||
// flush ->nocb_bypass to ->cblist.
|
||||
if ((ncbs && j != READ_ONCE(rdp->nocb_bypass_first)) ||
|
||||
if ((ncbs && !bypass_is_lazy && j != READ_ONCE(rdp->nocb_bypass_first)) ||
|
||||
(ncbs && bypass_is_lazy &&
|
||||
(time_after(j, READ_ONCE(rdp->nocb_bypass_first) + jiffies_till_flush))) ||
|
||||
ncbs >= qhimark) {
|
||||
rcu_nocb_lock(rdp);
|
||||
if (!rcu_nocb_flush_bypass(rdp, rhp, j)) {
|
||||
*was_alldone = !rcu_segcblist_pend_cbs(&rdp->cblist);
|
||||
*was_alldone = !rcu_segcblist_pend_cbs(&rdp->cblist);
|
||||
|
||||
if (!rcu_nocb_flush_bypass(rdp, rhp, j, lazy)) {
|
||||
if (*was_alldone)
|
||||
trace_rcu_nocb_wake(rcu_state.name, rdp->cpu,
|
||||
TPS("FirstQ"));
|
||||
@@ -447,7 +500,12 @@ static bool rcu_nocb_try_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
rcu_advance_cbs_nowake(rdp->mynode, rdp);
|
||||
rdp->nocb_gp_adv_time = j;
|
||||
}
|
||||
rcu_nocb_unlock_irqrestore(rdp, flags);
|
||||
|
||||
// The flush succeeded and we moved CBs into the regular list.
|
||||
// Don't wait for the wake up timer as it may be too far ahead.
|
||||
// Wake up the GP thread now instead, if the cblist was empty.
|
||||
__call_rcu_nocb_wake(rdp, *was_alldone, flags);
|
||||
|
||||
return true; // Callback already enqueued.
|
||||
}
|
||||
|
||||
@@ -457,13 +515,24 @@ static bool rcu_nocb_try_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
ncbs = rcu_cblist_n_cbs(&rdp->nocb_bypass);
|
||||
rcu_segcblist_inc_len(&rdp->cblist); /* Must precede enqueue. */
|
||||
rcu_cblist_enqueue(&rdp->nocb_bypass, rhp);
|
||||
|
||||
if (lazy)
|
||||
WRITE_ONCE(rdp->lazy_len, rdp->lazy_len + 1);
|
||||
|
||||
if (!ncbs) {
|
||||
WRITE_ONCE(rdp->nocb_bypass_first, j);
|
||||
trace_rcu_nocb_wake(rcu_state.name, rdp->cpu, TPS("FirstBQ"));
|
||||
}
|
||||
rcu_nocb_bypass_unlock(rdp);
|
||||
smp_mb(); /* Order enqueue before wake. */
|
||||
if (ncbs) {
|
||||
// A wake up of the grace period kthread or timer adjustment
|
||||
// needs to be done only if:
|
||||
// 1. Bypass list was fully empty before (this is the first
|
||||
// bypass list entry), or:
|
||||
// 2. Both of these conditions are met:
|
||||
// a. The bypass list previously had only lazy CBs, and:
|
||||
// b. The new CB is non-lazy.
|
||||
if (ncbs && (!bypass_is_lazy || lazy)) {
|
||||
local_irq_restore(flags);
|
||||
} else {
|
||||
// No-CBs GP kthread might be indefinitely asleep, if so, wake.
|
||||
@@ -491,8 +560,10 @@ static void __call_rcu_nocb_wake(struct rcu_data *rdp, bool was_alldone,
|
||||
unsigned long flags)
|
||||
__releases(rdp->nocb_lock)
|
||||
{
|
||||
long bypass_len;
|
||||
unsigned long cur_gp_seq;
|
||||
unsigned long j;
|
||||
long lazy_len;
|
||||
long len;
|
||||
struct task_struct *t;
|
||||
|
||||
@@ -506,9 +577,16 @@ static void __call_rcu_nocb_wake(struct rcu_data *rdp, bool was_alldone,
|
||||
}
|
||||
// Need to actually to a wakeup.
|
||||
len = rcu_segcblist_n_cbs(&rdp->cblist);
|
||||
bypass_len = rcu_cblist_n_cbs(&rdp->nocb_bypass);
|
||||
lazy_len = READ_ONCE(rdp->lazy_len);
|
||||
if (was_alldone) {
|
||||
rdp->qlen_last_fqs_check = len;
|
||||
if (!irqs_disabled_flags(flags)) {
|
||||
// Only lazy CBs in bypass list
|
||||
if (lazy_len && bypass_len == lazy_len) {
|
||||
rcu_nocb_unlock_irqrestore(rdp, flags);
|
||||
wake_nocb_gp_defer(rdp, RCU_NOCB_WAKE_LAZY,
|
||||
TPS("WakeLazy"));
|
||||
} else if (!irqs_disabled_flags(flags)) {
|
||||
/* ... if queue was empty ... */
|
||||
rcu_nocb_unlock_irqrestore(rdp, flags);
|
||||
wake_nocb_gp(rdp, false);
|
||||
@@ -599,12 +677,12 @@ static void nocb_gp_sleep(struct rcu_data *my_rdp, int cpu)
|
||||
static void nocb_gp_wait(struct rcu_data *my_rdp)
|
||||
{
|
||||
bool bypass = false;
|
||||
long bypass_ncbs;
|
||||
int __maybe_unused cpu = my_rdp->cpu;
|
||||
unsigned long cur_gp_seq;
|
||||
unsigned long flags;
|
||||
bool gotcbs = false;
|
||||
unsigned long j = jiffies;
|
||||
bool lazy = false;
|
||||
bool needwait_gp = false; // This prevents actual uninitialized use.
|
||||
bool needwake;
|
||||
bool needwake_gp;
|
||||
@@ -634,24 +712,43 @@ static void nocb_gp_wait(struct rcu_data *my_rdp)
|
||||
* won't be ignored for long.
|
||||
*/
|
||||
list_for_each_entry(rdp, &my_rdp->nocb_head_rdp, nocb_entry_rdp) {
|
||||
long bypass_ncbs;
|
||||
bool flush_bypass = false;
|
||||
long lazy_ncbs;
|
||||
|
||||
trace_rcu_nocb_wake(rcu_state.name, rdp->cpu, TPS("Check"));
|
||||
rcu_nocb_lock_irqsave(rdp, flags);
|
||||
lockdep_assert_held(&rdp->nocb_lock);
|
||||
bypass_ncbs = rcu_cblist_n_cbs(&rdp->nocb_bypass);
|
||||
if (bypass_ncbs &&
|
||||
lazy_ncbs = READ_ONCE(rdp->lazy_len);
|
||||
|
||||
if (bypass_ncbs && (lazy_ncbs == bypass_ncbs) &&
|
||||
(time_after(j, READ_ONCE(rdp->nocb_bypass_first) + jiffies_till_flush) ||
|
||||
bypass_ncbs > 2 * qhimark)) {
|
||||
flush_bypass = true;
|
||||
} else if (bypass_ncbs && (lazy_ncbs != bypass_ncbs) &&
|
||||
(time_after(j, READ_ONCE(rdp->nocb_bypass_first) + 1) ||
|
||||
bypass_ncbs > 2 * qhimark)) {
|
||||
// Bypass full or old, so flush it.
|
||||
(void)rcu_nocb_try_flush_bypass(rdp, j);
|
||||
bypass_ncbs = rcu_cblist_n_cbs(&rdp->nocb_bypass);
|
||||
flush_bypass = true;
|
||||
} else if (!bypass_ncbs && rcu_segcblist_empty(&rdp->cblist)) {
|
||||
rcu_nocb_unlock_irqrestore(rdp, flags);
|
||||
continue; /* No callbacks here, try next. */
|
||||
}
|
||||
|
||||
if (flush_bypass) {
|
||||
// Bypass full or old, so flush it.
|
||||
(void)rcu_nocb_try_flush_bypass(rdp, j);
|
||||
bypass_ncbs = rcu_cblist_n_cbs(&rdp->nocb_bypass);
|
||||
lazy_ncbs = READ_ONCE(rdp->lazy_len);
|
||||
}
|
||||
|
||||
if (bypass_ncbs) {
|
||||
trace_rcu_nocb_wake(rcu_state.name, rdp->cpu,
|
||||
TPS("Bypass"));
|
||||
bypass = true;
|
||||
bypass_ncbs == lazy_ncbs ? TPS("Lazy") : TPS("Bypass"));
|
||||
if (bypass_ncbs == lazy_ncbs)
|
||||
lazy = true;
|
||||
else
|
||||
bypass = true;
|
||||
}
|
||||
rnp = rdp->mynode;
|
||||
|
||||
@@ -699,12 +796,20 @@ static void nocb_gp_wait(struct rcu_data *my_rdp)
|
||||
my_rdp->nocb_gp_gp = needwait_gp;
|
||||
my_rdp->nocb_gp_seq = needwait_gp ? wait_gp_seq : 0;
|
||||
|
||||
if (bypass && !rcu_nocb_poll) {
|
||||
// At least one child with non-empty ->nocb_bypass, so set
|
||||
// timer in order to avoid stranding its callbacks.
|
||||
wake_nocb_gp_defer(my_rdp, RCU_NOCB_WAKE_BYPASS,
|
||||
TPS("WakeBypassIsDeferred"));
|
||||
// At least one child with non-empty ->nocb_bypass, so set
|
||||
// timer in order to avoid stranding its callbacks.
|
||||
if (!rcu_nocb_poll) {
|
||||
// If bypass list only has lazy CBs. Add a deferred lazy wake up.
|
||||
if (lazy && !bypass) {
|
||||
wake_nocb_gp_defer(my_rdp, RCU_NOCB_WAKE_LAZY,
|
||||
TPS("WakeLazyIsDeferred"));
|
||||
// Otherwise add a deferred bypass wake up.
|
||||
} else if (bypass) {
|
||||
wake_nocb_gp_defer(my_rdp, RCU_NOCB_WAKE_BYPASS,
|
||||
TPS("WakeBypassIsDeferred"));
|
||||
}
|
||||
}
|
||||
|
||||
if (rcu_nocb_poll) {
|
||||
/* Polling, so trace if first poll in the series. */
|
||||
if (gotcbs)
|
||||
@@ -1030,7 +1135,7 @@ static long rcu_nocb_rdp_deoffload(void *arg)
|
||||
* return false, which means that future calls to rcu_nocb_try_bypass()
|
||||
* will refuse to put anything into the bypass.
|
||||
*/
|
||||
WARN_ON_ONCE(!rcu_nocb_flush_bypass(rdp, NULL, jiffies));
|
||||
WARN_ON_ONCE(!rcu_nocb_flush_bypass(rdp, NULL, jiffies, false));
|
||||
/*
|
||||
* Start with invoking rcu_core() early. This way if the current thread
|
||||
* happens to preempt an ongoing call to rcu_core() in the middle,
|
||||
@@ -1207,47 +1312,87 @@ int rcu_nocb_cpu_offload(int cpu)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcu_nocb_cpu_offload);
|
||||
|
||||
static unsigned long
|
||||
lazy_rcu_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
|
||||
{
|
||||
int cpu;
|
||||
unsigned long count = 0;
|
||||
|
||||
/* Snapshot count of all CPUs */
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu);
|
||||
|
||||
count += READ_ONCE(rdp->lazy_len);
|
||||
}
|
||||
|
||||
return count ? count : SHRINK_EMPTY;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
lazy_rcu_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
|
||||
{
|
||||
int cpu;
|
||||
unsigned long flags;
|
||||
unsigned long count = 0;
|
||||
|
||||
/* Snapshot count of all CPUs */
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu);
|
||||
int _count = READ_ONCE(rdp->lazy_len);
|
||||
|
||||
if (_count == 0)
|
||||
continue;
|
||||
rcu_nocb_lock_irqsave(rdp, flags);
|
||||
WRITE_ONCE(rdp->lazy_len, 0);
|
||||
rcu_nocb_unlock_irqrestore(rdp, flags);
|
||||
wake_nocb_gp(rdp, false);
|
||||
sc->nr_to_scan -= _count;
|
||||
count += _count;
|
||||
if (sc->nr_to_scan <= 0)
|
||||
break;
|
||||
}
|
||||
return count ? count : SHRINK_STOP;
|
||||
}
|
||||
|
||||
static struct shrinker lazy_rcu_shrinker = {
|
||||
.count_objects = lazy_rcu_shrink_count,
|
||||
.scan_objects = lazy_rcu_shrink_scan,
|
||||
.batch = 0,
|
||||
.seeks = DEFAULT_SEEKS,
|
||||
};
|
||||
|
||||
void __init rcu_init_nohz(void)
|
||||
{
|
||||
int cpu;
|
||||
bool need_rcu_nocb_mask = false;
|
||||
bool offload_all = false;
|
||||
struct rcu_data *rdp;
|
||||
|
||||
#if defined(CONFIG_RCU_NOCB_CPU_DEFAULT_ALL)
|
||||
if (!rcu_state.nocb_is_setup) {
|
||||
need_rcu_nocb_mask = true;
|
||||
offload_all = true;
|
||||
}
|
||||
#endif /* #if defined(CONFIG_RCU_NOCB_CPU_DEFAULT_ALL) */
|
||||
const struct cpumask *cpumask = NULL;
|
||||
|
||||
#if defined(CONFIG_NO_HZ_FULL)
|
||||
if (tick_nohz_full_running && !cpumask_empty(tick_nohz_full_mask)) {
|
||||
need_rcu_nocb_mask = true;
|
||||
offload_all = false; /* NO_HZ_FULL has its own mask. */
|
||||
}
|
||||
#endif /* #if defined(CONFIG_NO_HZ_FULL) */
|
||||
if (tick_nohz_full_running && !cpumask_empty(tick_nohz_full_mask))
|
||||
cpumask = tick_nohz_full_mask;
|
||||
#endif
|
||||
|
||||
if (need_rcu_nocb_mask) {
|
||||
if (IS_ENABLED(CONFIG_RCU_NOCB_CPU_DEFAULT_ALL) &&
|
||||
!rcu_state.nocb_is_setup && !cpumask)
|
||||
cpumask = cpu_possible_mask;
|
||||
|
||||
if (cpumask) {
|
||||
if (!cpumask_available(rcu_nocb_mask)) {
|
||||
if (!zalloc_cpumask_var(&rcu_nocb_mask, GFP_KERNEL)) {
|
||||
pr_info("rcu_nocb_mask allocation failed, callback offloading disabled.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cpumask_or(rcu_nocb_mask, rcu_nocb_mask, cpumask);
|
||||
rcu_state.nocb_is_setup = true;
|
||||
}
|
||||
|
||||
if (!rcu_state.nocb_is_setup)
|
||||
return;
|
||||
|
||||
#if defined(CONFIG_NO_HZ_FULL)
|
||||
if (tick_nohz_full_running)
|
||||
cpumask_or(rcu_nocb_mask, rcu_nocb_mask, tick_nohz_full_mask);
|
||||
#endif /* #if defined(CONFIG_NO_HZ_FULL) */
|
||||
|
||||
if (offload_all)
|
||||
cpumask_setall(rcu_nocb_mask);
|
||||
if (register_shrinker(&lazy_rcu_shrinker, "rcu-lazy"))
|
||||
pr_err("Failed to register lazy_rcu shrinker!\n");
|
||||
|
||||
if (!cpumask_subset(rcu_nocb_mask, cpu_possible_mask)) {
|
||||
pr_info("\tNote: kernel parameter 'rcu_nocbs=', 'nohz_full', or 'isolcpus=' contains nonexistent CPUs.\n");
|
||||
@@ -1284,6 +1429,7 @@ static void __init rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp)
|
||||
raw_spin_lock_init(&rdp->nocb_gp_lock);
|
||||
timer_setup(&rdp->nocb_timer, do_nocb_deferred_wakeup_timer, 0);
|
||||
rcu_cblist_init(&rdp->nocb_bypass);
|
||||
WRITE_ONCE(rdp->lazy_len, 0);
|
||||
mutex_init(&rdp->nocb_gp_kthread_mutex);
|
||||
}
|
||||
|
||||
@@ -1564,14 +1710,19 @@ static void rcu_init_one_nocb(struct rcu_node *rnp)
|
||||
{
|
||||
}
|
||||
|
||||
static bool wake_nocb_gp(struct rcu_data *rdp, bool force)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool rcu_nocb_flush_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
unsigned long j)
|
||||
unsigned long j, bool lazy)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool rcu_nocb_try_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
bool *was_alldone, unsigned long flags)
|
||||
bool *was_alldone, unsigned long flags, bool lazy)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1221,11 +1221,13 @@ static void rcu_spawn_one_boost_kthread(struct rcu_node *rnp)
|
||||
* We don't include outgoingcpu in the affinity set, use -1 if there is
|
||||
* no outgoing CPU. If there are no CPUs left in the affinity set,
|
||||
* this function allows the kthread to execute on any CPU.
|
||||
*
|
||||
* Any future concurrent calls are serialized via ->boost_kthread_mutex.
|
||||
*/
|
||||
static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu)
|
||||
{
|
||||
struct task_struct *t = rnp->boost_kthread_task;
|
||||
unsigned long mask = rcu_rnp_online_cpus(rnp);
|
||||
unsigned long mask;
|
||||
cpumask_var_t cm;
|
||||
int cpu;
|
||||
|
||||
@@ -1234,6 +1236,7 @@ static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu)
|
||||
if (!zalloc_cpumask_var(&cm, GFP_KERNEL))
|
||||
return;
|
||||
mutex_lock(&rnp->boost_kthread_mutex);
|
||||
mask = rcu_rnp_online_cpus(rnp);
|
||||
for_each_leaf_node_possible_cpu(rnp, cpu)
|
||||
if ((mask & leaf_node_cpu_bit(rnp, cpu)) &&
|
||||
cpu != outgoingcpu)
|
||||
|
||||
+1
-1
@@ -1775,7 +1775,7 @@ bool queue_rcu_work(struct workqueue_struct *wq, struct rcu_work *rwork)
|
||||
|
||||
if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) {
|
||||
rwork->wq = wq;
|
||||
call_rcu(&rwork->rcu, rcu_work_rcufn);
|
||||
call_rcu_hurry(&rwork->rcu, rcu_work_rcufn);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -125,7 +125,7 @@ config KCSAN_SKIP_WATCH
|
||||
default 4000
|
||||
help
|
||||
The number of per-CPU memory operations to skip, before another
|
||||
watchpoint is set up, i.e. one in KCSAN_WATCH_SKIP per-CPU
|
||||
watchpoint is set up, i.e. one in KCSAN_SKIP_WATCH per-CPU
|
||||
memory operations are used to set up a watchpoint. A smaller value
|
||||
results in more aggressive race detection, whereas a larger value
|
||||
improves system performance at the cost of missing some races.
|
||||
@@ -135,8 +135,8 @@ config KCSAN_SKIP_WATCH_RANDOMIZE
|
||||
default y
|
||||
help
|
||||
If instruction skip count should be randomized, where the maximum is
|
||||
KCSAN_WATCH_SKIP. If false, the chosen value is always
|
||||
KCSAN_WATCH_SKIP.
|
||||
KCSAN_SKIP_WATCH. If false, the chosen value is always
|
||||
KCSAN_SKIP_WATCH.
|
||||
|
||||
config KCSAN_INTERRUPT_WATCHER
|
||||
bool "Interruptible watchers" if !KCSAN_STRICT
|
||||
|
||||
@@ -21,11 +21,7 @@ static void is_signed_type_test(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, is_signed_type(bool), false);
|
||||
KUNIT_EXPECT_EQ(test, is_signed_type(signed char), true);
|
||||
KUNIT_EXPECT_EQ(test, is_signed_type(unsigned char), false);
|
||||
#ifdef __CHAR_UNSIGNED__
|
||||
KUNIT_EXPECT_EQ(test, is_signed_type(char), false);
|
||||
#else
|
||||
KUNIT_EXPECT_EQ(test, is_signed_type(char), true);
|
||||
#endif
|
||||
KUNIT_EXPECT_EQ(test, is_signed_type(int), true);
|
||||
KUNIT_EXPECT_EQ(test, is_signed_type(unsigned int), false);
|
||||
KUNIT_EXPECT_EQ(test, is_signed_type(long), true);
|
||||
|
||||
@@ -230,7 +230,8 @@ static void __percpu_ref_switch_to_atomic(struct percpu_ref *ref,
|
||||
percpu_ref_noop_confirm_switch;
|
||||
|
||||
percpu_ref_get(ref); /* put after confirmation */
|
||||
call_rcu(&ref->data->rcu, percpu_ref_switch_to_atomic_rcu);
|
||||
call_rcu_hurry(&ref->data->rcu,
|
||||
percpu_ref_switch_to_atomic_rcu);
|
||||
}
|
||||
|
||||
static void __percpu_ref_switch_to_percpu(struct percpu_ref *ref)
|
||||
|
||||
@@ -179,18 +179,6 @@ test_number(void)
|
||||
* behaviour.
|
||||
*/
|
||||
test("00|0|0|0|0", "%.2d|%.1d|%.0d|%.*d|%1.0d", 0, 0, 0, 0, 0, 0);
|
||||
#ifndef __CHAR_UNSIGNED__
|
||||
{
|
||||
/*
|
||||
* Passing a 'char' to a %02x specifier doesn't do
|
||||
* what was presumably the intention when char is
|
||||
* signed and the value is negative. One must either &
|
||||
* with 0xff or cast to u8.
|
||||
*/
|
||||
char val = -16;
|
||||
test("0xfffffff0|0xf0|0xf0", "%#02x|%#02x|%#02x", val, val & 0xff, (u8)val);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __init
|
||||
|
||||
+1
-1
@@ -174,7 +174,7 @@ void dst_release(struct dst_entry *dst)
|
||||
net_warn_ratelimited("%s: dst:%p refcnt:%d\n",
|
||||
__func__, dst, newrefcnt);
|
||||
if (!newrefcnt)
|
||||
call_rcu(&dst->rcu_head, dst_destroy_rcu);
|
||||
call_rcu_hurry(&dst->rcu_head, dst_destroy_rcu);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(dst_release);
|
||||
|
||||
+10
-9
@@ -234,13 +234,20 @@ static void inet_free_ifa(struct in_ifaddr *ifa)
|
||||
call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
|
||||
}
|
||||
|
||||
static void in_dev_free_rcu(struct rcu_head *head)
|
||||
{
|
||||
struct in_device *idev = container_of(head, struct in_device, rcu_head);
|
||||
|
||||
kfree(rcu_dereference_protected(idev->mc_hash, 1));
|
||||
kfree(idev);
|
||||
}
|
||||
|
||||
void in_dev_finish_destroy(struct in_device *idev)
|
||||
{
|
||||
struct net_device *dev = idev->dev;
|
||||
|
||||
WARN_ON(idev->ifa_list);
|
||||
WARN_ON(idev->mc_list);
|
||||
kfree(rcu_dereference_protected(idev->mc_hash, 1));
|
||||
#ifdef NET_REFCNT_DEBUG
|
||||
pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
|
||||
#endif
|
||||
@@ -248,7 +255,7 @@ void in_dev_finish_destroy(struct in_device *idev)
|
||||
if (!idev->dead)
|
||||
pr_err("Freeing alive in_device %p\n", idev);
|
||||
else
|
||||
kfree(idev);
|
||||
call_rcu(&idev->rcu_head, in_dev_free_rcu);
|
||||
}
|
||||
EXPORT_SYMBOL(in_dev_finish_destroy);
|
||||
|
||||
@@ -298,12 +305,6 @@ out_kfree:
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void in_dev_rcu_put(struct rcu_head *head)
|
||||
{
|
||||
struct in_device *idev = container_of(head, struct in_device, rcu_head);
|
||||
in_dev_put(idev);
|
||||
}
|
||||
|
||||
static void inetdev_destroy(struct in_device *in_dev)
|
||||
{
|
||||
struct net_device *dev;
|
||||
@@ -328,7 +329,7 @@ static void inetdev_destroy(struct in_device *in_dev)
|
||||
neigh_parms_release(&arp_tbl, in_dev->arp_parms);
|
||||
arp_ifdown(dev);
|
||||
|
||||
call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
|
||||
in_dev_put(in_dev);
|
||||
}
|
||||
|
||||
int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
|
||||
|
||||
@@ -464,9 +464,10 @@ to address dependencies, since the address of a location accessed
|
||||
through a pointer will depend on the value read earlier from that
|
||||
pointer.
|
||||
|
||||
Finally, a read event and another memory access event are linked by a
|
||||
control dependency if the value obtained by the read affects whether
|
||||
the second event is executed at all. Simple example:
|
||||
Finally, a read event X and a write event Y are linked by a control
|
||||
dependency if Y syntactically lies within an arm of an if statement and
|
||||
X affects the evaluation of the if condition via a data or address
|
||||
dependency (or similarly for a switch statement). Simple example:
|
||||
|
||||
int x, y;
|
||||
|
||||
|
||||
@@ -999,6 +999,16 @@ static const char *uaccess_safe_builtin[] = {
|
||||
"__tsan_read_write4",
|
||||
"__tsan_read_write8",
|
||||
"__tsan_read_write16",
|
||||
"__tsan_volatile_read1",
|
||||
"__tsan_volatile_read2",
|
||||
"__tsan_volatile_read4",
|
||||
"__tsan_volatile_read8",
|
||||
"__tsan_volatile_read16",
|
||||
"__tsan_volatile_write1",
|
||||
"__tsan_volatile_write2",
|
||||
"__tsan_volatile_write4",
|
||||
"__tsan_volatile_write8",
|
||||
"__tsan_volatile_write16",
|
||||
"__tsan_atomic8_load",
|
||||
"__tsan_atomic16_load",
|
||||
"__tsan_atomic32_load",
|
||||
|
||||
@@ -95,6 +95,7 @@ all: run
|
||||
sysroot: sysroot/$(ARCH)/include
|
||||
|
||||
sysroot/$(ARCH)/include:
|
||||
$(Q)rm -rf sysroot/$(ARCH) sysroot/sysroot
|
||||
$(QUIET_MKDIR)mkdir -p sysroot
|
||||
$(Q)$(MAKE) -C ../../../include/nolibc ARCH=$(ARCH) OUTPUT=$(CURDIR)/sysroot/ headers_standalone
|
||||
$(Q)mv sysroot/sysroot sysroot/$(ARCH)
|
||||
@@ -133,3 +134,5 @@ clean:
|
||||
$(Q)rm -rf initramfs
|
||||
$(call QUIET_CLEAN, run.out)
|
||||
$(Q)rm -rf run.out
|
||||
|
||||
.PHONY: sysroot/$(ARCH)/include
|
||||
|
||||
@@ -565,6 +565,13 @@ int run_stdlib(int min, int max)
|
||||
CASE_TEST(strchr_foobar_z); EXPECT_STRZR(1, strchr("foobar", 'z')); break;
|
||||
CASE_TEST(strrchr_foobar_o); EXPECT_STREQ(1, strrchr("foobar", 'o'), "obar"); break;
|
||||
CASE_TEST(strrchr_foobar_z); EXPECT_STRZR(1, strrchr("foobar", 'z')); break;
|
||||
CASE_TEST(memcmp_20_20); EXPECT_EQ(1, memcmp("aaa\x20", "aaa\x20", 4), 0); break;
|
||||
CASE_TEST(memcmp_20_60); EXPECT_LT(1, memcmp("aaa\x20", "aaa\x60", 4), 0); break;
|
||||
CASE_TEST(memcmp_60_20); EXPECT_GT(1, memcmp("aaa\x60", "aaa\x20", 4), 0); break;
|
||||
CASE_TEST(memcmp_20_e0); EXPECT_LT(1, memcmp("aaa\x20", "aaa\xe0", 4), 0); break;
|
||||
CASE_TEST(memcmp_e0_20); EXPECT_GT(1, memcmp("aaa\xe0", "aaa\x20", 4), 0); break;
|
||||
CASE_TEST(memcmp_80_e0); EXPECT_LT(1, memcmp("aaa\x80", "aaa\xe0", 4), 0); break;
|
||||
CASE_TEST(memcmp_e0_80); EXPECT_GT(1, memcmp("aaa\xe0", "aaa\x80", 4), 0); break;
|
||||
case __LINE__:
|
||||
return ret; /* must be last */
|
||||
/* note: do not set any defaults so as to permit holes above */
|
||||
|
||||
@@ -30,9 +30,8 @@ else
|
||||
fi
|
||||
scenarios="`echo $scenariosarg | sed -e "s/\<CFLIST\>/$defaultconfigs/g"`"
|
||||
|
||||
T=/tmp/config2latex.sh.$$
|
||||
T=`mktemp -d /tmp/config2latex.sh.XXXXXX`
|
||||
trap 'rm -rf $T' 0
|
||||
mkdir $T
|
||||
|
||||
cat << '---EOF---' >> $T/p.awk
|
||||
END {
|
||||
|
||||
@@ -29,9 +29,8 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
T=${TMPDIR-/tmp}/config_override.sh.$$
|
||||
T="`mktemp -d ${TMPDIR-/tmp}/config_override.sh.XXXXXX`"
|
||||
trap 'rm -rf $T' 0
|
||||
mkdir $T
|
||||
|
||||
sed < $override -e 's/^/grep -v "/' -e 's/=.*$/="/' |
|
||||
awk '
|
||||
|
||||
@@ -7,9 +7,8 @@
|
||||
#
|
||||
# Authors: Paul E. McKenney <paulmck@linux.ibm.com>
|
||||
|
||||
T=${TMPDIR-/tmp}/abat-chk-config.sh.$$
|
||||
T="`mktemp -d ${TMPDIR-/tmp}/configcheck.sh.XXXXXX`"
|
||||
trap 'rm -rf $T' 0
|
||||
mkdir $T
|
||||
|
||||
cat $1 > $T/.config
|
||||
|
||||
|
||||
@@ -15,9 +15,8 @@
|
||||
#
|
||||
# Authors: Paul E. McKenney <paulmck@linux.ibm.com>
|
||||
|
||||
T=${TMPDIR-/tmp}/configinit.sh.$$
|
||||
T="`mktemp -d ${TMPDIR-/tmp}/configinit.sh.XXXXXX`"
|
||||
trap 'rm -rf $T' 0
|
||||
mkdir $T
|
||||
|
||||
# Capture config spec file.
|
||||
|
||||
|
||||
@@ -12,9 +12,8 @@
|
||||
scriptname=$0
|
||||
args="$*"
|
||||
|
||||
T=${TMPDIR-/tmp}/kvm-again.sh.$$
|
||||
T="`mktemp -d ${TMPDIR-/tmp}/kvm-again.sh.XXXXXX`"
|
||||
trap 'rm -rf $T' 0
|
||||
mkdir $T
|
||||
|
||||
if ! test -d tools/testing/selftests/rcutorture/bin
|
||||
then
|
||||
@@ -51,27 +50,56 @@ RCUTORTURE="`pwd`/tools/testing/selftests/rcutorture"; export RCUTORTURE
|
||||
PATH=${RCUTORTURE}/bin:$PATH; export PATH
|
||||
. functions.sh
|
||||
|
||||
bootargs=
|
||||
dryrun=
|
||||
dur=
|
||||
default_link="cp -R"
|
||||
rundir="`pwd`/tools/testing/selftests/rcutorture/res/`date +%Y.%m.%d-%H.%M.%S-again`"
|
||||
resdir="`pwd`/tools/testing/selftests/rcutorture/res"
|
||||
rundir="$resdir/`date +%Y.%m.%d-%H.%M.%S-again`"
|
||||
got_datestamp=
|
||||
got_rundir=
|
||||
|
||||
startdate="`date`"
|
||||
starttime="`get_starttime`"
|
||||
|
||||
usage () {
|
||||
echo "Usage: $scriptname $oldrun [ arguments ]:"
|
||||
echo " --bootargs kernel-boot-arguments"
|
||||
echo " --datestamp string"
|
||||
echo " --dryrun"
|
||||
echo " --duration minutes | <seconds>s | <hours>h | <days>d"
|
||||
echo " --link hard|soft|copy"
|
||||
echo " --remote"
|
||||
echo " --rundir /new/res/path"
|
||||
echo "Command line: $scriptname $args"
|
||||
exit 1
|
||||
}
|
||||
|
||||
while test $# -gt 0
|
||||
do
|
||||
case "$1" in
|
||||
--bootargs|--bootarg)
|
||||
checkarg --bootargs "(list of kernel boot arguments)" "$#" "$2" '.*' '^--'
|
||||
bootargs="$bootargs $2"
|
||||
shift
|
||||
;;
|
||||
--datestamp)
|
||||
checkarg --datestamp "(relative pathname)" "$#" "$2" '^[a-zA-Z0-9._/-]*$' '^--'
|
||||
if test -n "$got_rundir" || test -n "$got_datestamp"
|
||||
then
|
||||
echo Only one of --datestamp or --rundir may be specified
|
||||
usage
|
||||
fi
|
||||
got_datestamp=y
|
||||
ds=$2
|
||||
rundir="$resdir/$ds"
|
||||
if test -e "$rundir"
|
||||
then
|
||||
echo "--datestamp $2: Already exists."
|
||||
usage
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--dryrun)
|
||||
dryrun=1
|
||||
;;
|
||||
@@ -113,6 +141,12 @@ do
|
||||
;;
|
||||
--rundir)
|
||||
checkarg --rundir "(absolute pathname)" "$#" "$2" '^/' '^error'
|
||||
if test -n "$got_rundir" || test -n "$got_datestamp"
|
||||
then
|
||||
echo Only one of --datestamp or --rundir may be specified
|
||||
usage
|
||||
fi
|
||||
got_rundir=y
|
||||
rundir=$2
|
||||
if test -e "$rundir"
|
||||
then
|
||||
@@ -122,8 +156,11 @@ do
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo Unknown argument $1
|
||||
usage
|
||||
if test -n "$1"
|
||||
then
|
||||
echo Unknown argument $1
|
||||
usage
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
@@ -156,7 +193,7 @@ do
|
||||
qemu_cmd_dir="`dirname "$i"`"
|
||||
kernel_dir="`echo $qemu_cmd_dir | sed -e 's/\.[0-9]\+$//'`"
|
||||
jitter_dir="`dirname "$kernel_dir"`"
|
||||
kvm-transform.sh "$kernel_dir/bzImage" "$qemu_cmd_dir/console.log" "$jitter_dir" $dur < $T/qemu-cmd > $i
|
||||
kvm-transform.sh "$kernel_dir/bzImage" "$qemu_cmd_dir/console.log" "$jitter_dir" $dur "$bootargs" < $T/qemu-cmd > $i
|
||||
if test -n "$arg_remote"
|
||||
then
|
||||
echo "# TORTURE_KCONFIG_GDB_ARG=''" >> $i
|
||||
|
||||
@@ -7,9 +7,8 @@
|
||||
#
|
||||
# Usage: kvm-assign-cpus.sh /path/to/sysfs
|
||||
|
||||
T=/tmp/kvm-assign-cpus.sh.$$
|
||||
T="`mktemp -d ${TMPDIR-/tmp}/kvm-assign-cpus.sh.XXXXXX`"
|
||||
trap 'rm -rf $T' 0 2
|
||||
mkdir $T
|
||||
|
||||
sysfsdir=${1-/sys/devices/system/node}
|
||||
if ! cd "$sysfsdir" > $T/msg 2>&1
|
||||
|
||||
@@ -23,9 +23,8 @@ then
|
||||
fi
|
||||
resdir=${2}
|
||||
|
||||
T=${TMPDIR-/tmp}/test-linux.sh.$$
|
||||
T="`mktemp -d ${TMPDIR-/tmp}/kvm-build.sh.XXXXXX`"
|
||||
trap 'rm -rf $T' 0
|
||||
mkdir $T
|
||||
|
||||
cp ${config_template} $T/config
|
||||
cat << ___EOF___ >> $T/config
|
||||
|
||||
@@ -18,9 +18,8 @@ then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
T=${TMPDIR-/tmp}/kvm-end-run-stats.sh.$$
|
||||
T="`mktemp -d ${TMPDIR-/tmp}/kvm-end-run-stats.sh.XXXXXX`"
|
||||
trap 'rm -rf $T' 0
|
||||
mkdir $T
|
||||
|
||||
RCUTORTURE="`pwd`/tools/testing/selftests/rcutorture"; export RCUTORTURE
|
||||
PATH=${RCUTORTURE}/bin:$PATH; export PATH
|
||||
|
||||
@@ -30,7 +30,7 @@ do
|
||||
resdir=`echo $i | sed -e 's,/$,,' -e 's,/[^/]*$,,'`
|
||||
head -1 $resdir/log
|
||||
fi
|
||||
TORTURE_SUITE="`cat $i/../torture_suite`"
|
||||
TORTURE_SUITE="`cat $i/../torture_suite`" ; export TORTURE_SUITE
|
||||
configfile=`echo $i | sed -e 's,^.*/,,'`
|
||||
rm -f $i/console.log.*.diags
|
||||
case "${TORTURE_SUITE}" in
|
||||
|
||||
@@ -34,19 +34,18 @@ fi
|
||||
shift
|
||||
|
||||
# Pathnames:
|
||||
# T: /tmp/kvm-remote.sh.$$
|
||||
# resdir: /tmp/kvm-remote.sh.$$/res
|
||||
# rundir: /tmp/kvm-remote.sh.$$/res/$ds ("-remote" suffix)
|
||||
# T: /tmp/kvm-remote.sh.NNNNNN where "NNNNNN" is set by mktemp
|
||||
# resdir: /tmp/kvm-remote.sh.NNNNNN/res
|
||||
# rundir: /tmp/kvm-remote.sh.NNNNNN/res/$ds ("-remote" suffix)
|
||||
# oldrun: `pwd`/tools/testing/.../res/$otherds
|
||||
#
|
||||
# Pathname segments:
|
||||
# TD: kvm-remote.sh.$$
|
||||
# TD: kvm-remote.sh.NNNNNN
|
||||
# ds: yyyy.mm.dd-hh.mm.ss-remote
|
||||
|
||||
TD=kvm-remote.sh.$$
|
||||
T=${TMPDIR-/tmp}/$TD
|
||||
T="`mktemp -d ${TMPDIR-/tmp}/kvm-remote.sh.XXXXXX`"
|
||||
trap 'rm -rf $T' 0
|
||||
mkdir $T
|
||||
TD="`basename "$T"`"
|
||||
|
||||
resdir="$T/res"
|
||||
ds=`date +%Y.%m.%d-%H.%M.%S`-remote
|
||||
|
||||
@@ -13,9 +13,8 @@
|
||||
#
|
||||
# Authors: Paul E. McKenney <paulmck@kernel.org>
|
||||
|
||||
T=${TMPDIR-/tmp}/kvm-test-1-run-batch.sh.$$
|
||||
T="`mktemp -d ${TMPDIR-/tmp}/kvm-test-1-run-batch.sh.XXXXXX`"
|
||||
trap 'rm -rf $T' 0
|
||||
mkdir $T
|
||||
|
||||
echo ---- Running batch $*
|
||||
# Check arguments
|
||||
|
||||
@@ -17,9 +17,8 @@
|
||||
#
|
||||
# Authors: Paul E. McKenney <paulmck@kernel.org>
|
||||
|
||||
T=${TMPDIR-/tmp}/kvm-test-1-run-qemu.sh.$$
|
||||
T="`mktemp -d ${TMPDIR-/tmp}/kvm-test-1-run-qemu.sh.XXXXXX`"
|
||||
trap 'rm -rf $T' 0
|
||||
mkdir $T
|
||||
|
||||
resdir="$1"
|
||||
if ! test -d "$resdir"
|
||||
@@ -109,7 +108,7 @@ do
|
||||
if test $kruntime -lt $seconds
|
||||
then
|
||||
echo Completed in $kruntime vs. $seconds >> $resdir/Warnings 2>&1
|
||||
grep "^(qemu) qemu:" $resdir/kvm-test-1-run.sh.out >> $resdir/Warnings 2>&1
|
||||
grep "^(qemu) qemu:" $resdir/kvm-test-1-run*.sh.out >> $resdir/Warnings 2>&1
|
||||
killpid="`sed -n "s/^(qemu) qemu: terminating on signal [0-9]* from pid \([0-9]*\).*$/\1/p" $resdir/Warnings`"
|
||||
if test -n "$killpid"
|
||||
then
|
||||
|
||||
@@ -25,9 +25,8 @@
|
||||
#
|
||||
# Authors: Paul E. McKenney <paulmck@linux.ibm.com>
|
||||
|
||||
T=${TMPDIR-/tmp}/kvm-test-1-run.sh.$$
|
||||
T="`mktemp -d ${TMPDIR-/tmp}/kvm-test-1-run.sh.XXXXXX`"
|
||||
trap 'rm -rf $T' 0
|
||||
mkdir $T
|
||||
|
||||
. functions.sh
|
||||
. $CONFIGFRAG/ver_functions.sh
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
#
|
||||
# Transform a qemu-cmd file to allow reuse.
|
||||
#
|
||||
# Usage: kvm-transform.sh bzImage console.log jitter_dir [ seconds ] < qemu-cmd-in > qemu-cmd-out
|
||||
# Usage: kvm-transform.sh bzImage console.log jitter_dir seconds [ bootargs ] < qemu-cmd-in > qemu-cmd-out
|
||||
#
|
||||
# bzImage: Kernel and initrd from the same prior kvm.sh run.
|
||||
# console.log: File into which to place console output.
|
||||
# jitter_dir: Jitter directory for TORTURE_JITTER_START and
|
||||
# TORTURE_JITTER_STOP environment variables.
|
||||
# seconds: Run duaration for *.shutdown_secs module parameter.
|
||||
# bootargs: New kernel boot parameters. Beware of Robert Tables.
|
||||
#
|
||||
# The original qemu-cmd file is provided on standard input.
|
||||
# The transformed qemu-cmd file is on standard output.
|
||||
@@ -17,6 +21,9 @@
|
||||
#
|
||||
# Authors: Paul E. McKenney <paulmck@kernel.org>
|
||||
|
||||
T=`mktemp -d /tmp/kvm-transform.sh.XXXXXXXXXX`
|
||||
trap 'rm -rf $T' 0 2
|
||||
|
||||
image="$1"
|
||||
if test -z "$image"
|
||||
then
|
||||
@@ -41,9 +48,17 @@ then
|
||||
echo "Invalid duration, should be numeric in seconds: '$seconds'"
|
||||
exit 1
|
||||
fi
|
||||
bootargs="$5"
|
||||
|
||||
# Build awk program.
|
||||
echo "BEGIN {" > $T/bootarg.awk
|
||||
echo $bootargs | tr -s ' ' '\012' |
|
||||
awk -v dq='"' '/./ { print "\tbootarg[" NR "] = " dq $1 dq ";" }' >> $T/bootarg.awk
|
||||
echo $bootargs | tr -s ' ' '\012' | sed -e 's/=.*$//' |
|
||||
awk -v dq='"' '/./ { print "\tbootpar[" NR "] = " dq $1 dq ";" }' >> $T/bootarg.awk
|
||||
cat >> $T/bootarg.awk << '___EOF___'
|
||||
}
|
||||
|
||||
awk -v image="$image" -v consolelog="$consolelog" -v jitter_dir="$jitter_dir" \
|
||||
-v seconds="$seconds" '
|
||||
/^# seconds=/ {
|
||||
if (seconds == "")
|
||||
print $0;
|
||||
@@ -70,13 +85,7 @@ awk -v image="$image" -v consolelog="$consolelog" -v jitter_dir="$jitter_dir" \
|
||||
{
|
||||
line = "";
|
||||
for (i = 1; i <= NF; i++) {
|
||||
if ("" seconds != "" && $i ~ /\.shutdown_secs=[0-9]*$/) {
|
||||
sub(/[0-9]*$/, seconds, $i);
|
||||
if (line == "")
|
||||
line = $i;
|
||||
else
|
||||
line = line " " $i;
|
||||
} else if (line == "") {
|
||||
if (line == "") {
|
||||
line = $i;
|
||||
} else {
|
||||
line = line " " $i;
|
||||
@@ -87,7 +96,44 @@ awk -v image="$image" -v consolelog="$consolelog" -v jitter_dir="$jitter_dir" \
|
||||
} else if ($i == "-kernel") {
|
||||
i++;
|
||||
line = line " " image;
|
||||
} else if ($i == "-append") {
|
||||
for (i++; i <= NF; i++) {
|
||||
arg = $i;
|
||||
lq = "";
|
||||
rq = "";
|
||||
if ("" seconds != "" && $i ~ /\.shutdown_secs=[0-9]*$/)
|
||||
sub(/[0-9]*$/, seconds, arg);
|
||||
if (arg ~ /^"/) {
|
||||
lq = substr(arg, 1, 1);
|
||||
arg = substr(arg, 2);
|
||||
}
|
||||
if (arg ~ /"$/) {
|
||||
rq = substr(arg, length($i), 1);
|
||||
arg = substr(arg, 1, length($i) - 1);
|
||||
}
|
||||
par = arg;
|
||||
gsub(/=.*$/, "", par);
|
||||
j = 1;
|
||||
while (bootpar[j] != "") {
|
||||
if (bootpar[j] == par) {
|
||||
arg = "";
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
if (line == "")
|
||||
line = lq arg;
|
||||
else
|
||||
line = line " " lq arg;
|
||||
}
|
||||
for (j in bootarg)
|
||||
line = line " " bootarg[j];
|
||||
line = line rq;
|
||||
}
|
||||
}
|
||||
print line;
|
||||
}'
|
||||
}
|
||||
___EOF___
|
||||
|
||||
awk -v image="$image" -v consolelog="$consolelog" -v jitter_dir="$jitter_dir" \
|
||||
-v seconds="$seconds" -f $T/bootarg.awk
|
||||
|
||||
@@ -14,9 +14,8 @@
|
||||
scriptname=$0
|
||||
args="$*"
|
||||
|
||||
T=${TMPDIR-/tmp}/kvm.sh.$$
|
||||
T="`mktemp -d ${TMPDIR-/tmp}/kvm.sh.XXXXXX`"
|
||||
trap 'rm -rf $T' 0
|
||||
mkdir $T
|
||||
|
||||
cd `dirname $scriptname`/../../../../../
|
||||
|
||||
|
||||
@@ -15,9 +15,8 @@
|
||||
|
||||
F=$1
|
||||
title=$2
|
||||
T=${TMPDIR-/tmp}/parse-build.sh.$$
|
||||
T="`mktemp -d ${TMPDIR-/tmp}/parse-build.sh.XXXXXX`"
|
||||
trap 'rm -rf $T' 0
|
||||
mkdir $T
|
||||
|
||||
. functions.sh
|
||||
|
||||
|
||||
@@ -206,9 +206,8 @@ ds="`date +%Y.%m.%d-%H.%M.%S`-torture"
|
||||
startdate="`date`"
|
||||
starttime="`get_starttime`"
|
||||
|
||||
T=/tmp/torture.sh.$$
|
||||
T="`mktemp -d ${TMPDIR-/tmp}/torture.sh.XXXXXX`"
|
||||
trap 'rm -rf $T' 0 2
|
||||
mkdir $T
|
||||
|
||||
echo " --- " $scriptname $args | tee -a $T/log
|
||||
echo " --- Results directory: " $ds | tee -a $T/log
|
||||
@@ -278,6 +277,8 @@ function torture_one {
|
||||
then
|
||||
cat $T/$curflavor.out | tee -a $T/log
|
||||
echo retcode=$retcode | tee -a $T/log
|
||||
else
|
||||
echo $resdir > $T/last-resdir
|
||||
fi
|
||||
if test "$retcode" == 0
|
||||
then
|
||||
@@ -303,10 +304,12 @@ function torture_set {
|
||||
shift
|
||||
curflavor=$flavor
|
||||
torture_one "$@"
|
||||
mv $T/last-resdir $T/last-resdir-nodebug || :
|
||||
if test "$do_kasan" = "yes"
|
||||
then
|
||||
curflavor=${flavor}-kasan
|
||||
torture_one "$@" --kasan
|
||||
mv $T/last-resdir $T/last-resdir-kasan || :
|
||||
fi
|
||||
if test "$do_kcsan" = "yes"
|
||||
then
|
||||
@@ -317,6 +320,7 @@ function torture_set {
|
||||
cur_kcsan_kmake_args="$kcsan_kmake_args"
|
||||
fi
|
||||
torture_one "$@" --kconfig "CONFIG_DEBUG_LOCK_ALLOC=y CONFIG_PROVE_LOCKING=y" $kcsan_kmake_tag $cur_kcsan_kmake_args --kcsan
|
||||
mv $T/last-resdir $T/last-resdir-kcsan || :
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -326,20 +330,34 @@ then
|
||||
echo " --- allmodconfig:" Start `date` | tee -a $T/log
|
||||
amcdir="tools/testing/selftests/rcutorture/res/$ds/allmodconfig"
|
||||
mkdir -p "$amcdir"
|
||||
echo " --- make clean" > "$amcdir/Make.out" 2>&1
|
||||
echo " --- make clean" | tee $amcdir/log > "$amcdir/Make.out" 2>&1
|
||||
make -j$MAKE_ALLOTED_CPUS clean >> "$amcdir/Make.out" 2>&1
|
||||
echo " --- make allmodconfig" >> "$amcdir/Make.out" 2>&1
|
||||
cp .config $amcdir
|
||||
make -j$MAKE_ALLOTED_CPUS allmodconfig >> "$amcdir/Make.out" 2>&1
|
||||
echo " --- make " >> "$amcdir/Make.out" 2>&1
|
||||
make -j$MAKE_ALLOTED_CPUS >> "$amcdir/Make.out" 2>&1
|
||||
retcode="$?"
|
||||
echo $retcode > "$amcdir/Make.exitcode"
|
||||
if test "$retcode" == 0
|
||||
retcode=$?
|
||||
buildphase='"make clean"'
|
||||
if test "$retcode" -eq 0
|
||||
then
|
||||
echo " --- make allmodconfig" | tee -a $amcdir/log >> "$amcdir/Make.out" 2>&1
|
||||
cp .config $amcdir
|
||||
make -j$MAKE_ALLOTED_CPUS allmodconfig >> "$amcdir/Make.out" 2>&1
|
||||
retcode=$?
|
||||
buildphase='"make allmodconfig"'
|
||||
fi
|
||||
if test "$retcode" -eq 0
|
||||
then
|
||||
echo " --- make " | tee -a $amcdir/log >> "$amcdir/Make.out" 2>&1
|
||||
make -j$MAKE_ALLOTED_CPUS >> "$amcdir/Make.out" 2>&1
|
||||
retcode="$?"
|
||||
echo $retcode > "$amcdir/Make.exitcode"
|
||||
buildphase='"make"'
|
||||
fi
|
||||
if test "$retcode" -eq 0
|
||||
then
|
||||
echo "allmodconfig($retcode)" $amcdir >> $T/successes
|
||||
echo Success >> $amcdir/log
|
||||
else
|
||||
echo "allmodconfig($retcode)" $amcdir >> $T/failures
|
||||
echo " --- allmodconfig Test summary:" >> $amcdir/log
|
||||
echo " --- Summary: Exit code $retcode from $buildphase, see Make.out" >> $amcdir/log
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -379,11 +397,48 @@ then
|
||||
else
|
||||
primlist=
|
||||
fi
|
||||
firsttime=1
|
||||
do_kasan_save="$do_kasan"
|
||||
do_kcsan_save="$do_kcsan"
|
||||
for prim in $primlist
|
||||
do
|
||||
torture_bootargs="refscale.scale_type="$prim" refscale.nreaders=$HALF_ALLOTED_CPUS refscale.loops=10000 refscale.holdoff=20 torture.disable_onoff_at_boot"
|
||||
torture_set "refscale-$prim" tools/testing/selftests/rcutorture/bin/kvm.sh --torture refscale --allcpus --duration 5 --kconfig "CONFIG_TASKS_TRACE_RCU=y CONFIG_NR_CPUS=$HALF_ALLOTED_CPUS" --bootargs "verbose_batched=$VERBOSE_BATCH_CPUS torture.verbose_sleep_frequency=8 torture.verbose_sleep_duration=$VERBOSE_BATCH_CPUS" --trust-make
|
||||
if test -n "$firsttime"
|
||||
then
|
||||
torture_bootargs="refscale.scale_type="$prim" refscale.nreaders=$HALF_ALLOTED_CPUS refscale.loops=10000 refscale.holdoff=20 torture.disable_onoff_at_boot"
|
||||
torture_set "refscale-$prim" tools/testing/selftests/rcutorture/bin/kvm.sh --torture refscale --allcpus --duration 5 --kconfig "CONFIG_TASKS_TRACE_RCU=y CONFIG_NR_CPUS=$HALF_ALLOTED_CPUS" --bootargs "verbose_batched=$VERBOSE_BATCH_CPUS torture.verbose_sleep_frequency=8 torture.verbose_sleep_duration=$VERBOSE_BATCH_CPUS" --trust-make
|
||||
mv $T/last-resdir-nodebug $T/first-resdir-nodebug || :
|
||||
if test -f "$T/last-resdir-kasan"
|
||||
then
|
||||
mv $T/last-resdir-kasan $T/first-resdir-kasan || :
|
||||
fi
|
||||
if test -f "$T/last-resdir-kcsan"
|
||||
then
|
||||
mv $T/last-resdir-kcsan $T/first-resdir-kcsan || :
|
||||
fi
|
||||
firsttime=
|
||||
do_kasan=
|
||||
do_kcsan=
|
||||
else
|
||||
torture_bootargs=
|
||||
for i in $T/first-resdir-*
|
||||
do
|
||||
case "$i" in
|
||||
*-nodebug)
|
||||
torture_suffix=
|
||||
;;
|
||||
*-kasan)
|
||||
torture_suffix="-kasan"
|
||||
;;
|
||||
*-kcsan)
|
||||
torture_suffix="-kcsan"
|
||||
;;
|
||||
esac
|
||||
torture_set "refscale-$prim$torture_suffix" tools/testing/selftests/rcutorture/bin/kvm-again.sh "`cat "$i"`" --duration 5 --bootargs "refscale.scale_type=$prim"
|
||||
done
|
||||
fi
|
||||
done
|
||||
do_kasan="$do_kasan_save"
|
||||
do_kcsan="$do_kcsan_save"
|
||||
|
||||
if test "$do_rcuscale" = yes
|
||||
then
|
||||
@@ -391,11 +446,48 @@ then
|
||||
else
|
||||
primlist=
|
||||
fi
|
||||
firsttime=1
|
||||
do_kasan_save="$do_kasan"
|
||||
do_kcsan_save="$do_kcsan"
|
||||
for prim in $primlist
|
||||
do
|
||||
torture_bootargs="rcuscale.scale_type="$prim" rcuscale.nwriters=$HALF_ALLOTED_CPUS rcuscale.holdoff=20 torture.disable_onoff_at_boot"
|
||||
torture_set "rcuscale-$prim" tools/testing/selftests/rcutorture/bin/kvm.sh --torture rcuscale --allcpus --duration 5 --kconfig "CONFIG_TASKS_TRACE_RCU=y CONFIG_NR_CPUS=$HALF_ALLOTED_CPUS" --trust-make
|
||||
if test -n "$firsttime"
|
||||
then
|
||||
torture_bootargs="rcuscale.scale_type="$prim" rcuscale.nwriters=$HALF_ALLOTED_CPUS rcuscale.holdoff=20 torture.disable_onoff_at_boot"
|
||||
torture_set "rcuscale-$prim" tools/testing/selftests/rcutorture/bin/kvm.sh --torture rcuscale --allcpus --duration 5 --kconfig "CONFIG_TASKS_TRACE_RCU=y CONFIG_NR_CPUS=$HALF_ALLOTED_CPUS" --trust-make
|
||||
mv $T/last-resdir-nodebug $T/first-resdir-nodebug || :
|
||||
if test -f "$T/last-resdir-kasan"
|
||||
then
|
||||
mv $T/last-resdir-kasan $T/first-resdir-kasan || :
|
||||
fi
|
||||
if test -f "$T/last-resdir-kcsan"
|
||||
then
|
||||
mv $T/last-resdir-kcsan $T/first-resdir-kcsan || :
|
||||
fi
|
||||
firsttime=
|
||||
do_kasan=
|
||||
do_kcsan=
|
||||
else
|
||||
torture_bootargs=
|
||||
for i in $T/first-resdir-*
|
||||
do
|
||||
case "$i" in
|
||||
*-nodebug)
|
||||
torture_suffix=
|
||||
;;
|
||||
*-kasan)
|
||||
torture_suffix="-kasan"
|
||||
;;
|
||||
*-kcsan)
|
||||
torture_suffix="-kcsan"
|
||||
;;
|
||||
esac
|
||||
torture_set "rcuscale-$prim$torture_suffix" tools/testing/selftests/rcutorture/bin/kvm-again.sh "`cat "$i"`" --duration 5 --bootargs "rcuscale.scale_type=$prim"
|
||||
done
|
||||
fi
|
||||
done
|
||||
do_kasan="$do_kasan_save"
|
||||
do_kcsan="$do_kcsan_save"
|
||||
|
||||
if test "$do_kvfree" = "yes"
|
||||
then
|
||||
@@ -458,7 +550,10 @@ if test -n "$tdir" && test $compress_concurrency -gt 0
|
||||
then
|
||||
# KASAN vmlinux files can approach 1GB in size, so compress them.
|
||||
echo Looking for K[AC]SAN files to compress: `date` > "$tdir/log-xz" 2>&1
|
||||
find "$tdir" -type d -name '*-k[ac]san' -print > $T/xz-todo
|
||||
find "$tdir" -type d -name '*-k[ac]san' -print > $T/xz-todo-all
|
||||
find "$tdir" -type f -name 're-run' -print | sed -e 's,/re-run,,' |
|
||||
grep -e '-k[ac]san$' > $T/xz-todo-copy
|
||||
sort $T/xz-todo-all $T/xz-todo-copy | uniq -u > $T/xz-todo
|
||||
ncompresses=0
|
||||
batchno=1
|
||||
if test -s $T/xz-todo
|
||||
@@ -490,6 +585,24 @@ then
|
||||
echo Waiting for final batch $batchno of $ncompresses compressions `date` | tee -a "$tdir/log-xz" | tee -a $T/log
|
||||
fi
|
||||
wait
|
||||
if test -s $T/xz-todo-copy
|
||||
then
|
||||
# The trick here is that we need corresponding
|
||||
# vmlinux files from corresponding scenarios.
|
||||
echo Linking vmlinux.xz files to re-use scenarios `date` | tee -a "$tdir/log-xz" | tee -a $T/log
|
||||
dirstash="`pwd`"
|
||||
for i in `cat $T/xz-todo-copy`
|
||||
do
|
||||
cd $i
|
||||
find . -name vmlinux -print > $T/xz-todo-copy-vmlinux
|
||||
for v in `cat $T/xz-todo-copy-vmlinux`
|
||||
do
|
||||
rm -f "$v"
|
||||
cp -l `cat $i/re-run`/"$i/$v".xz "`dirname "$v"`"
|
||||
done
|
||||
cd "$dirstash"
|
||||
done
|
||||
fi
|
||||
echo Size after compressing $n2compress files: `du -sh $tdir | awk '{ print $1 }'` `date` 2>&1 | tee -a "$tdir/log-xz" | tee -a $T/log
|
||||
echo Total duration `get_starttime_duration $starttime`. | tee -a $T/log
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user