Merge tag 'lkmm-scripting.2023.04.07a' of git://git.kernel.org/pub/scm/linux/kernel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 24 Apr 2023 19:02:25 +0000 (12:02 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 24 Apr 2023 19:02:25 +0000 (12:02 -0700)
Pull Linux Kernel Memory Model scripting updates from Paul McKenney:
 "This improves litmus-test documentation and improves the ability to do
  before/after tests on the https://github.com/paulmckrcu/litmus repo"

* tag 'lkmm-scripting.2023.04.07a' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu: (32 commits)
  tools/memory-model: Remove out-of-date SRCU documentation
  tools/memory-model: Document LKMM test procedure
  tools/memory-model: Use "grep -E" instead of "egrep"
  tools/memory-model: Use "-unroll 0" to keep --hw runs finite
  tools/memory-model: Make judgelitmus.sh handle scripted Result: tag
  tools/memory-model: Add data-race capabilities to judgelitmus.sh
  tools/memory-model: Add checktheselitmus.sh to run specified litmus tests
  tools/memory-model: Repair parseargs.sh header comment
  tools/memory-model:  Add "--" to parseargs.sh for additional arguments
  tools/memory-model: Make history-check scripts use mselect7
  tools/memory-model: Make checkghlitmus.sh use mselect7
  tools/memory-model: Fix scripting --jobs argument
  tools/memory-model: Implement --hw support for checkghlitmus.sh
  tools/memory-model: Add -v flag to jingle7 runs
  tools/memory-model: Make runlitmus.sh check for jingle errors
  tools/memory-model: Allow herd to deduce CPU type
  tools/memory-model: Keep assembly-language litmus tests
  tools/memory-model: Move from .AArch64.litmus.out to .litmus.AArch.out
  tools/memory-model: Make runlitmus.sh generate .litmus.out for --hw
  tools/memory-model: Split runlitmus.sh out of checklitmus.sh
  ...

22 files changed:
Documentation/litmus-tests/locking/DCL-broken.litmus [new file with mode: 0644]
Documentation/litmus-tests/locking/DCL-fixed.litmus [new file with mode: 0644]
Documentation/litmus-tests/locking/RM-broken.litmus [new file with mode: 0644]
Documentation/litmus-tests/locking/RM-fixed.litmus [new file with mode: 0644]
tools/memory-model/Documentation/litmus-tests.txt
tools/memory-model/Documentation/locking.txt [new file with mode: 0644]
tools/memory-model/litmus-tests/.gitignore
tools/memory-model/scripts/README
tools/memory-model/scripts/checkalllitmus.sh
tools/memory-model/scripts/checkghlitmus.sh
tools/memory-model/scripts/checklitmus.sh
tools/memory-model/scripts/checklitmushist.sh
tools/memory-model/scripts/checktheselitmus.sh [new file with mode: 0755]
tools/memory-model/scripts/cmplitmushist.sh
tools/memory-model/scripts/hwfnseg.sh [new file with mode: 0755]
tools/memory-model/scripts/initlitmushist.sh
tools/memory-model/scripts/judgelitmus.sh
tools/memory-model/scripts/newlitmushist.sh
tools/memory-model/scripts/parseargs.sh
tools/memory-model/scripts/runlitmus.sh [new file with mode: 0755]
tools/memory-model/scripts/runlitmushist.sh
tools/memory-model/scripts/simpletest.sh [new file with mode: 0755]

diff --git a/Documentation/litmus-tests/locking/DCL-broken.litmus b/Documentation/litmus-tests/locking/DCL-broken.litmus
new file mode 100644 (file)
index 0000000..bfb7ba4
--- /dev/null
@@ -0,0 +1,54 @@
+C DCL-broken
+
+(*
+ * Result: Sometimes
+ *
+ * This litmus test demonstrates more than just locking is required to
+ * correctly implement double-checked locking.
+ *)
+
+{
+       int flag;
+       int data;
+}
+
+P0(int *flag, int *data, spinlock_t *lck)
+{
+       int r0;
+       int r1;
+       int r2;
+
+       r0 = READ_ONCE(*flag);
+       if (r0 == 0) {
+               spin_lock(lck);
+               r1 = READ_ONCE(*flag);
+               if (r1 == 0) {
+                       WRITE_ONCE(*data, 1);
+                       WRITE_ONCE(*flag, 1);
+               }
+               spin_unlock(lck);
+       }
+       r2 = READ_ONCE(*data);
+}
+
+P1(int *flag, int *data, spinlock_t *lck)
+{
+       int r0;
+       int r1;
+       int r2;
+
+       r0 = READ_ONCE(*flag);
+       if (r0 == 0) {
+               spin_lock(lck);
+               r1 = READ_ONCE(*flag);
+               if (r1 == 0) {
+                       WRITE_ONCE(*data, 1);
+                       WRITE_ONCE(*flag, 1);
+               }
+               spin_unlock(lck);
+       }
+       r2 = READ_ONCE(*data);
+}
+
+locations [flag;data;0:r0;0:r1;1:r0;1:r1]
+exists (0:r2=0 \/ 1:r2=0)
diff --git a/Documentation/litmus-tests/locking/DCL-fixed.litmus b/Documentation/litmus-tests/locking/DCL-fixed.litmus
new file mode 100644 (file)
index 0000000..d1b60bc
--- /dev/null
@@ -0,0 +1,55 @@
+C DCL-fixed
+
+(*
+ * Result: Never
+ *
+ * This litmus test demonstrates that double-checked locking can be
+ * reliable given proper use of smp_load_acquire() and smp_store_release()
+ * in addition to the locking.
+ *)
+
+{
+       int flag;
+       int data;
+}
+
+P0(int *flag, int *data, spinlock_t *lck)
+{
+       int r0;
+       int r1;
+       int r2;
+
+       r0 = smp_load_acquire(flag);
+       if (r0 == 0) {
+               spin_lock(lck);
+               r1 = READ_ONCE(*flag);
+               if (r1 == 0) {
+                       WRITE_ONCE(*data, 1);
+                       smp_store_release(flag, 1);
+               }
+               spin_unlock(lck);
+       }
+       r2 = READ_ONCE(*data);
+}
+
+P1(int *flag, int *data, spinlock_t *lck)
+{
+       int r0;
+       int r1;
+       int r2;
+
+       r0 = smp_load_acquire(flag);
+       if (r0 == 0) {
+               spin_lock(lck);
+               r1 = READ_ONCE(*flag);
+               if (r1 == 0) {
+                       WRITE_ONCE(*data, 1);
+                       smp_store_release(flag, 1);
+               }
+               spin_unlock(lck);
+       }
+       r2 = READ_ONCE(*data);
+}
+
+locations [flag;data;0:r0;0:r1;1:r0;1:r1]
+exists (0:r2=0 \/ 1:r2=0)
diff --git a/Documentation/litmus-tests/locking/RM-broken.litmus b/Documentation/litmus-tests/locking/RM-broken.litmus
new file mode 100644 (file)
index 0000000..b7ef30c
--- /dev/null
@@ -0,0 +1,41 @@
+C RM-broken
+
+(*
+ * Result: DEADLOCK
+ *
+ * This litmus test demonstrates that the old "roach motel" approach
+ * to locking, where code can be freely moved into critical sections,
+ * cannot be used in the Linux kernel.
+ *)
+
+{
+       int x;
+       atomic_t y;
+}
+
+P0(int *x, atomic_t *y, spinlock_t *lck)
+{
+       int r2;
+
+       spin_lock(lck);
+       r2 = atomic_inc_return(y);
+       WRITE_ONCE(*x, 1);
+       spin_unlock(lck);
+}
+
+P1(int *x, atomic_t *y, spinlock_t *lck)
+{
+       int r0;
+       int r1;
+       int r2;
+
+       spin_lock(lck);
+       r0 = READ_ONCE(*x);
+       r1 = READ_ONCE(*x);
+       r2 = atomic_inc_return(y);
+       spin_unlock(lck);
+}
+
+locations [x;0:r2;1:r0;1:r1;1:r2]
+filter (1:r0=0 /\ 1:r1=1)
+exists (1:r2=1)
diff --git a/Documentation/litmus-tests/locking/RM-fixed.litmus b/Documentation/litmus-tests/locking/RM-fixed.litmus
new file mode 100644 (file)
index 0000000..b628175
--- /dev/null
@@ -0,0 +1,41 @@
+C RM-fixed
+
+(*
+ * Result: Never
+ *
+ * This litmus test demonstrates that the old "roach motel" approach
+ * to locking, where code can be freely moved into critical sections,
+ * cannot be used in the Linux kernel.
+ *)
+
+{
+       int x;
+       atomic_t y;
+}
+
+P0(int *x, atomic_t *y, spinlock_t *lck)
+{
+       int r2;
+
+       spin_lock(lck);
+       r2 = atomic_inc_return(y);
+       WRITE_ONCE(*x, 1);
+       spin_unlock(lck);
+}
+
+P1(int *x, atomic_t *y, spinlock_t *lck)
+{
+       int r0;
+       int r1;
+       int r2;
+
+       r0 = READ_ONCE(*x);
+       r1 = READ_ONCE(*x);
+       spin_lock(lck);
+       r2 = atomic_inc_return(y);
+       spin_unlock(lck);
+}
+
+locations [x;0:r2;1:r0;1:r1;1:r2]
+filter (1:r0=0 /\ 1:r1=1)
+exists (1:r2=1)
index 26554b1c5575e086578d1af42318bc461cb3587c..acac527328a1f092dc8aea877444020394153caa 100644 (file)
@@ -1028,32 +1028,7 @@ Limitations of the Linux-kernel memory model (LKMM) include:
                additional call_rcu() process to the site of the
                emulated rcu-barrier().
 
-       e.      Although sleepable RCU (SRCU) is now modeled, there
-               are some subtle differences between its semantics and
-               those in the Linux kernel.  For example, the kernel
-               might interpret the following sequence as two partially
-               overlapping SRCU read-side critical sections:
-
-                        1  r1 = srcu_read_lock(&my_srcu);
-                        2  do_something_1();
-                        3  r2 = srcu_read_lock(&my_srcu);
-                        4  do_something_2();
-                        5  srcu_read_unlock(&my_srcu, r1);
-                        6  do_something_3();
-                        7  srcu_read_unlock(&my_srcu, r2);
-
-               In contrast, LKMM will interpret this as a nested pair of
-               SRCU read-side critical sections, with the outer critical
-               section spanning lines 1-7 and the inner critical section
-               spanning lines 3-5.
-
-               This difference would be more of a concern had anyone
-               identified a reasonable use case for partially overlapping
-               SRCU read-side critical sections.  For more information
-               on the trickiness of such overlapping, please see:
-               https://paulmck.livejournal.com/40593.html
-
-       f.      Reader-writer locking is not modeled.  It can be
+       e.      Reader-writer locking is not modeled.  It can be
                emulated in litmus tests using atomic read-modify-write
                operations.
 
diff --git a/tools/memory-model/Documentation/locking.txt b/tools/memory-model/Documentation/locking.txt
new file mode 100644 (file)
index 0000000..65c898c
--- /dev/null
@@ -0,0 +1,298 @@
+Locking
+=======
+
+Locking is well-known and the common use cases are straightforward: Any
+CPU holding a given lock sees any changes previously seen or made by any
+CPU before it previously released that same lock.  This last sentence
+is the only part of this document that most developers will need to read.
+
+However, developers who would like to also access lock-protected shared
+variables outside of their corresponding locks should continue reading.
+
+
+Locking and Prior Accesses
+--------------------------
+
+The basic rule of locking is worth repeating:
+
+       Any CPU holding a given lock sees any changes previously seen
+       or made by any CPU before it previously released that same lock.
+
+Note that this statement is a bit stronger than "Any CPU holding a
+given lock sees all changes made by any CPU during the time that CPU was
+previously holding this same lock".  For example, consider the following
+pair of code fragments:
+
+       /* See MP+polocks.litmus. */
+       void CPU0(void)
+       {
+               WRITE_ONCE(x, 1);
+               spin_lock(&mylock);
+               WRITE_ONCE(y, 1);
+               spin_unlock(&mylock);
+       }
+
+       void CPU1(void)
+       {
+               spin_lock(&mylock);
+               r0 = READ_ONCE(y);
+               spin_unlock(&mylock);
+               r1 = READ_ONCE(x);
+       }
+
+The basic rule guarantees that if CPU0() acquires mylock before CPU1(),
+then both r0 and r1 must be set to the value 1.  This also has the
+consequence that if the final value of r0 is equal to 1, then the final
+value of r1 must also be equal to 1.  In contrast, the weaker rule would
+say nothing about the final value of r1.
+
+
+Locking and Subsequent Accesses
+-------------------------------
+
+The converse to the basic rule also holds:  Any CPU holding a given
+lock will not see any changes that will be made by any CPU after it
+subsequently acquires this same lock.  This converse statement is
+illustrated by the following litmus test:
+
+       /* See MP+porevlocks.litmus. */
+       void CPU0(void)
+       {
+               r0 = READ_ONCE(y);
+               spin_lock(&mylock);
+               r1 = READ_ONCE(x);
+               spin_unlock(&mylock);
+       }
+
+       void CPU1(void)
+       {
+               spin_lock(&mylock);
+               WRITE_ONCE(x, 1);
+               spin_unlock(&mylock);
+               WRITE_ONCE(y, 1);
+       }
+
+This converse to the basic rule guarantees that if CPU0() acquires
+mylock before CPU1(), then both r0 and r1 must be set to the value 0.
+This also has the consequence that if the final value of r1 is equal
+to 0, then the final value of r0 must also be equal to 0.  In contrast,
+the weaker rule would say nothing about the final value of r0.
+
+These examples show only a single pair of CPUs, but the effects of the
+locking basic rule extend across multiple acquisitions of a given lock
+across multiple CPUs.
+
+
+Double-Checked Locking
+----------------------
+
+It is well known that more than just a lock is required to make
+double-checked locking work correctly,  This litmus test illustrates
+one incorrect approach:
+
+       /* See Documentation/litmus-tests/locking/DCL-broken.litmus. */
+       void CPU0(void)
+       {
+               r0 = READ_ONCE(flag);
+               if (r0 == 0) {
+                       spin_lock(&lck);
+                       r1 = READ_ONCE(flag);
+                       if (r1 == 0) {
+                               WRITE_ONCE(data, 1);
+                               WRITE_ONCE(flag, 1);
+                       }
+                       spin_unlock(&lck);
+               }
+               r2 = READ_ONCE(data);
+       }
+       /* CPU1() is the exactly the same as CPU0(). */
+
+There are two problems.  First, there is no ordering between the first
+READ_ONCE() of "flag" and the READ_ONCE() of "data".  Second, there is
+no ordering between the two WRITE_ONCE() calls.  It should therefore be
+no surprise that "r2" can be zero, and a quick herd7 run confirms this.
+
+One way to fix this is to use smp_load_acquire() and smp_store_release()
+as shown in this corrected version:
+
+       /* See Documentation/litmus-tests/locking/DCL-fixed.litmus. */
+       void CPU0(void)
+       {
+               r0 = smp_load_acquire(&flag);
+               if (r0 == 0) {
+                       spin_lock(&lck);
+                       r1 = READ_ONCE(flag);
+                       if (r1 == 0) {
+                               WRITE_ONCE(data, 1);
+                               smp_store_release(&flag, 1);
+                       }
+                       spin_unlock(&lck);
+               }
+               r2 = READ_ONCE(data);
+       }
+       /* CPU1() is the exactly the same as CPU0(). */
+
+The smp_load_acquire() guarantees that its load from "flags" will
+be ordered before the READ_ONCE() from data, thus solving the first
+problem.  The smp_store_release() guarantees that its store will be
+ordered after the WRITE_ONCE() to "data", solving the second problem.
+The smp_store_release() pairs with the smp_load_acquire(), thus ensuring
+that the ordering provided by each actually takes effect.  Again, a
+quick herd7 run confirms this.
+
+In short, if you access a lock-protected variable without holding the
+corresponding lock, you will need to provide additional ordering, in
+this case, via the smp_load_acquire() and the smp_store_release().
+
+
+Ordering Provided by a Lock to CPUs Not Holding That Lock
+---------------------------------------------------------
+
+It is not necessarily the case that accesses ordered by locking will be
+seen as ordered by CPUs not holding that lock.  Consider this example:
+
+       /* See Z6.0+pooncelock+pooncelock+pombonce.litmus. */
+       void CPU0(void)
+       {
+               spin_lock(&mylock);
+               WRITE_ONCE(x, 1);
+               WRITE_ONCE(y, 1);
+               spin_unlock(&mylock);
+       }
+
+       void CPU1(void)
+       {
+               spin_lock(&mylock);
+               r0 = READ_ONCE(y);
+               WRITE_ONCE(z, 1);
+               spin_unlock(&mylock);
+       }
+
+       void CPU2(void)
+       {
+               WRITE_ONCE(z, 2);
+               smp_mb();
+               r1 = READ_ONCE(x);
+       }
+
+Counter-intuitive though it might be, it is quite possible to have
+the final value of r0 be 1, the final value of z be 2, and the final
+value of r1 be 0.  The reason for this surprising outcome is that CPU2()
+never acquired the lock, and thus did not fully benefit from the lock's
+ordering properties.
+
+Ordering can be extended to CPUs not holding the lock by careful use
+of smp_mb__after_spinlock():
+
+       /* See Z6.0+pooncelock+poonceLock+pombonce.litmus. */
+       void CPU0(void)
+       {
+               spin_lock(&mylock);
+               WRITE_ONCE(x, 1);
+               WRITE_ONCE(y, 1);
+               spin_unlock(&mylock);
+       }
+
+       void CPU1(void)
+       {
+               spin_lock(&mylock);
+               smp_mb__after_spinlock();
+               r0 = READ_ONCE(y);
+               WRITE_ONCE(z, 1);
+               spin_unlock(&mylock);
+       }
+
+       void CPU2(void)
+       {
+               WRITE_ONCE(z, 2);
+               smp_mb();
+               r1 = READ_ONCE(x);
+       }
+
+This addition of smp_mb__after_spinlock() strengthens the lock
+acquisition sufficiently to rule out the counter-intuitive outcome.
+In other words, the addition of the smp_mb__after_spinlock() prohibits
+the counter-intuitive result where the final value of r0 is 1, the final
+value of z is 2, and the final value of r1 is 0.
+
+
+No Roach-Motel Locking!
+-----------------------
+
+This example requires familiarity with the herd7 "filter" clause, so
+please read up on that topic in litmus-tests.txt.
+
+It is tempting to allow memory-reference instructions to be pulled
+into a critical section, but this cannot be allowed in the general case.
+For example, consider a spin loop preceding a lock-based critical section.
+Now, herd7 does not model spin loops, but we can emulate one with two
+loads, with a "filter" clause to constrain the first to return the
+initial value and the second to return the updated value, as shown below:
+
+       /* See Documentation/litmus-tests/locking/RM-fixed.litmus. */
+       void CPU0(void)
+       {
+               spin_lock(&lck);
+               r2 = atomic_inc_return(&y);
+               WRITE_ONCE(x, 1);
+               spin_unlock(&lck);
+       }
+
+       void CPU1(void)
+       {
+               r0 = READ_ONCE(x);
+               r1 = READ_ONCE(x);
+               spin_lock(&lck);
+               r2 = atomic_inc_return(&y);
+               spin_unlock(&lck);
+       }
+
+       filter (1:r0=0 /\ 1:r1=1)
+       exists (1:r2=1)
+
+The variable "x" is the control variable for the emulated spin loop.
+CPU0() sets it to "1" while holding the lock, and CPU1() emulates the
+spin loop by reading it twice, first into "1:r0" (which should get the
+initial value "0") and then into "1:r1" (which should get the updated
+value "1").
+
+The "filter" clause takes this into account, constraining "1:r0" to
+equal "0" and "1:r1" to equal 1.
+
+Then the "exists" clause checks to see if CPU1() acquired its lock first,
+which should not happen given the filter clause because CPU0() updates
+"x" while holding the lock.  And herd7 confirms this.
+
+But suppose that the compiler was permitted to reorder the spin loop
+into CPU1()'s critical section, like this:
+
+       /* See Documentation/litmus-tests/locking/RM-broken.litmus. */
+       void CPU0(void)
+       {
+               int r2;
+
+               spin_lock(&lck);
+               r2 = atomic_inc_return(&y);
+               WRITE_ONCE(x, 1);
+               spin_unlock(&lck);
+       }
+
+       void CPU1(void)
+       {
+               spin_lock(&lck);
+               r0 = READ_ONCE(x);
+               r1 = READ_ONCE(x);
+               r2 = atomic_inc_return(&y);
+               spin_unlock(&lck);
+       }
+
+       filter (1:r0=0 /\ 1:r1=1)
+       exists (1:r2=1)
+
+If "1:r0" is equal to "0", "1:r1" can never equal "1" because CPU0()
+cannot update "x" while CPU1() holds the lock.  And herd7 confirms this,
+showing zero executions matching the "filter" criteria.
+
+And this is why Linux-kernel lock and unlock primitives must prevent
+code from entering critical sections.  It is not sufficient to only
+prevent code from leaving them.
index c492a1ddad91d54a5b76e218a61a92d441ac8659..19c379cf069d2369db344673624a783834f9adce 100644 (file)
@@ -1,2 +1,2 @@
 # SPDX-License-Identifier: GPL-2.0-only
-*.litmus.out
+*.litmus.*
index 095c7eb36f9f90a38c2acf50a49ec276fa02f097..fb39bd0fd1b9d2f8e099f83e8da36e8d63774e68 100644 (file)
@@ -27,6 +27,14 @@ checklitmushist.sh
 checklitmus.sh
 
        Check a single litmus test against its "Result:" expected result.
+       Not intended to for manual use.
+
+checktheselitmus.sh
+
+       Check the specified list of litmus tests against their "Result:"
+       expected results.  This takes optional parseargs.sh arguments,
+       followed by "--" followed by pathnames starting from the current
+       directory.
 
 cmplitmushist.sh
 
@@ -43,10 +51,10 @@ initlitmushist.sh
 
 judgelitmus.sh
 
-       Given a .litmus file and its .litmus.out herd7 output, check the
-       .litmus.out file against the .litmus file's "Result:" comment to
-       judge whether the test ran correctly.  Not normally run manually,
-       provided instead for use by other scripts.
+       Given a .litmus file and its herd7 output, check the output file
+       against the .litmus file's "Result:" comment to judge whether
+       the test ran correctly.  Not normally run manually, provided
+       instead for use by other scripts.
 
 newlitmushist.sh
 
@@ -68,3 +76,35 @@ runlitmushist.sh
 README
 
        This file
+
+Testing a change to LKMM might go as follows:
+
+       # Populate expected results without that change, and
+       # runs for about an hour on an 8-CPU x86 system:
+       scripts/initlitmushist.sh --timeout 10m --procs 10
+       # Incorporate the change:
+       git am -s -3 /path/to/patch # Or whatever it takes.
+
+       # Test the new version of LKMM as follows...
+
+       # Runs in seconds, good smoke test:
+       scripts/checkalllitmus.sh
+
+       # Compares results to those produced by initlitmushist.sh,
+       # and runs for about an hour on an 8-CPU x86 system:
+       scripts/checklitmushist.sh --timeout 10m --procs 10
+
+       # Checks results against Result tags, runs in minutes:
+       scripts/checkghlitmus.sh --timeout 10m --procs 10
+
+The checkghlitmus.sh should not report errors in cases where the
+checklitmushist.sh script did not also report a change.  However,
+this check is nevertheless valuable because it can find errors in the
+original version of LKMM.  Note however, that given the above procedure,
+an error in the original LKMM version that is fixed by the patch will
+be reported both as a mismatch by checklitmushist.sh and as an error
+by checkghlitmus.sh.  One exception to this rule of thumb is when the
+test fails completely on the original version of LKMM and passes on the
+new version.  In this case, checklitmushist.sh will report a mismatch
+and checkghlitmus.sh will report success.  This happens when the change
+to LKMM introduces a new primitive for which litmus tests already existed.
index 3c0c7fbbd223b77d173a070d167553edd350daec..2d3ee850a8399ee49f59342f2e13f0333c745f38 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 # SPDX-License-Identifier: GPL-2.0+
 #
 # Run herd7 tests on all .litmus files in the litmus-tests directory
@@ -8,6 +8,11 @@
 # "^^^".  It also outputs verification results to a file whose name is
 # that of the specified litmus test, but with ".out" appended.
 #
+# If the --hw argument is specified, this script translates the .litmus
+# C-language file to the specified type of assembly and verifies that.
+# But in this case, litmus tests using complex synchronization (such as
+# locking, RCU, and SRCU) are cheerfully ignored.
+#
 # Usage:
 #      checkalllitmus.sh
 #
@@ -17,7 +22,7 @@
 #
 # Copyright IBM Corporation, 2018
 #
-# Author: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
+# Author: Paul E. McKenney <paulmck@linux.ibm.com>
 
 . scripts/parseargs.sh
 
@@ -30,29 +35,23 @@ else
        exit 255
 fi
 
-# Create any new directories that have appeared in the github litmus
-# repo since the last run.
+# Create any new directories that have appeared in the litmus-tests
+# directory since the last run.
 if test "$LKMM_DESTDIR" != "."
 then
        find $litmusdir -type d -print |
        ( cd "$LKMM_DESTDIR"; sed -e 's/^/mkdir -p /' | sh )
 fi
 
-# Find the checklitmus script.  If it is not where we expect it, then
-# assume that the caller has the PATH environment variable set
-# appropriately.
-if test -x scripts/checklitmus.sh
-then
-       clscript=scripts/checklitmus.sh
-else
-       clscript=checklitmus.sh
-fi
-
 # Run the script on all the litmus tests in the specified directory
 ret=0
 for i in $litmusdir/*.litmus
 do
-       if ! $clscript $i
+       if test -n "$LKMM_HW_MAP_FILE" && ! scripts/simpletest.sh $i
+       then
+               continue
+       fi
+       if ! scripts/checklitmus.sh $i
        then
                ret=1
        fi
index 6589fbb6f6538339c2a126288f75b8b476a3f0c3..d3dfb321259f223ef342733fe96dc65fa96d9624 100755 (executable)
@@ -10,6 +10,7 @@
 # parseargs.sh scripts for arguments.
 
 . scripts/parseargs.sh
+. scripts/hwfnseg.sh
 
 T=/tmp/checkghlitmus.sh.$$
 trap 'rm -rf $T' 0
@@ -32,19 +33,19 @@ then
        ( cd "$LKMM_DESTDIR"; sed -e 's/^/mkdir -p /' | sh )
 fi
 
-# Create a list of the C-language litmus tests previously run.
-( cd $LKMM_DESTDIR; find litmus -name '*.litmus.out' -print ) |
-       sed -e 's/\.out$//' |
-       xargs -r egrep -l '^ \* Result: (Never|Sometimes|Always|DEADLOCK)' |
+# Create a list of the specified litmus tests previously run.
+( cd $LKMM_DESTDIR; find litmus -name "*.litmus${hwfnseg}.out" -print ) |
+       sed -e "s/${hwfnseg}"'\.out$//' |
+       xargs -r grep -E -l '^ \* Result: (Never|Sometimes|Always|DEADLOCK)' |
        xargs -r grep -L "^P${LKMM_PROCS}"> $T/list-C-already
 
 # Create a list of C-language litmus tests with "Result:" commands and
 # no more than the specified number of processes.
-find litmus -name '*.litmus' -exec grep -l -m 1 "^C " {} \; > $T/list-C
-xargs < $T/list-C -r egrep -l '^ \* Result: (Never|Sometimes|Always|DEADLOCK)' > $T/list-C-result
+find litmus -name '*.litmus' -print | mselect7 -arch C > $T/list-C
+xargs < $T/list-C -r grep -E -l '^ \* Result: (Never|Sometimes|Always|DEADLOCK)' > $T/list-C-result
 xargs < $T/list-C-result -r grep -L "^P${LKMM_PROCS}" > $T/list-C-result-short
 
-# Form list of tests without corresponding .litmus.out files
+# Form list of tests without corresponding .out files
 sort $T/list-C-already $T/list-C-result-short | uniq -u > $T/list-C-needed
 
 # Run any needed tests.
index 11461ed40b5e456be0145e45d2ef4464895fed15..4c1d0cf0ddadcf93eb860c986167f013b354daf7 100755 (executable)
@@ -1,10 +1,8 @@
 #!/bin/sh
 # SPDX-License-Identifier: GPL-2.0+
 #
-# Run a herd7 test and invokes judgelitmus.sh to check the result against
-# a "Result:" comment within the litmus test.  It also outputs verification
-# results to a file whose name is that of the specified litmus test, but
-# with ".out" appended.
+# Invokes runlitmus.sh and judgelitmus.sh on its arguments to run the
+# specified litmus test and pass judgment on the results.
 #
 # Usage:
 #      checklitmus.sh file.litmus
 #
 # Copyright IBM Corporation, 2018
 #
-# Author: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
+# Author: Paul E. McKenney <paulmck@linux.ibm.com>
 
-litmus=$1
-herdoptions=${LKMM_HERD_OPTIONS--conf linux-kernel.cfg}
-
-if test -f "$litmus" -a -r "$litmus"
-then
-       :
-else
-       echo ' --- ' error: \"$litmus\" is not a readable file
-       exit 255
-fi
-
-echo Herd options: $herdoptions > $LKMM_DESTDIR/$litmus.out
-/usr/bin/time $LKMM_TIMEOUT_CMD herd7 $herdoptions $litmus >> $LKMM_DESTDIR/$litmus.out 2>&1
-
-scripts/judgelitmus.sh $litmus
+scripts/runlitmus.sh $1
+scripts/judgelitmus.sh $1
index 1d210ffb7c8af6084518eabcf073864f1fed6a27..406ecfc0aee4ca8654ae4ebf4b8b168dd5e25bb0 100755 (executable)
@@ -12,7 +12,7 @@
 #
 # Copyright IBM Corporation, 2018
 #
-# Author: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
+# Author: Paul E. McKenney <paulmck@linux.ibm.com>
 
 . scripts/parseargs.sh
 
diff --git a/tools/memory-model/scripts/checktheselitmus.sh b/tools/memory-model/scripts/checktheselitmus.sh
new file mode 100755 (executable)
index 0000000..10eeb5e
--- /dev/null
@@ -0,0 +1,43 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Invokes checklitmus.sh on its arguments to run the specified litmus
+# test and pass judgment on the results.
+#
+# Usage:
+#      checktheselitmus.sh -- [ file1.litmus [ file2.litmus ... ] ]
+#
+# Run this in the directory containing the memory model, specifying the
+# pathname of the litmus test to check.  The usual parseargs.sh arguments
+# can be specified prior to the "--".
+#
+# This script is intended for use with pathnames that start from the
+# tools/memory-model directory.  If some of the pathnames instead start at
+# the root directory, they all must do so and the "--destdir /" parseargs.sh
+# argument must be specified prior to the "--".  Alternatively, some other
+# "--destdir" argument can be supplied as long as the needed subdirectories
+# are populated.
+#
+# Copyright IBM Corporation, 2018
+#
+# Author: Paul E. McKenney <paulmck@linux.ibm.com>
+
+. scripts/parseargs.sh
+
+ret=0
+for i in "$@"
+do
+       if scripts/checklitmus.sh $i
+       then
+               :
+       else
+               ret=1
+       fi
+done
+if test "$ret" -ne 0
+then
+       echo " ^^^ VERIFICATION MISMATCHES" 1>&2
+else
+       echo All litmus tests verified as was expected. 1>&2
+fi
+exit $ret
index 0f498aeeccf5ec6cc76fd2634aa1204db73bdce5..ca1ac8b646144ac95b0ae13d5534bbebcf411ea6 100755 (executable)
@@ -12,12 +12,49 @@ trap 'rm -rf $T' 0
 mkdir $T
 
 # comparetest oldpath newpath
+badmacnam=0
+timedout=0
 perfect=0
 obsline=0
 noobsline=0
 obsresult=0
 badcompare=0
 comparetest () {
+       if grep -q ': Unknown macro ' $1 || grep -q ': Unknown macro ' $2
+       then
+               if grep -q ': Unknown macro ' $1
+               then
+                       badname=`grep ': Unknown macro ' $1 |
+                               sed -e 's/^.*: Unknown macro //' |
+                               sed -e 's/ (User error).*$//'`
+                       echo 'Current LKMM version does not know "'$badname'"' $1
+               fi
+               if grep -q ': Unknown macro ' $2
+               then
+                       badname=`grep ': Unknown macro ' $2 |
+                               sed -e 's/^.*: Unknown macro //' |
+                               sed -e 's/ (User error).*$//'`
+                       echo 'Current LKMM version does not know "'$badname'"' $2
+               fi
+               badmacnam=`expr "$badmacnam" + 1`
+               return 0
+       elif grep -q '^Command exited with non-zero status 124' $1 ||
+            grep -q '^Command exited with non-zero status 124' $2
+       then
+               if grep -q '^Command exited with non-zero status 124' $1 &&
+                  grep -q '^Command exited with non-zero status 124' $2
+               then
+                       echo Both runs timed out: $2
+               elif grep -q '^Command exited with non-zero status 124' $1
+               then
+                       echo Old run timed out: $2
+               elif grep -q '^Command exited with non-zero status 124' $2
+               then
+                       echo New run timed out: $2
+               fi
+               timedout=`expr "$timedout" + 1`
+               return 0
+       fi
        grep -v 'maxresident)k\|minor)pagefaults\|^Time' $1 > $T/oldout
        grep -v 'maxresident)k\|minor)pagefaults\|^Time' $2 > $T/newout
        if cmp -s $T/oldout $T/newout && grep -q '^Observation' $1
@@ -38,7 +75,7 @@ comparetest () {
                        return 0
                fi
        else
-               echo Missing Observation line "(e.g., herd7 timeout)": $2
+               echo Missing Observation line "(e.g., syntax error)": $2
                noobsline=`expr "$noobsline" + 1`
                return 0
        fi
@@ -72,12 +109,20 @@ then
 fi
 if test "$noobsline" -ne 0
 then
-       echo Missing Observation line "(e.g., herd7 timeout)": $noobsline 1>&2
+       echo Missing Observation line "(e.g., syntax error)": $noobsline 1>&2
 fi
 if test "$obsresult" -ne 0
 then
        echo Matching Observation Always/Sometimes/Never result: $obsresult 1>&2
 fi
+if test "$timedout" -ne 0
+then
+       echo "!!!" Timed out: $timedout 1>&2
+fi
+if test "$badmacnam" -ne 0
+then
+       echo "!!!" Unknown primitive: $badmacnam 1>&2
+fi
 if test "$badcompare" -ne 0
 then
        echo "!!!" Result changed: $badcompare 1>&2
diff --git a/tools/memory-model/scripts/hwfnseg.sh b/tools/memory-model/scripts/hwfnseg.sh
new file mode 100755 (executable)
index 0000000..580c328
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Generate the hardware extension to the litmus-test filename, or the
+# empty string if this is an LKMM run.  The extension is placed in
+# the shell variable hwfnseg.
+#
+# Usage:
+#      . hwfnseg.sh
+#
+# Copyright IBM Corporation, 2019
+#
+# Author: Paul E. McKenney <paulmck@linux.ibm.com>
+
+if test -z "$LKMM_HW_MAP_FILE"
+then
+       hwfnseg=
+else
+       hwfnseg=".$LKMM_HW_MAP_FILE"
+fi
index 956b6957484d879351d312a1d217df11d9dfd9f2..31ea782955d3f0115ab740d9a0b064228f058878 100755 (executable)
@@ -60,7 +60,7 @@ fi
 
 # Create a list of the C-language litmus tests with no more than the
 # specified number of processes (per the --procs argument).
-find litmus -name '*.litmus' -exec grep -l -m 1 "^C " {} \; > $T/list-C
+find litmus -name '*.litmus' -print | mselect7 -arch C > $T/list-C
 xargs < $T/list-C -r grep -L "^P${LKMM_PROCS}" > $T/list-C-short
 
 scripts/runlitmushist.sh < $T/list-C-short
index 0cc63875e395d06fe890c0f711e748a6226f25ea..1ec5d89fcfbb2a2f6e93803882c94104b20d992d 100755 (executable)
@@ -1,9 +1,22 @@
 #!/bin/sh
 # SPDX-License-Identifier: GPL-2.0+
 #
-# Given a .litmus test and the corresponding .litmus.out file, check
-# the .litmus.out file against the "Result:" comment to judge whether
-# the test ran correctly.
+# Given a .litmus test and the corresponding litmus output file, check
+# the .litmus.out file against the "Result:" comment to judge whether the
+# test ran correctly.  If the --hw argument is omitted, check against the
+# LKMM output, which is assumed to be in file.litmus.out. If either a
+# "DATARACE" marker in the "Result:" comment or a "Flag data-race" marker
+# in the LKMM output is present, the other must also be as well, at least
+# for litmus tests having a "Result:" comment. In this case, a failure of
+# the Always/Sometimes/Never portion of the "Result:" prediction will be
+# noted, but forgiven.
+#
+# If the --hw argument is provided, this is assumed to be a hardware
+# test, and the output is assumed to be in file.litmus.HW.out, where
+# "HW" is the --hw argument. In addition, non-Sometimes verification
+# results will be noted, but forgiven.  Furthermore, if there is no
+# "Result:" comment but there is an LKMM .litmus.out file, the observation
+# in that file will be used to judge the assembly-language verification.
 #
 # Usage:
 #      judgelitmus.sh file.litmus
@@ -13,7 +26,7 @@
 #
 # Copyright IBM Corporation, 2018
 #
-# Author: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
+# Author: Paul E. McKenney <paulmck@linux.ibm.com>
 
 litmus=$1
 
@@ -24,55 +37,120 @@ else
        echo ' --- ' error: \"$litmus\" is not a readable file
        exit 255
 fi
-if test -f "$LKMM_DESTDIR/$litmus".out -a -r "$LKMM_DESTDIR/$litmus".out
+if test -z "$LKMM_HW_MAP_FILE"
+then
+       litmusout=$litmus.out
+       lkmmout=
+else
+       litmusout="`echo $litmus |
+               sed -e 's/\.litmus$/.litmus.'${LKMM_HW_MAP_FILE}'/'`.out"
+       lkmmout=$litmus.out
+fi
+if test -f "$LKMM_DESTDIR/$litmusout" -a -r "$LKMM_DESTDIR/$litmusout"
 then
        :
 else
-       echo ' --- ' error: \"$LKMM_DESTDIR/$litmus\".out is not a readable file
+       echo ' --- ' error: \"$LKMM_DESTDIR/$litmusout is not a readable file
        exit 255
 fi
-if grep -q '^ \* Result: ' $litmus
+if grep -q '^Flag data-race$' "$LKMM_DESTDIR/$litmusout"
+then
+       datarace_modeled=1
+fi
+if grep -q '^[( ]\* Result: ' $litmus
+then
+       outcome=`grep -m 1 '^[( ]\* Result: ' $litmus | awk '{ print $3 }'`
+       if grep -m1 '^[( ]\* Result: .* DATARACE' $litmus
+       then
+               datarace_predicted=1
+       fi
+       if test -n "$datarace_predicted" -a -z "$datarace_modeled" -a -z "$LKMM_HW_MAP_FILE"
+       then
+               echo '!!! Predicted data race not modeled' $litmus
+               exit 252
+       elif test -z "$datarace_predicted" -a -n "$datarace_modeled"
+       then
+               # Note that hardware models currently don't model data races
+               echo '!!! Unexpected data race modeled' $litmus
+               exit 253
+       fi
+elif test -n "$LKMM_HW_MAP_FILE" && grep -q '^Observation' $LKMM_DESTDIR/$lkmmout > /dev/null 2>&1
 then
-       outcome=`grep -m 1 '^ \* Result: ' $litmus | awk '{ print $3 }'`
+       outcome=`grep -m 1 '^Observation ' $LKMM_DESTDIR/$lkmmout | awk '{ print $3 }'`
 else
        outcome=specified
 fi
 
-grep '^Observation' $LKMM_DESTDIR/$litmus.out
-if grep -q '^Observation' $LKMM_DESTDIR/$litmus.out
+grep '^Observation' $LKMM_DESTDIR/$litmusout
+if grep -q '^Observation' $LKMM_DESTDIR/$litmusout
 then
        :
+elif grep ': Unknown macro ' $LKMM_DESTDIR/$litmusout
+then
+       badname=`grep ': Unknown macro ' $LKMM_DESTDIR/$litmusout |
+               sed -e 's/^.*: Unknown macro //' |
+               sed -e 's/ (User error).*$//'`
+       badmsg=' !!! Current LKMM version does not know "'$badname'"'" $litmus"
+       echo $badmsg
+       if ! grep -q '!!!' $LKMM_DESTDIR/$litmusout
+       then
+               echo ' !!! '$badmsg >> $LKMM_DESTDIR/$litmusout 2>&1
+       fi
+       exit 254
+elif grep '^Command exited with non-zero status 124' $LKMM_DESTDIR/$litmusout
+then
+       echo ' !!! Timeout' $litmus
+       if ! grep -q '!!!' $LKMM_DESTDIR/$litmusout
+       then
+               echo ' !!! Timeout' >> $LKMM_DESTDIR/$litmusout 2>&1
+       fi
+       exit 124
 else
        echo ' !!! Verification error' $litmus
-       if ! grep -q '!!!' $LKMM_DESTDIR/$litmus.out
+       if ! grep -q '!!!' $LKMM_DESTDIR/$litmusout
        then
-               echo ' !!! Verification error' >> $LKMM_DESTDIR/$litmus.out 2>&1
+               echo ' !!! Verification error' >> $LKMM_DESTDIR/$litmusout 2>&1
        fi
        exit 255
 fi
 if test "$outcome" = DEADLOCK
 then
-       if grep '^Observation' $LKMM_DESTDIR/$litmus.out | grep -q 'Never 0 0$'
+       if grep '^Observation' $LKMM_DESTDIR/$litmusout | grep -q 'Never 0 0$'
        then
                ret=0
        else
                echo " !!! Unexpected non-$outcome verification" $litmus
-               if ! grep -q '!!!' $LKMM_DESTDIR/$litmus.out
+               if ! grep -q '!!!' $LKMM_DESTDIR/$litmusout
                then
-                       echo " !!! Unexpected non-$outcome verification" >> $LKMM_DESTDIR/$litmus.out 2>&1
+                       echo " !!! Unexpected non-$outcome verification" >> $LKMM_DESTDIR/$litmusout 2>&1
                fi
                ret=1
        fi
-elif grep '^Observation' $LKMM_DESTDIR/$litmus.out | grep -q $outcome || test "$outcome" = Maybe
+elif grep '^Observation' $LKMM_DESTDIR/$litmusout | grep -q 'Never 0 0$'
+then
+       echo " !!! Unexpected non-$outcome deadlock" $litmus
+       if ! grep -q '!!!' $LKMM_DESTDIR/$litmusout
+       then
+               echo " !!! Unexpected non-$outcome deadlock" $litmus >> $LKMM_DESTDIR/$litmusout 2>&1
+       fi
+       ret=1
+elif grep '^Observation' $LKMM_DESTDIR/$litmusout | grep -q $outcome || test "$outcome" = Maybe
 then
        ret=0
 else
-       echo " !!! Unexpected non-$outcome verification" $litmus
-       if ! grep -q '!!!' $LKMM_DESTDIR/$litmus.out
+       if test \( -n "$LKMM_HW_MAP_FILE" -a "$outcome" = Sometimes \) -o -n "$datarace_modeled"
        then
-               echo " !!! Unexpected non-$outcome verification" >> $LKMM_DESTDIR/$litmus.out 2>&1
+               flag="--- Forgiven"
+               ret=0
+       else
+               flag="!!! Unexpected"
+               ret=1
+       fi
+       echo " $flag non-$outcome verification" $litmus
+       if ! grep -qe "$flag" $LKMM_DESTDIR/$litmusout
+       then
+               echo " $flag non-$outcome verification" >> $LKMM_DESTDIR/$litmusout 2>&1
        fi
-       ret=1
 fi
-tail -2 $LKMM_DESTDIR/$litmus.out | head -1
+tail -2 $LKMM_DESTDIR/$litmusout | head -1
 exit $ret
index 991f8f81488174b1c6b165efcc53cec167787138..25235e2049cf05bbc1aa870e822085b1204781c5 100755 (executable)
@@ -12,7 +12,7 @@
 #
 # Copyright IBM Corporation, 2018
 #
-# Author: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
+# Author: Paul E. McKenney <paulmck@linux.ibm.com>
 
 . scripts/parseargs.sh
 
@@ -43,7 +43,7 @@ fi
 
 # Form full list of litmus tests with no more than the specified
 # number of processes (per the --procs argument).
-find litmus -name '*.litmus' -exec grep -l -m 1 "^C " {} \; > $T/list-C-all
+find litmus -name '*.litmus' -print | mselect7 -arch C > $T/list-C-all
 xargs < $T/list-C-all -r grep -L "^P${LKMM_PROCS}" > $T/list-C-short
 
 # Form list of new tests.  Note: This does not handle litmus-test deletion!
index 40f52080fdbd6eb9d82bc3f3bb647fe1da46ffb8..08ded59098607d44c829e6f0a58da8d12545ea46 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 # SPDX-License-Identifier: GPL-2.0+
 #
-# the corresponding .litmus.out file, and does not judge the result.
+# Parse arguments common to the various scripts.
 #
 # . scripts/parseargs.sh
 #
@@ -9,7 +9,7 @@
 #
 # Copyright IBM Corporation, 2018
 #
-# Author: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
+# Author: Paul E. McKenney <paulmck@linux.ibm.com>
 
 T=/tmp/parseargs.sh.$$
 mkdir $T
@@ -27,6 +27,7 @@ initparam () {
 
 initparam LKMM_DESTDIR "."
 initparam LKMM_HERD_OPTIONS "-conf linux-kernel.cfg"
+initparam LKMM_HW_MAP_FILE ""
 initparam LKMM_JOBS `getconf _NPROCESSORS_ONLN`
 initparam LKMM_PROCS "3"
 initparam LKMM_TIMEOUT "1m"
@@ -37,10 +38,11 @@ usagehelp () {
        echo "Usage $scriptname [ arguments ]"
        echo "      --destdir path (place for .litmus.out, default by .litmus)"
        echo "      --herdopts -conf linux-kernel.cfg ..."
+       echo "      --hw AArch64"
        echo "      --jobs N (number of jobs, default one per CPU)"
        echo "      --procs N (litmus tests with at most this many processes)"
        echo "      --timeout N (herd7 timeout (e.g., 10s, 1m, 2hr, 1d, '')"
-       echo "Defaults: --destdir '$LKMM_DESTDIR_DEF' --herdopts '$LKMM_HERD_OPTIONS_DEF' --jobs '$LKMM_JOBS_DEF' --procs '$LKMM_PROCS_DEF' --timeout '$LKMM_TIMEOUT_DEF'"
+       echo "Defaults: --destdir '$LKMM_DESTDIR_DEF' --herdopts '$LKMM_HERD_OPTIONS_DEF' --hw '$LKMM_HW_MAP_FILE' --jobs '$LKMM_JOBS_DEF' --procs '$LKMM_PROCS_DEF' --timeout '$LKMM_TIMEOUT_DEF'"
        exit 1
 }
 
@@ -81,7 +83,7 @@ do
                        echo "Cannot create directory --destdir '$LKMM_DESTDIR'"
                        usage
                fi
-               if test -d "$LKMM_DESTDIR" -a -w "$LKMM_DESTDIR" -a -x "$LKMM_DESTDIR"
+               if test -d "$LKMM_DESTDIR" -a -x "$LKMM_DESTDIR"
                then
                        :
                else
@@ -95,6 +97,11 @@ do
                LKMM_HERD_OPTIONS="$2"
                shift
                ;;
+       --hw)
+               checkarg --hw "(.map file architecture name)" "$#" "$2" '^[A-Za-z0-9_-]\+' '^--'
+               LKMM_HW_MAP_FILE="$2"
+               shift
+               ;;
        -j[1-9]*)
                njobs="`echo $1 | sed -e 's/^-j//'`"
                trailchars="`echo $njobs | sed -e 's/[0-9]\+\(.*\)$/\1/'`"
@@ -106,7 +113,7 @@ do
                LKMM_JOBS="`echo $njobs | sed -e 's/^\([0-9]\+\).*$/\1/'`"
                ;;
        --jobs|--job|-j)
-               checkarg --jobs "(number)" "$#" "$2" '^[1-9][0-9]\+$' '^--'
+               checkarg --jobs "(number)" "$#" "$2" '^[1-9][0-9]*$' '^--'
                LKMM_JOBS="$2"
                shift
                ;;
@@ -120,6 +127,10 @@ do
                LKMM_TIMEOUT="$2"
                shift
                ;;
+       --)
+               shift
+               break
+               ;;
        *)
                echo Unknown argument $1
                usage
diff --git a/tools/memory-model/scripts/runlitmus.sh b/tools/memory-model/scripts/runlitmus.sh
new file mode 100755 (executable)
index 0000000..94608d4
--- /dev/null
@@ -0,0 +1,80 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Without the -hw argument, runs a herd7 test and outputs verification
+# results to a file whose name is that of the specified litmus test,
+# but with ".out" appended.
+#
+# If the --hw argument is specified, this script translates the .litmus
+# C-language file to the specified type of assembly and verifies that.
+# But in this case, litmus tests using complex synchronization (such as
+# locking, RCU, and SRCU) are cheerfully ignored.
+#
+# Either way, return the status of the herd7 command.
+#
+# Usage:
+#      runlitmus.sh file.litmus
+#
+# Run this in the directory containing the memory model, specifying the
+# pathname of the litmus test to check.  The caller is expected to have
+# properly set up the LKMM environment variables.
+#
+# Copyright IBM Corporation, 2019
+#
+# Author: Paul E. McKenney <paulmck@linux.ibm.com>
+
+litmus=$1
+if test -f "$litmus" -a -r "$litmus"
+then
+       :
+else
+       echo ' !!! ' error: \"$litmus\" is not a readable file
+       exit 255
+fi
+
+if test -z "$LKMM_HW_MAP_FILE" -o ! -e $LKMM_DESTDIR/$litmus.out
+then
+       # LKMM run
+       herdoptions=${LKMM_HERD_OPTIONS--conf linux-kernel.cfg}
+       echo Herd options: $herdoptions > $LKMM_DESTDIR/$litmus.out
+       /usr/bin/time $LKMM_TIMEOUT_CMD herd7 $herdoptions $litmus >> $LKMM_DESTDIR/$litmus.out 2>&1
+       ret=$?
+       if test -z "$LKMM_HW_MAP_FILE"
+       then
+               exit $ret
+       fi
+       echo " --- " Automatically generated LKMM output for '"'--hw $LKMM_HW_MAP_FILE'"' run
+fi
+
+# Hardware run
+
+T=/tmp/checklitmushw.sh.$$
+trap 'rm -rf $T' 0 2
+mkdir $T
+
+# Generate filenames
+mapfile="Linux2${LKMM_HW_MAP_FILE}.map"
+themefile="$T/${LKMM_HW_MAP_FILE}.theme"
+herdoptions="-model $LKMM_HW_CAT_FILE"
+hwlitmus=`echo $litmus | sed -e 's/\.litmus$/.litmus.'${LKMM_HW_MAP_FILE}'/'`
+hwlitmusfile=`echo $hwlitmus | sed -e 's,^.*/,,'`
+
+# Don't run on litmus tests with complex synchronization
+if ! scripts/simpletest.sh $litmus
+then
+       echo ' --- ' error: \"$litmus\" contains locking, RCU, or SRCU
+       exit 254
+fi
+
+# Generate the assembly code and run herd7 on it.
+gen_theme7 -n 10 -map $mapfile -call Linux.call > $themefile
+jingle7 -v -theme $themefile $litmus > $LKMM_DESTDIR/$hwlitmus 2> $T/$hwlitmusfile.jingle7.out
+if grep -q "Generated 0 tests" $T/$hwlitmusfile.jingle7.out
+then
+       echo ' !!! ' jingle7 failed, errors in $hwlitmus.err
+       cp $T/$hwlitmusfile.jingle7.out $LKMM_DESTDIR/$hwlitmus.err
+       exit 253
+fi
+/usr/bin/time $LKMM_TIMEOUT_CMD herd7 -unroll 0 $LKMM_DESTDIR/$hwlitmus > $LKMM_DESTDIR/$hwlitmus.out 2>&1
+
+exit $?
index 6ed376f495bb4eadc72504c66ba5f1ac9a2f9c2f..c6c2bdc67a502169700fbbe6731d75905ade312b 100755 (executable)
@@ -13,7 +13,9 @@
 #
 # Copyright IBM Corporation, 2018
 #
-# Author: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
+# Author: Paul E. McKenney <paulmck@linux.ibm.com>
+
+. scripts/hwfnseg.sh
 
 T=/tmp/runlitmushist.sh.$$
 trap 'rm -rf $T' 0
@@ -30,15 +32,12 @@ fi
 # Prefixes for per-CPU scripts
 for ((i=0;i<$LKMM_JOBS;i++))
 do
-       echo dir="$LKMM_DESTDIR" > $T/$i.sh
        echo T=$T >> $T/$i.sh
-       echo herdoptions=\"$LKMM_HERD_OPTIONS\" >> $T/$i.sh
        cat << '___EOF___' >> $T/$i.sh
        runtest () {
-               echo ' ... ' /usr/bin/time $LKMM_TIMEOUT_CMD herd7 $herdoptions $1 '>' $dir/$1.out '2>&1'
-               if /usr/bin/time $LKMM_TIMEOUT_CMD herd7 $herdoptions $1 > $dir/$1.out 2>&1
+               if scripts/runlitmus.sh $1
                then
-                       if ! grep -q '^Observation ' $dir/$1.out
+                       if ! grep -q '^Observation ' $LKMM_DESTDIR/$1$2.out
                        then
                                echo ' !!! Herd failed, no Observation:' $1
                        fi
@@ -47,10 +46,16 @@ do
                        if test "$exitcode" -eq 124
                        then
                                exitmsg="timed out"
+                       elif test "$exitcode" -eq 253
+                       then
+                               exitmsg=
                        else
                                exitmsg="failed, exit code $exitcode"
                        fi
-                       echo ' !!! Herd' ${exitmsg}: $1
+                       if test -n "$exitmsg"
+                       then
+                               echo ' !!! Herd' ${exitmsg}: $1
+                       fi
                fi
        }
 ___EOF___
@@ -59,11 +64,13 @@ done
 awk -v q="'" -v b='\\' '
 {
        print "echo `grep " q "^P[0-9]" b "+(" q " " $0 " | tail -1 | sed -e " q "s/^P" b "([0-9]" b "+" b ")(.*$/" b "1/" q "` " $0
-}' | bash |
-sort -k1n |
-awk -v ncpu=$LKMM_JOBS -v t=$T '
+}' | sh | sort -k1n |
+awk -v dq='"' -v hwfnseg="$hwfnseg" -v ncpu="$LKMM_JOBS" -v t="$T" '
 {
-       print "runtest " $2 >> t "/" NR % ncpu ".sh";
+       print "if test -z " dq hwfnseg dq " || scripts/simpletest.sh " dq $2 dq
+       print "then"
+       print "\techo runtest " dq $2 dq " " hwfnseg " >> " t "/" NR % ncpu ".sh";
+       print "fi"
 }
 
 END {
diff --git a/tools/memory-model/scripts/simpletest.sh b/tools/memory-model/scripts/simpletest.sh
new file mode 100755 (executable)
index 0000000..7edc5d3
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Give zero status if this is a simple test and non-zero otherwise.
+# Simple tests do not contain locking, RCU, or SRCU.
+#
+# Usage:
+#      simpletest.sh file.litmus
+#
+# Copyright IBM Corporation, 2019
+#
+# Author: Paul E. McKenney <paulmck@linux.ibm.com>
+
+
+litmus=$1
+
+if test -f "$litmus" -a -r "$litmus"
+then
+       :
+else
+       echo ' --- ' error: \"$litmus\" is not a readable file
+       exit 255
+fi
+exclude="^[[:space:]]*\("
+exclude="${exclude}spin_lock(\|spin_unlock(\|spin_trylock(\|spin_is_locked("
+exclude="${exclude}\|rcu_read_lock(\|rcu_read_unlock("
+exclude="${exclude}\|synchronize_rcu(\|synchronize_rcu_expedited("
+exclude="${exclude}\|srcu_read_lock(\|srcu_read_unlock("
+exclude="${exclude}\|synchronize_srcu(\|synchronize_srcu_expedited("
+exclude="${exclude}\)"
+if grep -q $exclude $litmus
+then
+       exit 255
+fi
+exit 0