diff --git a/check-parallel b/check-parallel
index d34c73f66..a341c9c2e 100755
--- a/check-parallel
+++ b/check-parallel
@@ -9,18 +9,115 @@
 # for them and runs the test in the background. When it completes, it tears down
 # the loop devices.
 
-export SRC_DIR="tests"
-basedir=$1
-shift
-check_args="$*"
+basedir=""
 runners=$(getconf _NPROCESSORS_CONF)
 runner_list=()
 runtimes=()
+show_test_list=
+run_section=""
 
+tmp=/tmp/check-parallel.$$
 
-# tests in auto group
-test_list=$(awk '/^[0-9].*auto/ { print "generic/" $1 }' tests/generic/group.list)
-test_list+=$(awk '/^[0-9].*auto/ { print "xfs/" $1 }' tests/xfs/group.list)
+export FSTYP=xfs
+
+# We need to include the test list processing first as argument parsing
+# requires test list parsing and setup.
+. ./common/test_names
+. ./common/test_list
+
+usage()
+{
+    echo "Usage: $0 [options] [testlist]"'
+
+check options
+    -D <dir>		Directory to run in
+    -n			Output test list, do not run tests
+    -r			randomize test order
+    --exact-order	run tests in the exact order specified
+    -s section		run only specified section from config file
+
+testlist options
+    -g group[,group...]	include tests from these groups
+    -x group[,group...]	exclude tests from these groups
+    -X exclude_file	exclude individual tests
+    -e testlist         exclude a specific list of tests
+    -E external_file	exclude individual tests
+    [testlist]		include tests matching names in testlist
+
+testlist argument is a list of tests in the form of <test dir>/<test name>.
+
+<test dir> is a directory under tests that contains a group file,
+with a list of the names of the tests in that directory.
+
+<test name> may be either a specific test file name (e.g. xfs/001) or
+a test file name match pattern (e.g. xfs/*).
+
+group argument is either a name of a tests group to collect from all
+the test dirs (e.g. quick) or a name of a tests group to collect from
+a specific tests dir in the form of <test dir>/<group name> (e.g. xfs/quick).
+If you want to run all the tests in the test suite, use "-g all" to specify all
+groups.
+
+exclude_file argument refers to a name of a file inside each test directory.
+for every test dir where this file is found, the listed test names are
+excluded from the list of tests to run from that test dir.
+
+external_file argument is a path to a single file containing a list of tests
+to exclude in the form of <test dir>/<test name>.
+
+examples:
+ check-parallel -D /mnt xfs/001
+ check-parallel -D /mnt -g quick
+ check-parallel -D /mnt -g xfs/quick
+ check-parallel -D /mnt -x stress xfs/*
+ check-parallel -D /mnt -X .exclude -g auto
+ check-parallel -D /mnt -E ~/.xfstests.exclude
+'
+	    exit 1
+}
+
+# Process command arguments first.
+while [ $# -gt 0 ]; do
+	case "$1" in
+	-\? | -h | --help) usage ;;
+
+	-D)	basedir=$2; shift ;;
+	-g)	_tl_setup_group $2 ; shift ;;
+	-e)	_tl_setup_exclude_tests $2 ; shift ;;
+	-E)	_tl_setup_exclude_file $2 ; shift ;;
+	-x)	_tl_setup_exclude_group $2; shift ;;
+	-X)	_tl_setup_exclude_subdir $2; shift ;;
+	-r)	_tl_setup_randomise ;;
+	--exact-order) _tl_setup_ordered ;;
+	-n)	show_test_list="yes" ;;
+
+	-s)	run_section="$run_section -s $2"; shift ;;
+
+	-*)	usage ;;
+	*)	# not an argument, we've got tests now.
+		_tl_setup_cli $*
+	esac
+
+	# if we've found a test specification, the break out of the processing
+	# loop before we shift the arguments so that this is the first argument
+	# that we process in the test arg loop below.
+	if $_tl_have_test_args; then
+		break;
+	fi
+
+	shift
+done
+
+if [ ! -d "$basedir" ]; then
+	echo "Invalid basedir specification"
+	usage
+fi
+if [ -d "$basedir/runner-0/" ]; then
+	prev_results=`ls -tr $basedir/runner-0/ | grep results | tail -1`
+fi
+
+_tl_prepare_test_list
+_tl_strip_test_list
 
 # grab all previously run tests and order them from highest runtime to lowest
 # We are going to try to run the longer tests first, hopefully so we can avoid
@@ -30,25 +127,23 @@ test_list+=$(awk '/^[0-9].*auto/ { print "xfs/" $1 }' tests/xfs/group.list)
 #
 # If we have tests in the test list that don't have runtimes recorded, then
 # append them to be run last.
-
-build_runner_list()
+time_order_test_list()
 {
 	local runtimes
 	local run_list=()
-	local prev_results=`ls -tr $basedir/runner-0/ | grep results | tail -1`
 
 	runtimes=$(cat $basedir/*/$prev_results/check.time | sort -k 2 -nr | cut -d " " -f 1)
 
 	# Iterate the timed list first. For every timed list entry that
 	# is found in the test_list, add it to the local runner list.
 	local -a _list=( $runtimes )
-	local -a _tlist=( $test_list )
+	local -a _tlist=( $_tl_tests )
 	local rx=0
 	local ix
 	local jx
 	#set -x
 	for ((ix = 0; ix < ${#_list[*]}; ix++)); do
-		echo $test_list | grep -q ${_list[$ix]}
+		echo $_tl_tests | grep -q ${_list[$ix]}
 		if [ $? == 0 ]; then
 			# add the test to the new run list and remove
 			# it from the remaining test list.
@@ -60,20 +155,21 @@ build_runner_list()
 
 	# The final test list is all the time ordered tests followed by
 	# all the tests we didn't find time records for.
-	test_list="${run_list[*]} ${_tlist[*]}"
+	_tl_tests="${run_list[*]} ${_tlist[*]}"
 }
 
-if [ -f $basedir/runner-0/results/check.time ]; then
-	build_runner_list
+if ! $_tl_randomise -a ! $_tl_exact_order; then
+	if [ -f $basedir/runner-0/$prev_results/check.time ]; then
+		time_order_test_list
+	fi
 fi
 
 # split the list amongst N runners
-
 split_runner_list()
 {
 	local ix
 	local rx
-	local -a _list=( $test_list )
+	local -a _list=( $_tl_tests )
 	for ((ix = 0; ix < ${#_list[*]}; ix++)); do
 		seq="${_list[$ix]}"
 		rx=$((ix % $runners))
@@ -137,7 +233,7 @@ runner_go()
 
 	# Run the tests in it's own mount namespace, as per the comment below
 	# that precedes making the basedir a private mount.
-	./src/nsexec -m ./check $check_args -x unreliable_in_parallel --exact-order ${runner_list[$id]} > $me/log 2>&1
+	./src/nsexec -m ./check $run_section -x unreliable_in_parallel --exact-order ${runner_list[$id]} > $me/log 2>&1
 
 	wait
 	sleep 1
@@ -165,6 +261,13 @@ cleanup()
 
 trap "cleanup; exit" HUP INT QUIT TERM
 
+split_runner_list
+if [ -n "$show_test_list" ]; then
+	echo Time ordered test list:
+	echo $_tl_tests
+	echo
+fi
+
 
 # Each parallel test runner needs to only see it's own mount points. If we
 # leave the basedir as shared, then all tests see all mounts and then we get
@@ -178,15 +281,23 @@ trap "cleanup; exit" HUP INT QUIT TERM
 # in it's own mount namespace so that they cannot see mounts that other tests
 # are performing.
 mount --make-private $basedir
-split_runner_list
+
 now=`date +%Y-%m-%d-%H:%M:%S`
 for ((i = 0; i < $runners; i++)); do
 
-	runner_go $i $now &
+	if [ -n "$show_test_list" ]; then
+		echo "Runner $i: ${runner_list[$i]}"
+	else
+		runner_go $i $now &
+	fi
 
 done;
 wait
 
+if [ -n "$show_test_list" ]; then
+	exit 0
+fi
+
 echo -n "Tests run: "
 grep Ran /mnt/xfs/*/log | sed -e 's,^.*:,,' -e 's, ,\n,g' | sort | uniq | wc -l
 
@@ -195,7 +306,7 @@ grep Failures: $basedir/*/log | uniq | sed -e "s/^.*Failures://" -e "s,\([0-9]\)
 echo
 
 echo Ten slowest tests - runtime in seconds:
-cat $basedir/*/results/check.time | sort -k 2 -nr | head -10
+cat $basedir/*/results-$now/check.time | sort -k 2 -nr | head -10
 
 echo
 echo Cleanup on Aisle 5?
diff --git a/common/test_list b/common/test_list
index 2432be6f7..2b3ae9fbf 100644
--- a/common/test_list
+++ b/common/test_list
@@ -24,6 +24,7 @@ _tl_file="$tmp.test_list"
 _tl_exclude_tests=()
 _tl_tests=
 
+# strip 'tests\' prefix from the provided test name
 _tl_strip_src_dir()
 {
 	local test="$1"
@@ -31,6 +32,12 @@ _tl_strip_src_dir()
 	echo ${test#$_tl_src_dir/}
 }
 
+# strip 'tests\' prefix from all the tests in the test list
+_tl_strip_test_list()
+{
+	_tl_tests=$(echo $_tl_tests | sed -e "s/$_tl_src_dir\///g")
+}
+
 get_sub_group_list()
 {
 	local d=$1
