From a80a6b85b428e6ce12a8363bb1f08d44c50f3252 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 8 Nov 2012 15:53:35 -0800 Subject: revert "epoll: support for disabling items, and a self-test app" Revert commit 03a7beb55b9f ("epoll: support for disabling items, and a self-test app") pending resolution of the issues identified by Michael Kerrisk, copied below. We'll revisit this for 3.8. : I've taken a look at this patch as it currently stands in 3.7-rc1, and : done a bit of testing. (By the way, the test program : tools/testing/selftests/epoll/test_epoll.c does not compile...) : : There are one or two places where the behavior seems a little strange, : so I have a question or two at the end of this mail. But other than : that, I want to check my understanding so that the interface can be : correctly documented. : : Just to go though my understanding, the problem is the following : scenario in a multithreaded application: : : 1. Multiple threads are performing epoll_wait() operations, : and maintaining a user-space cache that contains information : corresponding to each file descriptor being monitored by : epoll_wait(). : : 2. At some point, a thread wants to delete (EPOLL_CTL_DEL) : a file descriptor from the epoll interest list, and : delete the corresponding record from the user-space cache. : : 3. The problem with (2) is that some other thread may have : previously done an epoll_wait() that retrieved information : about the fd in question, and may be in the middle of using : information in the cache that relates to that fd. Thus, : there is a potential race. : : 4. The race can't solved purely in user space, because doing : so would require applying a mutex across the epoll_wait() : call, which would of course blow thread concurrency. : : Right? : : Your solution is the EPOLL_CTL_DISABLE operation. I want to : confirm my understanding about how to use this flag, since : the description that has accompanied the patches so far : has been a bit sparse : : 0. In the scenario you're concerned about, deleting a file : descriptor means (safely) doing the following: : (a) Deleting the file descriptor from the epoll interest list : using EPOLL_CTL_DEL : (b) Deleting the corresponding record in the user-space cache : : 1. It's only meaningful to use this EPOLL_CTL_DISABLE in : conjunction with EPOLLONESHOT. : : 2. Using EPOLL_CTL_DISABLE without using EPOLLONESHOT in : conjunction is a logical error. : : 3. The correct way to code multithreaded applications using : EPOLL_CTL_DISABLE and EPOLLONESHOT is as follows: : : a. All EPOLL_CTL_ADD and EPOLL_CTL_MOD operations should : should EPOLLONESHOT. : : b. When a thread wants to delete a file descriptor, it : should do the following: : : [1] Call epoll_ctl(EPOLL_CTL_DISABLE) : [2] If the return status from epoll_ctl(EPOLL_CTL_DISABLE) : was zero, then the file descriptor can be safely : deleted by the thread that made this call. : [3] If the epoll_ctl(EPOLL_CTL_DISABLE) fails with EBUSY, : then the descriptor is in use. In this case, the calling : thread should set a flag in the user-space cache to : indicate that the thread that is using the descriptor : should perform the deletion operation. : : Is all of the above correct? : : The implementation depends on checking on whether : (events & ~EP_PRIVATE_BITS) == 0 : This replies on the fact that EPOLL_CTL_AD and EPOLL_CTL_MOD always : set EPOLLHUP and EPOLLERR in the 'events' mask, and EPOLLONESHOT : causes those flags (as well as all others in ~EP_PRIVATE_BITS) to be : cleared. : : A corollary to the previous paragraph is that using EPOLL_CTL_DISABLE : is only useful in conjunction with EPOLLONESHOT. However, as things : stand, one can use EPOLL_CTL_DISABLE on a file descriptor that does : not have EPOLLONESHOT set in 'events' This results in the following : (slightly surprising) behavior: : : (a) The first call to epoll_ctl(EPOLL_CTL_DISABLE) returns 0 : (the indicator that the file descriptor can be safely deleted). : (b) The next call to epoll_ctl(EPOLL_CTL_DISABLE) fails with EBUSY. : : This doesn't seem particularly useful, and in fact is probably an : indication that the user made a logic error: they should only be using : epoll_ctl(EPOLL_CTL_DISABLE) on a file descriptor for which : EPOLLONESHOT was set in 'events'. If that is correct, then would it : not make sense to return an error to user space for this case? Cc: Michael Kerrisk Cc: "Paton J. Lewis" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/Makefile | 2 +- tools/testing/selftests/epoll/Makefile | 11 - tools/testing/selftests/epoll/test_epoll.c | 344 ----------------------------- 3 files changed, 1 insertion(+), 356 deletions(-) delete mode 100644 tools/testing/selftests/epoll/Makefile delete mode 100644 tools/testing/selftests/epoll/test_epoll.c (limited to 'tools/testing') diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 43480149119e..85baf11e2acd 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,4 +1,4 @@ -TARGETS = breakpoints kcmp mqueue vm cpu-hotplug memory-hotplug epoll +TARGETS = breakpoints kcmp mqueue vm cpu-hotplug memory-hotplug all: for TARGET in $(TARGETS); do \ diff --git a/tools/testing/selftests/epoll/Makefile b/tools/testing/selftests/epoll/Makefile deleted file mode 100644 index 19806ed62f50..000000000000 --- a/tools/testing/selftests/epoll/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Makefile for epoll selftests - -all: test_epoll -%: %.c - gcc -pthread -g -o $@ $^ - -run_tests: all - ./test_epoll - -clean: - $(RM) test_epoll diff --git a/tools/testing/selftests/epoll/test_epoll.c b/tools/testing/selftests/epoll/test_epoll.c deleted file mode 100644 index f7525392ce84..000000000000 --- a/tools/testing/selftests/epoll/test_epoll.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * tools/testing/selftests/epoll/test_epoll.c - * - * Copyright 2012 Adobe Systems Incorporated - * - * This program 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; either version 2 of the License, or - * (at your option) any later version. - * - * Paton J. Lewis - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * A pointer to an epoll_item_private structure will be stored in the epoll - * item's event structure so that we can get access to the epoll_item_private - * data after calling epoll_wait: - */ -struct epoll_item_private { - int index; /* Position of this struct within the epoll_items array. */ - int fd; - uint32_t events; - pthread_mutex_t mutex; /* Guards the following variables... */ - int stop; - int status; /* Stores any error encountered while handling item. */ - /* The following variable allows us to test whether we have encountered - a problem while attempting to cancel and delete the associated - event. When the test program exits, 'deleted' should be exactly - one. If it is greater than one, then the failed test reflects a real - world situation where we would have tried to access the epoll item's - private data after deleting it: */ - int deleted; -}; - -struct epoll_item_private *epoll_items; - -/* - * Delete the specified item from the epoll set. In a real-world secneario this - * is where we would free the associated data structure, but in this testing - * environment we retain the structure so that we can test for double-deletion: - */ -void delete_item(int index) -{ - __sync_fetch_and_add(&epoll_items[index].deleted, 1); -} - -/* - * A pointer to a read_thread_data structure will be passed as the argument to - * each read thread: - */ -struct read_thread_data { - int stop; - int status; /* Indicates any error encountered by the read thread. */ - int epoll_set; -}; - -/* - * The function executed by the read threads: - */ -void *read_thread_function(void *function_data) -{ - struct read_thread_data *thread_data = - (struct read_thread_data *)function_data; - struct epoll_event event_data; - struct epoll_item_private *item_data; - char socket_data; - - /* Handle events until we encounter an error or this thread's 'stop' - condition is set: */ - while (1) { - int result = epoll_wait(thread_data->epoll_set, - &event_data, - 1, /* Number of desired events */ - 1000); /* Timeout in ms */ - if (result < 0) { - /* Breakpoints signal all threads. Ignore that while - debugging: */ - if (errno == EINTR) - continue; - thread_data->status = errno; - return 0; - } else if (thread_data->stop) - return 0; - else if (result == 0) /* Timeout */ - continue; - - /* We need the mutex here because checking for the stop - condition and re-enabling the epoll item need to be done - together as one atomic operation when EPOLL_CTL_DISABLE is - available: */ - item_data = (struct epoll_item_private *)event_data.data.ptr; - pthread_mutex_lock(&item_data->mutex); - - /* Remove the item from the epoll set if we want to stop - handling that event: */ - if (item_data->stop) - delete_item(item_data->index); - else { - /* Clear the data that was written to the other end of - our non-blocking socket: */ - do { - if (read(item_data->fd, &socket_data, 1) < 1) { - if ((errno == EAGAIN) || - (errno == EWOULDBLOCK)) - break; - else - goto error_unlock; - } - } while (item_data->events & EPOLLET); - - /* The item was one-shot, so re-enable it: */ - event_data.events = item_data->events; - if (epoll_ctl(thread_data->epoll_set, - EPOLL_CTL_MOD, - item_data->fd, - &event_data) < 0) - goto error_unlock; - } - - pthread_mutex_unlock(&item_data->mutex); - } - -error_unlock: - thread_data->status = item_data->status = errno; - pthread_mutex_unlock(&item_data->mutex); - return 0; -} - -/* - * A pointer to a write_thread_data structure will be passed as the argument to - * the write thread: - */ -struct write_thread_data { - int stop; - int status; /* Indicates any error encountered by the write thread. */ - int n_fds; - int *fds; -}; - -/* - * The function executed by the write thread. It writes a single byte to each - * socket in turn until the stop condition for this thread is set. If writing to - * a socket would block (i.e. errno was EAGAIN), we leave that socket alone for - * the moment and just move on to the next socket in the list. We don't care - * about the order in which we deliver events to the epoll set. In fact we don't - * care about the data we're writing to the pipes at all; we just want to - * trigger epoll events: - */ -void *write_thread_function(void *function_data) -{ - const char data = 'X'; - int index; - struct write_thread_data *thread_data = - (struct write_thread_data *)function_data; - while (!thread_data->stop) - for (index = 0; - !thread_data->stop && (index < thread_data->n_fds); - ++index) - if ((write(thread_data->fds[index], &data, 1) < 1) && - (errno != EAGAIN) && - (errno != EWOULDBLOCK)) { - thread_data->status = errno; - return; - } -} - -/* - * Arguments are currently ignored: - */ -int main(int argc, char **argv) -{ - const int n_read_threads = 100; - const int n_epoll_items = 500; - int index; - int epoll_set = epoll_create1(0); - struct write_thread_data write_thread_data = { - 0, 0, n_epoll_items, malloc(n_epoll_items * sizeof(int)) - }; - struct read_thread_data *read_thread_data = - malloc(n_read_threads * sizeof(struct read_thread_data)); - pthread_t *read_threads = malloc(n_read_threads * sizeof(pthread_t)); - pthread_t write_thread; - - printf("-----------------\n"); - printf("Runing test_epoll\n"); - printf("-----------------\n"); - - epoll_items = malloc(n_epoll_items * sizeof(struct epoll_item_private)); - - if (epoll_set < 0 || epoll_items == 0 || write_thread_data.fds == 0 || - read_thread_data == 0 || read_threads == 0) - goto error; - - if (sysconf(_SC_NPROCESSORS_ONLN) < 2) { - printf("Error: please run this test on a multi-core system.\n"); - goto error; - } - - /* Create the socket pairs and epoll items: */ - for (index = 0; index < n_epoll_items; ++index) { - int socket_pair[2]; - struct epoll_event event_data; - if (socketpair(AF_UNIX, - SOCK_STREAM | SOCK_NONBLOCK, - 0, - socket_pair) < 0) - goto error; - write_thread_data.fds[index] = socket_pair[0]; - epoll_items[index].index = index; - epoll_items[index].fd = socket_pair[1]; - if (pthread_mutex_init(&epoll_items[index].mutex, NULL) != 0) - goto error; - /* We always use EPOLLONESHOT because this test is currently - structured to demonstrate the need for EPOLL_CTL_DISABLE, - which only produces useful information in the EPOLLONESHOT - case (without EPOLLONESHOT, calling epoll_ctl with - EPOLL_CTL_DISABLE will never return EBUSY). If support for - testing events without EPOLLONESHOT is desired, it should - probably be implemented in a separate unit test. */ - epoll_items[index].events = EPOLLIN | EPOLLONESHOT; - if (index < n_epoll_items / 2) - epoll_items[index].events |= EPOLLET; - epoll_items[index].stop = 0; - epoll_items[index].status = 0; - epoll_items[index].deleted = 0; - event_data.events = epoll_items[index].events; - event_data.data.ptr = &epoll_items[index]; - if (epoll_ctl(epoll_set, - EPOLL_CTL_ADD, - epoll_items[index].fd, - &event_data) < 0) - goto error; - } - - /* Create and start the read threads: */ - for (index = 0; index < n_read_threads; ++index) { - read_thread_data[index].stop = 0; - read_thread_data[index].status = 0; - read_thread_data[index].epoll_set = epoll_set; - if (pthread_create(&read_threads[index], - NULL, - read_thread_function, - &read_thread_data[index]) != 0) - goto error; - } - - if (pthread_create(&write_thread, - NULL, - write_thread_function, - &write_thread_data) != 0) - goto error; - - /* Cancel all event pollers: */ -#ifdef EPOLL_CTL_DISABLE - for (index = 0; index < n_epoll_items; ++index) { - pthread_mutex_lock(&epoll_items[index].mutex); - ++epoll_items[index].stop; - if (epoll_ctl(epoll_set, - EPOLL_CTL_DISABLE, - epoll_items[index].fd, - NULL) == 0) - delete_item(index); - else if (errno != EBUSY) { - pthread_mutex_unlock(&epoll_items[index].mutex); - goto error; - } - /* EBUSY means events were being handled; allow the other thread - to delete the item. */ - pthread_mutex_unlock(&epoll_items[index].mutex); - } -#else - for (index = 0; index < n_epoll_items; ++index) { - pthread_mutex_lock(&epoll_items[index].mutex); - ++epoll_items[index].stop; - pthread_mutex_unlock(&epoll_items[index].mutex); - /* Wait in case a thread running read_thread_function is - currently executing code between epoll_wait and - pthread_mutex_lock with this item. Note that a longer delay - would make double-deletion less likely (at the expense of - performance), but there is no guarantee that any delay would - ever be sufficient. Note also that we delete all event - pollers at once for testing purposes, but in a real-world - environment we are likely to want to be able to cancel event - pollers at arbitrary times. Therefore we can't improve this - situation by just splitting this loop into two loops - (i.e. signal 'stop' for all items, sleep, and then delete all - items). We also can't fix the problem via EPOLL_CTL_DEL - because that command can't prevent the case where some other - thread is executing read_thread_function within the region - mentioned above: */ - usleep(1); - pthread_mutex_lock(&epoll_items[index].mutex); - if (!epoll_items[index].deleted) - delete_item(index); - pthread_mutex_unlock(&epoll_items[index].mutex); - } -#endif - - /* Shut down the read threads: */ - for (index = 0; index < n_read_threads; ++index) - __sync_fetch_and_add(&read_thread_data[index].stop, 1); - for (index = 0; index < n_read_threads; ++index) { - if (pthread_join(read_threads[index], NULL) != 0) - goto error; - if (read_thread_data[index].status) - goto error; - } - - /* Shut down the write thread: */ - __sync_fetch_and_add(&write_thread_data.stop, 1); - if ((pthread_join(write_thread, NULL) != 0) || write_thread_data.status) - goto error; - - /* Check for final error conditions: */ - for (index = 0; index < n_epoll_items; ++index) { - if (epoll_items[index].status != 0) - goto error; - if (pthread_mutex_destroy(&epoll_items[index].mutex) < 0) - goto error; - } - for (index = 0; index < n_epoll_items; ++index) - if (epoll_items[index].deleted != 1) { - printf("Error: item data deleted %1d times.\n", - epoll_items[index].deleted); - goto error; - } - - printf("[PASS]\n"); - return 0; - - error: - printf("[FAIL]\n"); - return errno; -} -- cgit v1.3-14-g43fede From a15ba91361d46b4cc71d76d5712fb6420b517d8a Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 13 Nov 2012 14:30:37 -0500 Subject: ktest: Add support for grub2 As only grub or 'script' is supported for rebooting to a new kernel, and Fedora 17 has dropped support for grub, I decided to add grub2 support as well (I also plan on adding syslinux/extlinux support too). The options GRUB_FILE and GRUB_REBOOT were added to allow the user to specify where to find the grub.cfg and what tool to use to reboot into the next kernel respectively. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 69 +++++++++++++++++++++++++++++++++++++++-- tools/testing/ktest/sample.conf | 26 ++++++++++++++-- 2 files changed, 91 insertions(+), 4 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index c7ba7614061b..63245a56aca3 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -53,6 +53,7 @@ my %default = ( "STOP_AFTER_FAILURE" => 60, "STOP_TEST_AFTER" => 600, "MAX_MONITOR_WAIT" => 1800, + "GRUB_REBOOT" => "grub2-reboot", # required, and we will ask users if they don't have them but we keep the default # value something that is common. @@ -105,7 +106,9 @@ my $scp_to_target; my $scp_to_target_install; my $power_off; my $grub_menu; +my $grub_file; my $grub_number; +my $grub_reboot; my $target; my $make; my $pre_install; @@ -232,6 +235,8 @@ my %option_map = ( "ADD_CONFIG" => \$addconfig, "REBOOT_TYPE" => \$reboot_type, "GRUB_MENU" => \$grub_menu, + "GRUB_FILE" => \$grub_file, + "GRUB_REBOOT" => \$grub_reboot, "PRE_INSTALL" => \$pre_install, "POST_INSTALL" => \$post_install, "NO_INSTALL" => \$no_install, @@ -368,7 +373,7 @@ EOF ; $config_help{"REBOOT_TYPE"} = << "EOF" Way to reboot the box to the test kernel. - Only valid options so far are "grub" and "script". + Only valid options so far are "grub", "grub2", and "script". If you specify grub, it will assume grub version 1 and will search in /boot/grub/menu.lst for the title \$GRUB_MENU @@ -378,11 +383,14 @@ $config_help{"REBOOT_TYPE"} = << "EOF" The entry in /boot/grub/menu.lst must be entered in manually. The test will not modify that file. + + If you specify grub2, then you also need to specify both \$GRUB_MENU + and \$GRUB_FILE. EOF ; $config_help{"GRUB_MENU"} = << "EOF" The grub title name for the test kernel to boot - (Only mandatory if REBOOT_TYPE = grub) + (Only mandatory if REBOOT_TYPE = grub or grub2) Note, ktest.pl will not update the grub menu.lst, you need to manually add an option for the test. ktest.pl will search @@ -393,6 +401,17 @@ $config_help{"GRUB_MENU"} = << "EOF" title Test Kernel kernel vmlinuz-test GRUB_MENU = Test Kernel + + For grub2, a search of \$GRUB_FILE is performed for the lines + that begin with "menuentry". It will not detect submenus. The + menu must be a non-nested menu. Add the quotes used in the menu + to guarantee your selection, as the first menuentry with the content + of \$GRUB_MENU that is found will be used. +EOF + ; +$config_help{"GRUB_FILE"} = << "EOF" + If grub2 is used, the full path for the grub.cfg file is placed + here. Use something like /boot/grub2/grub.cfg to search. EOF ; $config_help{"REBOOT_SCRIPT"} = << "EOF" @@ -521,6 +540,11 @@ sub get_ktest_configs { if ($rtype eq "grub") { get_ktest_config("GRUB_MENU"); } + + if ($rtype eq "grub2") { + get_ktest_config("GRUB_MENU"); + get_ktest_config("GRUB_FILE"); + } } sub process_variables { @@ -1452,8 +1476,44 @@ sub run_scp_mod { return run_scp($src, $dst, $cp_scp); } +sub get_grub2_index { + + return if (defined($grub_number)); + + doprint "Find grub2 menu ... "; + $grub_number = -1; + + my $ssh_grub = $ssh_exec; + $ssh_grub =~ s,\$SSH_COMMAND,cat $grub_file,g; + + open(IN, "$ssh_grub |") + or die "unable to get $grub_file"; + + my $found = 0; + + while () { + if (/^menuentry.*$grub_menu/) { + $grub_number++; + $found = 1; + last; + } elsif (/^menuentry\s/) { + $grub_number++; + } + } + close(IN); + + die "Could not find '$grub_menu' in $grub_file on $machine" + if (!$found); + doprint "$grub_number\n"; +} + sub get_grub_index { + if ($reboot_type eq "grub2") { + get_grub2_index; + return; + } + if ($reboot_type ne "grub") { return; } @@ -1524,6 +1584,8 @@ sub reboot_to { if ($reboot_type eq "grub") { run_ssh "'(echo \"savedefault --default=$grub_number --once\" | grub --batch)'"; + } elsif ($reboot_type eq "grub2") { + run_ssh "$grub_reboot $grub_number"; } elsif (defined $reboot_script) { run_command "$reboot_script"; } @@ -3700,6 +3762,9 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $target = "$ssh_user\@$machine"; if ($reboot_type eq "grub") { dodie "GRUB_MENU not defined" if (!defined($grub_menu)); + } elsif ($reboot_type eq "grub2") { + dodie "GRUB_MENU not defined" if (!defined($grub_menu)); + dodie "GRUB_FILE not defined" if (!defined($grub_file)); } } diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index de28a0a3b8fc..57617ec88c3b 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -332,8 +332,18 @@ # from other linux builds on the system. #LOCALVERSION = -test +# For REBOOT_TYPE = grub2, you must specify where the grub.cfg +# file is. This is the file that is searched to find the menu +# option to boot to with GRUB_REBOOT +#GRUB_FILE = /boot/grub2/grub.cfg + +# The tool for REBOOT_TYPE = grub2 to set the next reboot kernel +# to boot into (one shot mode). +# (default grub2_reboot) +#GRUB_REBOOT = grub2_reboot + # The grub title name for the test kernel to boot -# (Only mandatory if REBOOT_TYPE = grub) +# (Only mandatory if REBOOT_TYPE = grub or grub2) # # Note, ktest.pl will not update the grub menu.lst, you need to # manually add an option for the test. ktest.pl will search @@ -343,6 +353,15 @@ # For example, if in the /boot/grub/menu.lst the test kernel title has: # title Test Kernel # kernel vmlinuz-test +# +# For grub2, a search of top level "menuentry"s are done. No +# submenu is searched. The menu is found by searching for the +# contents of GRUB_MENU in the line that starts with "menuentry". +# You may want to include the quotes around the option. For example: +# for: menuentry 'Test Kernel' +# do a: GRUB_MENU = 'Test Kernel' +# For customizing, add your entry in /etc/grub.d/40_custom. +# #GRUB_MENU = Test Kernel # A script to reboot the target into the test kernel @@ -497,7 +516,7 @@ #POST_BUILD_DIE = 1 # Way to reboot the box to the test kernel. -# Only valid options so far are "grub" and "script" +# Only valid options so far are "grub", "grub2" and "script" # (default grub) # If you specify grub, it will assume grub version 1 # and will search in /boot/grub/menu.lst for the title $GRUB_MENU @@ -505,6 +524,9 @@ # your setup, then specify "script" and have a command or script # specified in REBOOT_SCRIPT to boot to the target. # +# For REBOOT_TYPE = grub2, you must define both GRUB_MENU and +# GRUB_FILE. +# # The entry in /boot/grub/menu.lst must be entered in manually. # The test will not modify that file. #REBOOT_TYPE = grub -- cgit v1.3-14-g43fede From a496872212e50a7e543a027faf35b0909459ec9e Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 11 Dec 2012 14:59:05 -0500 Subject: ktest: Sync before reboot Before rebooting the target, run the sync command, as it seems that either Grub2 or systemd gets screwed up if you update to reboot a kernel once and do a reboot without doing a sync. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools/testing') diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 63245a56aca3..f4b8f96a9430 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1147,6 +1147,9 @@ sub wait_for_monitor; sub reboot { my ($time) = @_; + # Make sure everything has been written to disk + run_ssh("sync"); + if (defined($time)) { start_monitor; # flush out current monitor -- cgit v1.3-14-g43fede From 7786954c95dbeba26ef39d2b6899bf06fbf2b629 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 11 Dec 2012 17:37:41 -0500 Subject: ktest: Add native support for syslinux boot loader I installed Fedora 17 which no longer supports grub v1. I worked with grub2 for a while, but there's so many issues with it and automated rebooting, that I decided to switch to syslinux. Instead of using the REBOOT_SCRIPT and add customized changes to get syslinux booted, I thought it better to make ktest aware of syslinux and add options to simplify the use of syslinux on a target test box. Acked-by: H. Peter Anvin Cc: John 'Warthog9' Hawley Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 28 +++++++++++++++++++++++++++- tools/testing/ktest/sample.conf | 22 +++++++++++++++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index f4b8f96a9430..067337bbce7b 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -54,6 +54,8 @@ my %default = ( "STOP_TEST_AFTER" => 600, "MAX_MONITOR_WAIT" => 1800, "GRUB_REBOOT" => "grub2-reboot", + "SYSLINUX" => "extlinux", + "SYSLINUX_PATH" => "/boot/extlinux", # required, and we will ask users if they don't have them but we keep the default # value something that is common. @@ -109,6 +111,9 @@ my $grub_menu; my $grub_file; my $grub_number; my $grub_reboot; +my $syslinux; +my $syslinux_path; +my $syslinux_label; my $target; my $make; my $pre_install; @@ -237,6 +242,9 @@ my %option_map = ( "GRUB_MENU" => \$grub_menu, "GRUB_FILE" => \$grub_file, "GRUB_REBOOT" => \$grub_reboot, + "SYSLINUX" => \$syslinux, + "SYSLINUX_PATH" => \$syslinux_path, + "SYSLINUX_LABEL" => \$syslinux_label, "PRE_INSTALL" => \$pre_install, "POST_INSTALL" => \$post_install, "NO_INSTALL" => \$no_install, @@ -373,7 +381,7 @@ EOF ; $config_help{"REBOOT_TYPE"} = << "EOF" Way to reboot the box to the test kernel. - Only valid options so far are "grub", "grub2", and "script". + Only valid options so far are "grub", "grub2", "syslinux", and "script". If you specify grub, it will assume grub version 1 and will search in /boot/grub/menu.lst for the title \$GRUB_MENU @@ -386,6 +394,11 @@ $config_help{"REBOOT_TYPE"} = << "EOF" If you specify grub2, then you also need to specify both \$GRUB_MENU and \$GRUB_FILE. + + If you specify syslinux, then you may use SYSLINUX to define the syslinux + command (defaults to extlinux), and SYSLINUX_PATH to specify the path to + the syslinux install (defaults to /boot/extlinux). But you have to specify + SYSLINUX_LABEL to define the label to boot to for the test kernel. EOF ; $config_help{"GRUB_MENU"} = << "EOF" @@ -414,6 +427,11 @@ $config_help{"GRUB_FILE"} = << "EOF" here. Use something like /boot/grub2/grub.cfg to search. EOF ; +$config_help{"SYSLINUX_LABEL"} = << "EOF" + If syslinux is used, the label that boots the target kernel must + be specified with SYSLINUX_LABEL. +EOF + ; $config_help{"REBOOT_SCRIPT"} = << "EOF" A script to reboot the target into the test kernel (Only mandatory if REBOOT_TYPE = script) @@ -545,6 +563,10 @@ sub get_ktest_configs { get_ktest_config("GRUB_MENU"); get_ktest_config("GRUB_FILE"); } + + if ($rtype eq "syslinux") { + get_ktest_config("SYSLINUX_LABEL"); + } } sub process_variables { @@ -1589,6 +1611,8 @@ sub reboot_to { run_ssh "'(echo \"savedefault --default=$grub_number --once\" | grub --batch)'"; } elsif ($reboot_type eq "grub2") { run_ssh "$grub_reboot $grub_number"; + } elsif ($reboot_type eq "syslinux") { + run_ssh "$syslinux --once \\\"$syslinux_label\\\" $syslinux_path"; } elsif (defined $reboot_script) { run_command "$reboot_script"; } @@ -3768,6 +3792,8 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { } elsif ($reboot_type eq "grub2") { dodie "GRUB_MENU not defined" if (!defined($grub_menu)); dodie "GRUB_FILE not defined" if (!defined($grub_file)); + } elsif ($reboot_type eq "syslinux") { + dodie "SYSLINUX_LABEL not defined" if (!defined($syslinux_label)); } } diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 57617ec88c3b..4012e9330344 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -364,6 +364,22 @@ # #GRUB_MENU = Test Kernel +# For REBOOT_TYPE = syslinux, the name of the syslinux executable +# (on the target) to use to set up the next reboot to boot the +# test kernel. +# (default extlinux) +#SYSLINUX = syslinux + +# For REBOOT_TYPE = syslinux, the path that is passed to to the +# syslinux command where syslinux is installed. +# (default /boot/extlinux) +#SYSLINUX_PATH = /boot/syslinux + +# For REBOOT_TYPE = syslinux, the syslinux label that references the +# test kernel in the syslinux config file. +# (default undefined) +#SYSLINUX_LABEL = "test-kernel" + # A script to reboot the target into the test kernel # This and SWITCH_TO_TEST are about the same, except # SWITCH_TO_TEST is run even for REBOOT_TYPE = grub. @@ -516,7 +532,7 @@ #POST_BUILD_DIE = 1 # Way to reboot the box to the test kernel. -# Only valid options so far are "grub", "grub2" and "script" +# Only valid options so far are "grub", "grub2", "syslinux" and "script" # (default grub) # If you specify grub, it will assume grub version 1 # and will search in /boot/grub/menu.lst for the title $GRUB_MENU @@ -527,6 +543,10 @@ # For REBOOT_TYPE = grub2, you must define both GRUB_MENU and # GRUB_FILE. # +# For REBOOT_TYPE = syslinux, you must define SYSLINUX_LABEL, and +# perhaps modify SYSLINUX (default extlinux) and SYSLINUX_PATH +# (default /boot/extlinux) +# # The entry in /boot/grub/menu.lst must be entered in manually. # The test will not modify that file. #REBOOT_TYPE = grub -- cgit v1.3-14-g43fede From fcc1f2d5dd3480214ab52e06d081d123019814ed Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 11 Dec 2012 16:01:36 -0800 Subject: selftests: add a test program for variable huge page sizes in mmap/shmget Also remove -Wextra because gcc-4.6 emits lots of irritating signed/unsigned comparison warnings. Signed-off-by: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/vm/Makefile | 4 +- tools/testing/selftests/vm/thuge-gen.c | 254 +++++++++++++++++++++++++++++++++ 2 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 tools/testing/selftests/vm/thuge-gen.c (limited to 'tools/testing') diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile index b336b24aa6c0..7300d0702efe 100644 --- a/tools/testing/selftests/vm/Makefile +++ b/tools/testing/selftests/vm/Makefile @@ -1,9 +1,9 @@ # Makefile for vm selftests CC = $(CROSS_COMPILE)gcc -CFLAGS = -Wall -Wextra +CFLAGS = -Wall -all: hugepage-mmap hugepage-shm map_hugetlb +all: hugepage-mmap hugepage-shm map_hugetlb thuge-gen %: %.c $(CC) $(CFLAGS) -o $@ $^ diff --git a/tools/testing/selftests/vm/thuge-gen.c b/tools/testing/selftests/vm/thuge-gen.c new file mode 100644 index 000000000000..c87957295f74 --- /dev/null +++ b/tools/testing/selftests/vm/thuge-gen.c @@ -0,0 +1,254 @@ +/* Test selecting other page sizes for mmap/shmget. + + Before running this huge pages for each huge page size must have been + reserved. + For large pages beyond MAX_ORDER (like 1GB on x86) boot options must be used. + Also shmmax must be increased. + And you need to run as root to work around some weird permissions in shm. + And nothing using huge pages should run in parallel. + When the program aborts you may need to clean up the shm segments with + ipcrm -m by hand, like this + sudo ipcs | awk '$1 == "0x00000000" {print $2}' | xargs -n1 sudo ipcrm -m + (warning this will remove all if someone else uses them) */ + +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define err(x) perror(x), exit(1) + +#define MAP_HUGE_2MB (21 << MAP_HUGE_SHIFT) +#define MAP_HUGE_1GB (30 << MAP_HUGE_SHIFT) +#define MAP_HUGE_SHIFT 26 +#define MAP_HUGE_MASK 0x3f +#define MAP_HUGETLB 0x40000 + +#define SHM_HUGETLB 04000 /* segment will use huge TLB pages */ +#define SHM_HUGE_SHIFT 26 +#define SHM_HUGE_MASK 0x3f +#define SHM_HUGE_2MB (21 << SHM_HUGE_SHIFT) +#define SHM_HUGE_1GB (30 << SHM_HUGE_SHIFT) + +#define NUM_PAGESIZES 5 + +#define NUM_PAGES 4 + +#define Dprintf(fmt...) // printf(fmt) + +unsigned long page_sizes[NUM_PAGESIZES]; +int num_page_sizes; + +int ilog2(unsigned long v) +{ + int l = 0; + while ((1UL << l) < v) + l++; + return l; +} + +void find_pagesizes(void) +{ + glob_t g; + int i; + glob("/sys/kernel/mm/hugepages/hugepages-*kB", 0, NULL, &g); + assert(g.gl_pathc <= NUM_PAGESIZES); + for (i = 0; i < g.gl_pathc; i++) { + sscanf(g.gl_pathv[i], "/sys/kernel/mm/hugepages/hugepages-%lukB", + &page_sizes[i]); + page_sizes[i] <<= 10; + printf("Found %luMB\n", page_sizes[i] >> 20); + } + num_page_sizes = g.gl_pathc; + globfree(&g); +} + +unsigned long default_huge_page_size(void) +{ + unsigned long hps = 0; + char *line = NULL; + size_t linelen = 0; + FILE *f = fopen("/proc/meminfo", "r"); + if (!f) + return 0; + while (getline(&line, &linelen, f) > 0) { + if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) { + hps <<= 10; + break; + } + } + free(line); + return hps; +} + +void show(unsigned long ps) +{ + char buf[100]; + if (ps == getpagesize()) + return; + printf("%luMB: ", ps >> 20); + fflush(stdout); + snprintf(buf, sizeof buf, + "cat /sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages", + ps >> 10); + system(buf); +} + +unsigned long read_sysfs(int warn, char *fmt, ...) +{ + char *line = NULL; + size_t linelen = 0; + char buf[100]; + FILE *f; + va_list ap; + unsigned long val = 0; + + va_start(ap, fmt); + vsnprintf(buf, sizeof buf, fmt, ap); + va_end(ap); + + f = fopen(buf, "r"); + if (!f) { + if (warn) + printf("missing %s\n", buf); + return 0; + } + if (getline(&line, &linelen, f) > 0) { + sscanf(line, "%lu", &val); + } + fclose(f); + free(line); + return val; +} + +unsigned long read_free(unsigned long ps) +{ + return read_sysfs(ps != getpagesize(), + "/sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages", + ps >> 10); +} + +void test_mmap(unsigned long size, unsigned flags) +{ + char *map; + unsigned long before, after; + int err; + + before = read_free(size); + map = mmap(NULL, size*NUM_PAGES, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB|flags, 0, 0); + + if (map == (char *)-1) err("mmap"); + memset(map, 0xff, size*NUM_PAGES); + after = read_free(size); + Dprintf("before %lu after %lu diff %ld size %lu\n", + before, after, before - after, size); + assert(size == getpagesize() || (before - after) == NUM_PAGES); + show(size); + err = munmap(map, size); + assert(!err); +} + +void test_shmget(unsigned long size, unsigned flags) +{ + int id; + unsigned long before, after; + int err; + + before = read_free(size); + id = shmget(IPC_PRIVATE, size * NUM_PAGES, IPC_CREAT|0600|flags); + if (id < 0) err("shmget"); + + struct shm_info i; + if (shmctl(id, SHM_INFO, (void *)&i) < 0) err("shmctl"); + Dprintf("alloc %lu res %lu\n", i.shm_tot, i.shm_rss); + + + Dprintf("id %d\n", id); + char *map = shmat(id, NULL, 0600); + if (map == (char*)-1) err("shmat"); + + shmctl(id, IPC_RMID, NULL); + + memset(map, 0xff, size*NUM_PAGES); + after = read_free(size); + + Dprintf("before %lu after %lu diff %ld size %lu\n", + before, after, before - after, size); + assert(size == getpagesize() || (before - after) == NUM_PAGES); + show(size); + err = shmdt(map); + assert(!err); +} + +void sanity_checks(void) +{ + int i; + unsigned long largest = getpagesize(); + + for (i = 0; i < num_page_sizes; i++) { + if (page_sizes[i] > largest) + largest = page_sizes[i]; + + if (read_free(page_sizes[i]) < NUM_PAGES) { + printf("Not enough huge pages for page size %lu MB, need %u\n", + page_sizes[i] >> 20, + NUM_PAGES); + exit(0); + } + } + + if (read_sysfs(0, "/proc/sys/kernel/shmmax") < NUM_PAGES * largest) { + printf("Please do echo %lu > /proc/sys/kernel/shmmax", largest * NUM_PAGES); + exit(0); + } + +#if defined(__x86_64__) + if (largest != 1U<<30) { + printf("No GB pages available on x86-64\n" + "Please boot with hugepagesz=1G hugepages=%d\n", NUM_PAGES); + exit(0); + } +#endif +} + +int main(void) +{ + int i; + unsigned default_hps = default_huge_page_size(); + + find_pagesizes(); + + sanity_checks(); + + for (i = 0; i < num_page_sizes; i++) { + unsigned long ps = page_sizes[i]; + int arg = ilog2(ps) << MAP_HUGE_SHIFT; + printf("Testing %luMB mmap with shift %x\n", ps >> 20, arg); + test_mmap(ps, MAP_HUGETLB | arg); + } + printf("Testing default huge mmap\n"); + test_mmap(default_hps, SHM_HUGETLB); + + puts("Testing non-huge shmget"); + test_shmget(getpagesize(), 0); + + for (i = 0; i < num_page_sizes; i++) { + unsigned long ps = page_sizes[i]; + int arg = ilog2(ps) << SHM_HUGE_SHIFT; + printf("Testing %luMB shmget with shift %x\n", ps >> 20, arg); + test_shmget(ps, SHM_HUGETLB | arg); + } + puts("default huge shmget"); + test_shmget(default_hps, SHM_HUGETLB); + + return 0; +} -- cgit v1.3-14-g43fede From 189251705649bdfdf5e5850eb178f8cbfdac5480 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 11 Dec 2012 20:16:03 -0500 Subject: ktest: Fix breakage from change of oldnoconfig to olddefconfig Commit fb16d891 "kconfig: replace 'oldnoconfig' with 'olddefconfig', and keep the old name", changed ktest's default config update from oldnoconfig to olddefconfig without adding oldnoconfig as a backup. The make oldnoconfig works much better than its backup of: yes '' | make oldconfig But due to this change, and the fact that ktest is used to build lots of older kernels (and for bisects), it forgoes the oldnoconfig completely. Cc: Adam Lee Cc: Michal Marek Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 067337bbce7b..6b1e0c5edc5b 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1966,10 +1966,14 @@ sub make_oldconfig { if (!run_command "$make olddefconfig") { # Perhaps olddefconfig doesn't exist in this version of the kernel - # try a yes '' | oldconfig - doprint "olddefconfig failed, trying yes '' | make oldconfig\n"; - run_command "yes '' | $make oldconfig" or - dodie "failed make config oldconfig"; + # try oldnoconfig + doprint "olddefconfig failed, trying make oldnoconfig\n"; + if (!run_command "$make oldnoconfig") { + doprint "oldnoconfig failed, trying yes '' | make oldconfig\n"; + # try a yes '' | oldconfig + run_command "yes '' | $make oldconfig" or + dodie "failed make config oldconfig"; + } } } -- cgit v1.3-14-g43fede From e1a6c3d748ef0ee093e764af3fdd0c1a5cd2b664 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 11 Dec 2012 21:19:41 -0500 Subject: ktest: Test if target machine is up before install Sometimes a test kernel will crash or hang on reboot (this is even more apparent when testing a config without CGROUPS on a box running systemd). When this happens, on the next iteration of installing a kernel, ktest will fail when it tries to install. Have ktest do a check to see if the target can be connected to via ssh before it tries to install. If it can't connect, then reboot again. This time the reboot will fail because it can't connect and will force a power cycle. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'tools/testing') diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 6b1e0c5edc5b..35fc584a4ffe 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1807,6 +1807,14 @@ sub do_post_install { dodie "Failed to run post install"; } +# Sometimes the reboot fails, and will hang. We try to ssh to the box +# and if we fail, we force another reboot, that should powercycle it. +sub test_booted { + if (!run_ssh "echo testing connection") { + reboot $sleep_time; + } +} + sub install { return if ($no_install); @@ -1819,6 +1827,8 @@ sub install { my $cp_target = eval_kernel_version $target_image; + test_booted; + run_scp_install "$outputdir/$build_target", "$cp_target" or dodie "failed to copy image"; -- cgit v1.3-14-g43fede From 000e06b01f7e1d615345815550306a0e0622433c Mon Sep 17 00:00:00 2001 From: Dave Young Date: Mon, 17 Dec 2012 16:04:38 -0800 Subject: vm selftests: print failure status instead of cause make error Original behavior: bash-4.1$ make -C vm run_tests make: Entering directory `/home/dave/git/linux-2.6/tools/testing/selftests/vm' /bin/sh ./run_vmtests ./run_vmtests: line 24: /proc/sys/vm/nr_hugepages: Permission denied Please run this test as root make: *** [run_tests] Error 1 make: Leaving directory `/home/dave/git/linux-2.6/tools/testing/selftests/vm' After applying the patch: bash-4.1$ make -C vm run_tests make: Entering directory `/home/dave/git/linux-2.6/tools/testing/selftests/vm' ./run_vmtests: line 24: /proc/sys/vm/nr_hugepages: Permission denied Please run this test as root vmtests: [FAIL] make: Leaving directory `/home/dave/git/linux-2.6/tools/testing/selftests/vm' Signed-off-by: Dave Young Cc: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/vm/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile index 7300d0702efe..436d2e81868b 100644 --- a/tools/testing/selftests/vm/Makefile +++ b/tools/testing/selftests/vm/Makefile @@ -8,7 +8,7 @@ all: hugepage-mmap hugepage-shm map_hugetlb thuge-gen $(CC) $(CFLAGS) -o $@ $^ run_tests: all - /bin/sh ./run_vmtests + @/bin/sh ./run_vmtests || echo "vmtests: [FAIL]" clean: $(RM) hugepage-mmap hugepage-shm map_hugetlb -- cgit v1.3-14-g43fede From 9ed1d90ed1e0b5788e5b9148ed70adb1a017078c Mon Sep 17 00:00:00 2001 From: Dave Young Date: Mon, 17 Dec 2012 16:04:39 -0800 Subject: mqueue selftests: print failure status instead of cause make error Original behavior: bash-4.1$ make -C mqueue run_tests make: Entering directory `/home/dave/git/linux-2.6/tools/testing/selftests/mqueue' ./mq_open_tests /test1 Not running as root, but almost all tests require root in order to modify system settings. Exiting. make: *** [run_tests] Error 1 make: Leaving directory `/home/dave/git/linux-2.6/tools/testing/selftests/mqueue' After applying the patch: bash-4.1$ make -C mqueue run_tests make: Entering directory `/home/dave/git/linux-2.6/tools/testing/selftests/mqueue' Not running as root, but almost all tests require root in order to modify system settings. Exiting. mq_open_tests: [FAIL] Not running as root, but almost all tests require root in order to modify system settings. Exiting. mq_perf_tests: [FAIL] make: Leaving directory `/home/dave/git/linux-2.6/tools/testing/selftests/mqueue' Signed-off-by: Dave Young Reviewed-by: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/mqueue/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/mqueue/Makefile b/tools/testing/selftests/mqueue/Makefile index 54c0aad2b47c..218a122c7951 100644 --- a/tools/testing/selftests/mqueue/Makefile +++ b/tools/testing/selftests/mqueue/Makefile @@ -3,8 +3,8 @@ all: gcc -O2 -lrt -lpthread -lpopt -o mq_perf_tests mq_perf_tests.c run_tests: - ./mq_open_tests /test1 - ./mq_perf_tests + @./mq_open_tests /test1 || echo "mq_open_tests: [FAIL]" + @./mq_perf_tests || echo "mq_perf_tests: [FAIL]" clean: rm -f mq_open_tests mq_perf_tests -- cgit v1.3-14-g43fede From a58130ddc896e5a15e4de2bf50a1d89247118c23 Mon Sep 17 00:00:00 2001 From: Dave Young Date: Mon, 17 Dec 2012 16:04:42 -0800 Subject: cpu-hotplug selftests: print failure status instead of cause make error Original behavior: bash-4.1$ make -C cpu-hotplug run_tests make: Entering directory `/home/dave/git/linux-2.6/tools/testing/selftests/cpu-hotplug' ./on-off-test.sh make: execvp: ./on-off-test.sh: Permission denied make: *** [run_tests] Error 127 make: Leaving directory `/home/dave/git/linux-2.6/tools/testing/selftests/cpu-hotplug' After applying the patch: bash-4.1$ make -C cpu-hotplug run_tests make: Entering directory `/home/dave/git/linux-2.6/tools/testing/selftests/cpu-hotplug' /bin/sh: ./on-off-test.sh: Permission denied cpu-hotplug selftests: [FAIL] make: Leaving directory `/home/dave/git/linux-2.6/tools/testing/selftests/cpu-hotplug' Signed-off-by: Dave Young Reviewed-by: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/cpu-hotplug/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/cpu-hotplug/Makefile b/tools/testing/selftests/cpu-hotplug/Makefile index 7c9c20ff578a..12657a5e4bf9 100644 --- a/tools/testing/selftests/cpu-hotplug/Makefile +++ b/tools/testing/selftests/cpu-hotplug/Makefile @@ -1,6 +1,6 @@ all: run_tests: - ./on-off-test.sh + @./on-off-test.sh || echo "cpu-hotplug selftests: [FAIL]" clean: -- cgit v1.3-14-g43fede From aabccae6e9e2106e67a40d65eb423163cd69d2fb Mon Sep 17 00:00:00 2001 From: Dave Young Date: Mon, 17 Dec 2012 16:04:45 -0800 Subject: mem-hotplug selftests: print failure status instead of cause make error Original behavior: bash-4.1$ make -C memory-hotplug run_tests make: Entering directory `/home/dave/git/linux-2.6/tools/testing/selftests/memory-hotplug' ./on-off-test.sh make: execvp: ./on-off-test.sh: Permission denied make: *** [run_tests] Error 127 make: Leaving directory `/home/dave/git/linux-2.6/tools/testing/selftests/memory-hotplug' After applying the patch: bash-4.1$ make -C memory-hotplug run_tests make: Entering directory `/home/dave/git/linux-2.6/tools/testing/selftests/memory-hotplug' /bin/sh: ./on-off-test.sh: Permission denied memory-hotplug selftests: [FAIL] make: Leaving directory `/home/dave/git/linux-2.6/tools/testing/selftests/memory-hotplug' Signed-off-by: Dave Young Reviewed-by: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/memory-hotplug/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/memory-hotplug/Makefile b/tools/testing/selftests/memory-hotplug/Makefile index 7c9c20ff578a..0f49c3f5f58d 100644 --- a/tools/testing/selftests/memory-hotplug/Makefile +++ b/tools/testing/selftests/memory-hotplug/Makefile @@ -1,6 +1,6 @@ all: run_tests: - ./on-off-test.sh + @./on-off-test.sh || echo "memory-hotplug selftests: [FAIL]" clean: -- cgit v1.3-14-g43fede From 63d233673a9f6c524969e40e7012e3e461aafd32 Mon Sep 17 00:00:00 2001 From: Dave Young Date: Mon, 17 Dec 2012 16:04:47 -0800 Subject: kcmp selftests: make run_tests fix make run_tests need the target is run_tests instead of run-tests Also gcc output should be kcmp_test. Fix these two issues. Signed-off-by: Dave Young Cc: Pekka Enberg Cc: Cyrill Gorcunov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/kcmp/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/kcmp/Makefile b/tools/testing/selftests/kcmp/Makefile index dc79b86ea65c..c608945a0ef5 100644 --- a/tools/testing/selftests/kcmp/Makefile +++ b/tools/testing/selftests/kcmp/Makefile @@ -16,12 +16,12 @@ CFLAGS += -I../../../../arch/x86/include/ all: ifeq ($(ARCH),X86) - gcc $(CFLAGS) kcmp_test.c -o run_test + gcc $(CFLAGS) kcmp_test.c -o kcmp_test else echo "Not an x86 target, can't build kcmp selftest" endif -run-tests: all +run_tests: all ./kcmp_test clean: -- cgit v1.3-14-g43fede From ed8ad10c3b2fab18dd92d21afd4277a8a521ecfd Mon Sep 17 00:00:00 2001 From: Dave Young Date: Mon, 17 Dec 2012 16:04:49 -0800 Subject: kcmp selftests: print fail status instead of cause make error In case kcmp_test exit non zero value it will cause make error. Better way is just print the test failure status. Signed-off-by: Dave Young Reviewed-by: Pekka Enberg Cc: Cyrill Gorcunov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/kcmp/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/kcmp/Makefile b/tools/testing/selftests/kcmp/Makefile index c608945a0ef5..56eb5523dbb8 100644 --- a/tools/testing/selftests/kcmp/Makefile +++ b/tools/testing/selftests/kcmp/Makefile @@ -22,7 +22,7 @@ else endif run_tests: all - ./kcmp_test + @./kcmp_test || echo "kcmp_test: [FAIL]" clean: rm -fr ./run_test -- cgit v1.3-14-g43fede From 5a55f8bb2d0c4e5c05837b688ed1b2132a0ed0e7 Mon Sep 17 00:00:00 2001 From: Dave Young Date: Mon, 17 Dec 2012 16:04:50 -0800 Subject: breakpoint selftests: print failure status instead of cause make error In case breakpoint test exit non zero value it will cause make error. Better way is just print the test failure status. Signed-off-by: Dave Young Reviewed-by: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/breakpoints/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/breakpoints/Makefile b/tools/testing/selftests/breakpoints/Makefile index 931278035f5c..e18b42b254af 100644 --- a/tools/testing/selftests/breakpoints/Makefile +++ b/tools/testing/selftests/breakpoints/Makefile @@ -17,7 +17,7 @@ else endif run_tests: - ./breakpoint_test + @./breakpoint_test || echo "breakpoints selftests: [FAIL]" clean: rm -fr breakpoint_test -- cgit v1.3-14-g43fede From 2bf1cbf1c616b4dd85a3a8a715af9c5701c16a91 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Mon, 17 Dec 2012 16:04:52 -0800 Subject: tools/testing/selftests/kcmp/kcmp_test.c: print reason for failure in kcmp_test I was curious why sys_kcmp wasn't working, which led me to the testcase. It turned out I hadn't enabled CHECKPOINT_RESTORE in the kernel I was testing. Add a decoding of errno to the testcase to make that obvious. Signed-off-by: Dave Jones Acked-by: Cyrill Gorcunov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/kcmp/kcmp_test.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/kcmp/kcmp_test.c b/tools/testing/selftests/kcmp/kcmp_test.c index 358cc6bfa35d..fa4f1b37e045 100644 --- a/tools/testing/selftests/kcmp/kcmp_test.c +++ b/tools/testing/selftests/kcmp/kcmp_test.c @@ -72,7 +72,8 @@ int main(int argc, char **argv) /* 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); + printf("FAIL: 0 expected but %d returned (%s)\n", + ret, strerror(errno)); ret = -1; } else printf("PASS: 0 returned as expected\n"); @@ -80,7 +81,8 @@ int main(int argc, char **argv) /* Compare with self */ ret = sys_kcmp(pid1, pid1, KCMP_VM, 0, 0); if (ret) { - printf("FAIL: 0 expected but %li returned\n", ret); + printf("FAIL: 0 expected but %li returned (%s)\n", + ret, strerror(errno)); ret = -1; } else printf("PASS: 0 returned as expected\n"); -- cgit v1.3-14-g43fede From 3a665531a3b7c2ad2c87903b24646be6916340e4 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Fri, 4 Jan 2013 15:34:56 -0800 Subject: selftests: IPC message queue copy feature test This test can be used to check wheither kernel supports IPC message queue copy and restore features (required by CRIU project). Signed-off-by: Stanislav Kinsbursky Cc: Serge Hallyn Cc: "Eric W. Biederman" Cc: Pavel Emelyanov Cc: Al Viro Cc: KOSAKI Motohiro Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/msg.h | 3 +- ipc/compat.c | 3 +- tools/testing/selftests/ipc/Makefile | 25 ++++ tools/testing/selftests/ipc/msgque.c | 246 +++++++++++++++++++++++++++++++++++ 4 files changed, 275 insertions(+), 2 deletions(-) create mode 100644 tools/testing/selftests/ipc/Makefile create mode 100644 tools/testing/selftests/ipc/msgque.c (limited to 'tools/testing') diff --git a/include/linux/msg.h b/include/linux/msg.h index fc5743a554e6..391af8d11cce 100644 --- a/include/linux/msg.h +++ b/include/linux/msg.h @@ -36,6 +36,7 @@ extern long do_msgsnd(int msqid, long mtype, void __user *mtext, size_t msgsz, int msgflg); extern long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg, - long (*msg_fill)(void __user *, struct msg_msg *, size_t)); + long (*msg_fill)(void __user *, struct msg_msg *, + size_t)); #endif /* _LINUX_MSG_H */ diff --git a/ipc/compat.c b/ipc/compat.c index eb3ea16d2d1d..2547f29dcd1b 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -365,7 +365,8 @@ long compat_sys_msgrcv(int first, int second, int msgtyp, int third, uptr = compat_ptr(ipck.msgp); msgtyp = ipck.msgtyp; } - return do_msgrcv(first, uptr, second, msgtyp, third, compat_do_msg_fill); + return do_msgrcv(first, uptr, second, msgtyp, third, + compat_do_msg_fill); } #else long compat_sys_semctl(int semid, int semnum, int cmd, int arg) diff --git a/tools/testing/selftests/ipc/Makefile b/tools/testing/selftests/ipc/Makefile new file mode 100644 index 000000000000..5386fd7c43ae --- /dev/null +++ b/tools/testing/selftests/ipc/Makefile @@ -0,0 +1,25 @@ +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../../../../usr/include/ + +all: +ifeq ($(ARCH),X86) + gcc $(CFLAGS) msgque.c -o msgque_test +else + echo "Not an x86 target, can't build msgque selftest" +endif + +run_tests: all + ./msgque_test + +clean: + rm -fr ./msgque_test diff --git a/tools/testing/selftests/ipc/msgque.c b/tools/testing/selftests/ipc/msgque.c new file mode 100644 index 000000000000..d66418237d21 --- /dev/null +++ b/tools/testing/selftests/ipc/msgque.c @@ -0,0 +1,246 @@ +#include +#include +#include +#include +#include +#include + +#define MAX_MSG_SIZE 32 + +struct msg1 { + int msize; + long mtype; + char mtext[MAX_MSG_SIZE]; +}; + +#define TEST_STRING "Test sysv5 msg" +#define MSG_TYPE 1 + +#define ANOTHER_TEST_STRING "Yet another test sysv5 msg" +#define ANOTHER_MSG_TYPE 26538 + +struct msgque_data { + key_t key; + int msq_id; + int qbytes; + int qnum; + int mode; + struct msg1 *messages; +}; + +int restore_queue(struct msgque_data *msgque) +{ + int fd, ret, id, i; + char buf[32]; + + fd = open("/proc/sys/kernel/msg_next_id", O_WRONLY); + if (fd == -1) { + printf("Failed to open /proc/sys/kernel/msg_next_id\n"); + return -errno; + } + sprintf(buf, "%d", msgque->msq_id); + + ret = write(fd, buf, strlen(buf)); + if (ret != strlen(buf)) { + printf("Failed to write to /proc/sys/kernel/msg_next_id\n"); + return -errno; + } + + id = msgget(msgque->key, msgque->mode | IPC_CREAT | IPC_EXCL); + if (id == -1) { + printf("Failed to create queue\n"); + return -errno; + } + + if (id != msgque->msq_id) { + printf("Restored queue has wrong id (%d instead of %d)\n", + id, msgque->msq_id); + ret = -EFAULT; + goto destroy; + } + + for (i = 0; i < msgque->qnum; i++) { + if (msgsnd(msgque->msq_id, &msgque->messages[i].mtype, + msgque->messages[i].msize, IPC_NOWAIT) != 0) { + printf("msgsnd failed (%m)\n"); + ret = -errno; + goto destroy; + }; + } + return 0; + +destroy: + if (msgctl(id, IPC_RMID, 0)) + printf("Failed to destroy queue: %d\n", -errno); + return ret; +} + +int check_and_destroy_queue(struct msgque_data *msgque) +{ + struct msg1 message; + int cnt = 0, ret; + + while (1) { + ret = msgrcv(msgque->msq_id, &message.mtype, MAX_MSG_SIZE, + 0, IPC_NOWAIT); + if (ret < 0) { + if (errno == ENOMSG) + break; + printf("Failed to read IPC message: %m\n"); + ret = -errno; + goto err; + } + if (ret != msgque->messages[cnt].msize) { + printf("Wrong message size: %d (expected %d)\n", ret, + msgque->messages[cnt].msize); + ret = -EINVAL; + goto err; + } + if (message.mtype != msgque->messages[cnt].mtype) { + printf("Wrong message type\n"); + ret = -EINVAL; + goto err; + } + if (memcmp(message.mtext, msgque->messages[cnt].mtext, ret)) { + printf("Wrong message content\n"); + ret = -EINVAL; + goto err; + } + cnt++; + } + + if (cnt != msgque->qnum) { + printf("Wrong message number\n"); + ret = -EINVAL; + goto err; + } + + ret = 0; +err: + if (msgctl(msgque->msq_id, IPC_RMID, 0)) { + printf("Failed to destroy queue: %d\n", -errno); + return -errno; + } + return ret; +} + +int dump_queue(struct msgque_data *msgque) +{ + struct msqid64_ds ds; + int kern_id; + int i, ret; + + for (kern_id = 0; kern_id < 256; kern_id++) { + ret = msgctl(kern_id, MSG_STAT, &ds); + if (ret < 0) { + if (errno == -EINVAL) + continue; + printf("Failed to get stats for IPC queue with id %d\n", + kern_id); + return -errno; + } + + if (ret == msgque->msq_id) + break; + } + + msgque->messages = malloc(sizeof(struct msg1) * ds.msg_qnum); + if (msgque->messages == NULL) { + printf("Failed to get stats for IPC queue\n"); + return -ENOMEM; + } + + msgque->qnum = ds.msg_qnum; + msgque->mode = ds.msg_perm.mode; + msgque->qbytes = ds.msg_qbytes; + + for (i = 0; i < msgque->qnum; i++) { + ret = msgrcv(msgque->msq_id, &msgque->messages[i].mtype, + MAX_MSG_SIZE, i, IPC_NOWAIT | MSG_COPY); + if (ret < 0) { + printf("Failed to copy IPC message: %m (%d)\n", errno); + return -errno; + } + msgque->messages[i].msize = ret; + } + return 0; +} + +int fill_msgque(struct msgque_data *msgque) +{ + struct msg1 msgbuf; + + msgbuf.mtype = MSG_TYPE; + memcpy(msgbuf.mtext, TEST_STRING, sizeof(TEST_STRING)); + if (msgsnd(msgque->msq_id, &msgbuf.mtype, sizeof(TEST_STRING), + IPC_NOWAIT) != 0) { + printf("First message send failed (%m)\n"); + return -errno; + }; + + msgbuf.mtype = ANOTHER_MSG_TYPE; + memcpy(msgbuf.mtext, ANOTHER_TEST_STRING, sizeof(ANOTHER_TEST_STRING)); + if (msgsnd(msgque->msq_id, &msgbuf.mtype, sizeof(ANOTHER_TEST_STRING), + IPC_NOWAIT) != 0) { + printf("Second message send failed (%m)\n"); + return -errno; + }; + return 0; +} + +int main(int argc, char **argv) +{ + int msg, pid, err; + struct msgque_data msgque; + + msgque.key = ftok(argv[0], 822155650); + if (msgque.key == -1) { + printf("Can't make key\n"); + return -errno; + } + + msgque.msq_id = msgget(msgque.key, IPC_CREAT | IPC_EXCL | 0666); + if (msgque.msq_id == -1) { + printf("Can't create queue\n"); + goto err_out; + } + + err = fill_msgque(&msgque); + if (err) { + printf("Failed to fill queue\n"); + goto err_destroy; + } + + err = dump_queue(&msgque); + if (err) { + printf("Failed to dump queue\n"); + goto err_destroy; + } + + err = check_and_destroy_queue(&msgque); + if (err) { + printf("Failed to check and destroy queue\n"); + goto err_out; + } + + err = restore_queue(&msgque); + if (err) { + printf("Failed to restore queue\n"); + goto err_destroy; + } + + err = check_and_destroy_queue(&msgque); + if (err) { + printf("Failed to test queue\n"); + goto err_out; + } + return 0; + +err_destroy: + if (msgctl(msgque.msq_id, IPC_RMID, 0)) { + printf("Failed to destroy queue: %d\n", -errno); + return -errno; + } +err_out: + return err; +} -- cgit v1.3-14-g43fede From 319ab14f051aeb04d6da20d82b389ad875083041 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 30 Jan 2013 12:25:38 -0500 Subject: ktest: Do not require CONSOLE for build or install bisects If the user is doing a build or install bisect, there's no reason to have them define CONSOLE, as the console does not need to be read. The console only needs to be read for boot tests. CONSOLE is not required for normal build or install tests, let's not require it for bisect tests with BISECT_TYPE of build or install. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 35fc584a4ffe..d6690df5e7f1 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -618,6 +618,18 @@ sub set_value { if ($buildonly && $lvalue =~ /^TEST_TYPE(\[.*\])?$/ && $prvalue ne "build") { # Note if a test is something other than build, then we # will need other manditory options. + if ($prvalue ne "install") { + # for bisect, we need to check BISECT_TYPE + if ($prvalue ne "bisect") { + $buildonly = 0; + } + } else { + # install still limits some manditory options. + $buildonly = 2; + } + } + + if ($buildonly && $lvalue =~ /^BISECT_TYPE(\[.*\])?$/ && $prvalue ne "build") { if ($prvalue ne "install") { $buildonly = 0; } else { @@ -1279,6 +1291,7 @@ sub start_monitor { } sub end_monitor { + return if (!defined $console); if (--$monitor_cnt) { return; } @@ -1585,7 +1598,7 @@ sub wait_for_input $rin = ''; vec($rin, fileno($fp), 1) = 1; - $ready = select($rin, undef, undef, $time); + ($ready, $time) = select($rin, undef, undef, $time); $line = ""; @@ -1891,15 +1904,19 @@ sub get_version { sub start_monitor_and_boot { # Make sure the stable kernel has finished booting - start_monitor; - wait_for_monitor 5; - end_monitor; + + # Install bisects, don't need console + if (defined $console) { + start_monitor; + wait_for_monitor 5; + end_monitor; + } get_grub_index; get_version; install; - start_monitor; + start_monitor if (defined $console); return monitor; } -- cgit v1.3-14-g43fede From 35275685bf6123529e67c1dc91b8c05e479124e8 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 30 Jan 2013 12:28:15 -0500 Subject: ktest: Strip off '\n' when reading which files were modified The patchcheck test looks at what files are modified for each patch it checks and makes sure that those files do not produce any warnings. Unfortunately, when it read the diffstat, the newlines were added on the files and this made compares miss warnings, and commits that should not have passed, ktest let pass. Fix this by using the perl command "chomp" that strips off whitespace at the end of lines. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tools/testing') diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index d6690df5e7f1..cc9925a68f2a 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1925,6 +1925,10 @@ sub check_buildlog { my @files = `git show $patch | diffstat -l`; + foreach my $file (@files) { + chomp $file; + } + open(IN, "git show $patch |") or dodie "failed to show $patch"; while () { -- cgit v1.3-14-g43fede From 04262be3db53d2b77ec09fa3e4d18313b6b9dcf9 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Thu, 31 Jan 2013 10:12:20 -0500 Subject: ktest: Allow a test option to use its default option Options are allowed to use other options, for example: LOG_FILE = ${OUTPUT_DIR}/${MACHINE}.log where the option LOG_FILE used the options OUTPUT_DIR and MACHINE. But if a test option were to use a default option, it will not get substituted: OUTPUT_DIR = ${THIS_DIR}/${MACHINE} TEST_START OUTPUT_DIR = ${OUTPUT_DIR}/t1 For the above test, OUTPUT_DIR will stay literally "${OUTPUT_DIR}/t1" and not be converted to "${THIS_DIR}/${MACHINE}/t1". When the test runs, it will pass the ${OUTPUT_DIR} to the shell, which would probaly interpret it as "", and the output directory will end up as "/t1". Change the code where if a test option has its own option name in its defined field, and a default option exists, then substitute the default option in its place. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index cc9925a68f2a..3d19ee445249 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1074,7 +1074,7 @@ sub read_config { } sub __eval_option { - my ($option, $i) = @_; + my ($name, $option, $i) = @_; # Add space to evaluate the character before $ $option = " $option"; @@ -1106,7 +1106,11 @@ sub __eval_option { my $o = "$var\[$i\]"; my $parento = "$var\[$parent\]"; - if (defined($opt{$o})) { + # If a variable contains itself, use the default var + if (($var eq $name) && defined($opt{$var})) { + $o = $opt{$var}; + $retval = "$retval$o"; + } elsif (defined($opt{$o})) { $o = $opt{$o}; $retval = "$retval$o"; } elsif ($repeated && defined($opt{$parento})) { @@ -1130,7 +1134,7 @@ sub __eval_option { } sub eval_option { - my ($option, $i) = @_; + my ($name, $option, $i) = @_; my $prev = ""; @@ -1146,7 +1150,7 @@ sub eval_option { "Check for recursive variables\n"; } $prev = $option; - $option = __eval_option($option, $i); + $option = __eval_option($name, $option, $i); } return $option; @@ -3683,7 +3687,7 @@ EOF read_config $ktest_config; if (defined($opt{"LOG_FILE"})) { - $opt{"LOG_FILE"} = eval_option($opt{"LOG_FILE"}, -1); + $opt{"LOG_FILE"} = eval_option("LOG_FILE", $opt{"LOG_FILE"}, -1); } # Append any configs entered in manually to the config file. @@ -3760,7 +3764,7 @@ sub set_test_option { my $option = __set_test_option($name, $i); return $option if (!defined($option)); - return eval_option($option, $i); + return eval_option($name, $option, $i); } # First we need to do is the builds -- cgit v1.3-14-g43fede From 4283b169abfb0380850b56287ee644697ecf321a Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 30 Jan 2013 18:37:47 -0500 Subject: ktest: Add make_warnings_file and process full warnings Although the patchcheck test checks for warnings in the files that were changed, this check does not catch warnings that were caused by header file changes and the warnings appear in C files not touched by the commit. Add a new option called WARNINGS_FILE. If this option is set, then the file it points to is read before bulid, and the file should contain a list of known warnings. If a warning appears in the build, this file is checked, and if the warning does not exist in this file, then it fails the build showing the new warning. If the WARNINGS_FILE points to a file that does not exist, this will cause any warning in the build to fail. A new test is also added called "make_warnings_file". This test will create do a build and record any warnings it finds into the WARNINGS_FILE. This test is something that can be run before other tests to build a warnings file of "known warnings", ie, warnings that were there before your changes. Signed-off-by: Steven Rostedt --- .../testing/ktest/examples/include/patchcheck.conf | 37 +++++++ tools/testing/ktest/ktest.pl | 110 +++++++++++++++++++-- tools/testing/ktest/sample.conf | 44 +++++++++ 3 files changed, 185 insertions(+), 6 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/ktest/examples/include/patchcheck.conf b/tools/testing/ktest/examples/include/patchcheck.conf index 339d3e1700ff..0eb0a5ac77da 100644 --- a/tools/testing/ktest/examples/include/patchcheck.conf +++ b/tools/testing/ktest/examples/include/patchcheck.conf @@ -14,6 +14,16 @@ PATCH_START := HEAD~3 PATCH_END := HEAD +# Use the oldconfig if build_type wasn't defined +DEFAULTS IF NOT DEFINED BUILD_TYPE +DO_BUILD_TYPE := oldconfig + +DEFAULTS ELSE +DO_BUILD_TYPE := ${BUILD_TYPE} + +DEFAULTS + + # 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 @@ -43,6 +53,31 @@ PATCH_TEST_TYPE := boot # (space delimited) #IGNORE_WARNINGS = 39eaf7ef884dcc44f7ff1bac803ca2a1dcf43544 6edb2a8a385f0cdef51dae37ff23e74d76d8a6ce +# Instead of just checking for warnings to files that are changed +# it can be advantageous to check for any new warnings. If a +# header file is changed, it could cause a warning in a file not +# touched by the commit. To detect these kinds of warnings, you +# can use the WARNINGS_FILE option. +# +# If the variable CREATE_WARNINGS_FILE is set, this config will +# enable the WARNINGS_FILE during the patchcheck test. Also, +# before running the patchcheck test, it will create the +# warnings file. +# +DEFAULTS IF DEFINED CREATE_WARNINGS_FILE +WARNINGS_FILE = ${OUTPUT_DIR}/warnings_file + +TEST_START IF DEFINED CREATE_WARNINGS_FILE +# WARNINGS_FILE is already set by the DEFAULTS above +TEST_TYPE = make_warnings_file +# Checkout the commit before the patches to test, +# and record all the warnings that exist before the patches +# to test are added +CHECKOUT = ${PATCHCHECK_START}~1 +# Force a full build +BUILD_NOCLEAN = 0 +BUILD_TYPE = ${DO_BUILD_TYPE} + # 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 @@ -61,6 +96,7 @@ PATCHCHECK_TYPE = ${PATCH_TEST_TYPE} PATCHCHECK_START = ${PATCH_START1} PATCHCHECK_END = ${PATCH_END} CHECKOUT = ${PATCH_CHECKOUT} +BUILD_TYPE = ${DO_BUILD_TYPE} TEST_START IF ${TEST} == patchcheck && ${MULTI} TEST_TYPE = patchcheck @@ -72,3 +108,4 @@ PATCHCHECK_END = ${PATCH_END} CHECKOUT = ${PATCH_CHECKOUT} # Use multi to test different compilers? MAKE_CMD = CC=gcc-4.5.1 make +BUILD_TYPE = ${DO_BUILD_TYPE} diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 3d19ee445249..3fd768e65998 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -126,6 +126,7 @@ my $start_minconfig_defined; my $output_minconfig; my $minconfig_type; my $use_output_minconfig; +my $warnings_file; my $ignore_config; my $ignore_errors; my $addconfig; @@ -193,6 +194,9 @@ my $patchcheck_end; # which would require more options. my $buildonly = 1; +# tell build not to worry about warnings, even when WARNINGS_FILE is set +my $warnings_ok = 0; + # set when creating a new config my $newconfig = 0; @@ -235,6 +239,7 @@ my %option_map = ( "START_MIN_CONFIG" => \$start_minconfig, "MIN_CONFIG_TYPE" => \$minconfig_type, "USE_OUTPUT_MIN_CONFIG" => \$use_output_minconfig, + "WARNINGS_FILE" => \$warnings_file, "IGNORE_CONFIG" => \$ignore_config, "TEST" => \$run_test, "ADD_CONFIG" => \$addconfig, @@ -1924,7 +1929,60 @@ sub start_monitor_and_boot { return monitor; } +my $check_build_re = ".*:.*(warning|error|Error):.*"; +my $utf8_quote = "\\x{e2}\\x{80}(\\x{98}|\\x{99})"; + +# Read buildlog and check against warnings file for any +# new warnings. +# +# Returns 1 if OK +# 0 otherwise sub check_buildlog { + return 1 if (!defined $warnings_file); + + my %warnings_list; + + # Failed builds should not reboot the target + my $save_no_reboot = $no_reboot; + $no_reboot = 1; + + if (-f $warnings_file) { + open(IN, $warnings_file) or + dodie "Error opening $warnings_file"; + + while () { + if (/$check_build_re/) { + chomp; + $warnings_list{$_} = 1; + } + } + close(IN); + } + + # If warnings file didn't exist, and WARNINGS_FILE exist, + # then we fail on any warning! + + open(IN, $buildlog) or dodie "Can't open $buildlog"; + while () { + if (/$check_build_re/) { + + # Some compilers use UTF-8 extended for quotes + # for distcc heterogeneous systems, this causes issues + s/$utf8_quote/'/g; + + chomp; + if (!defined $warnings_list{$_}) { + fail "New warning found (not in $warnings_file)\n$_\n"; + $no_reboot = $save_no_reboot; + return 0; + } + } + } + $no_reboot = $save_no_reboot; + close(IN); +} + +sub check_patch_buildlog { my ($patch) = @_; my @files = `git show $patch | diffstat -l`; @@ -3080,11 +3138,13 @@ sub patchcheck { build "oldconfig" or return 0; } - - if (!defined($ignored_warnings{$sha1})) { - check_buildlog $sha1 or return 0; + # No need to do per patch checking if warnings file exists + if (!defined($warnings_file) && !defined($ignored_warnings{$sha1})) { + check_patch_buildlog $sha1 or return 0; } + check_buildlog or return 0; + next if ($type eq "build"); my $failed = 0; @@ -3642,6 +3702,39 @@ sub make_min_config { return 1; } +sub make_warnings_file { + my ($i) = @_; + + if (!defined($warnings_file)) { + dodie "Must define WARNINGS_FILE for make_warnings_file test"; + } + + if ($build_type eq "nobuild") { + dodie "BUILD_TYPE can not be 'nobuild' for make_warnings_file test"; + } + + build $build_type or dodie "Failed to build"; + + open(OUT, ">$warnings_file") or dodie "Can't create $warnings_file"; + + open(IN, $buildlog) or dodie "Can't open $buildlog"; + while () { + + # Some compilers use UTF-8 extended for quotes + # for distcc heterogeneous systems, this causes issues + s/$utf8_quote/'/g; + + if (/$check_build_re/) { + print OUT; + } + } + close(IN); + + close(OUT); + + success $i; +} + $#ARGV < 1 or die "ktest.pl version: $VERSION\n usage: ktest.pl config-file\n"; if ($#ARGV == 0) { @@ -3843,9 +3936,9 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { $run_type = $bisect_type; } elsif ($test_type eq "config_bisect") { $run_type = $config_bisect_type; - } - - if ($test_type eq "make_min_config") { + } elsif ($test_type eq "make_min_config") { + $run_type = ""; + } elsif ($test_type eq "make_warnings_file") { $run_type = ""; } @@ -3902,10 +3995,15 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { } elsif ($test_type eq "make_min_config") { make_min_config $i; next; + } elsif ($test_type eq "make_warnings_file") { + $no_reboot = 1; + make_warnings_file $i; + next; } if ($build_type ne "nobuild") { build $build_type or next; + check_buildlog or next; } if ($test_type eq "install") { diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 4012e9330344..0a290fb4cd5e 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -793,6 +793,20 @@ # Example for a virtual guest call "Guest". #POWER_OFF = virsh destroy Guest +# To have the build fail on "new" warnings, create a file that +# contains a list of all known warnings (they must match exactly +# to the line with 'warning:', 'error:' or 'Error:'. If the option +# WARNINGS_FILE is set, then that file will be read, and if the +# build detects a warning, it will examine this file and if the +# warning does not exist in it, it will fail the build. +# +# Note, if this option is defined to a file that does not exist +# then any warning will fail the build. +# (see make_warnings_file below) +# +# (optional, default undefined) +#WARNINGS_FILE = ${OUTPUT_DIR}/warnings_file + # The way to execute a command on the target # (default ssh $SSH_USER@$MACHINE $SSH_COMMAND";) # The variables SSH_USER, MACHINE and SSH_COMMAND are defined @@ -1222,3 +1236,33 @@ # MIN_CONFIG_TYPE = test # TEST = ssh ${USER}@${MACHINE} echo hi # +# +# +# +# For TEST_TYPE = make_warnings_file +# +# If you want the build to fail when a new warning is discovered +# you set the WARNINGS_FILE to point to a file of known warnings. +# +# The test "make_warnings_file" will let you create a new warnings +# file before you run other tests, like patchcheck. +# +# What this test does is to run just a build, you still need to +# specify BUILD_TYPE to tell the test what type of config to use. +# A BUILD_TYPE of nobuild will fail this test. +# +# The test will do the build and scan for all warnings. Any warning +# it discovers will be saved in the WARNINGS_FILE (required) option. +# +# It is recommended (but not necessary) to make sure BUILD_NOCLEAN is +# off, so that a full build is done (make mrproper is performed). +# That way, all warnings will be captured. +# +# Example: +# +# TEST_TYPE = make_warnings_file +# WARNINGS_FILE = ${OUTPUT_DIR} +# BUILD_TYPE = useconfig:oldconfig +# CHECKOUT = v3.8 +# BUILD_NOCLEAN = 0 +# -- cgit v1.3-14-g43fede From d6845536236a72382a20483887943a599d7c2b69 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Mon, 4 Feb 2013 23:08:49 -0500 Subject: ktest: Search for linux banner for successful reboot Sometimes when a test kernel passed fine, but on reboot it crashed, ktest could get stuck and not proceed. This would be frustrating if you let a test run overnight to find out the next morning that it was stuck on the first test. To fix this, I made reboot check for the REBOOT_SUCCESS_LINE. If the line was not detected, then it would power cycle the box. What it didn't cover was if the REBOOT_SUCCESS_LINE wasn't defined or if a 'good' kernel did not display the line. Instead have it search for the Linux banner "Linux version". The reboot just needs to get to the start of the next kernel, it does not need to test if the next kernel makes it to a boot prompt. After we find the next kernel has booted, then we just wait for either the REBOOT_SUCCESS_LINE to appear or the timeout. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'tools/testing') diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 3fd768e65998..839989d08dc2 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1212,11 +1212,16 @@ sub reboot { } if (defined($time)) { - if (wait_for_monitor($time, $reboot_success_line)) { + # Look for the good kernel to boot + if (wait_for_monitor($time, "Linux version")) { # reboot got stuck? doprint "Reboot did not finish. Forcing power cycle\n"; run_command "$power_cycle"; } + + # Still need to wait for the reboot to finish + wait_for_monitor($time, $reboot_success_line); + end_monitor; } } -- cgit v1.3-14-g43fede From 4c0b67a27d96e01a4b4ede2fda57da9f7c50af21 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 5 Feb 2013 09:56:00 -0500 Subject: ktest: Ignore warnings during reboot The reboot just wants to get to the next kernel. But if a warning (Call Trace) appears, the monitor will report an error, and the reboot will think something went wrong and power cycle the box, even though we successfully made it to the next kernel. Ignore warnings during the reboot until we get to the next kernel. It will still timeout if we never get to the next kernel and then a power cycle will happen. That's what we want it to do. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'tools/testing') diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 839989d08dc2..f3bb5da3193b 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1212,6 +1212,12 @@ sub reboot { } if (defined($time)) { + + # We only want to get to the new kernel, don't fail + # if we stumble over a call trace. + my $save_ignore_errors = $ignore_errors; + $ignore_errors = 1; + # Look for the good kernel to boot if (wait_for_monitor($time, "Linux version")) { # reboot got stuck? @@ -1219,6 +1225,8 @@ sub reboot { run_command "$power_cycle"; } + $ignore_errors = $save_ignore_errors; + # Still need to wait for the reboot to finish wait_for_monitor($time, $reboot_success_line); -- cgit v1.3-14-g43fede From 7328735cbf68b7cd4d7ef16e172013743cdc2bc4 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Mon, 18 Feb 2013 09:35:49 -0500 Subject: ktest: Remove indexes from warnings check The index of a line where a warning is tested can be returned differently on different versions of gcc (or same version compiled differently). That is, a tab + space can give different results. This causes the warning check to produce a false positive. Removing the index from the check fixes this issue. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index f3bb5da3193b..4e67d52eb3a2 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1945,6 +1945,27 @@ sub start_monitor_and_boot { my $check_build_re = ".*:.*(warning|error|Error):.*"; my $utf8_quote = "\\x{e2}\\x{80}(\\x{98}|\\x{99})"; +sub process_warning_line { + my ($line) = @_; + + chomp $line; + + # for distcc heterogeneous systems, some compilers + # do things differently causing warning lines + # to be slightly different. This makes an attempt + # to fixe those issues. + + # chop off the index into the line + # using distcc, some compilers give different indexes + # depending on white space + $line =~ s/^(\s*\S+:\d+:)\d+/$1/; + + # Some compilers use UTF-8 extended for quotes and some don't. + $line =~ s/$utf8_quote/'/g; + + return $line; +} + # Read buildlog and check against warnings file for any # new warnings. # @@ -1965,8 +1986,9 @@ sub check_buildlog { while () { if (/$check_build_re/) { - chomp; - $warnings_list{$_} = 1; + my $warning = process_warning_line $_; + + $warnings_list{$warning} = 1; } } close(IN); @@ -1978,13 +2000,9 @@ sub check_buildlog { open(IN, $buildlog) or dodie "Can't open $buildlog"; while () { if (/$check_build_re/) { + my $warning = process_warning_line $_; - # Some compilers use UTF-8 extended for quotes - # for distcc heterogeneous systems, this causes issues - s/$utf8_quote/'/g; - - chomp; - if (!defined $warnings_list{$_}) { + if (!defined $warnings_list{$warning}) { fail "New warning found (not in $warnings_file)\n$_\n"; $no_reboot = $save_no_reboot; return 0; -- cgit v1.3-14-g43fede From 455ce1c721b1787e6695c535528034e9e7271f37 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Wed, 27 Feb 2013 17:05:52 -0800 Subject: selftests: add tests for efivarfs This change adds a few initial efivarfs tests to the tools/testing/selftests directory. The open-unlink test is based on code from Lingzhu Xiang. Signed-off-by: Jeremy Kerr Cc: Matt Fleming Cc: Lingzhu Xiang Cc: Dave Young Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/Makefile | 2 +- tools/testing/selftests/efivarfs/Makefile | 12 +++ tools/testing/selftests/efivarfs/efivarfs.sh | 119 +++++++++++++++++++++++++ tools/testing/selftests/efivarfs/open-unlink.c | 63 +++++++++++++ 4 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/efivarfs/Makefile create mode 100644 tools/testing/selftests/efivarfs/efivarfs.sh create mode 100644 tools/testing/selftests/efivarfs/open-unlink.c (limited to 'tools/testing') diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 85baf11e2acd..dee19ddc52fc 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,4 +1,4 @@ -TARGETS = breakpoints kcmp mqueue vm cpu-hotplug memory-hotplug +TARGETS = breakpoints kcmp mqueue vm cpu-hotplug memory-hotplug efivarfs all: for TARGET in $(TARGETS); do \ diff --git a/tools/testing/selftests/efivarfs/Makefile b/tools/testing/selftests/efivarfs/Makefile new file mode 100644 index 000000000000..b64f61467e26 --- /dev/null +++ b/tools/testing/selftests/efivarfs/Makefile @@ -0,0 +1,12 @@ +CC = $(CROSS_COMPILE)gcc +CFLAGS = -Wall + +test_objs = open-unlink + +all: $(test_objs) + +run_tests: all + @/bin/bash ./efivarfs.sh || echo "efivarfs selftests: [FAIL]" + +clean: + rm -f $(test_objs) diff --git a/tools/testing/selftests/efivarfs/efivarfs.sh b/tools/testing/selftests/efivarfs/efivarfs.sh new file mode 100644 index 000000000000..e8c0d27b381b --- /dev/null +++ b/tools/testing/selftests/efivarfs/efivarfs.sh @@ -0,0 +1,119 @@ +#!/bin/bash + +efivarfs_mount=/sys/firmware/efi/efivars +test_guid=210be57c-9849-4fc7-a635-e6382d1aec27 + +check_prereqs() +{ + local msg="skip all tests:" + + if [ $UID != 0 ]; then + echo $msg must be run as root >&2 + exit 0 + fi + + if ! grep -q "^\S\+ $efivarfs_mount efivarfs" /proc/mounts; then + echo $msg efivarfs is not mounted on $efivarfs_mount >&2 + exit 0 + fi +} + +run_test() +{ + local test="$1" + + echo "--------------------" + echo "running $test" + echo "--------------------" + + if [ "$(type -t $test)" = 'function' ]; then + ( $test ) + else + ( ./$test ) + fi + + if [ $? -ne 0 ]; then + echo " [FAIL]" + rc=1 + else + echo " [PASS]" + fi +} + +test_create() +{ + local attrs='\x07\x00\x00\x00' + local file=$efivarfs_mount/$FUNCNAME-$test_guid + + printf "$attrs\x00" > $file + + if [ ! -e $file ]; then + echo "$file couldn't be created" >&2 + exit 1 + fi + + if [ $(stat -c %s $file) -ne 5 ]; then + echo "$file has invalid size" >&2 + exit 1 + fi +} + +test_delete() +{ + local attrs='\x07\x00\x00\x00' + local file=$efivarfs_mount/$FUNCNAME-$test_guid + + printf "$attrs\x00" > $file + + if [ ! -e $file ]; then + echo "$file couldn't be created" >&2 + exit 1 + fi + + rm $file + + if [ -e $file ]; then + echo "$file couldn't be deleted" >&2 + exit 1 + fi + +} + +# test that we can remove a variable by issuing a write with only +# attributes specified +test_zero_size_delete() +{ + local attrs='\x07\x00\x00\x00' + local file=$efivarfs_mount/$FUNCNAME-$test_guid + + printf "$attrs\x00" > $file + + if [ ! -e $file ]; then + echo "$file does not exist" >&2 + exit 1 + fi + + printf "$attrs" > $file + + if [ -e $file ]; then + echo "$file should have been deleted" >&2 + exit 1 + fi +} + +test_open_unlink() +{ + local file=$efivarfs_mount/$FUNCNAME-$test_guid + ./open-unlink $file +} + +check_prereqs + +rc=0 + +run_test test_create +run_test test_delete +run_test test_zero_size_delete +run_test test_open_unlink + +exit $rc diff --git a/tools/testing/selftests/efivarfs/open-unlink.c b/tools/testing/selftests/efivarfs/open-unlink.c new file mode 100644 index 000000000000..8c0764407b3c --- /dev/null +++ b/tools/testing/selftests/efivarfs/open-unlink.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + const char *path; + char buf[5]; + int fd, rc; + + if (argc < 2) { + fprintf(stderr, "usage: %s \n", argv[0]); + return EXIT_FAILURE; + } + + path = argv[1]; + + /* attributes: EFI_VARIABLE_NON_VOLATILE | + * EFI_VARIABLE_BOOTSERVICE_ACCESS | + * EFI_VARIABLE_RUNTIME_ACCESS + */ + *(uint32_t *)buf = 0x7; + buf[4] = 0; + + /* create a test variable */ + fd = open(path, O_WRONLY | O_CREAT); + if (fd < 0) { + perror("open(O_WRONLY)"); + return EXIT_FAILURE; + } + + rc = write(fd, buf, sizeof(buf)); + if (rc != sizeof(buf)) { + perror("write"); + return EXIT_FAILURE; + } + + close(fd); + + fd = open(path, O_RDONLY); + if (fd < 0) { + perror("open"); + return EXIT_FAILURE; + } + + if (unlink(path) < 0) { + perror("unlink"); + return EXIT_FAILURE; + } + + rc = read(fd, buf, sizeof(buf)); + if (rc > 0) { + fprintf(stderr, "reading from an unlinked variable " + "shouldn't be possible\n"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} -- cgit v1.3-14-g43fede From 033a1a7fe7e27b9320496ca7da6905a5f654a768 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Wed, 27 Feb 2013 17:05:53 -0800 Subject: selftests/efivarfs: add empty file creation test Signed-off-by: Jeremy Kerr Cc: Matt Fleming Cc: Lingzhu Xiang Cc: Dave Young Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/efivarfs/efivarfs.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'tools/testing') diff --git a/tools/testing/selftests/efivarfs/efivarfs.sh b/tools/testing/selftests/efivarfs/efivarfs.sh index e8c0d27b381b..3af4b370e70d 100644 --- a/tools/testing/selftests/efivarfs/efivarfs.sh +++ b/tools/testing/selftests/efivarfs/efivarfs.sh @@ -58,6 +58,18 @@ test_create() fi } +test_create_empty() +{ + local file=$efivarfs_mount/$FUNCNAME-$test_guid + + : > $file + + if [ ! -e $file ]; then + echo "$file can not be created without writing" >&2 + exit 1 + fi +} + test_delete() { local attrs='\x07\x00\x00\x00' @@ -112,6 +124,7 @@ check_prereqs rc=0 run_test test_create +run_test test_create_empty run_test test_delete run_test test_zero_size_delete run_test test_open_unlink -- cgit v1.3-14-g43fede From d974f67a528fa7ef5547318ea09bf581c8c1d3d9 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Wed, 27 Feb 2013 17:05:55 -0800 Subject: selftests/efivarfs: add create-read test Test that reads from a newly-created efivarfs file (with no data written) will return EOF. Signed-off-by: Jeremy Kerr Cc: Matt Fleming Cc: Lingzhu Xiang Cc: Dave Young Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/efivarfs/Makefile | 2 +- tools/testing/selftests/efivarfs/create-read.c | 38 ++++++++++++++++++++++++++ tools/testing/selftests/efivarfs/efivarfs.sh | 7 +++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/efivarfs/create-read.c (limited to 'tools/testing') diff --git a/tools/testing/selftests/efivarfs/Makefile b/tools/testing/selftests/efivarfs/Makefile index b64f61467e26..29e8c6bc81b0 100644 --- a/tools/testing/selftests/efivarfs/Makefile +++ b/tools/testing/selftests/efivarfs/Makefile @@ -1,7 +1,7 @@ CC = $(CROSS_COMPILE)gcc CFLAGS = -Wall -test_objs = open-unlink +test_objs = open-unlink create-read all: $(test_objs) diff --git a/tools/testing/selftests/efivarfs/create-read.c b/tools/testing/selftests/efivarfs/create-read.c new file mode 100644 index 000000000000..7feef1880968 --- /dev/null +++ b/tools/testing/selftests/efivarfs/create-read.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + const char *path; + char buf[4]; + int fd, rc; + + if (argc < 2) { + fprintf(stderr, "usage: %s \n", argv[0]); + return EXIT_FAILURE; + } + + path = argv[1]; + + /* create a test variable */ + fd = open(path, O_RDWR | O_CREAT, 0600); + if (fd < 0) { + perror("open(O_WRONLY)"); + return EXIT_FAILURE; + } + + rc = read(fd, buf, sizeof(buf)); + if (rc != 0) { + fprintf(stderr, "Reading a new var should return EOF\n"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/tools/testing/selftests/efivarfs/efivarfs.sh b/tools/testing/selftests/efivarfs/efivarfs.sh index 3af4b370e70d..880cdd5dc63f 100644 --- a/tools/testing/selftests/efivarfs/efivarfs.sh +++ b/tools/testing/selftests/efivarfs/efivarfs.sh @@ -70,6 +70,12 @@ test_create_empty() fi } +test_create_read() +{ + local file=$efivarfs_mount/$FUNCNAME-$test_guid + ./create-read $file +} + test_delete() { local attrs='\x07\x00\x00\x00' @@ -125,6 +131,7 @@ rc=0 run_test test_create run_test test_create_empty +run_test test_create_read run_test test_delete run_test test_zero_size_delete run_test test_open_unlink -- cgit v1.3-14-g43fede From 66a01b9659016cbb74dfac64861f28c71c804c97 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 27 Feb 2013 17:05:56 -0800 Subject: tools/testing/selftests/Makefile: rearrange targets Do it one-per-line to reduce patch conflict pain. Cc: Dave Young Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/Makefile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index dee19ddc52fc..3cc0ad7ae863 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,4 +1,10 @@ -TARGETS = breakpoints kcmp mqueue vm cpu-hotplug memory-hotplug efivarfs +TARGETS = breakpoints +TARGETS += kcmp +TARGETS += mqueue +TARGETS += vm +TARGETS += cpu-hotplug +TARGETS += memory-hotplug +TARGETS += efivarfs all: for TARGET in $(TARGETS); do \ -- cgit v1.3-14-g43fede From 80d03428597056f4e2d1aed389929ece7879dad1 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Wed, 27 Feb 2013 17:05:57 -0800 Subject: selftests: add a simple doc This change adds a little documentation to the tests under tools/testing/selftests/, based on akpm's explanation. [akpm@linux-foundation.org: move from Documentation to tools/testing/selftests/README.txt] Signed-off-by: Jeremy Kerr Cc: Dave Young Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/README.txt | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tools/testing/selftests/README.txt (limited to 'tools/testing') diff --git a/tools/testing/selftests/README.txt b/tools/testing/selftests/README.txt new file mode 100644 index 000000000000..5e2faf9c55d3 --- /dev/null +++ b/tools/testing/selftests/README.txt @@ -0,0 +1,42 @@ +Linux Kernel Selftests + +The kernel contains a set of "self tests" under the tools/testing/selftests/ +directory. These are intended to be small unit tests to exercise individual +code paths in the kernel. + +Running the selftests +===================== + +To build the tests: + + $ make -C tools/testing/selftests + + +To run the tests: + + $ make -C tools/testing/selftests run_tests + +- note that some tests will require root privileges. + + +To run only tests targetted for a single subsystem: + + $ make -C tools/testing/selftests TARGETS=cpu-hotplug run_tests + +See the top-level tools/testing/selftests/Makefile for the list of all possible +targets. + + +Contributing new tests +====================== + +In general, the rules for for selftests are + + * Do as much as you can if you're not root; + + * Don't take too long; + + * Don't break the build on any architecture, and + + * Don't cause the top-level "make run_tests" to fail if your feature is + unconfigured. -- cgit v1.3-14-g43fede From 123abd76edf56c02a76b46d3d673897177ef067b Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Tue, 5 Mar 2013 07:40:16 +0000 Subject: efivars: efivarfs_valid_name() should handle pstore syntax Stricter validation was introduced with commit da27a24383b2b ("efivarfs: guid part of filenames are case-insensitive") and commit 47f531e8ba3b ("efivarfs: Validate filenames much more aggressively"), which is necessary for the guid portion of efivarfs filenames, but we don't need to be so strict with the first part, the variable name. The UEFI specification doesn't impose any constraints on variable names other than they be a NULL-terminated string. The above commits caused a regression that resulted in users seeing the following message, $ sudo mount -v /sys/firmware/efi/efivars mount: Cannot allocate memory whenever pstore EFI variables were present in the variable store, since their variable names failed to pass the following check, /* GUID should be right after the first '-' */ if (s - 1 != strchr(str, '-')) as a typical pstore filename is of the form, dump-type0-10-1-. The fix is trivial since the guid portion of the filename is GUID_LEN bytes, we can use (len - GUID_LEN) to ensure the '-' character is where we expect it to be. (The bogus ENOMEM error value will be fixed in a separate patch.) Reported-by: Joseph Yasi Tested-by: Joseph Yasi Reported-by: Lingzhu Xiang Cc: Josh Boyer Cc: Jeremy Kerr Cc: Matthew Garrett Cc: # v3.8 Signed-off-by: Matt Fleming --- drivers/firmware/efivars.c | 4 +- tools/testing/selftests/efivarfs/efivarfs.sh | 59 ++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) (limited to 'tools/testing') diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 0d504971ed4c..1b9a6e17e3dc 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -974,8 +974,8 @@ static bool efivarfs_valid_name(const char *str, int len) if (len < GUID_LEN + 2) return false; - /* GUID should be right after the first '-' */ - if (s - 1 != strchr(str, '-')) + /* GUID must be preceded by a '-' */ + if (*(s - 1) != '-') return false; /* diff --git a/tools/testing/selftests/efivarfs/efivarfs.sh b/tools/testing/selftests/efivarfs/efivarfs.sh index 880cdd5dc63f..77edcdcc016b 100644 --- a/tools/testing/selftests/efivarfs/efivarfs.sh +++ b/tools/testing/selftests/efivarfs/efivarfs.sh @@ -125,6 +125,63 @@ test_open_unlink() ./open-unlink $file } +# test that we can create a range of filenames +test_valid_filenames() +{ + local attrs='\x07\x00\x00\x00' + local ret=0 + + local file_list="abc dump-type0-11-1-1362436005 1234 -" + for f in $file_list; do + local file=$efivarfs_mount/$f-$test_guid + + printf "$attrs\x00" > $file + + if [ ! -e $file ]; then + echo "$file could not be created" >&2 + ret=1 + else + rm $file + fi + done + + exit $ret +} + +test_invalid_filenames() +{ + local attrs='\x07\x00\x00\x00' + local ret=0 + + local file_list=" + -1234-1234-1234-123456789abc + foo + foo-bar + -foo- + foo-barbazba-foob-foob-foob-foobarbazfoo + foo------------------------------------- + -12345678-1234-1234-1234-123456789abc + a-12345678=1234-1234-1234-123456789abc + a-12345678-1234=1234-1234-123456789abc + a-12345678-1234-1234=1234-123456789abc + a-12345678-1234-1234-1234=123456789abc + 1112345678-1234-1234-1234-123456789abc" + + for f in $file_list; do + local file=$efivarfs_mount/$f + + printf "$attrs\x00" 2>/dev/null > $file + + if [ -e $file ]; then + echo "Creating $file should have failed" >&2 + rm $file + ret=1 + fi + done + + exit $ret +} + check_prereqs rc=0 @@ -135,5 +192,7 @@ run_test test_create_read run_test test_delete run_test test_zero_size_delete run_test test_open_unlink +run_test test_valid_filenames +run_test test_invalid_filenames exit $rc -- cgit v1.3-14-g43fede