mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-12-27 10:01:39 -05:00
Merge branch 'rcu/misc' into next
- In order to prepare the layout for nohz_full work deferral to user exit, the context tracking state must shrink the counter of transitions to/from RCU not watching. The only possible hazard is to trigger wrap-around more easily, delaying a bit grace periods when that happens. This should be a rare event though. Yet add debugging and torture code to test that assumption. - Fix memory leak on locktorture module - Annotate accesses in rculist_nulls.h to prevent from KCSAN warnings. On recent discussions, we also concluded that all those WRITE_ONCE() and READ_ONCE() on list APIs deserve appropriate comments. Something to be expected for the next cycle. - Provide a script to apply several configs to several commits with torture. - Allow torture to reuse a build directory in order to save needless rebuild time. - Various cleanups.
This commit is contained in:
@@ -18,12 +18,6 @@ enum ctx_state {
|
||||
CT_STATE_MAX = 4,
|
||||
};
|
||||
|
||||
/* Odd value for watching, else even. */
|
||||
#define CT_RCU_WATCHING CT_STATE_MAX
|
||||
|
||||
#define CT_STATE_MASK (CT_STATE_MAX - 1)
|
||||
#define CT_RCU_WATCHING_MASK (~CT_STATE_MASK)
|
||||
|
||||
struct context_tracking {
|
||||
#ifdef CONFIG_CONTEXT_TRACKING_USER
|
||||
/*
|
||||
@@ -44,9 +38,45 @@ struct context_tracking {
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* We cram two different things within the same atomic variable:
|
||||
*
|
||||
* CT_RCU_WATCHING_START CT_STATE_START
|
||||
* | |
|
||||
* v v
|
||||
* MSB [ RCU watching counter ][ context_state ] LSB
|
||||
* ^ ^
|
||||
* | |
|
||||
* CT_RCU_WATCHING_END CT_STATE_END
|
||||
*
|
||||
* Bits are used from the LSB upwards, so unused bits (if any) will always be in
|
||||
* upper bits of the variable.
|
||||
*/
|
||||
#ifdef CONFIG_CONTEXT_TRACKING
|
||||
#define CT_SIZE (sizeof(((struct context_tracking *)0)->state) * BITS_PER_BYTE)
|
||||
|
||||
#define CT_STATE_WIDTH bits_per(CT_STATE_MAX - 1)
|
||||
#define CT_STATE_START 0
|
||||
#define CT_STATE_END (CT_STATE_START + CT_STATE_WIDTH - 1)
|
||||
|
||||
#define CT_RCU_WATCHING_MAX_WIDTH (CT_SIZE - CT_STATE_WIDTH)
|
||||
#define CT_RCU_WATCHING_WIDTH (IS_ENABLED(CONFIG_RCU_DYNTICKS_TORTURE) ? 2 : CT_RCU_WATCHING_MAX_WIDTH)
|
||||
#define CT_RCU_WATCHING_START (CT_STATE_END + 1)
|
||||
#define CT_RCU_WATCHING_END (CT_RCU_WATCHING_START + CT_RCU_WATCHING_WIDTH - 1)
|
||||
#define CT_RCU_WATCHING BIT(CT_RCU_WATCHING_START)
|
||||
|
||||
#define CT_STATE_MASK GENMASK(CT_STATE_END, CT_STATE_START)
|
||||
#define CT_RCU_WATCHING_MASK GENMASK(CT_RCU_WATCHING_END, CT_RCU_WATCHING_START)
|
||||
|
||||
#define CT_UNUSED_WIDTH (CT_RCU_WATCHING_MAX_WIDTH - CT_RCU_WATCHING_WIDTH)
|
||||
|
||||
static_assert(CT_STATE_WIDTH +
|
||||
CT_RCU_WATCHING_WIDTH +
|
||||
CT_UNUSED_WIDTH ==
|
||||
CT_SIZE);
|
||||
|
||||
DECLARE_PER_CPU(struct context_tracking, context_tracking);
|
||||
#endif
|
||||
#endif /* CONFIG_CONTEXT_TRACKING */
|
||||
|
||||
#ifdef CONFIG_CONTEXT_TRACKING_USER
|
||||
static __always_inline int __ct_state(void)
|
||||
|
||||
@@ -138,7 +138,7 @@ static inline void hlist_nulls_add_tail_rcu(struct hlist_nulls_node *n,
|
||||
|
||||
if (last) {
|
||||
WRITE_ONCE(n->next, last->next);
|
||||
n->pprev = &last->next;
|
||||
WRITE_ONCE(n->pprev, &last->next);
|
||||
rcu_assign_pointer(hlist_nulls_next_rcu(last), n);
|
||||
} else {
|
||||
hlist_nulls_add_head_rcu(n, h);
|
||||
@@ -148,8 +148,8 @@ static inline void hlist_nulls_add_tail_rcu(struct hlist_nulls_node *n,
|
||||
/* after that hlist_nulls_del will work */
|
||||
static inline void hlist_nulls_add_fake(struct hlist_nulls_node *n)
|
||||
{
|
||||
n->pprev = &n->next;
|
||||
n->next = (struct hlist_nulls_node *)NULLS_MARKER(NULL);
|
||||
WRITE_ONCE(n->pprev, &n->next);
|
||||
WRITE_ONCE(n->next, (struct hlist_nulls_node *)NULLS_MARKER(NULL));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -103,8 +103,8 @@ static const struct kernel_param_ops lt_bind_ops = {
|
||||
.get = param_get_cpumask,
|
||||
};
|
||||
|
||||
module_param_cb(bind_readers, <_bind_ops, &bind_readers, 0644);
|
||||
module_param_cb(bind_writers, <_bind_ops, &bind_writers, 0644);
|
||||
module_param_cb(bind_readers, <_bind_ops, &bind_readers, 0444);
|
||||
module_param_cb(bind_writers, <_bind_ops, &bind_writers, 0444);
|
||||
|
||||
long torture_sched_setaffinity(pid_t pid, const struct cpumask *in_mask, bool dowarn);
|
||||
|
||||
@@ -1211,6 +1211,10 @@ static void lock_torture_cleanup(void)
|
||||
cxt.cur_ops->exit();
|
||||
cxt.init_called = false;
|
||||
}
|
||||
|
||||
free_cpumask_var(bind_readers);
|
||||
free_cpumask_var(bind_writers);
|
||||
|
||||
torture_cleanup_end();
|
||||
}
|
||||
|
||||
|
||||
@@ -213,4 +213,19 @@ config RCU_STRICT_GRACE_PERIOD
|
||||
when looking for certain types of RCU usage bugs, for example,
|
||||
too-short RCU read-side critical sections.
|
||||
|
||||
|
||||
config RCU_DYNTICKS_TORTURE
|
||||
bool "Minimize RCU dynticks counter size"
|
||||
depends on RCU_EXPERT && !COMPILE_TEST
|
||||
default n
|
||||
help
|
||||
This option sets the width of the dynticks counter to its
|
||||
minimum usable value. This minimum width greatly increases
|
||||
the probability of flushing out bugs involving counter wrap,
|
||||
but it also increases the probability of extending grace period
|
||||
durations. This Kconfig option should therefore be avoided in
|
||||
production due to the consequent increased probability of OOMs.
|
||||
|
||||
This has no value for production and is only for testing.
|
||||
|
||||
endmenu # "RCU Debugging"
|
||||
|
||||
@@ -2438,10 +2438,8 @@ static bool rcu_torture_one_read(struct torture_random_state *trsp, long myid)
|
||||
newstate = rcutorture_extend_mask(rtors.readstate, trsp);
|
||||
WARN_ON_ONCE(newstate & RCUTORTURE_RDR_UPDOWN);
|
||||
rcutorture_one_extend(&rtors.readstate, newstate, trsp, rtors.rtrsp++);
|
||||
if (!rcu_torture_one_read_start(&rtors, trsp, myid)) {
|
||||
rcutorture_one_extend(&rtors.readstate, 0, trsp, rtors.rtrsp);
|
||||
if (!rcu_torture_one_read_start(&rtors, trsp, myid))
|
||||
return false;
|
||||
}
|
||||
rtors.rtrsp = rcutorture_loop_extend(&rtors.readstate, trsp, rtors.rtrsp);
|
||||
rcu_torture_one_read_end(&rtors, trsp);
|
||||
return true;
|
||||
|
||||
@@ -31,7 +31,7 @@ fi
|
||||
if ! cp "$oldrun/scenarios" $T/scenarios.oldrun
|
||||
then
|
||||
# Later on, can reconstitute this from console.log files.
|
||||
echo Prior run batches file does not exist: $oldrun/batches
|
||||
echo Prior run scenarios file does not exist: $oldrun/scenarios
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -68,7 +68,7 @@ usage () {
|
||||
echo " --datestamp string"
|
||||
echo " --dryrun"
|
||||
echo " --duration minutes | <seconds>s | <hours>h | <days>d"
|
||||
echo " --link hard|soft|copy"
|
||||
echo " --link hard|soft|copy|inplace|inplace-force"
|
||||
echo " --remote"
|
||||
echo " --rundir /new/res/path"
|
||||
echo "Command line: $scriptname $args"
|
||||
@@ -121,7 +121,7 @@ do
|
||||
shift
|
||||
;;
|
||||
--link)
|
||||
checkarg --link "hard|soft|copy" "$#" "$2" 'hard\|soft\|copy' '^--'
|
||||
checkarg --link "hard|soft|copy|inplace|inplace-force" "$#" "$2" 'hard\|soft\|copy\|inplace\|inplace-force' '^--'
|
||||
case "$2" in
|
||||
copy)
|
||||
arg_link="cp -R"
|
||||
@@ -132,6 +132,14 @@ do
|
||||
soft)
|
||||
arg_link="cp -Rs"
|
||||
;;
|
||||
inplace)
|
||||
arg_link="inplace"
|
||||
rundir="$oldrun"
|
||||
;;
|
||||
inplace-force)
|
||||
arg_link="inplace-force"
|
||||
rundir="$oldrun"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
;;
|
||||
@@ -172,21 +180,37 @@ fi
|
||||
|
||||
echo ---- Re-run results directory: $rundir
|
||||
|
||||
# Copy old run directory tree over and adjust.
|
||||
mkdir -p "`dirname "$rundir"`"
|
||||
if ! $arg_link "$oldrun" "$rundir"
|
||||
if test "$oldrun" != "$rundir"
|
||||
then
|
||||
echo "Cannot copy from $oldrun to $rundir."
|
||||
usage
|
||||
fi
|
||||
rm -f "$rundir"/*/{console.log,console.log.diags,qemu_pid,qemu-pid,qemu-retval,Warnings,kvm-test-1-run.sh.out,kvm-test-1-run-qemu.sh.out,vmlinux} "$rundir"/log
|
||||
touch "$rundir/log"
|
||||
echo $scriptname $args | tee -a "$rundir/log"
|
||||
echo $oldrun > "$rundir/re-run"
|
||||
if ! test -d "$rundir/../../bin"
|
||||
then
|
||||
$arg_link "$oldrun/../../bin" "$rundir/../.."
|
||||
# Copy old run directory tree over and adjust.
|
||||
mkdir -p "`dirname "$rundir"`"
|
||||
if ! $arg_link "$oldrun" "$rundir"
|
||||
then
|
||||
echo "Cannot copy from $oldrun to $rundir."
|
||||
usage
|
||||
fi
|
||||
rm -f "$rundir"/*/{console.log,console.log.diags,qemu_pid,qemu-pid,qemu-retval,Warnings,kvm-test-1-run.sh.out,kvm-test-1-run-qemu.sh.out,vmlinux} "$rundir"/log
|
||||
touch "$rundir/log"
|
||||
echo $scriptname $args | tee -a "$rundir/log"
|
||||
echo $oldrun > "$rundir/re-run"
|
||||
if ! test -d "$rundir/../../bin"
|
||||
then
|
||||
$arg_link "$oldrun/../../bin" "$rundir/../.."
|
||||
fi
|
||||
else
|
||||
# Check for a run having already happened.
|
||||
find "$rundir" -name console.log -print > $T/oldrun-console.log
|
||||
if test -s $T/oldrun-console.log
|
||||
then
|
||||
echo Run already took place in $rundir
|
||||
if test "$arg_link" = inplace
|
||||
then
|
||||
usage
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Find runs to be done based on their qemu-cmd files.
|
||||
for i in $rundir/*/qemu-cmd
|
||||
do
|
||||
cp "$i" $T
|
||||
|
||||
116
tools/testing/selftests/rcutorture/bin/kvm-series.sh
Executable file
116
tools/testing/selftests/rcutorture/bin/kvm-series.sh
Executable file
@@ -0,0 +1,116 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Usage: kvm-series.sh config-list commit-id-list [ kvm.sh parameters ]
|
||||
#
|
||||
# Tests the specified list of unadorned configs ("TREE01 SRCU-P" but not
|
||||
# "CFLIST" or "3*TRACE01") and an indication of a set of commits to test,
|
||||
# then runs each commit through the specified list of commits using kvm.sh.
|
||||
# The runs are grouped into a -series/config/commit directory tree.
|
||||
# Each run defaults to a duration of one minute.
|
||||
#
|
||||
# Run in top-level Linux source directory. Please note that this is in
|
||||
# no way a replacement for "git bisect"!!!
|
||||
#
|
||||
# This script is intended to replace kvm-check-branches.sh by providing
|
||||
# ease of use and faster execution.
|
||||
|
||||
T="`mktemp -d ${TMPDIR-/tmp}/kvm-series.sh.XXXXXX`"
|
||||
trap 'rm -rf $T' 0
|
||||
|
||||
scriptname=$0
|
||||
args="$*"
|
||||
|
||||
config_list="${1}"
|
||||
if test -z "${config_list}"
|
||||
then
|
||||
echo "$0: Need a quoted list of --config arguments for first argument."
|
||||
exit 1
|
||||
fi
|
||||
if test -z "${config_list}" || echo "${config_list}" | grep -q '\*'
|
||||
then
|
||||
echo "$0: Repetition ('*') not allowed in config list."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
commit_list="${2}"
|
||||
if test -z "${commit_list}"
|
||||
then
|
||||
echo "$0: Need a list of commits (e.g., HEAD^^^..) for second argument."
|
||||
exit 2
|
||||
fi
|
||||
git log --pretty=format:"%h" "${commit_list}" > $T/commits
|
||||
ret=$?
|
||||
if test "${ret}" -ne 0
|
||||
then
|
||||
echo "$0: Invalid commit list ('${commit_list}')."
|
||||
exit 2
|
||||
fi
|
||||
sha1_list=`cat $T/commits`
|
||||
|
||||
shift
|
||||
shift
|
||||
|
||||
RCUTORTURE="`pwd`/tools/testing/selftests/rcutorture"; export RCUTORTURE
|
||||
PATH=${RCUTORTURE}/bin:$PATH; export PATH
|
||||
. functions.sh
|
||||
|
||||
ret=0
|
||||
nfail=0
|
||||
nsuccess=0
|
||||
faillist=
|
||||
successlist=
|
||||
cursha1="`git rev-parse --abbrev-ref HEAD`"
|
||||
ds="`date +%Y.%m.%d-%H.%M.%S`-series"
|
||||
startdate="`date`"
|
||||
starttime="`get_starttime`"
|
||||
|
||||
echo " --- " $scriptname $args | tee -a $T/log
|
||||
echo " --- Results directory: " $ds | tee -a $T/log
|
||||
|
||||
for config in ${config_list}
|
||||
do
|
||||
sha_n=0
|
||||
for sha in ${sha1_list}
|
||||
do
|
||||
sha1=${sha_n}.${sha} # Enable "sort -k1nr" to list commits in order.
|
||||
echo Starting ${config}/${sha1} at `date` | tee -a $T/log
|
||||
git checkout "${sha}"
|
||||
time tools/testing/selftests/rcutorture/bin/kvm.sh --configs "$config" --datestamp "$ds/${config}/${sha1}" --duration 1 "$@"
|
||||
curret=$?
|
||||
if test "${curret}" -ne 0
|
||||
then
|
||||
nfail=$((nfail+1))
|
||||
faillist="$faillist ${config}/${sha1}(${curret})"
|
||||
else
|
||||
nsuccess=$((nsuccess+1))
|
||||
successlist="$successlist ${config}/${sha1}"
|
||||
# Successful run, so remove large files.
|
||||
rm -f ${RCUTORTURE}/$ds/${config}/${sha1}/{vmlinux,bzImage,System.map,Module.symvers}
|
||||
fi
|
||||
if test "${ret}" -eq 0
|
||||
then
|
||||
ret=${curret}
|
||||
fi
|
||||
sha_n=$((sha_n+1))
|
||||
done
|
||||
done
|
||||
git checkout "${cursha1}"
|
||||
|
||||
echo ${nsuccess} SUCCESSES: | tee -a $T/log
|
||||
echo ${successlist} | fmt | tee -a $T/log
|
||||
echo | tee -a $T/log
|
||||
echo ${nfail} FAILURES: | tee -a $T/log
|
||||
echo ${faillist} | fmt | tee -a $T/log
|
||||
if test -n "${faillist}"
|
||||
then
|
||||
echo | tee -a $T/log
|
||||
echo Failures across commits: | tee -a $T/log
|
||||
echo ${faillist} | tr ' ' '\012' | sed -e 's,^[^/]*/,,' -e 's/([0-9]*)//' |
|
||||
sort | uniq -c | sort -k2n | tee -a $T/log
|
||||
fi
|
||||
echo Started at $startdate, ended at `date`, duration `get_starttime_duration $starttime`. | tee -a $T/log
|
||||
echo Summary: Successes: ${nsuccess} Failures: ${nfail} | tee -a $T/log
|
||||
cp $T/log tools/testing/selftests/rcutorture/res/${ds}
|
||||
|
||||
exit "${ret}"
|
||||
@@ -16,3 +16,4 @@ CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
|
||||
CONFIG_RCU_EXPERT=y
|
||||
CONFIG_RCU_EQS_DEBUG=y
|
||||
CONFIG_RCU_LAZY=y
|
||||
CONFIG_RCU_DYNTICKS_TORTURE=y
|
||||
|
||||
Reference in New Issue
Block a user