From 683a3e6481a5cffc58496a590decf65909d0454b Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 18 May 2012 13:34:35 -0400 Subject: ktest: Fix kernelrevision with POST_BUILD The PRE_BUILD and POST_BUILD options of ktest are added to allow the user to add temporary patch to the system and remove it on builds. This is sometimes use to take a change from another git branch and add it to a series without the fix so that this series can be tested, when an unrelated bug exists in the series. The problem comes when a tagged commit is being used. For example, if v3.2 is being tested, and we add a patch to it, the kernelrelease for that commit will be 3.2.0+, but without the patch the version will be 3.2.0. This can cause problems when the kernelrelease is determined for creating the /lib/modules directory. The kernel booting has the '+' but the module directory will not, and the modules will be missing for that boot, and may not allow the kernel to succeed. The fix is to put the creation of the kernelrelease in the POST_BUILD logic, before it applies the POST_BUILD operation. The POST_BUILD is where the patch may be removed, removing the '+' from the kernelrelease. The calculation of the kernelrelease will also stay in its current location but will be ignored if it was already calculated previously. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'tools/testing') diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 4915408f6a9..50b6726c386 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -66,6 +66,7 @@ my %default = ( my $ktest_config; my $version; +my $have_version = 0; my $machine; my $ssh_user; my $tmpdir; @@ -1702,10 +1703,12 @@ sub install { sub get_version { # get the release name + return if ($have_version); doprint "$make kernelrelease ... "; $version = `$make kernelrelease | tail -1`; chomp($version); doprint "$version\n"; + $have_version = 1; } sub start_monitor_and_boot { @@ -1828,6 +1831,9 @@ sub build { my $save_no_reboot = $no_reboot; $no_reboot = 1; + # Calculate a new version from here. + $have_version = 0; + if (defined($pre_build)) { my $ret = run_command $pre_build; if (!$ret && defined($pre_build_die) && @@ -1887,6 +1893,9 @@ sub build { undef $redirect; if (defined($post_build)) { + # Because a post build may change the kernel version + # do it now. + get_version; my $ret = run_command $post_build; if (!$ret && defined($post_build_die) && $post_build_die) { @@ -3474,6 +3483,8 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $no_reboot = 1; $reboot_success = 0; + $have_version = 0; + $iteration = $i; my $makecmd = set_test_option("MAKE_CMD", $i); -- cgit v1.2.3-70-g09d2 From ccc513b688e1f409c03cfaa7117cda778331f6fb Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 21 May 2012 17:13:40 -0400 Subject: ktest: Add MIN_CONFIG_TYPE to allow making a minum .config that has network Add a MIN_CONFIG_TYPE that can be set to 'test' or 'boot'. The default is 'boot' which is what make_min_config has done previously: makes a config file that is the minimum needed to boot the target. But when MIN_CONFIG_TYPE is set to 'test', not only must the target boot, but it must also successfully run the TEST. This allows the creation of a config file that is the minimum to boot and also perform ssh to the target, or anything else a developer wants. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 14 ++++++++++++++ tools/testing/ktest/sample.conf | 10 ++++++++++ 2 files changed, 24 insertions(+) (limited to 'tools/testing') diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 50b6726c386..b6de81927cc 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -39,6 +39,7 @@ my %default = ( "CLEAR_LOG" => 0, "BISECT_MANUAL" => 0, "BISECT_SKIP" => 1, + "MIN_CONFIG_TYPE" => "boot", "SUCCESS_LINE" => "login:", "DETECT_TRIPLE_FAULT" => 1, "NO_INSTALL" => 0, @@ -107,6 +108,7 @@ my $minconfig; my $start_minconfig; my $start_minconfig_defined; my $output_minconfig; +my $minconfig_type; my $ignore_config; my $ignore_errors; my $addconfig; @@ -206,6 +208,7 @@ my %option_map = ( "MIN_CONFIG" => \$minconfig, "OUTPUT_MIN_CONFIG" => \$output_minconfig, "START_MIN_CONFIG" => \$start_minconfig, + "MIN_CONFIG_TYPE" => \$minconfig_type, "IGNORE_CONFIG" => \$ignore_config, "TEST" => \$run_test, "ADD_CONFIG" => \$addconfig, @@ -3128,6 +3131,12 @@ sub test_this_config { sub make_min_config { my ($i) = @_; + my $type = $minconfig_type; + if ($type ne "boot" && $type ne "test") { + fail "Invalid MIN_CONFIG_TYPE '$minconfig_type'\n" . + " make_min_config works only with 'boot' and 'test'\n" and return; + } + if (!defined($output_minconfig)) { fail "OUTPUT_MIN_CONFIG not defined" and return; } @@ -3287,6 +3296,11 @@ sub make_min_config { build "oldconfig" or $failed = 1; if (!$failed) { start_monitor_and_boot or $failed = 1; + + if ($type eq "test" && !$failed) { + do_run_test or $failed = 1; + } + end_monitor; } diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index b682456afda..1c1b7dc1343 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -1105,10 +1105,20 @@ # and will not be tested again in later runs. # (optional) # +# MIN_CONFIG_TYPE can be either 'boot' or 'test'. With 'boot' it will +# test if the created config can just boot the machine. If this is +# set to 'test', then the TEST option must be defined and the created +# config will not only boot the target, but also make sure that the +# config lets the test succeed. This is useful to make sure the final +# config that is generated allows network activity (ssh). +# (optional) +# # Example: # # TEST_TYPE = make_min_config # OUTPUT_MIN_CONFIG = /path/to/config-new-min # START_MIN_CONFIG = /path/to/config-min # IGNORE_CONFIG = /path/to/config-tested +# MIN_CONFIG_TYPE = test +# TEST = ssh ${USER}@${MACHINE} echo hi # -- cgit v1.2.3-70-g09d2 From 43de3316e97c5a9ac4446aa33a893c15cea512b7 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 21 May 2012 23:35:12 -0400 Subject: ktest: Add USE_OUTPUT_MIN_CONFIG to avoid prompt on make_min_config If the file that OUTPUT_MIN_CONFIG exists then ktest.pl will prompt the user and ask them if the OUTPUT_MIN_CONFIG should be used as the starting point for make_min_config instead of MIN_CONFIG. This is usually the case, and to allow the user to do so, which is helpful if the user is creating different min configs based on tests, and they know one is a superset of another test, they can set USE_OUTPUT_MIN_CONFIG to one, which will prevent kest.pl from prompting to use the OUTPUT_MIN_CONFIG and it will just use it. If USE_OUTPUT_MIN_CONIFG is set to zero, then ktest.pl will continue to use MIN_CONFIG instead. The default is that USE_OUTPUT_MIN_CONFIG is undefined. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 11 ++++++++++- tools/testing/ktest/sample.conf | 6 ++++++ 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'tools/testing') diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index b6de81927cc..292b13ad03f 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -109,6 +109,7 @@ my $start_minconfig; my $start_minconfig_defined; my $output_minconfig; my $minconfig_type; +my $use_output_minconfig; my $ignore_config; my $ignore_errors; my $addconfig; @@ -209,6 +210,7 @@ my %option_map = ( "OUTPUT_MIN_CONFIG" => \$output_minconfig, "START_MIN_CONFIG" => \$start_minconfig, "MIN_CONFIG_TYPE" => \$minconfig_type, + "USE_OUTPUT_MIN_CONFIG" => \$use_output_minconfig, "IGNORE_CONFIG" => \$ignore_config, "TEST" => \$run_test, "ADD_CONFIG" => \$addconfig, @@ -3146,8 +3148,15 @@ sub make_min_config { # that instead. if (-f $output_minconfig && !$start_minconfig_defined) { print "$output_minconfig exists\n"; - if (read_yn " Use it as minconfig?") { + if (!defined($use_output_minconfig)) { + if (read_yn " Use it as minconfig?") { + $start_minconfig = $output_minconfig; + } + } elsif ($use_output_minconfig > 0) { + doprint "Using $output_minconfig as MIN_CONFIG\n"; $start_minconfig = $output_minconfig; + } else { + doprint "Set to still use MIN_CONFIG as starting point\n"; } } diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 1c1b7dc1343..0e8191b6c5e 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -1113,6 +1113,12 @@ # config that is generated allows network activity (ssh). # (optional) # +# USE_OUTPUT_MIN_CONFIG set this to 1 if you do not want to be prompted +# about using the OUTPUT_MIN_CONFIG as the MIN_CONFIG as the starting +# point. Set it to 0 if you want to always just use the given MIN_CONFIG. +# If it is not defined, it will prompt you to pick which config +# to start with (MIN_CONFIG or OUTPUT_MIN_CONFIG). +# # Example: # # TEST_TYPE = make_min_config -- cgit v1.2.3-70-g09d2 From 6d76f469c8ac9ef0d769cca0d9cee4375b3d6293 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 22 May 2012 00:08:30 -0400 Subject: ktest: Add useful example configs I've been asked several times to provide more useful example configs for ktest.pl, as the sample.conf is too complex (because it explains all configs). This adds configs broken up by use case, and these configs are based on actual configs that I use on a daily basis. Signed-off-by: Steven Rostedt --- tools/testing/ktest/examples/include/bisect.conf | 90 ++++++++++++ tools/testing/ktest/examples/include/defaults.conf | 157 +++++++++++++++++++++ .../testing/ktest/examples/include/min-config.conf | 60 ++++++++ .../testing/ktest/examples/include/patchcheck.conf | 74 ++++++++++ tools/testing/ktest/examples/include/tests.conf | 74 ++++++++++ tools/testing/ktest/examples/test.conf | 62 ++++++++ 6 files changed, 517 insertions(+) create mode 100644 tools/testing/ktest/examples/include/bisect.conf create mode 100644 tools/testing/ktest/examples/include/defaults.conf create mode 100644 tools/testing/ktest/examples/include/min-config.conf create mode 100644 tools/testing/ktest/examples/include/patchcheck.conf create mode 100644 tools/testing/ktest/examples/include/tests.conf create mode 100644 tools/testing/ktest/examples/test.conf (limited to 'tools/testing') diff --git a/tools/testing/ktest/examples/include/bisect.conf b/tools/testing/ktest/examples/include/bisect.conf new file mode 100644 index 00000000000..009bea65bfb --- /dev/null +++ b/tools/testing/ktest/examples/include/bisect.conf @@ -0,0 +1,90 @@ +# +# This example shows the bisect tests (git bisect and config bisect) +# + + +# The config that includes this file may define a RUN_TEST +# variable that will tell this config what test to run. +# (what to set the TEST option to). +# +DEFAULTS IF NOT DEFINED RUN_TEST +# Requires that hackbench is in the PATH +RUN_TEST := ${SSH} hackbench 50 + + +# Set TEST to 'bisect' to do a normal git bisect. You need +# to modify the options below to make it bisect the exact +# commits you are interested in. +# +TEST_START IF ${TEST} == bisect +TEST_TYPE = bisect +# You must set the commit that was considered good (git bisect good) +BISECT_GOOD = v3.3 +# You must set the commit that was considered bad (git bisect bad) +BISECT_BAD = HEAD +# It's best to specify the branch to checkout before starting the bisect. +CHECKOUT = origin/master +# This can be build, boot, or test. Here we are doing a bisect +# that requires to run a test to know if the bisect was good or bad. +# The test should exit with 0 on good, non-zero for bad. But see +# the BISECT_RET_* options in samples.conf to override this. +BISECT_TYPE = test +TEST = ${RUN_TEST} +# It is usually a good idea to confirm that the GOOD and the BAD +# commits are truly good and bad respectively. Having BISECT_CHECK +# set to 1 will check both that the good commit works and the bad +# commit fails. If you only want to check one or the other, +# set BISECT_CHECK to 'good' or to 'bad'. +BISECT_CHECK = 1 +#BISECT_CHECK = good +#BISECT_CHECK = bad + +# Usually it's a good idea to specify the exact config you +# want to use throughout the entire bisect. Here we placed +# it in the directory we called ktest.pl from and named it +# 'config-bisect'. +MIN_CONFIG = ${THIS_DIR}/config-bisect +# By default, if we are doing a BISECT_TYPE = test run but the +# build or boot fails, ktest.pl will do a 'git bisect skip'. +# Uncomment the below option to make ktest stop testing on such +# an error. +#BISECT_SKIP = 0 +# Now if you had BISECT_SKIP = 0 and the test fails, you can +# examine what happened and then do 'git bisect log > /tmp/replay' +# Set BISECT_REPLAY to /tmp/replay and ktest.pl will run the +# 'git bisect replay /tmp/replay' before continuing the bisect test. +#BISECT_REPLAY = /tmp/replay +# If you used BISECT_REPLAY after the bisect test failed, you may +# not want to continue the bisect on that commit that failed. +# By setting BISECT_START to a new commit. ktest.pl will checkout +# that commit after it has performed the 'git bisect replay' but +# before it continues running the bisect test. +#BISECT_START = 2545eb6198e7e1ec50daa0cfc64a4cdfecf24ec9 + +# Now if you don't trust ktest.pl to make the decisions for you, then +# set BISECT_MANUAL to 1. This will cause ktest.pl not to decide +# if the commit was good or bad. Instead, it will ask you to tell +# it if the current commit was good. In the mean time, you could +# take the result, load it on any machine you want. Run several tests, +# or whatever you feel like. Then, when you are happy, you can tell +# ktest if you think it was good or not and ktest.pl will continue +# the git bisect. You can even change what commit it is currently at. +#BISECT_MANUAL = 1 + + +# One of the unique tests that ktest does is the config bisect. +# Currently (which hopefully will be fixed soon), the bad config +# must be a superset of the good config. This is because it only +# searches for a config that causes the target to fail. If the +# good config is not a subset of the bad config, or if the target +# fails because of a lack of a config, then it will not find +# the config for you. +TEST_START IF ${TEST} == config-bisect +TEST_TYPE = config_bisect +# set to build, boot, test +CONFIG_BISECT_TYPE = boot +# Set the config that is considered bad. +CONFIG_BISECT = ${THIS_DIR}/config-bad +# This config is optional. By default it uses the +# MIN_CONFIG as the good config. +CONFIG_BISECT_GOOD = ${THIS_DIR}/config-good diff --git a/tools/testing/ktest/examples/include/defaults.conf b/tools/testing/ktest/examples/include/defaults.conf new file mode 100644 index 00000000000..323a552ce64 --- /dev/null +++ b/tools/testing/ktest/examples/include/defaults.conf @@ -0,0 +1,157 @@ +# This file holds defaults for most the tests. It defines the options that +# are most common to tests that are likely to be shared. +# +# Note, after including this file, a config file may override any option +# with a DEFAULTS OVERRIDE section. +# + +# For those cases that use the same machine to boot a 64 bit +# and a 32 bit version. The MACHINE is the DNS name to get to the +# box (usually different if it was 64 bit or 32 bit) but the +# BOX here is defined as a variable that will be the name of the box +# itself. It is useful for calling scripts that will power cycle +# the box, as only one script needs to be created to power cycle +# even though the box itself has multiple operating systems on it. +# By default, BOX and MACHINE are the same. + +DEFAULTS IF NOT DEFINED BOX +BOX := ${MACHINE} + + +# Consider each box as 64 bit box, unless the config including this file +# has defined BITS = 32 + +DEFAULTS IF NOT DEFINED BITS +BITS := 64 + + +DEFAULTS + +# THIS_DIR is used through out the configs and defaults to ${PWD} which +# is the directory that ktest.pl was called from. + +THIS_DIR := ${PWD} + + +# to orginize your configs, having each machine save their configs +# into a separate directly is useful. +CONFIG_DIR := ${THIS_DIR}/configs/${MACHINE} + +# Reset the log before running each test. +CLEAR_LOG = 1 + +# As installing kernels usually requires root privilege, default the +# user on the target as root. It is also required that the target +# allows ssh to root from the host without asking for a password. + +SSH_USER = root + +# For accesing the machine, we will ssh to root@machine. +SSH := ssh ${SSH_USER}@${MACHINE} + +# Update this. The default here is ktest will ssh to the target box +# and run a script called 'run-test' located on that box. +TEST = ${SSH} run-test + +# Point build dir to the git repo you use +BUILD_DIR = ${THIS_DIR}/linux.git + +# Each machine will have its own output build directory. +OUTPUT_DIR = ${THIS_DIR}/build/${MACHINE} + +# Yes this config is focused on x86 (but ktest works for other archs too) +BUILD_TARGET = arch/x86/boot/bzImage +TARGET_IMAGE = /boot/vmlinuz-test + +# have directory for the scripts to reboot and power cycle the boxes +SCRIPTS_DIR := ${THIS_DIR}/scripts + +# You can have each box/machine have a script to power cycle it. +# Name your script -cycle. +POWER_CYCLE = ${SCRIPTS_DIR}/${BOX}-cycle + +# This script is used to power off the box. +POWER_OFF = ${SCRIPTS_DIR}/${BOX}-poweroff + +# Keep your test kernels separate from your other kernels. +LOCALVERSION = -test + +# The /boot/grub/menu.lst is searched for the line: +# title Test Kernel +# and ktest will use that kernel to reboot into. +# For grub2 or other boot loaders, you need to set BOOT_TYPE +# to 'script' and define other ways to load the kernel. +# See snowball.conf example. +# +GRUB_MENU = Test Kernel + +# The kernel build will use this option. +BUILD_OPTIONS = -j8 + +# Keeping the log file with the output dir is convenient. +LOG_FILE = ${OUTPUT_DIR}/${MACHINE}.log + +# Each box should have their own minum configuration +# See min-config.conf +MIN_CONFIG = ${CONFIG_DIR}/config-min + +# For things like randconfigs, there may be configs you find that +# are already broken, or there may be some configs that you always +# want set. Uncomment ADD_CONFIG and point it to the make config files +# that set the configs you want to keep on (or off) in your build. +# ADD_CONFIG is usually something to add configs to all machines, +# where as, MIN_CONFIG is specific per machine. +#ADD_CONFIG = ${THIS_DIR}/config-broken ${THIS_DIR}/config-general + +# To speed up reboots for bisects and patchcheck, instead of +# waiting 60 seconds for the console to be idle, if this line is +# seen in the console output, ktest will know the good kernel has +# finished rebooting and it will be able to continue the tests. +REBOOT_SUCCESS_LINE = ${MACHINE} login: + +# The following is different ways to end the test. +# by setting the variable REBOOT to: none, error, fail or +# something else, ktest will power cycle or reboot the target box +# at the end of the tests. +# +# REBOOT := none +# Don't do anything at the end of the test. +# +# REBOOT := error +# Reboot the box if ktest detects an error +# +# REBOOT := fail +# Do not stop on failure, and after all tests are complete +# power off the box (for both success and error) +# This is good to run over a weekend and you don't want to waste +# electricity. +# + +DEFAULTS IF ${REBOOT} == none +REBOOT_ON_SUCCESS = 0 +REBOOT_ON_ERROR = 0 +POWEROFF_ON_ERROR = 0 +POWEROFF_ON_SUCCESS = 0 + +DEFAULTS ELSE IF ${REBOOT} == error +REBOOT_ON_SUCCESS = 0 +REBOOT_ON_ERROR = 1 +POWEROFF_ON_ERROR = 0 +POWEROFF_ON_SUCCESS = 0 + +DEFAULTS ELSE IF ${REBOOT} == fail +REBOOT_ON_SUCCESS = 0 +POWEROFF_ON_ERROR = 1 +POWEROFF_ON_SUCCESS = 1 +POWEROFF_AFTER_HALT = 120 +DIE_ON_FAILURE = 0 + +# Store the failure information into this directory +# such as the .config, dmesg, and build log. +STORE_FAILURES = ${THIS_DIR}/failures + +DEFAULTS ELSE +REBOOT_ON_SUCCESS = 1 +REBOOT_ON_ERROR = 1 +POWEROFF_ON_ERROR = 0 +POWEROFF_ON_SUCCESS = 0 diff --git a/tools/testing/ktest/examples/include/min-config.conf b/tools/testing/ktest/examples/include/min-config.conf new file mode 100644 index 00000000000..c703cc46d15 --- /dev/null +++ b/tools/testing/ktest/examples/include/min-config.conf @@ -0,0 +1,60 @@ +# +# This file has some examples for creating a MIN_CONFIG. +# (A .config file that is the minimum for a machine to boot, or +# to boot and make a network connection.) +# +# A MIN_CONFIG is very useful as it is the minimum configuration +# needed to boot a given machine. You can debug someone else's +# .config by only setting the configs in your MIN_CONFIG. The closer +# your MIN_CONFIG is to the true minimum set of configs needed to +# boot your machine, the closer the config you test with will be +# to the users config that had the failure. +# +# The make_min_config test allows you to create a MIN_CONFIG that +# is truly the minimum set of configs needed to boot a box. +# +# In this example, the final config will reside in +# ${CONFIG_DIR}/config-new-min and ${CONFIG_DIR}/config-new-min-net. +# Just move one to the location you have set for MIN_CONFIG. +# +# The first test creates a MIN_CONFIG that will be the minimum +# configuration to boot ${MACHINE} and be able to ssh to it. +# +# The second test creates a MIN_CONFIG that will only boot +# the target and most likely will not let you ssh to it. (Notice +# how the second test uses the first test's result to continue with. +# This is because the second test config is a subset of the first). +# +# The ${CONFIG_DIR}/config-skip (and -net) will hold the configs +# that ktest.pl found would not boot the target without them set. +# The config-new-min holds configs that ktest.pl could not test +# directly because another config that was needed to boot the box +# selected them. Sometimes it is possible that this file will hold +# the true minimum configuration. You can test to see if this is +# the case by running the boot test with BOOT_TYPE = allnoconfig and +# setting setting the MIN_CONFIG to ${CONFIG_DIR}/config-skip. If the +# machine still boots, then you can use the config-skip as your MIN_CONFIG. +# +# These tests can run for several hours (and perhaps days). +# It's OK to kill the test with a Ctrl^C. By restarting without +# modifying this config, ktest.pl will notice that the config-new-min(-net) +# exists, and will use that instead as the starting point. +# The USE_OUTPUT_MIN_CONFIG is set to 1 to keep ktest.pl from asking +# you if you want to use the OUTPUT_MIN_CONFIG as the starting point. +# By using the OUTPUT_MIN_CONFIG as the starting point will allow ktest.pl to +# start almost where it left off. +# +TEST_START IF ${TEST} == min-config +TEST_TYPE = make_min_config +OUTPUT_MIN_CONFIG = ${CONFIG_DIR}/config-new-min-net +IGNORE_CONFIG = ${CONFIG_DIR}/config-skip-net +MIN_CONFIG_TYPE = test +TEST = ${SSH} echo hi +USE_OUTPUT_MIN_CONFIG = 1 + +TEST_START IF ${TEST} == min-config && ${MULTI} +TEST_TYPE = make_min_config +OUTPUT_MIN_CONFIG = ${CONFIG_DIR}/config-new-min +IGNORE_CONFIG = ${CONFIG_DIR}/config-skip +MIN_CONFIG = ${CONFIG_DIR}/config-new-min-net +USE_OUTPUT_MIN_CONFIG = 1 diff --git a/tools/testing/ktest/examples/include/patchcheck.conf b/tools/testing/ktest/examples/include/patchcheck.conf new file mode 100644 index 00000000000..339d3e1700f --- /dev/null +++ b/tools/testing/ktest/examples/include/patchcheck.conf @@ -0,0 +1,74 @@ +# patchcheck.conf +# +# This contains a test that takes two git commits and will test each +# commit between the two. The build test will look at what files the +# commit has touched, and if any of those files produce a warning, then +# the build will fail. + + +# PATCH_START is the commit to begin with and PATCH_END is the commit +# to end with (inclusive). This is similar to doing a git rebase -i PATCH_START~1 +# and then testing each commit and doing a git rebase --continue. +# You can use a SHA1, a git tag, or anything that git will accept for a checkout + +PATCH_START := HEAD~3 +PATCH_END := HEAD + +# Change PATCH_CHECKOUT to be the branch you want to test. The test will +# do a git checkout of this branch before starting. Obviously both +# PATCH_START and PATCH_END must be in this branch (and PATCH_START must +# be contained by PATCH_END). + +PATCH_CHECKOUT := test/branch + +# Usually it's a good idea to have a set config to use for testing individual +# patches. +PATCH_CONFIG := ${CONFIG_DIR}/config-patchcheck + +# Change PATCH_TEST to run some test for each patch. Each commit that is +# tested, after it is built and installed on the test machine, this command +# will be executed. Usually what is done is to ssh to the target box and +# run some test scripts. If you just want to boot test your patches +# comment PATCH_TEST out. +PATCH_TEST := ${SSH} "/usr/local/bin/ktest-test-script" + +DEFAULTS IF DEFINED PATCH_TEST +PATCH_TEST_TYPE := test + +DEFAULTS ELSE +PATCH_TEST_TYPE := boot + +# If for some reason a file has a warning that one of your patches touch +# but you do not care about it, set IGNORE_WARNINGS to that commit(s) +# (space delimited) +#IGNORE_WARNINGS = 39eaf7ef884dcc44f7ff1bac803ca2a1dcf43544 6edb2a8a385f0cdef51dae37ff23e74d76d8a6ce + +# If you are running a multi test, and the test failed on the first +# test but on, say the 5th patch. If you want to restart on the +# fifth patch, set PATCH_START1. This will make the first test start +# from this commit instead of the PATCH_START commit. +# Note, do not change this option. Just define PATCH_START1 in the +# top config (the one you pass to ktest.pl), and this will use it, +# otherwise it will just use PATCH_START if PATCH_START1 is not defined. +DEFAULTS IF NOT DEFINED PATCH_START1 +PATCH_START1 := ${PATCH_START} + +TEST_START IF ${TEST} == patchcheck +TEST_TYPE = patchcheck +MIN_CONFIG = ${PATCH_CONFIG} +TEST = ${PATCH_TEST} +PATCHCHECK_TYPE = ${PATCH_TEST_TYPE} +PATCHCHECK_START = ${PATCH_START1} +PATCHCHECK_END = ${PATCH_END} +CHECKOUT = ${PATCH_CHECKOUT} + +TEST_START IF ${TEST} == patchcheck && ${MULTI} +TEST_TYPE = patchcheck +MIN_CONFIG = ${PATCH_CONFIG} +TEST = ${PATCH_TEST} +PATCHCHECK_TYPE = ${PATCH_TEST_TYPE} +PATCHCHECK_START = ${PATCH_START} +PATCHCHECK_END = ${PATCH_END} +CHECKOUT = ${PATCH_CHECKOUT} +# Use multi to test different compilers? +MAKE_CMD = CC=gcc-4.5.1 make diff --git a/tools/testing/ktest/examples/include/tests.conf b/tools/testing/ktest/examples/include/tests.conf new file mode 100644 index 00000000000..4fdb811bd81 --- /dev/null +++ b/tools/testing/ktest/examples/include/tests.conf @@ -0,0 +1,74 @@ +# +# This is an example of various tests that you can run +# +# The variable TEST can be of boot, build, randconfig, or test. +# +# Note that TEST is a variable created with ':=' and only exists +# throughout the config processing (not during the tests itself). +# +# The TEST option (defined with '=') is used to tell ktest.pl +# what test to run after a successful boot. The TEST option is +# persistent into the test runs. +# + +# The config that includes this file may define a BOOT_TYPE +# variable that tells this config what type of boot test to run. +# If it's not defined, the below DEFAULTS will set the default +# to 'oldconfig'. +# +DEFAULTS IF NOT DEFINED BOOT_TYPE +BOOT_TYPE := oldconfig + +# The config that includes this file may define a RUN_TEST +# variable that will tell this config what test to run. +# (what to set the TEST option to). +# +DEFAULTS IF NOT DEFINED RUN_TEST +# Requires that hackbench is in the PATH +RUN_TEST := ${SSH} hackbench 50 + + +# If TEST is set to 'boot' then just build a kernel and boot +# the target. +TEST_START IF ${TEST} == boot +TEST_TYPE = boot +# Notice how we set the BUILD_TYPE option to the BOOT_TYPE variable. +BUILD_TYPE = ${BOOT_TYPE} +# Do not do a make mrproper. +BUILD_NOCLEAN = 1 + +# If you only want to build the kernel, and perhaps install +# and test it yourself, then just set TEST to build. +TEST_START IF ${TEST} == build +TEST_TYPE = build +BUILD_TYPE = ${BOOT_TYPE} +BUILD_NOCLEAN = 1 + +# Build, install, boot and test with a randconfg 10 times. +# It is important that you have set MIN_CONFIG in the config +# that includes this file otherwise it is likely that the +# randconfig will not have the neccessary configs needed to +# boot your box. This version of the test requires a min +# config that has enough to make sure the target has network +# working. +TEST_START ITERATE 10 IF ${TEST} == randconfig +MIN_CONFIG = ${CONFIG_DIR}/config-min-net +TEST_TYPE = test +BUILD_TYPE = randconfig +TEST = ${RUN_TEST} + +# This is the same as above, but only tests to a boot prompt. +# The MIN_CONFIG used here does not need to have networking +# working. +TEST_START ITERATE 10 IF ${TEST} == randconfig && ${MULTI} +TEST_TYPE = boot +BUILD_TYPE = randconfig +MIN_CONFIG = ${CONFIG_DIR}/config-min +MAKE_CMD = make + +# This builds, installs, boots and tests the target. +TEST_START IF ${TEST} == test +TEST_TYPE = test +BUILD_TYPE = ${BOOT_TYPE} +TEST = ${RUN_TEST} +BUILD_NOCLEAN = 1 diff --git a/tools/testing/ktest/examples/test.conf b/tools/testing/ktest/examples/test.conf new file mode 100644 index 00000000000..b725210efb7 --- /dev/null +++ b/tools/testing/ktest/examples/test.conf @@ -0,0 +1,62 @@ +# +# Generic config for a machine +# + +# Name your machine (the DNS name, what you ssh to) +MACHINE = foo + +# BOX can be different than foo, if the machine BOX has +# multiple partitions with different systems installed. For example, +# you may have a i386 and x86_64 installation on a test box. +# If this is the case, MACHINE defines the way to connect to the +# machine, which may be different between which system the machine +# is booting into. BOX is used for the scripts to reboot and power cycle +# the machine, where it does not matter which system the machine boots into. +# +#BOX := bar + +# Define a way to read the console +CONSOLE = stty -F /dev/ttyS0 115200 parodd; cat /dev/ttyS0 + +# The include files will set up the type of test to run. Just set TEST to +# which test you want to run. +# +# TESTS = patchcheck, randconfig, boot, test, config-bisect, bisect, min-config +# +# See the include/*.conf files that define these tests +# +TEST := patchcheck + +# Some tests may have more than one test to run. Define MULTI := 1 to run +# the extra tests. +MULTI := 0 + +# In case you want to differentiate which type of system you are testing +BITS := 64 + +# REBOOT = none, error, fail, empty +# See include/defaults.conf +REBOOT := empty + +# The defaults file will set up various settings that can be used by all +# machine configs. +INCLUDE include/defaults.conf + +# In case you need to add a patch for a bisect or something +#PRE_BUILD = patch -p1 < ${THIS_DIR}/fix.patch + +# Reset the repo after the build and remove all 'test' modules from the target +# Notice that DO_POST_BUILD is a variable (defined by ':=') and POST_BUILD +# is the option (defined by '=') + +DO_POST_BUILD := git reset --hard +POST_BUILD = ${SSH} 'rm -rf /lib/modules/*-test*'; ${DO_POST_BUILD} + +# The following files each handle a different test case. +# Having them included allows you to set up more than one machine and share +# the same tests. +INCLUDE include/patchcheck.conf +INCLUDE include/tests.conf +INCLUDE include/bisect.conf +INCLUDE include/min-config.conf + -- cgit v1.2.3-70-g09d2 From 3a7bef7917f8fd103197b4cc5969a3125d45deec Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 22 May 2012 00:10:12 -0400 Subject: ktest: Add kvm.conf example config Add an example config that explains how to use ktest with a virtual guest as the target. Signed-off-by: Steven Rostedt --- tools/testing/ktest/examples/kvm.conf | 88 +++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 tools/testing/ktest/examples/kvm.conf (limited to 'tools/testing') diff --git a/tools/testing/ktest/examples/kvm.conf b/tools/testing/ktest/examples/kvm.conf new file mode 100644 index 00000000000..831c7c5395f --- /dev/null +++ b/tools/testing/ktest/examples/kvm.conf @@ -0,0 +1,88 @@ +# +# This config is an example usage of ktest.pl with a kvm guest +# +# The guest is called 'Guest' and this would be something that +# could be run on the host to test a virtual machine target. + +MACHINE = Guest + + +# Use virsh to read the serial console of the guest +CONSOLE = virsh console ${MACHINE} + +#*************************************# +# This part is the same as test.conf # +#*************************************# + +# The include files will set up the type of test to run. Just set TEST to +# which test you want to run. +# +# TESTS = patchcheck, randconfig, boot, test, config-bisect, bisect, min-config +# +# See the include/*.conf files that define these tests +# +TEST := patchcheck + +# Some tests may have more than one test to run. Define MULTI := 1 to run +# the extra tests. +MULTI := 0 + +# In case you want to differentiate which type of system you are testing +BITS := 64 + +# REBOOT = none, error, fail, empty +# See include/defaults.conf +REBOOT := empty + + +# The defaults file will set up various settings that can be used by all +# machine configs. +INCLUDE include/defaults.conf + + +#*************************************# +# Now we are different from test.conf # +#*************************************# + + +# The example here assumes that Guest is running a Fedora release +# that uses dracut for its initfs. The POST_INSTALL will be executed +# after the install of the kernel and modules are complete. +# +POST_INSTALL = ${SSH} /sbin/dracut -f /boot/initramfs-test.img $KERNEL_VERSION + +# Guests sometimes get stuck on reboot. We wait 3 seconds after running +# the reboot command and then do a full power-cycle of the guest. +# This forces the guest to restart. +# +POWERCYCLE_AFTER_REBOOT = 3 + +# We do the same after the halt command, but this time we wait 20 seconds. +POWEROFF_AFTER_HALT = 20 + + +# As the defaults.conf file has a POWER_CYCLE option already defined, +# and options can not be defined in the same section more than once +# (all DEFAULTS sections are considered the same). We use the +# DEFAULTS OVERRIDE to tell ktest.pl to ignore the previous defined +# options, for the options set in the OVERRIDE section. +# +DEFAULTS OVERRIDE + +# Instead of using the default POWER_CYCLE option defined in +# defaults.conf, we use virsh to cycle it. To do so, we destroy +# the guest, wait 5 seconds, and then start it up again. +# Crude, but effective. +# +POWER_CYCLE = virsh destroy ${MACHINE}; sleep 5; virsh start ${MACHINE} + + +DEFAULTS + +# The following files each handle a different test case. +# Having them included allows you to set up more than one machine and share +# the same tests. +INCLUDE include/patchcheck.conf +INCLUDE include/tests.conf +INCLUDE include/bisect.conf +INCLUDE include/min-config.conf -- cgit v1.2.3-70-g09d2 From 2e109526225a560ef49d49a3bbae62f5cf3ad806 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 22 May 2012 00:11:00 -0400 Subject: ktest: Add an example config that does cross compiling of several archs Add the config that I use to test several archs. I downloaded several cross compilers from: http://kernel.org/pub/tools/crosstool/files/bin/x86_64/ and this config is an example to crosscompile several archs to make sure that your changes do not break archs that you are not working on. Signed-off-by: Steven Rostedt --- tools/testing/ktest/examples/crosstests.conf | 260 +++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 tools/testing/ktest/examples/crosstests.conf (limited to 'tools/testing') diff --git a/tools/testing/ktest/examples/crosstests.conf b/tools/testing/ktest/examples/crosstests.conf new file mode 100644 index 00000000000..46736604c26 --- /dev/null +++ b/tools/testing/ktest/examples/crosstests.conf @@ -0,0 +1,260 @@ +# +# Example config for cross compiling +# +# In this config, it is expected that the tool chains from: +# +# http://kernel.org/pub/tools/crosstool/files/bin/x86_64/ +# +# running on a x86_64 system have been downloaded and installed into: +# +# /usr/local/ +# +# such that the compiler binaries are something like: +# +# /usr/local/gcc-4.5.2-nolibc/mips-linux/bin/mips-linux-gcc +# +# Some of the archs will use gcc-4.5.1 instead of gcc-4.5.2 +# this config uses variables to differentiate them. +# +# Comments describe some of the options, but full descriptions of +# options are described in the samples.conf file. + +# ${PWD} is defined by ktest.pl to be the directory that the user +# was in when they executed ktest.pl. It may be better to hardcode the +# path name here. THIS_DIR is the variable used through out the config file +# in case you want to change it. + +THIS_DIR := ${PWD} + +# Update the BUILD_DIR option to the location of your git repo you want to test. +BUILD_DIR = ${THIS_DIR}/linux.git + +# The build will go into this directory. It will be created when you run the test. +OUTPUT_DIR = ${THIS_DIR}/cross-compile + +# The build will be compiled with -j8 +BUILD_OPTIONS = -j8 + +# The test will not stop when it hits a failure. +DIE_ON_FAILURE = 0 + +# If you want to have ktest.pl store the failure somewhere, uncomment this option +# and change the directory where ktest should store the failures. +#STORE_FAILURES = ${THIS_DIR}/failures + +# The log file is stored in the OUTPUT_DIR called cross.log +# If you enable this, you need to create the OUTPUT_DIR. It wont be created for you. +LOG_FILE = ${OUTPUT_DIR}/cross.log + +# The log file will be cleared each time you run ktest. +CLEAR_LOG = 1 + +# As some archs do not build with the defconfig, they have been marked +# to be ignored. If you want to test them anyway, change DO_FAILED to 1. +# If a test that has been marked as DO_FAILED passes, then you should change +# that test to be DO_DEFAULT + +DO_FAILED := 0 +DO_DEFAULT := 1 + +# By setting both DO_FAILED and DO_DEFAULT to zero, you can pick a single +# arch that you want to test. (uncomment RUN and chose your arch) +#RUN := m32r + +# At the bottom of the config file exists a bisect test. You can update that +# test and set DO_FAILED and DO_DEFAULT to zero, and uncomment this variable +# to run the bisect on the arch. +#RUN := bisect + +# By default all tests will be running gcc 4.5.2. Some tests are using 4.5.1 +# and they select that in the test. +# Note: GCC_VER is declared as on option and not a variable ('=' instead of ':=') +# This is important. A variable is used only in the config file and if it is set +# it stays that way for the rest of the config file until it is change again. +# Here we want GCC_VER to remain persistent and change for each test, as it is used in +# the MAKE_CMD. By using '=' instead of ':=' we achieve our goal. + +GCC_VER = 4.5.2 +MAKE_CMD = PATH=/usr/local/gcc-${GCC_VER}-nolibc/${CROSS}/bin:$PATH CROSS_COMPILE=${CROSS}- make ARCH=${ARCH} + +# all tests are only doing builds. +TEST_TYPE = build + +# If you want to add configs on top of the defconfig, you can add those configs into +# the add-config file and uncomment this option. This is useful if you want to test +# all cross compiles with PREEMPT set, or TRACING on, etc. +#ADD_CONFIG = ${THIS_DIR}/add-config + +# All tests are using defconfig +BUILD_TYPE = defconfig + +# The test names will have the arch and cross compiler used. This will be shown in +# the results. +TEST_NAME = ${ARCH} ${CROSS} + +# alpha +TEST_START IF ${RUN} == alpha || ${DO_DEFAULT} +# Notice that CROSS and ARCH are also options and not variables (again '=' instead +# of ':='). This is because TEST_NAME and MAKE_CMD wil use them for each test. +# Only options are available during runs. Variables are only present in parsing the +# config file. +CROSS = alpha-linux +ARCH = alpha + +# arm +TEST_START IF ${RUN} == arm || ${DO_DEFAULT} +CROSS = arm-unknown-linux-gnueabi +ARCH = arm + +# black fin +TEST_START IF ${RUN} == bfin || ${DO_DEFAULT} +CROSS = bfin-uclinux +ARCH = blackfin +BUILD_OPTIONS = -j8 vmlinux + +# cris - FAILS? +TEST_START IF ${RUN} == cris || ${RUN} == cris64 || ${DO_FAILED} +CROSS = cris-linux +ARCH = cris + +# cris32 - not right arch? +TEST_START IF ${RUN} == cris || ${RUN} == cris32 || ${DO_FAILED} +CROSS = crisv32-linux +ARCH = cris + +# ia64 +TEST_START IF ${RUN} == ia64 || ${DO_DEFAULT} +CROSS = ia64-linux +ARCH = ia64 + +# frv +TEST_START IF ${RUN} == frv || ${DO_FAILED} +CROSS = frv-linux +ARCH = frv +GCC_VER = 4.5.1 + +# h8300 - failed make defconfig?? +TEST_START IF ${RUN} == h8300 || ${DO_FAILED} +CROSS = h8300-elf +ARCH = h8300 +GCC_VER = 4.5.1 + +# m68k fails with error? +TEST_START IF ${RUN} == m68k || ${DO_DEFAULT} +CROSS = m68k-linux +ARCH = m68k + +# mips64 +TEST_START IF ${RUN} == mips || ${RUN} == mips64 || ${DO_DEFAULT} +CROSS = mips64-linux +ARCH = mips + +# mips32 +TEST_START IF ${RUN} == mips || ${RUN} == mips32 || ${DO_DEFAULT} +CROSS = mips-linux +ARCH = mips + +# m32r +TEST_START IF ${RUN} == m32r || ${DO_FAILED} +CROSS = m32r-linux +ARCH = m32r +GCC_VER = 4.5.1 +BUILD_OPTIONS = -j8 vmlinux + +# parisc64 failed? +TEST_START IF ${RUN} == hppa || ${RUN} == hppa64 || ${DO_FAILED} +CROSS = hppa64-linux +ARCH = parisc + +# parisc +TEST_START IF ${RUN} == hppa || ${RUN} == hppa32 || ${DO_FAILED} +CROSS = hppa-linux +ARCH = parisc + +# ppc +TEST_START IF ${RUN} == ppc || ${RUN} == ppc32 || ${DO_DEFAULT} +CROSS = powerpc-linux +ARCH = powerpc + +# ppc64 +TEST_START IF ${RUN} == ppc || ${RUN} == ppc64 || ${DO_DEFAULT} +CROSS = powerpc64-linux +ARCH = powerpc + +# s390 +TEST_START IF ${RUN} == s390 || ${DO_DEFAULT} +CROSS = s390x-linux +ARCH = s390 + +# sh +TEST_START IF ${RUN} == sh || ${DO_DEFAULT} +CROSS = sh4-linux +ARCH = sh + +# sparc64 +TEST_START IF ${RUN} == sparc || ${RUN} == sparc64 || ${DO_DEFAULT} +CROSS = sparc64-linux +ARCH = sparc64 + +# sparc +TEST_START IF ${RUN} == sparc || ${RUN} == sparc32 || ${DO_DEFAULT} +CROSS = sparc-linux +ARCH = sparc + +# xtensa failed +TEST_START IF ${RUN} == xtensa || ${DO_FAILED} +CROSS = xtensa-linux +ARCH = xtensa + +# UML +TEST_START IF ${RUN} == uml || ${DO_DEFAULT} +MAKE_CMD = make ARCH=um SUBARCH=x86_64 +ARCH = uml +CROSS = + +TEST_START IF ${RUN} == x86 || ${RUN} == i386 || ${DO_DEFAULT} +MAKE_CMD = make ARCH=i386 +ARCH = i386 +CROSS = + +TEST_START IF ${RUN} == x86 || ${RUN} == x86_64 || ${DO_DEFAULT} +MAKE_CMD = make ARCH=x86_64 +ARCH = x86_64 +CROSS = + +################################# + +# This is a bisect if needed. You need to give it a MIN_CONFIG that +# will be the config file it uses. Basically, just copy the created defconfig +# for the arch someplace and point MIN_CONFIG to it. +TEST_START IF ${RUN} == bisect +MIN_CONFIG = ${THIS_DIR}/min-config +CROSS = s390x-linux +ARCH = s390 +TEST_TYPE = bisect +BISECT_TYPE = build +BISECT_GOOD = v3.1 +BISECT_BAD = v3.2 +CHECKOUT = v3.2 + +################################# + +# These defaults are needed to keep ktest.pl from complaining. They are +# ignored because the test does not go pass the build. No install or +# booting of the target images. + +DEFAULTS +MACHINE = crosstest +SSH_USER = root +BUILD_TARGET = cross +TARGET_IMAGE = image +POWER_CYCLE = cycle +CONSOLE = console +LOCALVERSION = version +GRUB_MENU = grub + +REBOOT_ON_ERROR = 0 +POWEROFF_ON_ERROR = 0 +POWEROFF_ON_SUCCESS = 0 +REBOOT_ON_SUCCESS = 0 + -- cgit v1.2.3-70-g09d2 From b6d300361b82a478d83c8cccf7ea810fec601e59 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 22 May 2012 00:13:32 -0400 Subject: ktest: Add the snowball.conf example config I used the snowball.conf in a live demo that demonstrated how to use ktest.pl with a snowball ARM board. I've been asked to included that config in the ktest repository. Here it is. Signed-off-by: Steven Rostedt --- tools/testing/ktest/examples/snowball.conf | 53 ++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tools/testing/ktest/examples/snowball.conf (limited to 'tools/testing') diff --git a/tools/testing/ktest/examples/snowball.conf b/tools/testing/ktest/examples/snowball.conf new file mode 100644 index 00000000000..a82a3c5bc2b --- /dev/null +++ b/tools/testing/ktest/examples/snowball.conf @@ -0,0 +1,53 @@ +# This example was used to boot the snowball ARM board. +# See http://people.redhat.com/srostedt/ktest-embedded-2012/ + +# PWD is a ktest.pl variable that will result in the process working +# directory that ktest.pl is executed in. + +# THIS_DIR is automatically assigned the PWD of the path that generated +# the config file. It is best to use this variable when assigning other +# directory paths within this directory. This allows you to easily +# move the test cases to other locations or to other machines. +# +THIS_DIR := /home/rostedt/work/demo/ktest-embed +LOG_FILE = ${OUTPUT_DIR}/snowball.log +CLEAR_LOG = 1 +MAKE_CMD = PATH=/usr/local/gcc-4.5.2-nolibc/arm-unknown-linux-gnueabi/bin:$PATH CROSS_COMPILE=arm-unknown-linux-gnueabi- make ARCH=arm +ADD_CONFIG = ${THIS_DIR}/addconfig + +SCP_TO_TARGET = echo "don't do scp" + +TFTPBOOT := /var/lib/tftpboot +TFTPDEF := ${TFTPBOOT}/snowball-default +TFTPTEST := ${OUTPUT_DIR}/${BUILD_TARGET} + +SWITCH_TO_GOOD = cp ${TFTPDEF} ${TARGET_IMAGE} +SWITCH_TO_TEST = cp ${TFTPTEST} ${TARGET_IMAGE} + +# Define each test with TEST_START +# The config options below it will override the defaults +TEST_START SKIP +TEST_TYPE = boot +BUILD_TYPE = u8500_defconfig +BUILD_NOCLEAN = 1 + +TEST_START +TEST_TYPE = make_min_config +OUTPUT_MIN_CONFIG = ${THIS_DIR}/config.newmin +START_MIN_CONFIG = ${THIS_DIR}/config.orig +IGNORE_CONFIG = ${THIS_DIR}/config.ignore +BUILD_NOCLEAN = 1 + + +DEFAULTS +LOCALVERSION = -test +POWER_CYCLE = echo use the thumb luke; read a +CONSOLE = cat ${THIS_DIR}/snowball-cat +REBOOT_TYPE = script +SSH_USER = root +BUILD_OPTIONS = -j8 uImage +BUILD_DIR = ${THIS_DIR}/linux.git +OUTPUT_DIR = ${THIS_DIR}/snowball-build +MACHINE = snowball +TARGET_IMAGE = /var/lib/tftpboot/snowball-image +BUILD_TARGET = arch/arm/boot/uImage -- cgit v1.2.3-70-g09d2 From 24d0c030144a8a13dc569b7f2ce0d4c8bd68b85f Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 22 May 2012 00:15:12 -0400 Subject: ktest: Add README to explain what is in the examples directory Add a README that explains what the different example configs in the ktest example directory are about. Signed-off-by: Steven Rostedt --- tools/testing/ktest/examples/README | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tools/testing/ktest/examples/README (limited to 'tools/testing') diff --git a/tools/testing/ktest/examples/README b/tools/testing/ktest/examples/README new file mode 100644 index 00000000000..a12d295a09d --- /dev/null +++ b/tools/testing/ktest/examples/README @@ -0,0 +1,32 @@ +This directory contains example configs to use ktest for various tasks. +The configs still need to be customized for your environment, but it +is broken up by task which makes it easier to understand how to set up +ktest. + +The configs are based off of real working configs but have been modified +and commented to show more generic use cases that are more helpful for +developers. + +crosstests.conf - this config shows an example of testing a git repo against + lots of different architectures. It only does build tests, but makes + it easy to compile test different archs. You can download the arch + cross compilers from: + http://kernel.org/pub/tools/crosstool/files/bin/x86_64/ + +test.conf - A generic example of a config. This is based on an actual config + used to perform real testing. + +kvm.conf - A example of a config that is used to test a virtual guest running + on a host. + +snowball.conf - An example config that was used to demo ktest.pl against + a snowball ARM board. + +include/ - The include directory holds default configs that can be + included into other configs. This is a real use example that shows how + to reuse configs for various machines or set ups. The files here + are included by other config files, where the other config files define + options and variables that will make the included config work for the + given environment. + + -- cgit v1.2.3-70-g09d2 From 11c38b7575c2f4624d8e199c50cec2ba0d58a753 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Mon, 16 Apr 2012 19:40:24 +0200 Subject: ktest: Change singular "paranthesis" to plural "parentheses" Acked-by: Randy Dunlap Signed-off-by: Jesper Juhl Signed-off-by: Steven Rostedt --- tools/testing/ktest/sample.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/testing') diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 0e8191b6c5e..cf362b3d1ec 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -158,7 +158,7 @@ # # TEST_START IF (DEFINED ALL_TESTS || ${MYTEST} == boottest) && ${MACHINE} == gandalf # -# Notice the use of paranthesis. Without any paranthesis the above would be +# Notice the use of parentheses. Without any parentheses the above would be # processed the same as: # # TEST_START IF DEFINED ALL_TESTS || (${MYTEST} == boottest && ${MACHINE} == gandalf) -- cgit v1.2.3-70-g09d2 From 50069a5851323ba5def0e414a21e234345016870 Mon Sep 17 00:00:00 2001 From: Doug Ledford Date: Thu, 31 May 2012 16:26:34 -0700 Subject: selftests: add mq_open_tests Add a directory to house POSIX message queue subsystem specific tests. Add first test which checks the operation of mq_open() under various corner conditions. Signed-off-by: Doug Ledford Cc: KOSAKI Motohiro Cc: Doug Ledford Cc: Joe Korty Cc: Amerigo Wang Cc: Serge E. Hallyn Cc: Jiri Slaby Cc: Manfred Spraul Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/Makefile | 2 +- tools/testing/selftests/mqueue/Makefile | 8 + tools/testing/selftests/mqueue/mq_open_tests.c | 492 +++++++++++++++++++++++++ 3 files changed, 501 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/mqueue/Makefile create mode 100644 tools/testing/selftests/mqueue/mq_open_tests.c (limited to 'tools/testing') diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 28bc57ee757..14972017a43 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,4 +1,4 @@ -TARGETS = breakpoints vm +TARGETS = breakpoints mqueue vm all: for TARGET in $(TARGETS); do \ diff --git a/tools/testing/selftests/mqueue/Makefile b/tools/testing/selftests/mqueue/Makefile new file mode 100644 index 00000000000..bd74142a173 --- /dev/null +++ b/tools/testing/selftests/mqueue/Makefile @@ -0,0 +1,8 @@ +all: + gcc -O2 -lrt mq_open_tests.c -o mq_open_tests + +run_tests: + ./mq_open_tests /test1 + +clean: + rm -f mq_open_tests diff --git a/tools/testing/selftests/mqueue/mq_open_tests.c b/tools/testing/selftests/mqueue/mq_open_tests.c new file mode 100644 index 00000000000..711cc292304 --- /dev/null +++ b/tools/testing/selftests/mqueue/mq_open_tests.c @@ -0,0 +1,492 @@ +/* + * This application is Copyright 2012 Red Hat, Inc. + * Doug Ledford + * + * mq_open_tests is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * mq_open_tests is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For the full text of the license, see . + * + * mq_open_tests.c + * Tests the various situations that should either succeed or fail to + * open a posix message queue and then reports whether or not they + * did as they were supposed to. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *usage = +"Usage:\n" +" %s path\n" +"\n" +" path Path name of the message queue to create\n" +"\n" +" Note: this program must be run as root in order to enable all tests\n" +"\n"; + +char *DEF_MSGS = "/proc/sys/fs/mqueue/msg_default"; +char *DEF_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_default"; +char *MAX_MSGS = "/proc/sys/fs/mqueue/msg_max"; +char *MAX_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_max"; + +int default_settings; +struct rlimit saved_limits, cur_limits; +int saved_def_msgs, saved_def_msgsize, saved_max_msgs, saved_max_msgsize; +int cur_def_msgs, cur_def_msgsize, cur_max_msgs, cur_max_msgsize; +FILE *def_msgs, *def_msgsize, *max_msgs, *max_msgsize; +char *queue_path; +mqd_t queue = -1; + +static inline void __set(FILE *stream, int value, char *err_msg); +void shutdown(int exit_val, char *err_cause, int line_no); +static inline int get(FILE *stream); +static inline void set(FILE *stream, int value); +static inline void getr(int type, struct rlimit *rlim); +static inline void setr(int type, struct rlimit *rlim); +void validate_current_settings(); +static inline void test_queue(struct mq_attr *attr, struct mq_attr *result); +static inline int test_queue_fail(struct mq_attr *attr, struct mq_attr *result); + +static inline void __set(FILE *stream, int value, char *err_msg) +{ + rewind(stream); + if (fprintf(stream, "%d", value) < 0) + perror(err_msg); +} + + +void shutdown(int exit_val, char *err_cause, int line_no) +{ + static int in_shutdown = 0; + + /* In case we get called recursively by a set() call below */ + if (in_shutdown++) + return; + + seteuid(0); + + if (queue != -1) + if (mq_close(queue)) + perror("mq_close() during shutdown"); + if (queue_path) + /* + * Be silent if this fails, if we cleaned up already it's + * expected to fail + */ + mq_unlink(queue_path); + if (default_settings) { + if (saved_def_msgs) + __set(def_msgs, saved_def_msgs, + "failed to restore saved_def_msgs"); + if (saved_def_msgsize) + __set(def_msgsize, saved_def_msgsize, + "failed to restore saved_def_msgsize"); + } + if (saved_max_msgs) + __set(max_msgs, saved_max_msgs, + "failed to restore saved_max_msgs"); + if (saved_max_msgsize) + __set(max_msgsize, saved_max_msgsize, + "failed to restore saved_max_msgsize"); + if (exit_val) + error(exit_val, errno, "%s at %d", err_cause, line_no); + exit(0); +} + +static inline int get(FILE *stream) +{ + int value; + rewind(stream); + if (fscanf(stream, "%d", &value) != 1) + shutdown(4, "Error reading /proc entry", __LINE__ - 1); + return value; +} + +static inline void set(FILE *stream, int value) +{ + int new_value; + + rewind(stream); + if (fprintf(stream, "%d", value) < 0) + return shutdown(5, "Failed writing to /proc file", + __LINE__ - 1); + new_value = get(stream); + if (new_value != value) + return shutdown(5, "We didn't get what we wrote to /proc back", + __LINE__ - 1); +} + +static inline void getr(int type, struct rlimit *rlim) +{ + if (getrlimit(type, rlim)) + shutdown(6, "getrlimit()", __LINE__ - 1); +} + +static inline void setr(int type, struct rlimit *rlim) +{ + if (setrlimit(type, rlim)) + shutdown(7, "setrlimit()", __LINE__ - 1); +} + +void validate_current_settings() +{ + int rlim_needed; + + if (cur_limits.rlim_cur < 4096) { + printf("Current rlimit value for POSIX message queue bytes is " + "unreasonably low,\nincreasing.\n\n"); + cur_limits.rlim_cur = 8192; + cur_limits.rlim_max = 16384; + setr(RLIMIT_MSGQUEUE, &cur_limits); + } + + if (default_settings) { + rlim_needed = (cur_def_msgs + 1) * (cur_def_msgsize + 1 + + 2 * sizeof(void *)); + if (rlim_needed > cur_limits.rlim_cur) { + printf("Temporarily lowering default queue parameters " + "to something that will work\n" + "with the current rlimit values.\n\n"); + set(def_msgs, 10); + cur_def_msgs = 10; + set(def_msgsize, 128); + cur_def_msgsize = 128; + } + } else { + rlim_needed = (cur_max_msgs + 1) * (cur_max_msgsize + 1 + + 2 * sizeof(void *)); + if (rlim_needed > cur_limits.rlim_cur) { + printf("Temporarily lowering maximum queue parameters " + "to something that will work\n" + "with the current rlimit values in case this is " + "a kernel that ties the default\n" + "queue parameters to the maximum queue " + "parameters.\n\n"); + set(max_msgs, 10); + cur_max_msgs = 10; + set(max_msgsize, 128); + cur_max_msgsize = 128; + } + } +} + +/* + * test_queue - Test opening a queue, shutdown if we fail. This should + * only be called in situations that should never fail. We clean up + * after ourselves and return the queue attributes in *result. + */ +static inline void test_queue(struct mq_attr *attr, struct mq_attr *result) +{ + int flags = O_RDWR | O_EXCL | O_CREAT; + int perms = DEFFILEMODE; + + if ((queue = mq_open(queue_path, flags, perms, attr)) == -1) + shutdown(1, "mq_open()", __LINE__); + if (mq_getattr(queue, result)) + shutdown(1, "mq_getattr()", __LINE__); + if (mq_close(queue)) + shutdown(1, "mq_close()", __LINE__); + queue = -1; + if (mq_unlink(queue_path)) + shutdown(1, "mq_unlink()", __LINE__); +} + +/* + * Same as test_queue above, but failure is not fatal. + * Returns: + * 0 - Failed to create a queue + * 1 - Created a queue, attributes in *result + */ +static inline int test_queue_fail(struct mq_attr *attr, struct mq_attr *result) +{ + int flags = O_RDWR | O_EXCL | O_CREAT; + int perms = DEFFILEMODE; + + if ((queue = mq_open(queue_path, flags, perms, attr)) == -1) + return 0; + if (mq_getattr(queue, result)) + shutdown(1, "mq_getattr()", __LINE__); + if (mq_close(queue)) + shutdown(1, "mq_close()", __LINE__); + queue = -1; + if (mq_unlink(queue_path)) + shutdown(1, "mq_unlink()", __LINE__); + return 1; +} + +int main(int argc, char *argv[]) +{ + struct mq_attr attr, result; + + if (argc != 2) { + fprintf(stderr, "Must pass a valid queue name\n\n"); + fprintf(stderr, usage, argv[0]); + exit(1); + } + + /* + * Although we can create a msg queue with a non-absolute path name, + * unlink will fail. So, if the name doesn't start with a /, add one + * when we save it. + */ + if (*argv[1] == '/') + queue_path = strdup(argv[1]); + else { + queue_path = malloc(strlen(argv[1]) + 2); + if (!queue_path) { + perror("malloc()"); + exit(1); + } + queue_path[0] = '/'; + queue_path[1] = 0; + strcat(queue_path, argv[1]); + } + + if (getuid() != 0) { + fprintf(stderr, "Not running as root, but almost all tests " + "require root in order to modify\nsystem settings. " + "Exiting.\n"); + exit(1); + } + + /* Find out what files there are for us to make tweaks in */ + def_msgs = fopen(DEF_MSGS, "r+"); + def_msgsize = fopen(DEF_MSGSIZE, "r+"); + max_msgs = fopen(MAX_MSGS, "r+"); + max_msgsize = fopen(MAX_MSGSIZE, "r+"); + + if (!max_msgs) + shutdown(2, "Failed to open msg_max", __LINE__); + if (!max_msgsize) + shutdown(2, "Failed to open msgsize_max", __LINE__); + if (def_msgs || def_msgsize) + default_settings = 1; + + /* Load up the current system values for everything we can */ + getr(RLIMIT_MSGQUEUE, &saved_limits); + cur_limits = saved_limits; + if (default_settings) { + saved_def_msgs = cur_def_msgs = get(def_msgs); + saved_def_msgsize = cur_def_msgsize = get(def_msgsize); + } + saved_max_msgs = cur_max_msgs = get(max_msgs); + saved_max_msgsize = cur_max_msgsize = get(max_msgsize); + + /* Tell the user our initial state */ + printf("\nInitial system state:\n"); + printf("\tUsing queue path:\t\t%s\n", queue_path); + printf("\tRLIMIT_MSGQUEUE(soft):\t\t%d\n", saved_limits.rlim_cur); + printf("\tRLIMIT_MSGQUEUE(hard):\t\t%d\n", saved_limits.rlim_max); + printf("\tMaximum Message Size:\t\t%d\n", saved_max_msgsize); + printf("\tMaximum Queue Size:\t\t%d\n", saved_max_msgs); + if (default_settings) { + printf("\tDefault Message Size:\t\t%d\n", saved_def_msgsize); + printf("\tDefault Queue Size:\t\t%d\n", saved_def_msgs); + } else { + printf("\tDefault Message Size:\t\tNot Supported\n"); + printf("\tDefault Queue Size:\t\tNot Supported\n"); + } + printf("\n"); + + validate_current_settings(); + + printf("Adjusted system state for testing:\n"); + printf("\tRLIMIT_MSGQUEUE(soft):\t\t%d\n", cur_limits.rlim_cur); + printf("\tRLIMIT_MSGQUEUE(hard):\t\t%d\n", cur_limits.rlim_max); + printf("\tMaximum Message Size:\t\t%d\n", cur_max_msgsize); + printf("\tMaximum Queue Size:\t\t%d\n", cur_max_msgs); + if (default_settings) { + printf("\tDefault Message Size:\t\t%d\n", cur_def_msgsize); + printf("\tDefault Queue Size:\t\t%d\n", cur_def_msgs); + } + + printf("\n\nTest series 1, behavior when no attr struct " + "passed to mq_open:\n"); + if (!default_settings) { + test_queue(NULL, &result); + printf("Given sane system settings, mq_open without an attr " + "struct succeeds:\tPASS\n"); + if (result.mq_maxmsg != cur_max_msgs || + result.mq_msgsize != cur_max_msgsize) { + printf("Kernel does not support setting the default " + "mq attributes,\nbut also doesn't tie the " + "defaults to the maximums:\t\t\tPASS\n"); + } else { + set(max_msgs, ++cur_max_msgs); + set(max_msgsize, ++cur_max_msgsize); + test_queue(NULL, &result); + if (result.mq_maxmsg == cur_max_msgs && + result.mq_msgsize == cur_max_msgsize) + printf("Kernel does not support setting the " + "default mq attributes and\n" + "also ties system wide defaults to " + "the system wide maximums:\t\t" + "FAIL\n"); + else + printf("Kernel does not support setting the " + "default mq attributes,\n" + "but also doesn't tie the defaults to " + "the maximums:\t\t\tPASS\n"); + } + } else { + printf("Kernel supports setting defaults separately from " + "maximums:\t\tPASS\n"); + /* + * While we are here, go ahead and test that the kernel + * properly follows the default settings + */ + test_queue(NULL, &result); + printf("Given sane values, mq_open without an attr struct " + "succeeds:\t\tPASS\n"); + if (result.mq_maxmsg != cur_def_msgs || + result.mq_msgsize != cur_def_msgsize) + printf("Kernel supports setting defaults, but does " + "not actually honor them:\tFAIL\n\n"); + else { + set(def_msgs, ++cur_def_msgs); + set(def_msgsize, ++cur_def_msgsize); + /* In case max was the same as the default */ + set(max_msgs, ++cur_max_msgs); + set(max_msgsize, ++cur_max_msgsize); + test_queue(NULL, &result); + if (result.mq_maxmsg != cur_def_msgs || + result.mq_msgsize != cur_def_msgsize) + printf("Kernel supports setting defaults, but " + "does not actually honor them:\t" + "FAIL\n"); + else + printf("Kernel properly honors default setting " + "knobs:\t\t\t\tPASS\n"); + } + set(def_msgs, cur_max_msgs + 1); + cur_def_msgs = cur_max_msgs + 1; + set(def_msgsize, cur_max_msgsize + 1); + cur_def_msgsize = cur_max_msgsize + 1; + if (cur_def_msgs * (cur_def_msgsize + 2 * sizeof(void *)) >= + cur_limits.rlim_cur) { + cur_limits.rlim_cur = (cur_def_msgs + 2) * + (cur_def_msgsize + 2 * sizeof(void *)); + cur_limits.rlim_max = 2 * cur_limits.rlim_cur; + setr(RLIMIT_MSGQUEUE, &cur_limits); + } + if (test_queue_fail(NULL, &result)) { + if (result.mq_maxmsg == cur_max_msgs && + result.mq_msgsize == cur_max_msgsize) + printf("Kernel properly limits default values " + "to lesser of default/max:\t\tPASS\n"); + else + printf("Kernel does not properly set default " + "queue parameters when\ndefaults > " + "max:\t\t\t\t\t\t\t\tFAIL\n"); + } else + printf("Kernel fails to open mq because defaults are " + "greater than maximums:\tFAIL\n"); + set(def_msgs, --cur_def_msgs); + set(def_msgsize, --cur_def_msgsize); + cur_limits.rlim_cur = cur_limits.rlim_max = cur_def_msgs * + cur_def_msgsize; + setr(RLIMIT_MSGQUEUE, &cur_limits); + if (test_queue_fail(NULL, &result)) + printf("Kernel creates queue even though defaults " + "would exceed\nrlimit setting:" + "\t\t\t\t\t\t\t\tFAIL\n"); + else + printf("Kernel properly fails to create queue when " + "defaults would\nexceed rlimit:" + "\t\t\t\t\t\t\t\tPASS\n"); + } + + /* + * Test #2 - open with an attr struct that exceeds rlimit + */ + printf("\n\nTest series 2, behavior when attr struct is " + "passed to mq_open:\n"); + cur_max_msgs = 32; + cur_max_msgsize = cur_limits.rlim_max >> 4; + set(max_msgs, cur_max_msgs); + set(max_msgsize, cur_max_msgsize); + attr.mq_maxmsg = cur_max_msgs; + attr.mq_msgsize = cur_max_msgsize; + if (test_queue_fail(&attr, &result)) + printf("Queue open in excess of rlimit max when euid = 0 " + "succeeded:\t\tFAIL\n"); + else + printf("Queue open in excess of rlimit max when euid = 0 " + "failed:\t\tPASS\n"); + attr.mq_maxmsg = cur_max_msgs + 1; + attr.mq_msgsize = 10; + if (test_queue_fail(&attr, &result)) + printf("Queue open with mq_maxmsg > limit when euid = 0 " + "succeeded:\t\tPASS\n"); + else + printf("Queue open with mq_maxmsg > limit when euid = 0 " + "failed:\t\tFAIL\n"); + attr.mq_maxmsg = 1; + attr.mq_msgsize = cur_max_msgsize + 1; + if (test_queue_fail(&attr, &result)) + printf("Queue open with mq_msgsize > limit when euid = 0 " + "succeeded:\t\tPASS\n"); + else + printf("Queue open with mq_msgsize > limit when euid = 0 " + "failed:\t\tFAIL\n"); + attr.mq_maxmsg = 65536; + attr.mq_msgsize = 65536; + if (test_queue_fail(&attr, &result)) + printf("Queue open with total size > 2GB when euid = 0 " + "succeeded:\t\tFAIL\n"); + else + printf("Queue open with total size > 2GB when euid = 0 " + "failed:\t\t\tPASS\n"); + seteuid(99); + attr.mq_maxmsg = cur_max_msgs; + attr.mq_msgsize = cur_max_msgsize; + if (test_queue_fail(&attr, &result)) + printf("Queue open in excess of rlimit max when euid = 99 " + "succeeded:\t\tFAIL\n"); + else + printf("Queue open in excess of rlimit max when euid = 99 " + "failed:\t\tPASS\n"); + attr.mq_maxmsg = cur_max_msgs + 1; + attr.mq_msgsize = 10; + if (test_queue_fail(&attr, &result)) + printf("Queue open with mq_maxmsg > limit when euid = 99 " + "succeeded:\t\tFAIL\n"); + else + printf("Queue open with mq_maxmsg > limit when euid = 99 " + "failed:\t\tPASS\n"); + attr.mq_maxmsg = 1; + attr.mq_msgsize = cur_max_msgsize + 1; + if (test_queue_fail(&attr, &result)) + printf("Queue open with mq_msgsize > limit when euid = 99 " + "succeeded:\t\tFAIL\n"); + else + printf("Queue open with mq_msgsize > limit when euid = 99 " + "failed:\t\tPASS\n"); + attr.mq_maxmsg = 65536; + attr.mq_msgsize = 65536; + if (test_queue_fail(&attr, &result)) + printf("Queue open with total size > 2GB when euid = 99 " + "succeeded:\t\tFAIL\n"); + else + printf("Queue open with total size > 2GB when euid = 99 " + "failed:\t\t\tPASS\n"); + + shutdown(0,"",0); +} -- cgit v1.2.3-70-g09d2 From 7820b0715b6fb1378fab41b27fb7aa3950852cb7 Mon Sep 17 00:00:00 2001 From: Doug Ledford Date: Thu, 31 May 2012 16:26:37 -0700 Subject: tools/selftests: add mq_perf_tests Add the mq_perf_tests tool I used when creating my mq performance patch. Also add a local .gitignore to keep the binaries from showing up in git status output. [akpm@linux-foundation.org: checkpatch fixes] Signed-off-by: Doug Ledford Cc: Stephen Rothwell Cc: Manfred Spraul Cc: Frederic Weisbecker Acked-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/mqueue/.gitignore | 2 + tools/testing/selftests/mqueue/Makefile | 4 +- tools/testing/selftests/mqueue/mq_perf_tests.c | 741 +++++++++++++++++++++++++ 3 files changed, 746 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/mqueue/.gitignore create mode 100644 tools/testing/selftests/mqueue/mq_perf_tests.c (limited to 'tools/testing') diff --git a/tools/testing/selftests/mqueue/.gitignore b/tools/testing/selftests/mqueue/.gitignore new file mode 100644 index 00000000000..d8d42377205 --- /dev/null +++ b/tools/testing/selftests/mqueue/.gitignore @@ -0,0 +1,2 @@ +mq_open_tests +mq_perf_tests diff --git a/tools/testing/selftests/mqueue/Makefile b/tools/testing/selftests/mqueue/Makefile index bd74142a173..54c0aad2b47 100644 --- a/tools/testing/selftests/mqueue/Makefile +++ b/tools/testing/selftests/mqueue/Makefile @@ -1,8 +1,10 @@ all: gcc -O2 -lrt mq_open_tests.c -o mq_open_tests + gcc -O2 -lrt -lpthread -lpopt -o mq_perf_tests mq_perf_tests.c run_tests: ./mq_open_tests /test1 + ./mq_perf_tests clean: - rm -f mq_open_tests + rm -f mq_open_tests mq_perf_tests diff --git a/tools/testing/selftests/mqueue/mq_perf_tests.c b/tools/testing/selftests/mqueue/mq_perf_tests.c new file mode 100644 index 00000000000..2fadd4b9704 --- /dev/null +++ b/tools/testing/selftests/mqueue/mq_perf_tests.c @@ -0,0 +1,741 @@ +/* + * This application is Copyright 2012 Red Hat, Inc. + * Doug Ledford + * + * mq_perf_tests is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * mq_perf_tests is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For the full text of the license, see . + * + * mq_perf_tests.c + * Tests various types of message queue workloads, concentrating on those + * situations that invole large message sizes, large message queue depths, + * or both, and reports back useful metrics about kernel message queue + * performance. + * + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *usage = +"Usage:\n" +" %s [-c #[,#..] -f] path\n" +"\n" +" -c # Skip most tests and go straight to a high queue depth test\n" +" and then run that test continuously (useful for running at\n" +" the same time as some other workload to see how much the\n" +" cache thrashing caused by adding messages to a very deep\n" +" queue impacts the performance of other programs). The number\n" +" indicates which CPU core we should bind the process to during\n" +" the run. If you have more than one physical CPU, then you\n" +" will need one copy per physical CPU package, and you should\n" +" specify the CPU cores to pin ourself to via a comma separated\n" +" list of CPU values.\n" +" -f Only usable with continuous mode. Pin ourself to the CPUs\n" +" as requested, then instead of looping doing a high mq\n" +" workload, just busy loop. This will allow us to lock up a\n" +" single CPU just like we normally would, but without actually\n" +" thrashing the CPU cache. This is to make it easier to get\n" +" comparable numbers from some other workload running on the\n" +" other CPUs. One set of numbers with # CPUs locked up running\n" +" an mq workload, and another set of numbers with those same\n" +" CPUs locked away from the test workload, but not doing\n" +" anything to trash the cache like the mq workload might.\n" +" path Path name of the message queue to create\n" +"\n" +" Note: this program must be run as root in order to enable all tests\n" +"\n"; + +char *MAX_MSGS = "/proc/sys/fs/mqueue/msg_max"; +char *MAX_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_max"; + +#define min(a, b) ((a) < (b) ? (a) : (b)) +#define MAX_CPUS 64 +char *cpu_option_string; +int cpus_to_pin[MAX_CPUS]; +int num_cpus_to_pin; +pthread_t cpu_threads[MAX_CPUS]; +pthread_t main_thread; +cpu_set_t *cpu_set; +int cpu_set_size; +int cpus_online; + +#define MSG_SIZE 16 +#define TEST1_LOOPS 10000000 +#define TEST2_LOOPS 100000 +int continuous_mode; +int continuous_mode_fake; + +struct rlimit saved_limits, cur_limits; +int saved_max_msgs, saved_max_msgsize; +int cur_max_msgs, cur_max_msgsize; +FILE *max_msgs, *max_msgsize; +int cur_nice; +char *queue_path = "/mq_perf_tests"; +mqd_t queue = -1; +struct mq_attr result; +int mq_prio_max; + +const struct poptOption options[] = { + { + .longName = "continuous", + .shortName = 'c', + .argInfo = POPT_ARG_STRING, + .arg = &cpu_option_string, + .val = 'c', + .descrip = "Run continuous tests at a high queue depth in " + "order to test the effects of cache thrashing on " + "other tasks on the system. This test is intended " + "to be run on one core of each physical CPU while " + "some other CPU intensive task is run on all the other " + "cores of that same physical CPU and the other task " + "is timed. It is assumed that the process of adding " + "messages to the message queue in a tight loop will " + "impact that other task to some degree. Once the " + "tests are performed in this way, you should then " + "re-run the tests using fake mode in order to check " + "the difference in time required to perform the CPU " + "intensive task", + .argDescrip = "cpu[,cpu]", + }, + { + .longName = "fake", + .shortName = 'f', + .argInfo = POPT_ARG_NONE, + .arg = &continuous_mode_fake, + .val = 0, + .descrip = "Tie up the CPUs that we would normally tie up in" + "continuous mode, but don't actually do any mq stuff, " + "just keep the CPU busy so it can't be used to process " + "system level tasks as this would free up resources on " + "the other CPU cores and skew the comparison between " + "the no-mqueue work and mqueue work tests", + .argDescrip = NULL, + }, + { + .longName = "path", + .shortName = 'p', + .argInfo = POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, + .arg = &queue_path, + .val = 'p', + .descrip = "The name of the path to use in the mqueue " + "filesystem for our tests", + .argDescrip = "pathname", + }, + POPT_AUTOHELP + POPT_TABLEEND +}; + +static inline void __set(FILE *stream, int value, char *err_msg); +void shutdown(int exit_val, char *err_cause, int line_no); +void sig_action_SIGUSR1(int signum, siginfo_t *info, void *context); +void sig_action(int signum, siginfo_t *info, void *context); +static inline int get(FILE *stream); +static inline void set(FILE *stream, int value); +static inline int try_set(FILE *stream, int value); +static inline void getr(int type, struct rlimit *rlim); +static inline void setr(int type, struct rlimit *rlim); +static inline void open_queue(struct mq_attr *attr); +void increase_limits(void); + +static inline void __set(FILE *stream, int value, char *err_msg) +{ + rewind(stream); + if (fprintf(stream, "%d", value) < 0) + perror(err_msg); +} + + +void shutdown(int exit_val, char *err_cause, int line_no) +{ + static int in_shutdown = 0; + int errno_at_shutdown = errno; + int i; + + /* In case we get called by multiple threads or from an sighandler */ + if (in_shutdown++) + return; + + for (i = 0; i < num_cpus_to_pin; i++) + if (cpu_threads[i]) { + pthread_kill(cpu_threads[i], SIGUSR1); + pthread_join(cpu_threads[i], NULL); + } + + if (queue != -1) + if (mq_close(queue)) + perror("mq_close() during shutdown"); + if (queue_path) + /* + * Be silent if this fails, if we cleaned up already it's + * expected to fail + */ + mq_unlink(queue_path); + if (saved_max_msgs) + __set(max_msgs, saved_max_msgs, + "failed to restore saved_max_msgs"); + if (saved_max_msgsize) + __set(max_msgsize, saved_max_msgsize, + "failed to restore saved_max_msgsize"); + if (exit_val) + error(exit_val, errno_at_shutdown, "%s at %d", + err_cause, line_no); + exit(0); +} + +void sig_action_SIGUSR1(int signum, siginfo_t *info, void *context) +{ + if (pthread_self() != main_thread) + pthread_exit(0); + else { + fprintf(stderr, "Caught signal %d in SIGUSR1 handler, " + "exiting\n", signum); + shutdown(0, "", 0); + fprintf(stderr, "\n\nReturned from shutdown?!?!\n\n"); + exit(0); + } +} + +void sig_action(int signum, siginfo_t *info, void *context) +{ + if (pthread_self() != main_thread) + pthread_kill(main_thread, signum); + else { + fprintf(stderr, "Caught signal %d, exiting\n", signum); + shutdown(0, "", 0); + fprintf(stderr, "\n\nReturned from shutdown?!?!\n\n"); + exit(0); + } +} + +static inline int get(FILE *stream) +{ + int value; + rewind(stream); + if (fscanf(stream, "%d", &value) != 1) + shutdown(4, "Error reading /proc entry", __LINE__); + return value; +} + +static inline void set(FILE *stream, int value) +{ + int new_value; + + rewind(stream); + if (fprintf(stream, "%d", value) < 0) + return shutdown(5, "Failed writing to /proc file", __LINE__); + new_value = get(stream); + if (new_value != value) + return shutdown(5, "We didn't get what we wrote to /proc back", + __LINE__); +} + +static inline int try_set(FILE *stream, int value) +{ + int new_value; + + rewind(stream); + fprintf(stream, "%d", value); + new_value = get(stream); + return new_value == value; +} + +static inline void getr(int type, struct rlimit *rlim) +{ + if (getrlimit(type, rlim)) + shutdown(6, "getrlimit()", __LINE__); +} + +static inline void setr(int type, struct rlimit *rlim) +{ + if (setrlimit(type, rlim)) + shutdown(7, "setrlimit()", __LINE__); +} + +/** + * open_queue - open the global queue for testing + * @attr - An attr struct specifying the desired queue traits + * @result - An attr struct that lists the actual traits the queue has + * + * This open is not allowed to fail, failure will result in an orderly + * shutdown of the program. The global queue_path is used to set what + * queue to open, the queue descriptor is saved in the global queue + * variable. + */ +static inline void open_queue(struct mq_attr *attr) +{ + int flags = O_RDWR | O_EXCL | O_CREAT | O_NONBLOCK; + int perms = DEFFILEMODE; + + queue = mq_open(queue_path, flags, perms, attr); + if (queue == -1) + shutdown(1, "mq_open()", __LINE__); + if (mq_getattr(queue, &result)) + shutdown(1, "mq_getattr()", __LINE__); + printf("\n\tQueue %s created:\n", queue_path); + printf("\t\tmq_flags:\t\t\t%s\n", result.mq_flags & O_NONBLOCK ? + "O_NONBLOCK" : "(null)"); + printf("\t\tmq_maxmsg:\t\t\t%d\n", result.mq_maxmsg); + printf("\t\tmq_msgsize:\t\t\t%d\n", result.mq_msgsize); + printf("\t\tmq_curmsgs:\t\t\t%d\n", result.mq_curmsgs); +} + +void *fake_cont_thread(void *arg) +{ + int i; + + for (i = 0; i < num_cpus_to_pin; i++) + if (cpu_threads[i] == pthread_self()) + break; + printf("\tStarted fake continuous mode thread %d on CPU %d\n", i, + cpus_to_pin[i]); + while (1) + ; +} + +void *cont_thread(void *arg) +{ + char buff[MSG_SIZE]; + int i, priority; + + for (i = 0; i < num_cpus_to_pin; i++) + if (cpu_threads[i] == pthread_self()) + break; + printf("\tStarted continuous mode thread %d on CPU %d\n", i, + cpus_to_pin[i]); + while (1) { + while (mq_send(queue, buff, sizeof(buff), 0) == 0) + ; + mq_receive(queue, buff, sizeof(buff), &priority); + } +} + +#define drain_queue() \ + while (mq_receive(queue, buff, MSG_SIZE, &prio_in) == MSG_SIZE) + +#define do_untimed_send() \ + do { \ + if (mq_send(queue, buff, MSG_SIZE, prio_out)) \ + shutdown(3, "Test send failure", __LINE__); \ + } while (0) + +#define do_send_recv() \ + do { \ + clock_gettime(clock, &start); \ + if (mq_send(queue, buff, MSG_SIZE, prio_out)) \ + shutdown(3, "Test send failure", __LINE__); \ + clock_gettime(clock, &middle); \ + if (mq_receive(queue, buff, MSG_SIZE, &prio_in) != MSG_SIZE) \ + shutdown(3, "Test receive failure", __LINE__); \ + clock_gettime(clock, &end); \ + nsec = ((middle.tv_sec - start.tv_sec) * 1000000000) + \ + (middle.tv_nsec - start.tv_nsec); \ + send_total.tv_nsec += nsec; \ + if (send_total.tv_nsec >= 1000000000) { \ + send_total.tv_sec++; \ + send_total.tv_nsec -= 1000000000; \ + } \ + nsec = ((end.tv_sec - middle.tv_sec) * 1000000000) + \ + (end.tv_nsec - middle.tv_nsec); \ + recv_total.tv_nsec += nsec; \ + if (recv_total.tv_nsec >= 1000000000) { \ + recv_total.tv_sec++; \ + recv_total.tv_nsec -= 1000000000; \ + } \ + } while (0) + +struct test { + char *desc; + void (*func)(int *); +}; + +void const_prio(int *prio) +{ + return; +} + +void inc_prio(int *prio) +{ + if (++*prio == mq_prio_max) + *prio = 0; +} + +void dec_prio(int *prio) +{ + if (--*prio < 0) + *prio = mq_prio_max - 1; +} + +void random_prio(int *prio) +{ + *prio = random() % mq_prio_max; +} + +struct test test2[] = { + {"\n\tTest #2a: Time send/recv message, queue full, constant prio\n", + const_prio}, + {"\n\tTest #2b: Time send/recv message, queue full, increasing prio\n", + inc_prio}, + {"\n\tTest #2c: Time send/recv message, queue full, decreasing prio\n", + dec_prio}, + {"\n\tTest #2d: Time send/recv message, queue full, random prio\n", + random_prio}, + {NULL, NULL} +}; + +/** + * Tests to perform (all done with MSG_SIZE messages): + * + * 1) Time to add/remove message with 0 messages on queue + * 1a) with constant prio + * 2) Time to add/remove message when queue close to capacity: + * 2a) with constant prio + * 2b) with increasing prio + * 2c) with decreasing prio + * 2d) with random prio + * 3) Test limits of priorities honored (double check _SC_MQ_PRIO_MAX) + */ +void *perf_test_thread(void *arg) +{ + char buff[MSG_SIZE]; + int prio_out, prio_in; + int i; + clockid_t clock; + pthread_t *t; + struct timespec res, start, middle, end, send_total, recv_total; + unsigned long long nsec; + struct test *cur_test; + + t = &cpu_threads[0]; + printf("\n\tStarted mqueue performance test thread on CPU %d\n", + cpus_to_pin[0]); + mq_prio_max = sysconf(_SC_MQ_PRIO_MAX); + if (mq_prio_max == -1) + shutdown(2, "sysconf(_SC_MQ_PRIO_MAX)", __LINE__); + if (pthread_getcpuclockid(cpu_threads[0], &clock) != 0) + shutdown(2, "pthread_getcpuclockid", __LINE__); + + if (clock_getres(clock, &res)) + shutdown(2, "clock_getres()", __LINE__); + + printf("\t\tMax priorities:\t\t\t%d\n", mq_prio_max); + printf("\t\tClock resolution:\t\t%d nsec%s\n", res.tv_nsec, + res.tv_nsec > 1 ? "s" : ""); + + + + printf("\n\tTest #1: Time send/recv message, queue empty\n"); + printf("\t\t(%d iterations)\n", TEST1_LOOPS); + prio_out = 0; + send_total.tv_sec = 0; + send_total.tv_nsec = 0; + recv_total.tv_sec = 0; + recv_total.tv_nsec = 0; + for (i = 0; i < TEST1_LOOPS; i++) + do_send_recv(); + printf("\t\tSend msg:\t\t\t%d.%ds total time\n", + send_total.tv_sec, send_total.tv_nsec); + nsec = ((unsigned long long)send_total.tv_sec * 1000000000 + + send_total.tv_nsec) / TEST1_LOOPS; + printf("\t\t\t\t\t\t%d nsec/msg\n", nsec); + printf("\t\tRecv msg:\t\t\t%d.%ds total time\n", + recv_total.tv_sec, recv_total.tv_nsec); + nsec = ((unsigned long long)recv_total.tv_sec * 1000000000 + + recv_total.tv_nsec) / TEST1_LOOPS; + printf("\t\t\t\t\t\t%d nsec/msg\n", nsec); + + + for (cur_test = test2; cur_test->desc != NULL; cur_test++) { + printf(cur_test->desc); + printf("\t\t(%d iterations)\n", TEST2_LOOPS); + prio_out = 0; + send_total.tv_sec = 0; + send_total.tv_nsec = 0; + recv_total.tv_sec = 0; + recv_total.tv_nsec = 0; + printf("\t\tFilling queue..."); + fflush(stdout); + clock_gettime(clock, &start); + for (i = 0; i < result.mq_maxmsg - 1; i++) { + do_untimed_send(); + cur_test->func(&prio_out); + } + clock_gettime(clock, &end); + nsec = ((unsigned long long)(end.tv_sec - start.tv_sec) * + 1000000000) + (end.tv_nsec - start.tv_nsec); + printf("done.\t\t%lld.%llds\n", nsec / 1000000000, + nsec % 1000000000); + printf("\t\tTesting..."); + fflush(stdout); + for (i = 0; i < TEST2_LOOPS; i++) { + do_send_recv(); + cur_test->func(&prio_out); + } + printf("done.\n"); + printf("\t\tSend msg:\t\t\t%d.%ds total time\n", + send_total.tv_sec, send_total.tv_nsec); + nsec = ((unsigned long long)send_total.tv_sec * 1000000000 + + send_total.tv_nsec) / TEST2_LOOPS; + printf("\t\t\t\t\t\t%d nsec/msg\n", nsec); + printf("\t\tRecv msg:\t\t\t%d.%ds total time\n", + recv_total.tv_sec, recv_total.tv_nsec); + nsec = ((unsigned long long)recv_total.tv_sec * 1000000000 + + recv_total.tv_nsec) / TEST2_LOOPS; + printf("\t\t\t\t\t\t%d nsec/msg\n", nsec); + printf("\t\tDraining queue..."); + fflush(stdout); + clock_gettime(clock, &start); + drain_queue(); + clock_gettime(clock, &end); + nsec = ((unsigned long long)(end.tv_sec - start.tv_sec) * + 1000000000) + (end.tv_nsec - start.tv_nsec); + printf("done.\t\t%lld.%llds\n", nsec / 1000000000, + nsec % 1000000000); + } + return 0; +} + +void increase_limits(void) +{ + cur_limits.rlim_cur = RLIM_INFINITY; + cur_limits.rlim_max = RLIM_INFINITY; + setr(RLIMIT_MSGQUEUE, &cur_limits); + while (try_set(max_msgs, cur_max_msgs += 10)) + ; + cur_max_msgs = get(max_msgs); + while (try_set(max_msgsize, cur_max_msgsize += 1024)) + ; + cur_max_msgsize = get(max_msgsize); + if (setpriority(PRIO_PROCESS, 0, -20) != 0) + shutdown(2, "setpriority()", __LINE__); + cur_nice = -20; +} + +int main(int argc, char *argv[]) +{ + struct mq_attr attr; + char *option, *next_option; + int i, cpu; + struct sigaction sa; + poptContext popt_context; + char rc; + void *retval; + + main_thread = pthread_self(); + num_cpus_to_pin = 0; + + if (sysconf(_SC_NPROCESSORS_ONLN) == -1) { + perror("sysconf(_SC_NPROCESSORS_ONLN)"); + exit(1); + } + cpus_online = min(MAX_CPUS, sysconf(_SC_NPROCESSORS_ONLN)); + cpu_set = CPU_ALLOC(cpus_online); + if (cpu_set == NULL) { + perror("CPU_ALLOC()"); + exit(1); + } + cpu_set_size = CPU_ALLOC_SIZE(cpus_online); + CPU_ZERO_S(cpu_set_size, cpu_set); + + popt_context = poptGetContext(NULL, argc, (const char **)argv, + options, 0); + + while ((rc = poptGetNextOpt(popt_context)) > 0) { + switch (rc) { + case 'c': + continuous_mode = 1; + option = cpu_option_string; + do { + next_option = strchr(option, ','); + if (next_option) + *next_option = '\0'; + cpu = atoi(option); + if (cpu >= cpus_online) + fprintf(stderr, "CPU %d exceeds " + "cpus online, ignoring.\n", + cpu); + else + cpus_to_pin[num_cpus_to_pin++] = cpu; + if (next_option) + option = ++next_option; + } while (next_option && num_cpus_to_pin < MAX_CPUS); + /* Double check that they didn't give us the same CPU + * more than once */ + for (cpu = 0; cpu < num_cpus_to_pin; cpu++) { + if (CPU_ISSET_S(cpus_to_pin[cpu], cpu_set_size, + cpu_set)) { + fprintf(stderr, "Any given CPU may " + "only be given once.\n"); + exit(1); + } else + CPU_SET_S(cpus_to_pin[cpu], + cpu_set_size, cpu_set); + } + break; + case 'p': + /* + * Although we can create a msg queue with a + * non-absolute path name, unlink will fail. So, + * if the name doesn't start with a /, add one + * when we save it. + */ + option = queue_path; + if (*option != '/') { + queue_path = malloc(strlen(option) + 2); + if (!queue_path) { + perror("malloc()"); + exit(1); + } + queue_path[0] = '/'; + queue_path[1] = 0; + strcat(queue_path, option); + free(option); + } + break; + } + } + + if (continuous_mode && num_cpus_to_pin == 0) { + fprintf(stderr, "Must pass at least one CPU to continuous " + "mode.\n"); + poptPrintUsage(popt_context, stderr, 0); + exit(1); + } else if (!continuous_mode) { + num_cpus_to_pin = 1; + cpus_to_pin[0] = cpus_online - 1; + } + + if (getuid() != 0) { + fprintf(stderr, "Not running as root, but almost all tests " + "require root in order to modify\nsystem settings. " + "Exiting.\n"); + exit(1); + } + + max_msgs = fopen(MAX_MSGS, "r+"); + max_msgsize = fopen(MAX_MSGSIZE, "r+"); + if (!max_msgs) + shutdown(2, "Failed to open msg_max", __LINE__); + if (!max_msgsize) + shutdown(2, "Failed to open msgsize_max", __LINE__); + + /* Load up the current system values for everything we can */ + getr(RLIMIT_MSGQUEUE, &saved_limits); + cur_limits = saved_limits; + saved_max_msgs = cur_max_msgs = get(max_msgs); + saved_max_msgsize = cur_max_msgsize = get(max_msgsize); + errno = 0; + cur_nice = getpriority(PRIO_PROCESS, 0); + if (errno) + shutdown(2, "getpriority()", __LINE__); + + /* Tell the user our initial state */ + printf("\nInitial system state:\n"); + printf("\tUsing queue path:\t\t\t%s\n", queue_path); + printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%d\n", saved_limits.rlim_cur); + printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%d\n", saved_limits.rlim_max); + printf("\tMaximum Message Size:\t\t\t%d\n", saved_max_msgsize); + printf("\tMaximum Queue Size:\t\t\t%d\n", saved_max_msgs); + printf("\tNice value:\t\t\t\t%d\n", cur_nice); + printf("\n"); + + increase_limits(); + + printf("Adjusted system state for testing:\n"); + if (cur_limits.rlim_cur == RLIM_INFINITY) { + printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t(unlimited)\n"); + printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t(unlimited)\n"); + } else { + printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%d\n", + cur_limits.rlim_cur); + printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%d\n", + cur_limits.rlim_max); + } + printf("\tMaximum Message Size:\t\t\t%d\n", cur_max_msgsize); + printf("\tMaximum Queue Size:\t\t\t%d\n", cur_max_msgs); + printf("\tNice value:\t\t\t\t%d\n", cur_nice); + printf("\tContinuous mode:\t\t\t(%s)\n", continuous_mode ? + (continuous_mode_fake ? "fake mode" : "enabled") : + "disabled"); + printf("\tCPUs to pin:\t\t\t\t%d", cpus_to_pin[0]); + for (cpu = 1; cpu < num_cpus_to_pin; cpu++) + printf(",%d", cpus_to_pin[cpu]); + printf("\n"); + + sa.sa_sigaction = sig_action_SIGUSR1; + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGHUP); + sigaddset(&sa.sa_mask, SIGINT); + sigaddset(&sa.sa_mask, SIGQUIT); + sigaddset(&sa.sa_mask, SIGTERM); + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGUSR1, &sa, NULL) == -1) + shutdown(1, "sigaction(SIGUSR1)", __LINE__); + sa.sa_sigaction = sig_action; + if (sigaction(SIGHUP, &sa, NULL) == -1) + shutdown(1, "sigaction(SIGHUP)", __LINE__); + if (sigaction(SIGINT, &sa, NULL) == -1) + shutdown(1, "sigaction(SIGINT)", __LINE__); + if (sigaction(SIGQUIT, &sa, NULL) == -1) + shutdown(1, "sigaction(SIGQUIT)", __LINE__); + if (sigaction(SIGTERM, &sa, NULL) == -1) + shutdown(1, "sigaction(SIGTERM)", __LINE__); + + if (!continuous_mode_fake) { + attr.mq_flags = O_NONBLOCK; + attr.mq_maxmsg = cur_max_msgs; + attr.mq_msgsize = MSG_SIZE; + open_queue(&attr); + } + for (i = 0; i < num_cpus_to_pin; i++) { + pthread_attr_t thread_attr; + void *thread_func; + + if (continuous_mode_fake) + thread_func = &fake_cont_thread; + else if (continuous_mode) + thread_func = &cont_thread; + else + thread_func = &perf_test_thread; + + CPU_ZERO_S(cpu_set_size, cpu_set); + CPU_SET_S(cpus_to_pin[i], cpu_set_size, cpu_set); + pthread_attr_init(&thread_attr); + pthread_attr_setaffinity_np(&thread_attr, cpu_set_size, + cpu_set); + if (pthread_create(&cpu_threads[i], &thread_attr, thread_func, + NULL)) + shutdown(1, "pthread_create()", __LINE__); + pthread_attr_destroy(&thread_attr); + } + + if (!continuous_mode) { + pthread_join(cpu_threads[0], &retval); + shutdown((long)retval, "perf_test_thread()", __LINE__); + } else { + while (1) + sleep(1); + } + shutdown(0, "", 0); +} -- cgit v1.2.3-70-g09d2 From d97b46a64674a267bc41c9e16132ee2a98c3347d Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Thu, 31 May 2012 16:26:44 -0700 Subject: syscalls, x86: add __NR_kcmp syscall While doing the checkpoint-restore in the user space one need to determine whether various kernel objects (like mm_struct-s of file_struct-s) are shared between tasks and restore this state. The 2nd step can be solved by using appropriate CLONE_ flags and the unshare syscall, while there's currently no ways for solving the 1st one. One of the ways for checking whether two tasks share e.g. mm_struct is to provide some mm_struct ID of a task to its proc file, but showing such info considered to be not that good for security reasons. Thus after some debates we end up in conclusion that using that named 'comparison' syscall might be the best candidate. So here is it -- __NR_kcmp. It takes up to 5 arguments - the pids of the two tasks (which characteristics should be compared), the comparison type and (in case of comparison of files) two file descriptors. Lookups for pids are done in the caller's PID namespace only. At moment only x86 is supported and tested. [akpm@linux-foundation.org: fix up selftests, warnings] [akpm@linux-foundation.org: include errno.h] [akpm@linux-foundation.org: tweak comment text] Signed-off-by: Cyrill Gorcunov Acked-by: "Eric W. Biederman" Cc: Pavel Emelyanov Cc: Andrey Vagin Cc: KOSAKI Motohiro Cc: Ingo Molnar Cc: H. Peter Anvin Cc: Thomas Gleixner Cc: Glauber Costa Cc: Andi Kleen Cc: Tejun Heo Cc: Matt Helsley Cc: Pekka Enberg Cc: Eric Dumazet Cc: Vasiliy Kulikov Cc: Alexey Dobriyan Cc: Valdis.Kletnieks@vt.edu Cc: Michal Marek Cc: Frederic Weisbecker Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/syscalls/syscall_32.tbl | 1 + arch/x86/syscalls/syscall_64.tbl | 2 + include/linux/kcmp.h | 17 +++ include/linux/syscalls.h | 2 + kernel/Makefile | 3 + kernel/kcmp.c | 196 +++++++++++++++++++++++++++++++ kernel/sys_ni.c | 3 + tools/testing/selftests/Makefile | 2 +- tools/testing/selftests/kcmp/Makefile | 29 +++++ tools/testing/selftests/kcmp/kcmp_test.c | 94 +++++++++++++++ 10 files changed, 348 insertions(+), 1 deletion(-) create mode 100644 include/linux/kcmp.h create mode 100644 kernel/kcmp.c create mode 100644 tools/testing/selftests/kcmp/Makefile create mode 100644 tools/testing/selftests/kcmp/kcmp_test.c (limited to 'tools/testing') diff --git a/arch/x86/syscalls/syscall_32.tbl b/arch/x86/syscalls/syscall_32.tbl index 29f9f0554f7..7a35a6e71d4 100644 --- a/arch/x86/syscalls/syscall_32.tbl +++ b/arch/x86/syscalls/syscall_32.tbl @@ -355,3 +355,4 @@ 346 i386 setns sys_setns 347 i386 process_vm_readv sys_process_vm_readv compat_sys_process_vm_readv 348 i386 process_vm_writev sys_process_vm_writev compat_sys_process_vm_writev +349 i386 kcmp sys_kcmp diff --git a/arch/x86/syscalls/syscall_64.tbl b/arch/x86/syscalls/syscall_64.tbl index dd29a9ea27c..51171aeff0d 100644 --- a/arch/x86/syscalls/syscall_64.tbl +++ b/arch/x86/syscalls/syscall_64.tbl @@ -318,6 +318,8 @@ 309 common getcpu sys_getcpu 310 64 process_vm_readv sys_process_vm_readv 311 64 process_vm_writev sys_process_vm_writev +312 64 kcmp sys_kcmp + # # x32-specific system call numbers start at 512 to avoid cache impact # for native 64-bit operation. diff --git a/include/linux/kcmp.h b/include/linux/kcmp.h new file mode 100644 index 00000000000..2dcd1b3aafc --- /dev/null +++ b/include/linux/kcmp.h @@ -0,0 +1,17 @@ +#ifndef _LINUX_KCMP_H +#define _LINUX_KCMP_H + +/* Comparison type */ +enum kcmp_type { + KCMP_FILE, + KCMP_VM, + KCMP_FILES, + KCMP_FS, + KCMP_SIGHAND, + KCMP_IO, + KCMP_SYSVSEM, + + KCMP_TYPES, +}; + +#endif /* _LINUX_KCMP_H */ diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 3de3acb84a9..19439c75c5b 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -858,4 +858,6 @@ asmlinkage long sys_process_vm_writev(pid_t pid, unsigned long riovcnt, unsigned long flags); +asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type, + unsigned long idx1, unsigned long idx2); #endif diff --git a/kernel/Makefile b/kernel/Makefile index 6c07f30fa9b..80be6ca0cc7 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -25,6 +25,9 @@ endif obj-y += sched/ obj-y += power/ +ifeq ($(CONFIG_CHECKPOINT_RESTORE),y) +obj-$(CONFIG_X86) += kcmp.o +endif obj-$(CONFIG_FREEZER) += freezer.o obj-$(CONFIG_PROFILING) += profile.o obj-$(CONFIG_STACKTRACE) += stacktrace.o diff --git a/kernel/kcmp.c b/kernel/kcmp.c new file mode 100644 index 00000000000..30b7b225306 --- /dev/null +++ b/kernel/kcmp.c @@ -0,0 +1,196 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * We don't expose the real in-memory order of objects for security reasons. + * But still the comparison results should be suitable for sorting. So we + * obfuscate kernel pointers values and compare the production instead. + * + * The obfuscation is done in two steps. First we xor the kernel pointer with + * a random value, which puts pointer into a new position in a reordered space. + * Secondly we multiply the xor production with a large odd random number to + * permute its bits even more (the odd multiplier guarantees that the product + * is unique ever after the high bits are truncated, since any odd number is + * relative prime to 2^n). + * + * Note also that the obfuscation itself is invisible to userspace and if needed + * it can be changed to an alternate scheme. + */ +static unsigned long cookies[KCMP_TYPES][2] __read_mostly; + +static long kptr_obfuscate(long v, int type) +{ + return (v ^ cookies[type][0]) * cookies[type][1]; +} + +/* + * 0 - equal, i.e. v1 = v2 + * 1 - less than, i.e. v1 < v2 + * 2 - greater than, i.e. v1 > v2 + * 3 - not equal but ordering unavailable (reserved for future) + */ +static int kcmp_ptr(void *v1, void *v2, enum kcmp_type type) +{ + long ret; + + ret = kptr_obfuscate((long)v1, type) - kptr_obfuscate((long)v2, type); + + return (ret < 0) | ((ret > 0) << 1); +} + +/* The caller must have pinned the task */ +static struct file * +get_file_raw_ptr(struct task_struct *task, unsigned int idx) +{ + struct file *file = NULL; + + task_lock(task); + rcu_read_lock(); + + if (task->files) + file = fcheck_files(task->files, idx); + + rcu_read_unlock(); + task_unlock(task); + + return file; +} + +static void kcmp_unlock(struct mutex *m1, struct mutex *m2) +{ + if (likely(m2 != m1)) + mutex_unlock(m2); + mutex_unlock(m1); +} + +static int kcmp_lock(struct mutex *m1, struct mutex *m2) +{ + int err; + + if (m2 > m1) + swap(m1, m2); + + err = mutex_lock_killable(m1); + if (!err && likely(m1 != m2)) { + err = mutex_lock_killable_nested(m2, SINGLE_DEPTH_NESTING); + if (err) + mutex_unlock(m1); + } + + return err; +} + +SYSCALL_DEFINE5(kcmp, pid_t, pid1, pid_t, pid2, int, type, + unsigned long, idx1, unsigned long, idx2) +{ + struct task_struct *task1, *task2; + int ret; + + rcu_read_lock(); + + /* + * Tasks are looked up in caller's PID namespace only. + */ + task1 = find_task_by_vpid(pid1); + task2 = find_task_by_vpid(pid2); + if (!task1 || !task2) + goto err_no_task; + + get_task_struct(task1); + get_task_struct(task2); + + rcu_read_unlock(); + + /* + * One should have enough rights to inspect task details. + */ + ret = kcmp_lock(&task1->signal->cred_guard_mutex, + &task2->signal->cred_guard_mutex); + if (ret) + goto err; + if (!ptrace_may_access(task1, PTRACE_MODE_READ) || + !ptrace_may_access(task2, PTRACE_MODE_READ)) { + ret = -EPERM; + goto err_unlock; + } + + switch (type) { + case KCMP_FILE: { + struct file *filp1, *filp2; + + filp1 = get_file_raw_ptr(task1, idx1); + filp2 = get_file_raw_ptr(task2, idx2); + + if (filp1 && filp2) + ret = kcmp_ptr(filp1, filp2, KCMP_FILE); + else + ret = -EBADF; + break; + } + case KCMP_VM: + ret = kcmp_ptr(task1->mm, task2->mm, KCMP_VM); + break; + case KCMP_FILES: + ret = kcmp_ptr(task1->files, task2->files, KCMP_FILES); + break; + case KCMP_FS: + ret = kcmp_ptr(task1->fs, task2->fs, KCMP_FS); + break; + case KCMP_SIGHAND: + ret = kcmp_ptr(task1->sighand, task2->sighand, KCMP_SIGHAND); + break; + case KCMP_IO: + ret = kcmp_ptr(task1->io_context, task2->io_context, KCMP_IO); + break; + case KCMP_SYSVSEM: +#ifdef CONFIG_SYSVIPC + ret = kcmp_ptr(task1->sysvsem.undo_list, + task2->sysvsem.undo_list, + KCMP_SYSVSEM); +#else + ret = -EOPNOTSUPP; +#endif + break; + default: + ret = -EINVAL; + break; + } + +err_unlock: + kcmp_unlock(&task1->signal->cred_guard_mutex, + &task2->signal->cred_guard_mutex); +err: + put_task_struct(task1); + put_task_struct(task2); + + return ret; + +err_no_task: + rcu_read_unlock(); + return -ESRCH; +} + +static __init int kcmp_cookies_init(void) +{ + int i; + + get_random_bytes(cookies, sizeof(cookies)); + + for (i = 0; i < KCMP_TYPES; i++) + cookies[i][1] |= (~(~0UL >> 1) | 1); + + return 0; +} +arch_initcall(kcmp_cookies_init); diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 47bfa16430d..dbff751e408 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -203,3 +203,6 @@ cond_syscall(sys_fanotify_mark); cond_syscall(sys_name_to_handle_at); cond_syscall(sys_open_by_handle_at); cond_syscall(compat_sys_open_by_handle_at); + +/* compare kernel pointers */ +cond_syscall(sys_kcmp); diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 14972017a43..a4162e15c25 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,4 +1,4 @@ -TARGETS = breakpoints mqueue vm +TARGETS = breakpoints kcmp mqueue vm all: for TARGET in $(TARGETS); do \ diff --git a/tools/testing/selftests/kcmp/Makefile b/tools/testing/selftests/kcmp/Makefile new file mode 100644 index 00000000000..dc79b86ea65 --- /dev/null +++ b/tools/testing/selftests/kcmp/Makefile @@ -0,0 +1,29 @@ +uname_M := $(shell uname -m 2>/dev/null || echo not) +ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/) +ifeq ($(ARCH),i386) + ARCH := X86 + CFLAGS := -DCONFIG_X86_32 -D__i386__ +endif +ifeq ($(ARCH),x86_64) + ARCH := X86 + CFLAGS := -DCONFIG_X86_64 -D__x86_64__ +endif + +CFLAGS += -I../../../../arch/x86/include/generated/ +CFLAGS += -I../../../../include/ +CFLAGS += -I../../../../usr/include/ +CFLAGS += -I../../../../arch/x86/include/ + +all: +ifeq ($(ARCH),X86) + gcc $(CFLAGS) kcmp_test.c -o run_test +else + echo "Not an x86 target, can't build kcmp selftest" +endif + +run-tests: all + ./kcmp_test + +clean: + rm -fr ./run_test + rm -fr ./test-file diff --git a/tools/testing/selftests/kcmp/kcmp_test.c b/tools/testing/selftests/kcmp/kcmp_test.c new file mode 100644 index 00000000000..358cc6bfa35 --- /dev/null +++ b/tools/testing/selftests/kcmp/kcmp_test.c @@ -0,0 +1,94 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +static long sys_kcmp(int pid1, int pid2, int type, int fd1, int fd2) +{ + return syscall(__NR_kcmp, pid1, pid2, type, fd1, fd2); +} + +int main(int argc, char **argv) +{ + const char kpath[] = "kcmp-test-file"; + int pid1, pid2; + int fd1, fd2; + int status; + + fd1 = open(kpath, O_RDWR | O_CREAT | O_TRUNC, 0644); + pid1 = getpid(); + + if (fd1 < 0) { + perror("Can't create file"); + exit(1); + } + + pid2 = fork(); + if (pid2 < 0) { + perror("fork failed"); + exit(1); + } + + if (!pid2) { + int pid2 = getpid(); + int ret; + + fd2 = open(kpath, O_RDWR, 0644); + if (fd2 < 0) { + perror("Can't open file"); + exit(1); + } + + /* An example of output and arguments */ + printf("pid1: %6d pid2: %6d FD: %2ld FILES: %2ld VM: %2ld " + "FS: %2ld SIGHAND: %2ld IO: %2ld SYSVSEM: %2ld " + "INV: %2ld\n", + pid1, pid2, + sys_kcmp(pid1, pid2, KCMP_FILE, fd1, fd2), + sys_kcmp(pid1, pid2, KCMP_FILES, 0, 0), + sys_kcmp(pid1, pid2, KCMP_VM, 0, 0), + sys_kcmp(pid1, pid2, KCMP_FS, 0, 0), + sys_kcmp(pid1, pid2, KCMP_SIGHAND, 0, 0), + sys_kcmp(pid1, pid2, KCMP_IO, 0, 0), + sys_kcmp(pid1, pid2, KCMP_SYSVSEM, 0, 0), + + /* This one should fail */ + sys_kcmp(pid1, pid2, KCMP_TYPES + 1, 0, 0)); + + /* This one should return same fd */ + ret = sys_kcmp(pid1, pid2, KCMP_FILE, fd1, fd1); + if (ret) { + printf("FAIL: 0 expected but %d returned\n", ret); + ret = -1; + } else + printf("PASS: 0 returned as expected\n"); + + /* Compare with self */ + ret = sys_kcmp(pid1, pid1, KCMP_VM, 0, 0); + if (ret) { + printf("FAIL: 0 expected but %li returned\n", ret); + ret = -1; + } else + printf("PASS: 0 returned as expected\n"); + + exit(ret); + } + + waitpid(pid2, &status, P_ALL); + + return 0; +} -- cgit v1.2.3-70-g09d2