diff options
Diffstat (limited to 'tools')
50 files changed, 1779 insertions, 259 deletions
diff --git a/tools/hv/hv_fcopy_daemon.c b/tools/hv/hv_fcopy_daemon.c index 8f96b3ee0724..f437d739f37d 100644 --- a/tools/hv/hv_fcopy_daemon.c +++ b/tools/hv/hv_fcopy_daemon.c @@ -33,6 +33,7 @@ #include <sys/stat.h> #include <fcntl.h> #include <dirent.h> +#include <getopt.h> static int target_fd; static char target_fname[W_MAX_PATH]; @@ -126,15 +127,43 @@ static int hv_copy_cancel(void) } -int main(void) +void print_usage(char *argv[]) +{ + fprintf(stderr, "Usage: %s [options]\n" + "Options are:\n" + " -n, --no-daemon stay in foreground, don't daemonize\n" + " -h, --help print this help\n", argv[0]); +} + +int main(int argc, char *argv[]) { int fd, fcopy_fd, len; int error; + int daemonize = 1, long_index = 0, opt; int version = FCOPY_CURRENT_VERSION; char *buffer[4096 * 2]; struct hv_fcopy_hdr *in_msg; - if (daemon(1, 0)) { + static struct option long_options[] = { + {"help", no_argument, 0, 'h' }, + {"no-daemon", no_argument, 0, 'n' }, + {0, 0, 0, 0 } + }; + + while ((opt = getopt_long(argc, argv, "hn", long_options, + &long_index)) != -1) { + switch (opt) { + case 'n': + daemonize = 0; + break; + case 'h': + default: + print_usage(argv); + exit(EXIT_FAILURE); + } + } + + if (daemonize && daemon(1, 0)) { syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno)); exit(EXIT_FAILURE); } diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 4088b816a3ee..6a6432a20a1d 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -43,6 +43,7 @@ #include <fcntl.h> #include <dirent.h> #include <net/if.h> +#include <getopt.h> /* * KVP protocol: The user mode component first registers with the @@ -1417,7 +1418,15 @@ netlink_send(int fd, struct cn_msg *msg) return sendmsg(fd, &message, 0); } -int main(void) +void print_usage(char *argv[]) +{ + fprintf(stderr, "Usage: %s [options]\n" + "Options are:\n" + " -n, --no-daemon stay in foreground, don't daemonize\n" + " -h, --help print this help\n", argv[0]); +} + +int main(int argc, char *argv[]) { int fd, len, nl_group; int error; @@ -1435,9 +1444,30 @@ int main(void) struct hv_kvp_ipaddr_value *kvp_ip_val; char *kvp_recv_buffer; size_t kvp_recv_buffer_len; + int daemonize = 1, long_index = 0, opt; + + static struct option long_options[] = { + {"help", no_argument, 0, 'h' }, + {"no-daemon", no_argument, 0, 'n' }, + {0, 0, 0, 0 } + }; + + while ((opt = getopt_long(argc, argv, "hn", long_options, + &long_index)) != -1) { + switch (opt) { + case 'n': + daemonize = 0; + break; + case 'h': + default: + print_usage(argv); + exit(EXIT_FAILURE); + } + } - if (daemon(1, 0)) + if (daemonize && daemon(1, 0)) return 1; + openlog("KVP", 0, LOG_USER); syslog(LOG_INFO, "KVP starting; pid is:%d", getpid()); @@ -1529,8 +1559,15 @@ int main(void) addr_p, &addr_l); if (len < 0) { + int saved_errno = errno; syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s", addr.nl_pid, errno, strerror(errno)); + + if (saved_errno == ENOBUFS) { + syslog(LOG_ERR, "receive error: ignored"); + continue; + } + close(fd); return -1; } @@ -1733,8 +1770,15 @@ kvp_done: len = netlink_send(fd, incoming_cn_msg); if (len < 0) { + int saved_errno = errno; syslog(LOG_ERR, "net_link send failed; error: %d %s", errno, strerror(errno)); + + if (saved_errno == ENOMEM || saved_errno == ENOBUFS) { + syslog(LOG_ERR, "send error: ignored"); + continue; + } + exit(EXIT_FAILURE); } } diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c index 6a213b8cd7b9..5e63f70bd956 100644 --- a/tools/hv/hv_vss_daemon.c +++ b/tools/hv/hv_vss_daemon.c @@ -36,6 +36,7 @@ #include <linux/hyperv.h> #include <linux/netlink.h> #include <syslog.h> +#include <getopt.h> static struct sockaddr_nl addr; @@ -44,35 +45,51 @@ static struct sockaddr_nl addr; #endif -static int vss_do_freeze(char *dir, unsigned int cmd, char *fs_op) +/* Don't use syslog() in the function since that can cause write to disk */ +static int vss_do_freeze(char *dir, unsigned int cmd) { int ret, fd = open(dir, O_RDONLY); if (fd < 0) return 1; + ret = ioctl(fd, cmd, 0); - syslog(LOG_INFO, "VSS: %s of %s: %s\n", fs_op, dir, strerror(errno)); + + /* + * If a partition is mounted more than once, only the first + * FREEZE/THAW can succeed and the later ones will get + * EBUSY/EINVAL respectively: there could be 2 cases: + * 1) a user may mount the same partition to differnt directories + * by mistake or on purpose; + * 2) The subvolume of btrfs appears to have the same partition + * mounted more than once. + */ + if (ret) { + if ((cmd == FIFREEZE && errno == EBUSY) || + (cmd == FITHAW && errno == EINVAL)) { + close(fd); + return 0; + } + } + close(fd); return !!ret; } static int vss_operate(int operation) { - char *fs_op; char match[] = "/dev/"; FILE *mounts; struct mntent *ent; unsigned int cmd; - int error = 0, root_seen = 0; + int error = 0, root_seen = 0, save_errno = 0; switch (operation) { case VSS_OP_FREEZE: cmd = FIFREEZE; - fs_op = "freeze"; break; case VSS_OP_THAW: cmd = FITHAW; - fs_op = "thaw"; break; default: return -1; @@ -85,7 +102,7 @@ static int vss_operate(int operation) while ((ent = getmntent(mounts))) { if (strncmp(ent->mnt_fsname, match, strlen(match))) continue; - if (strcmp(ent->mnt_type, "iso9660") == 0) + if (hasmntopt(ent, MNTOPT_RO) != NULL) continue; if (strcmp(ent->mnt_type, "vfat") == 0) continue; @@ -93,14 +110,30 @@ static int vss_operate(int operation) root_seen = 1; continue; } - error |= vss_do_freeze(ent->mnt_dir, cmd, fs_op); + error |= vss_do_freeze(ent->mnt_dir, cmd); + if (error && operation == VSS_OP_FREEZE) + goto err; } - endmntent(mounts); if (root_seen) { - error |= vss_do_freeze("/", cmd, fs_op); + error |= vss_do_freeze("/", cmd); + if (error && operation == VSS_OP_FREEZE) + goto err; } + goto out; +err: + save_errno = errno; + vss_operate(VSS_OP_THAW); + /* Call syslog after we thaw all filesystems */ + if (ent) + syslog(LOG_ERR, "FREEZE of %s failed; error:%d %s", + ent->mnt_dir, save_errno, strerror(save_errno)); + else + syslog(LOG_ERR, "FREEZE of / failed; error:%d %s", save_errno, + strerror(save_errno)); +out: + endmntent(mounts); return error; } @@ -131,7 +164,15 @@ static int netlink_send(int fd, struct cn_msg *msg) return sendmsg(fd, &message, 0); } -int main(void) +void print_usage(char *argv[]) +{ + fprintf(stderr, "Usage: %s [options]\n" + "Options are:\n" + " -n, --no-daemon stay in foreground, don't daemonize\n" + " -h, --help print this help\n", argv[0]); +} + +int main(int argc, char *argv[]) { int fd, len, nl_group; int error; @@ -143,8 +184,28 @@ int main(void) struct hv_vss_msg *vss_msg; char *vss_recv_buffer; size_t vss_recv_buffer_len; + int daemonize = 1, long_index = 0, opt; + + static struct option long_options[] = { + {"help", no_argument, 0, 'h' }, + {"no-daemon", no_argument, 0, 'n' }, + {0, 0, 0, 0 } + }; + + while ((opt = getopt_long(argc, argv, "hn", long_options, + &long_index)) != -1) { + switch (opt) { + case 'n': + daemonize = 0; + break; + case 'h': + default: + print_usage(argv); + exit(EXIT_FAILURE); + } + } - if (daemon(1, 0)) + if (daemonize && daemon(1, 0)) return 1; openlog("Hyper-V VSS", 0, LOG_USER); @@ -249,8 +310,16 @@ int main(void) case VSS_OP_FREEZE: case VSS_OP_THAW: error = vss_operate(op); - if (error) + syslog(LOG_INFO, "VSS: op=%s: %s\n", + op == VSS_OP_FREEZE ? "FREEZE" : "THAW", + error ? "failed" : "succeeded"); + + if (error) { error = HV_E_FAIL; + syslog(LOG_ERR, "op=%d failed!", op); + syslog(LOG_ERR, "report it with these files:"); + syslog(LOG_ERR, "/etc/fstab and /proc/mounts"); + } break; default: syslog(LOG_ERR, "Illegal op:%d\n", op); diff --git a/tools/perf/util/include/asm/hash.h b/tools/perf/util/include/asm/hash.h deleted file mode 100644 index d82b170bb216..000000000000 --- a/tools/perf/util/include/asm/hash.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __ASM_GENERIC_HASH_H -#define __ASM_GENERIC_HASH_H - -/* Stub */ - -#endif /* __ASM_GENERIC_HASH_H */ diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index bf1398180785..b9cd036f0442 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -684,11 +684,8 @@ sub set_value { } ${$overrides}{$lvalue} = $prvalue; } - if ($rvalue =~ /^\s*$/) { - delete $opt{$lvalue}; - } else { - $opt{$lvalue} = $prvalue; - } + + $opt{$lvalue} = $prvalue; } sub set_eval { @@ -2005,7 +2002,7 @@ sub get_version { # get the release name return if ($have_version); doprint "$make kernelrelease ... "; - $version = `$make kernelrelease | tail -1`; + $version = `$make -s kernelrelease | tail -1`; chomp($version); doprint "$version\n"; $have_version = 1; @@ -3571,7 +3568,9 @@ sub test_this_config { undef %configs; assign_configs \%configs, $output_config; - return $config if (!defined($configs{$config})); + if (!defined($configs{$config}) || $configs{$config} =~ /^#/) { + return $config; + } doprint "disabling config $config did not change .config\n"; @@ -3945,12 +3944,22 @@ for (my $i = 0, my $repeat = 1; $i <= $opt{"NUM_TESTS"}; $i += $repeat) { } } +sub option_defined { + my ($option) = @_; + + if (defined($opt{$option}) && $opt{$option} !~ /^\s*$/) { + return 1; + } + + return 0; +} + sub __set_test_option { my ($name, $i) = @_; my $option = "$name\[$i\]"; - if (defined($opt{$option})) { + if (option_defined($option)) { return $opt{$option}; } @@ -3958,13 +3967,13 @@ sub __set_test_option { if ($i >= $test && $i < $test + $repeat_tests{$test}) { $option = "$name\[$test\]"; - if (defined($opt{$option})) { + if (option_defined($option)) { return $opt{$option}; } } } - if (defined($opt{$name})) { + if (option_defined($name)) { return $opt{$name}; } @@ -4077,8 +4086,14 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { my $installme = ""; $installme = " no_install" if ($no_install); + my $name = ""; + + if (defined($test_name)) { + $name = " ($test_name)"; + } + doprint "\n\n"; - doprint "RUNNING TEST $i of $opt{NUM_TESTS} with option $test_type $run_type$installme\n\n"; + doprint "RUNNING TEST $i of $opt{NUM_TESTS}$name with option $test_type $run_type$installme\n\n"; if (defined($pre_test)) { run_command $pre_test; diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 45f145c6f843..4e511221a0c1 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,20 +1,23 @@ TARGETS = breakpoints TARGETS += cpu-hotplug TARGETS += efivarfs +TARGETS += exec +TARGETS += firmware +TARGETS += ftrace TARGETS += kcmp TARGETS += memfd TARGETS += memory-hotplug -TARGETS += mqueue TARGETS += mount +TARGETS += mqueue TARGETS += net +TARGETS += powerpc TARGETS += ptrace +TARGETS += size +TARGETS += sysctl TARGETS += timers -TARGETS += vm -TARGETS += powerpc TARGETS += user -TARGETS += sysctl -TARGETS += firmware -TARGETS += ftrace +TARGETS += vm +#Please keep the TARGETS list alphabetically sorted TARGETS_HOTPLUG = cpu-hotplug TARGETS_HOTPLUG += memory-hotplug diff --git a/tools/testing/selftests/README.txt b/tools/testing/selftests/README.txt deleted file mode 100644 index 2660d5ff9179..000000000000 --- a/tools/testing/selftests/README.txt +++ /dev/null @@ -1,61 +0,0 @@ -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. - -On some systems, hot-plug tests could hang forever waiting for cpu and -memory to be ready to be offlined. A special hot-plug target is created -to run full range of hot-plug tests. In default mode, hot-plug tests run -in safe mode with a limited scope. In limited mode, cpu-hotplug test is -run on a single cpu as opposed to all hotplug capable cpus, and memory -hotplug test is run on 2% of hotplug capable memory instead of 10%. - -Running the selftests (hotplug tests are run in limited mode) -============================================================= - -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 targeted for a single subsystem: (including -hotplug targets in limited mode) - - $ 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. - -Running the full range hotplug selftests -======================================== - -To build the tests: - - $ make -C tools/testing/selftests hotplug - -To run the tests: - - $ make -C tools/testing/selftests run_hotplug - -- note that some tests will require root privileges. - -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. diff --git a/tools/testing/selftests/breakpoints/breakpoint_test.c b/tools/testing/selftests/breakpoints/breakpoint_test.c index a0743f3b2b57..120895ab5505 100644 --- a/tools/testing/selftests/breakpoints/breakpoint_test.c +++ b/tools/testing/selftests/breakpoints/breakpoint_test.c @@ -17,6 +17,8 @@ #include <sys/types.h> #include <sys/wait.h> +#include "../kselftest.h" + /* Breakpoint access modes */ enum { @@ -42,7 +44,7 @@ static void set_breakpoint_addr(void *addr, int n) offsetof(struct user, u_debugreg[n]), addr); if (ret) { perror("Can't set breakpoint addr\n"); - exit(-1); + ksft_exit_fail(); } } @@ -105,7 +107,7 @@ static void toggle_breakpoint(int n, int type, int len, offsetof(struct user, u_debugreg[7]), dr7); if (ret) { perror("Can't set dr7"); - exit(-1); + ksft_exit_fail(); } } @@ -275,7 +277,7 @@ static void check_success(const char *msg) msg2 = "Ok"; if (ptrace(PTRACE_POKEDATA, child_pid, &trapped, 1)) { perror("Can't poke\n"); - exit(-1); + ksft_exit_fail(); } } @@ -390,5 +392,5 @@ int main(int argc, char **argv) wait(NULL); - return 0; + return ksft_exit_pass(); } diff --git a/tools/testing/selftests/exec/.gitignore b/tools/testing/selftests/exec/.gitignore new file mode 100644 index 000000000000..64073e050c6a --- /dev/null +++ b/tools/testing/selftests/exec/.gitignore @@ -0,0 +1,9 @@ +subdir* +script* +execveat +execveat.symlink +execveat.moved +execveat.path.ephemeral +execveat.ephemeral +execveat.denatured +xxxxxxxx*
\ No newline at end of file diff --git a/tools/testing/selftests/exec/Makefile b/tools/testing/selftests/exec/Makefile new file mode 100644 index 000000000000..66dfc2ce1788 --- /dev/null +++ b/tools/testing/selftests/exec/Makefile @@ -0,0 +1,25 @@ +CC = $(CROSS_COMPILE)gcc +CFLAGS = -Wall +BINARIES = execveat +DEPS = execveat.symlink execveat.denatured script subdir +all: $(BINARIES) $(DEPS) + +subdir: + mkdir -p $@ +script: + echo '#!/bin/sh' > $@ + echo 'exit $$*' >> $@ + chmod +x $@ +execveat.symlink: execveat + ln -s -f $< $@ +execveat.denatured: execveat + cp $< $@ + chmod -x $@ +%: %.c + $(CC) $(CFLAGS) -o $@ $^ + +run_tests: all + ./execveat + +clean: + rm -rf $(BINARIES) $(DEPS) subdir.moved execveat.moved xxxxx* diff --git a/tools/testing/selftests/exec/execveat.c b/tools/testing/selftests/exec/execveat.c new file mode 100644 index 000000000000..33a5c06d95ca --- /dev/null +++ b/tools/testing/selftests/exec/execveat.c @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2014 Google, Inc. + * + * Licensed under the terms of the GNU GPL License version 2 + * + * Selftests for execveat(2). + */ + +#define _GNU_SOURCE /* to get O_PATH, AT_EMPTY_PATH */ +#include <sys/sendfile.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static char longpath[2 * PATH_MAX] = ""; +static char *envp[] = { "IN_TEST=yes", NULL, NULL }; +static char *argv[] = { "execveat", "99", NULL }; + +static int execveat_(int fd, const char *path, char **argv, char **envp, + int flags) +{ +#ifdef __NR_execveat + return syscall(__NR_execveat, fd, path, argv, envp, flags); +#else + errno = -ENOSYS; + return -1; +#endif +} + +#define check_execveat_fail(fd, path, flags, errno) \ + _check_execveat_fail(fd, path, flags, errno, #errno) +static int _check_execveat_fail(int fd, const char *path, int flags, + int expected_errno, const char *errno_str) +{ + int rc; + + errno = 0; + printf("Check failure of execveat(%d, '%s', %d) with %s... ", + fd, path?:"(null)", flags, errno_str); + rc = execveat_(fd, path, argv, envp, flags); + + if (rc > 0) { + printf("[FAIL] (unexpected success from execveat(2))\n"); + return 1; + } + if (errno != expected_errno) { + printf("[FAIL] (expected errno %d (%s) not %d (%s)\n", + expected_errno, strerror(expected_errno), + errno, strerror(errno)); + return 1; + } + printf("[OK]\n"); + return 0; +} + +static int check_execveat_invoked_rc(int fd, const char *path, int flags, + int expected_rc) +{ + int status; + int rc; + pid_t child; + int pathlen = path ? strlen(path) : 0; + + if (pathlen > 40) + printf("Check success of execveat(%d, '%.20s...%s', %d)... ", + fd, path, (path + pathlen - 20), flags); + else + printf("Check success of execveat(%d, '%s', %d)... ", + fd, path?:"(null)", flags); + child = fork(); + if (child < 0) { + printf("[FAIL] (fork() failed)\n"); + return 1; + } + if (child == 0) { + /* Child: do execveat(). */ + rc = execveat_(fd, path, argv, envp, flags); + printf("[FAIL]: execveat() failed, rc=%d errno=%d (%s)\n", + rc, errno, strerror(errno)); + exit(1); /* should not reach here */ + } + /* Parent: wait for & check child's exit status. */ + rc = waitpid(child, &status, 0); + if (rc != child) { + printf("[FAIL] (waitpid(%d,...) returned %d)\n", child, rc); + return 1; + } + if (!WIFEXITED(status)) { + printf("[FAIL] (child %d did not exit cleanly, status=%08x)\n", + child, status); + return 1; + } + if (WEXITSTATUS(status) != expected_rc) { + printf("[FAIL] (child %d exited with %d not %d)\n", + child, WEXITSTATUS(status), expected_rc); + return 1; + } + printf("[OK]\n"); + return 0; +} + +static int check_execveat(int fd, const char *path, int flags) +{ + return check_execveat_invoked_rc(fd, path, flags, 99); +} + +static char *concat(const char *left, const char *right) +{ + char *result = malloc(strlen(left) + strlen(right) + 1); + + strcpy(result, left); + strcat(result, right); + return result; +} + +static int open_or_die(const char *filename, int flags) +{ + int fd = open(filename, flags); + + if (fd < 0) { + printf("Failed to open '%s'; " + "check prerequisites are available\n", filename); + exit(1); + } + return fd; +} + +static void exe_cp(const char *src, const char *dest) +{ + int in_fd = open_or_die(src, O_RDONLY); + int out_fd = open(dest, O_RDWR|O_CREAT|O_TRUNC, 0755); + struct stat info; + + fstat(in_fd, &info); + sendfile(out_fd, in_fd, NULL, info.st_size); + close(in_fd); + close(out_fd); +} + +#define XX_DIR_LEN 200 +static int check_execveat_pathmax(int dot_dfd, const char *src, int is_script) +{ + int fail = 0; + int ii, count, len; + char longname[XX_DIR_LEN + 1]; + int fd; + + if (*longpath == '\0') { + /* Create a filename close to PATH_MAX in length */ + memset(longname, 'x', XX_DIR_LEN - 1); + longname[XX_DIR_LEN - 1] = '/'; + longname[XX_DIR_LEN] = '\0'; + count = (PATH_MAX - 3) / XX_DIR_LEN; + for (ii = 0; ii < count; ii++) { + strcat(longpath, longname); + mkdir(longpath, 0755); + } + len = (PATH_MAX - 3) - (count * XX_DIR_LEN); + if (len <= 0) + len = 1; + memset(longname, 'y', len); + longname[len] = '\0'; + strcat(longpath, longname); + } + exe_cp(src, longpath); + + /* + * Execute as a pre-opened file descriptor, which works whether this is + * a script or not (because the interpreter sees a filename like + * "/dev/fd/20"). + */ + fd = open(longpath, O_RDONLY); + if (fd > 0) { + printf("Invoke copy of '%s' via filename of length %lu:\n", + src, strlen(longpath)); + fail += check_execveat(fd, "", AT_EMPTY_PATH); + } else { + printf("Failed to open length %lu filename, errno=%d (%s)\n", + strlen(longpath), errno, strerror(errno)); + fail++; + } + + /* + * Execute as a long pathname relative to ".". If this is a script, + * the interpreter will launch but fail to open the script because its + * name ("/dev/fd/5/xxx....") is bigger than PATH_MAX. + */ + if (is_script) + fail += check_execveat_invoked_rc(dot_dfd, longpath, 0, 127); + else + fail += check_execveat(dot_dfd, longpath, 0); + + return fail; +} + +static int run_tests(void) +{ + int fail = 0; + char *fullname = realpath("execveat", NULL); + char *fullname_script = realpath("script", NULL); + char *fullname_symlink = concat(fullname, ".symlink"); + int subdir_dfd = open_or_die("subdir", O_DIRECTORY|O_RDONLY); + int subdir_dfd_ephemeral = open_or_die("subdir.ephemeral", + O_DIRECTORY|O_RDONLY); + int dot_dfd = open_or_die(".", O_DIRECTORY|O_RDONLY); + int dot_dfd_path = open_or_die(".", O_DIRECTORY|O_RDONLY|O_PATH); + int dot_dfd_cloexec = open_or_die(".", O_DIRECTORY|O_RDONLY|O_CLOEXEC); + int fd = open_or_die("execveat", O_RDONLY); + int fd_path = open_or_die("execveat", O_RDONLY|O_PATH); + int fd_symlink = open_or_die("execveat.symlink", O_RDONLY); + int fd_denatured = open_or_die("execveat.denatured", O_RDONLY); + int fd_denatured_path = open_or_die("execveat.denatured", + O_RDONLY|O_PATH); + int fd_script = open_or_die("script", O_RDONLY); + int fd_ephemeral = open_or_die("execveat.ephemeral", O_RDONLY); + int fd_ephemeral_path = open_or_die("execveat.path.ephemeral", + O_RDONLY|O_PATH); + int fd_script_ephemeral = open_or_die("script.ephemeral", O_RDONLY); + int fd_cloexec = open_or_die("execveat", O_RDONLY|O_CLOEXEC); + int fd_script_cloexec = open_or_die("script", O_RDONLY|O_CLOEXEC); + + /* Change file position to confirm it doesn't affect anything */ + lseek(fd, 10, SEEK_SET); + + /* Normal executable file: */ + /* dfd + path */ + fail += check_execveat(subdir_dfd, "../execveat", 0); + fail += check_execveat(dot_dfd, "execveat", 0); + fail += check_execveat(dot_dfd_path, "execveat", 0); + /* absolute path */ + fail += check_execveat(AT_FDCWD, fullname, 0); + /* absolute path with nonsense dfd */ + fail += check_execveat(99, fullname, 0); + /* fd + no path */ + fail += check_execveat(fd, "", AT_EMPTY_PATH); + /* O_CLOEXEC fd + no path */ + fail += check_execveat(fd_cloexec, "", AT_EMPTY_PATH); + /* O_PATH fd */ + fail += check_execveat(fd_path, "", AT_EMPTY_PATH); + + /* Mess with executable file that's already open: */ + /* fd + no path to a file that's been renamed */ + rename("execveat.ephemeral", "execveat.moved"); + fail += check_execveat(fd_ephemeral, "", AT_EMPTY_PATH); + /* fd + no path to a file that's been deleted */ + unlink("execveat.moved"); /* remove the file now fd open */ + fail += check_execveat(fd_ephemeral, "", AT_EMPTY_PATH); + + /* Mess with executable file that's already open with O_PATH */ + /* fd + no path to a file that's been deleted */ + unlink("execveat.path.ephemeral"); + fail += check_execveat(fd_ephemeral_path, "", AT_EMPTY_PATH); + + /* Invalid argument failures */ + fail += check_execveat_fail(fd, "", 0, ENOENT); + fail += check_execveat_fail(fd, NULL, AT_EMPTY_PATH, EFAULT); + + /* Symlink to executable file: */ + /* dfd + path */ + fail += check_execveat(dot_dfd, "execveat.symlink", 0); + fail += check_execveat(dot_dfd_path, "execveat.symlink", 0); + /* absolute path */ + fail += check_execveat(AT_FDCWD, fullname_symlink, 0); + /* fd + no path, even with AT_SYMLINK_NOFOLLOW (already followed) */ + fail += check_execveat(fd_symlink, "", AT_EMPTY_PATH); + fail += check_execveat(fd_symlink, "", + AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW); + + /* Symlink fails when AT_SYMLINK_NOFOLLOW set: */ + /* dfd + path */ + fail += check_execveat_fail(dot_dfd, "execveat.symlink", + AT_SYMLINK_NOFOLLOW, ELOOP); + fail += check_execveat_fail(dot_dfd_path, "execveat.symlink", + AT_SYMLINK_NOFOLLOW, ELOOP); + /* absolute path */ + fail += check_execveat_fail(AT_FDCWD, fullname_symlink, + AT_SYMLINK_NOFOLLOW, ELOOP); + + /* Shell script wrapping executable file: */ + /* dfd + path */ + fail += check_execveat(subdir_dfd, "../script", 0); + fail += check_execveat(dot_dfd, "script", 0); + fail += check_execveat(dot_dfd_path, "script", 0); + /* absolute path */ + fail += check_execveat(AT_FDCWD, fullname_script, 0); + /* fd + no path */ + fail += check_execveat(fd_script, "", AT_EMPTY_PATH); + fail += check_execveat(fd_script, "", + AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW); + /* O_CLOEXEC fd fails for a script (as script file inaccessible) */ + fail += check_execveat_fail(fd_script_cloexec, "", AT_EMPTY_PATH, + ENOENT); + fail += check_execveat_fail(dot_dfd_cloexec, "script", 0, ENOENT); + + /* Mess with script file that's already open: */ + /* fd + no path to a file that's been renamed */ + rename("script.ephemeral", "script.moved"); + fail += check_execveat(fd_script_ephemeral, "", AT_EMPTY_PATH); + /* fd + no path to a file that's been deleted */ + unlink("script.moved"); /* remove the file while fd open */ + fail += check_execveat(fd_script_ephemeral, "", AT_EMPTY_PATH); + + /* Rename a subdirectory in the path: */ + rename("subdir.ephemeral", "subdir.moved"); + fail += check_execveat(subdir_dfd_ephemeral, "../script", 0); + fail += check_execveat(subdir_dfd_ephemeral, "script", 0); + /* Remove the subdir and its contents */ + unlink("subdir.moved/script"); + unlink("subdir.moved"); + /* Shell loads via deleted subdir OK because name starts with .. */ + fail += check_execveat(subdir_dfd_ephemeral, "../script", 0); + fail += check_execveat_fail(subdir_dfd_ephemeral, "script", 0, ENOENT); + + /* Flag values other than AT_SYMLINK_NOFOLLOW => EINVAL */ + fail += check_execveat_fail(dot_dfd, "execveat", 0xFFFF, EINVAL); + /* Invalid path => ENOENT */ + fail += check_execveat_fail(dot_dfd, "no-such-file", 0, ENOENT); + fail += check_execveat_fail(dot_dfd_path, "no-such-file", 0, ENOENT); + fail += check_execveat_fail(AT_FDCWD, "no-such-file", 0, ENOENT); + /* Attempt to execute directory => EACCES */ + fail += check_execveat_fail(dot_dfd, "", AT_EMPTY_PATH, EACCES); + /* Attempt to execute non-executable => EACCES */ + fail += check_execveat_fail(dot_dfd, "Makefile", 0, EACCES); + fail += check_execveat_fail(fd_denatured, "", AT_EMPTY_PATH, EACCES); + fail += check_execveat_fail(fd_denatured_path, "", AT_EMPTY_PATH, + EACCES); + /* Attempt to execute nonsense FD => EBADF */ + fail += check_execveat_fail(99, "", AT_EMPTY_PATH, EBADF); + fail += check_execveat_fail(99, "execveat", 0, EBADF); + /* Attempt to execute relative to non-directory => ENOTDIR */ + fail += check_execveat_fail(fd, "execveat", 0, ENOTDIR); + + fail += check_execveat_pathmax(dot_dfd, "execveat", 0); + fail += check_execveat_pathmax(dot_dfd, "script", 1); + return fail; +} + +static void prerequisites(void) +{ + int fd; + const char *script = "#!/bin/sh\nexit $*\n"; + + /* Create ephemeral copies of files */ + exe_cp("execveat", "execveat.ephemeral"); + exe_cp("execveat", "execveat.path.ephemeral"); + exe_cp("script", "script.ephemeral"); + mkdir("subdir.ephemeral", 0755); + + fd = open("subdir.ephemeral/script", O_RDWR|O_CREAT|O_TRUNC, 0755); + write(fd, script, strlen(script)); + close(fd); +} + +int main(int argc, char **argv) +{ + int ii; + int rc; + const char *verbose = getenv("VERBOSE"); + + if (argc >= 2) { + /* If we are invoked with an argument, don't run tests. */ + const char *in_test = getenv("IN_TEST"); + + if (verbose) { + printf(" invoked with:"); + for (ii = 0; ii < argc; ii++) + printf(" [%d]='%s'", ii, argv[ii]); + printf("\n"); + } + + /* Check expected environment transferred. */ + if (!in_test || strcmp(in_test, "yes") != 0) { + printf("[FAIL] (no IN_TEST=yes in env)\n"); + return 1; + } + + /* Use the final argument as an exit code. */ + rc = atoi(argv[argc - 1]); + fflush(stdout); + } else { + prerequisites(); + if (verbose) + envp[1] = "VERBOSE=1"; + rc = run_tests(); + if (rc > 0) + printf("%d tests failed\n", rc); + } + return rc; +} diff --git a/tools/testing/selftests/ftrace/ftracetest b/tools/testing/selftests/ftrace/ftracetest index 515247601df4..da48812ab95e 100755 --- a/tools/testing/selftests/ftrace/ftracetest +++ b/tools/testing/selftests/ftrace/ftracetest @@ -13,6 +13,7 @@ echo "Usage: ftracetest [options] [testcase(s)] [testcase-directory(s)]" echo " Options:" echo " -h|--help Show help message" echo " -k|--keep Keep passed test logs" +echo " -v|--verbose Show all stdout messages in testcases" echo " -d|--debug Debug mode (trace all shell commands)" exit $1 } @@ -37,7 +38,7 @@ abspath() { } find_testcases() { #directory - echo `find $1 -name \*.tc` + echo `find $1 -name \*.tc | sort` } parse_opts() { # opts @@ -53,6 +54,10 @@ parse_opts() { # opts KEEP_LOG=1 shift 1 ;; + --verbose|-v) + VERBOSE=1 + shift 1 + ;; --debug|-d) DEBUG=1 shift 1 @@ -90,6 +95,7 @@ TEST_CASES=`find_testcases $TEST_DIR` LOG_DIR=$TOP_DIR/logs/`date +%Y%m%d-%H%M%S`/ KEEP_LOG=0 DEBUG=0 +VERBOSE=0 # Parse command-line options parse_opts $* @@ -135,15 +141,12 @@ TOTAL_RESULT=0 CASENO=0 testcase() { # testfile CASENO=$((CASENO+1)) - prlog -n "[$CASENO]"`grep "^#[ \t]*description:" $1 | cut -f2 -d:` + desc=`grep "^#[ \t]*description:" $1 | cut -f2 -d:` + prlog -n "[$CASENO]$desc" } -eval_result() { # retval sigval - local retval=$2 - if [ $2 -eq 0 ]; then - test $1 -ne 0 && retval=$FAIL - fi - case $retval in +eval_result() { # sigval + case $1 in $PASS) prlog " [PASS]" PASSED_CASES="$PASSED_CASES $CASENO" @@ -187,6 +190,9 @@ SIG_RESULT= SIG_BASE=36 # Use realtime signals SIG_PID=$$ +SIG_FAIL=$((SIG_BASE + FAIL)) +trap 'SIG_RESULT=$FAIL' $SIG_FAIL + SIG_UNRESOLVED=$((SIG_BASE + UNRESOLVED)) exit_unresolved () { kill -s $SIG_UNRESOLVED $SIG_PID @@ -215,17 +221,25 @@ exit_xfail () { } trap 'SIG_RESULT=$XFAIL' $SIG_XFAIL +__run_test() { # testfile + # setup PID and PPID, $$ is not updated. + (cd $TRACING_DIR; read PID _ < /proc/self/stat ; set -e; set -x; . $1) + [ $? -ne 0 ] && kill -s $SIG_FAIL $SIG_PID +} + # Run one test case run_test() { # testfile local testname=`basename $1` - local testlog=`mktemp --tmpdir=$LOG_DIR ${testname}-XXXXXX.log` + local testlog=`mktemp $LOG_DIR/${testname}-log.XXXXXX` testcase $1 echo "execute: "$1 > $testlog SIG_RESULT=0 - # setup PID and PPID, $$ is not updated. - (cd $TRACING_DIR; read PID _ < /proc/self/stat ; - set -e; set -x; . $1) >> $testlog 2>&1 - eval_result $? $SIG_RESULT + if [ $VERBOSE -ne 0 ]; then + __run_test $1 2>> $testlog | tee -a $testlog + else + __run_test $1 >> $testlog 2>&1 + fi + eval_result $SIG_RESULT if [ $? -eq 0 ]; then # Remove test log if the test was done as it was expected. [ $KEEP_LOG -eq 0 ] && rm $testlog @@ -235,6 +249,9 @@ run_test() { # testfile fi } +# load in the helper functions +. $TEST_DIR/functions + # Main loop for t in $TEST_CASES; do run_test $t diff --git a/tools/testing/selftests/ftrace/test.d/00basic/basic4.tc b/tools/testing/selftests/ftrace/test.d/00basic/basic4.tc new file mode 100644 index 000000000000..fd9c49a13612 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/00basic/basic4.tc @@ -0,0 +1,5 @@ +#!/bin/sh +# description: Basic event tracing check +test -f available_events -a -f set_event -a -d events +# check scheduler events are available +grep -q sched available_events && exit 0 || exit -1
\ No newline at end of file diff --git a/tools/testing/selftests/ftrace/test.d/event/event-enable.tc b/tools/testing/selftests/ftrace/test.d/event/event-enable.tc new file mode 100644 index 000000000000..668616d9bb03 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/event/event-enable.tc @@ -0,0 +1,53 @@ +#!/bin/sh +# description: event tracing - enable/disable with event level files + +do_reset() { + echo > set_event + clear_trace +} + +fail() { #msg + do_reset + echo $1 + exit -1 +} + +if [ ! -f set_event -o ! -d events/sched ]; then + echo "event tracing is not supported" + exit_unsupported +fi + +reset_tracer +do_reset + +echo 'sched:sched_switch' > set_event +usleep 1 + +count=`cat trace | grep sched_switch | wc -l` +if [ $count -eq 0 ]; then + fail "sched_switch events are not recorded" +fi + +do_reset + +echo 1 > events/sched/sched_switch/enable +usleep 1 + +count=`cat trace | grep sched_switch | wc -l` +if [ $count -eq 0 ]; then + fail "sched_switch events are not recorded" +fi + +do_reset + +echo 0 > events/sched/sched_switch/enable +usleep 1 + +count=`cat trace | grep sched_switch | wc -l` +if [ $count -ne 0 ]; then + fail "sched_switch events should not be recorded" +fi + +do_reset + +exit 0 diff --git a/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc b/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc new file mode 100644 index 000000000000..655c415b6e7f --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc @@ -0,0 +1,53 @@ +#!/bin/sh +# description: event tracing - enable/disable with subsystem level files + +do_reset() { + echo > set_event + clear_trace +} + +fail() { #msg + do_reset + echo $1 + exit -1 +} + +if [ ! -f set_event -o ! -d events/sched ]; then + echo "event tracing is not supported" + exit_unsupported +fi + +reset_tracer +do_reset + +echo 'sched:*' > set_event +usleep 1 + +count=`cat trace | grep -v ^# | awk '{ print $5 }' | sort -u | wc -l` +if [ $count -lt 3 ]; then + fail "at least fork, exec and exit events should be recorded" +fi + +do_reset + +echo 1 > events/sched/enable +usleep 1 + +count=`cat trace | grep -v ^# | awk '{ print $5 }' | sort -u | wc -l` +if [ $count -lt 3 ]; then + fail "at least fork, exec and exit events should be recorded" +fi + +do_reset + +echo 0 > events/sched/enable +usleep 1 + +count=`cat trace | grep -v ^# | awk '{ print $5 }' | sort -u | wc -l` +if [ $count -ne 0 ]; then + fail "any of scheduler events should not be recorded" +fi + +do_reset + +exit 0 diff --git a/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc b/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc new file mode 100644 index 000000000000..480845774007 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc @@ -0,0 +1,47 @@ +#!/bin/sh +# description: event tracing - enable/disable with top level files + +do_reset() { + echo > set_event + clear_trace +} + +fail() { #msg + do_reset + echo $1 + exit -1 +} + +if [ ! -f available_events -o ! -f set_event -o ! -d events ]; then + echo "event tracing is not supported" + exit_unsupported +fi + +reset_tracer +do_reset + +echo '*:*' > set_event +count=`cat trace | grep -v ^# | wc -l` +if [ $count -eq 0 ]; then + fail "none of events are recorded" +fi + +do_reset + +echo 1 > events/enable +count=`cat trace | grep -v ^# | wc -l` +if [ $count -eq 0 ]; then + fail "none of events are recorded" +fi + +do_reset + +echo 0 > events/enable +count=`cat trace | grep -v ^# | wc -l` +if [ $count -ne 0 ]; then + fail "any of events should not be recorded" +fi + +do_reset + +exit 0 diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter-stack.tc b/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter-stack.tc new file mode 100644 index 000000000000..c15e018e0220 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter-stack.tc @@ -0,0 +1,89 @@ +#!/bin/sh +# description: ftrace - function graph filters with stack tracer + +# Make sure that function graph filtering works, and is not +# affected by other tracers enabled (like stack tracer) + +if ! grep -q function_graph available_tracers; then + echo "no function graph tracer configured" + exit_unsupported +fi + +if [ ! -f set_ftrace_filter ]; then + echo "set_ftrace_filter not found? Is dynamic ftrace not set?" + exit_unsupported +fi + +do_reset() { + reset_tracer + echo 0 > /proc/sys/kernel/stack_tracer_enabled + enable_tracing + clear_trace + echo > set_ftrace_filter +} + +fail() { # msg + do_reset + echo $1 + exit -1 +} + +disable_tracing +clear_trace; + +# filter something, schedule is always good +if ! echo "schedule" > set_ftrace_filter; then + # test for powerpc 64 + if ! echo ".schedule" > set_ftrace_filter; then + fail "can not enable schedule filter" + fi +fi + +echo function_graph > current_tracer + +if [ ! -f stack_trace ]; then + echo "Stack tracer not configured" + do_reset + exit_unsupported; +fi + +echo "Now testing with stack tracer" + +echo 1 > /proc/sys/kernel/stack_tracer_enabled + +disable_tracing +clear_trace +enable_tracing +sleep 1 + +count=`cat trace | grep '()' | grep -v schedule | wc -l` + +if [ $count -ne 0 ]; then + fail "Graph filtering not working with stack tracer?" +fi + +# Make sure we did find something +count=`cat trace | grep 'schedule()' | wc -l` +if [ $count -eq 0 ]; then + fail "No schedule traces found?" +fi + +echo 0 > /proc/sys/kernel/stack_tracer_enabled +clear_trace +sleep 1 + + +count=`cat trace | grep '()' | grep -v schedule | wc -l` + +if [ $count -ne 0 ]; then + fail "Graph filtering not working after stack tracer disabled?" +fi + +count=`cat trace | grep 'schedule()' | wc -l` +if [ $count -eq 0 ]; then + fail "No schedule traces found?" +fi + +do_reset + +exit 0 diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter.tc b/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter.tc new file mode 100644 index 000000000000..6af5f6360b18 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter.tc @@ -0,0 +1,52 @@ +#!/bin/sh +# description: ftrace - function graph filters + +# Make sure that function graph filtering works + +if ! grep -q function_graph available_tracers; then + echo "no function graph tracer configured" + exit_unsupported +fi + +do_reset() { + reset_tracer + enable_tracing + clear_trace +} + +fail() { # msg + do_reset + echo $1 + exit -1 +} + +disable_tracing +clear_trace + +# filter something, schedule is always good +if ! echo "schedule" > set_ftrace_filter; then + # test for powerpc 64 + if ! echo ".schedule" > set_ftrace_filter; then + fail "can not enable schedule filter" + fi +fi + +echo function_graph > current_tracer +enable_tracing +sleep 1 +# search for functions (has "()" on the line), and make sure +# that only the schedule function was found +count=`cat trace | grep '()' | grep -v schedule | wc -l` +if [ $count -ne 0 ]; then + fail "Graph filtering not working by itself?" +fi + +# Make sure we did find something +count=`cat trace | grep 'schedule()' | wc -l` +if [ $count -eq 0 ]; then + fail "No schedule traces found?" +fi + +do_reset + +exit 0 diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func_profiler.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func_profiler.tc new file mode 100644 index 000000000000..2e719cb1fc4d --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func_profiler.tc @@ -0,0 +1,80 @@ +#!/bin/sh +# description: ftrace - function profiler with function tracing + +# There was a bug after a rewrite of the ftrace infrastructure that +# caused the function_profiler not to be able to run with the function +# tracer, because the function_profiler used the function_graph tracer +# and it was assumed the two could not run simultaneously. +# +# There was another related bug where the solution to the first bug +# broke the way filtering of the function tracer worked. +# +# This test triggers those bugs on those kernels. +# +# We need function_graph and profiling to to run this test +if ! grep -q function_graph available_tracers; then + echo "no function graph tracer configured" + exit_unsupported; +fi + +if [ ! -f set_ftrace_filter ]; then + echo "set_ftrace_filter not found? Is dynamic ftrace not set?" + exit_unsupported +fi + +if [ ! -f function_profile_enabled ]; then + echo "function_profile_enabled not found, function profiling enabled?" + exit_unsupported +fi + +fail() { # mesg + reset_tracer + echo > set_ftrace_filter + echo $1 + exit -1 +} + +echo "Testing function tracer with profiler:" +echo "enable function tracer" +echo function > current_tracer +echo "enable profiler" +echo 1 > function_profile_enabled + +sleep 1 + +echo "Now filter on just schedule" +echo '*schedule' > set_ftrace_filter +clear_trace + +echo "Now disable function profiler" +echo 0 > function_profile_enabled + +sleep 1 + +# make sure only schedule functions exist + +echo "testing if only schedule is being traced" +if grep -v -e '^#' -e 'schedule' trace; then + fail "more than schedule was found" +fi + +echo "Make sure schedule was traced" +if ! grep -e 'schedule' trace > /dev/null; then + cat trace + fail "can not find schedule in trace" +fi + +echo > set_ftrace_filter +clear_trace + +sleep 1 + +echo "make sure something other than scheduler is being traced" +if ! grep -v -e '^#' -e 'schedule' trace > /dev/null; then + cat trace + fail "no other functions besides schedule was found" +fi + +reset_tracer + +exit 0 diff --git a/tools/testing/selftests/ftrace/test.d/functions b/tools/testing/selftests/ftrace/test.d/functions new file mode 100644 index 000000000000..5d8cd06d920f --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/functions @@ -0,0 +1,16 @@ + +clear_trace() { # reset trace output + echo > trace +} + +disable_tracing() { # stop trace recording + echo 0 > tracing_on +} + +enable_tracing() { # start trace recording + echo 1 > tracing_on +} + +reset_tracer() { # reset the current tracer + echo nop > current_tracer +} diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/add_and_remove.tc b/tools/testing/selftests/ftrace/test.d/kprobe/add_and_remove.tc index 1b8b665ab2b3..a5a426211129 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/add_and_remove.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/add_and_remove.tc @@ -9,3 +9,4 @@ echo p:myevent do_fork > kprobe_events grep myevent kprobe_events test -d events/kprobes/myevent echo > kprobe_events +clear_trace diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc b/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc index b55c84003587..d8c7bb6581fe 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc @@ -11,3 +11,4 @@ echo 1 > events/kprobes/myevent/enable echo > kprobe_events && exit 1 # this must fail echo 0 > events/kprobes/myevent/enable echo > kprobe_events # this must succeed +clear_trace diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc index a603d3f8db7b..c45ee2761354 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc @@ -12,5 +12,6 @@ echo 1 > events/kprobes/testprobe/enable ( echo "forked") echo 0 > events/kprobes/testprobe/enable echo "-:testprobe" >> kprobe_events +clear_trace test -d events/kprobes/testprobe && exit 1 || exit 0 diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_ftrace.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_ftrace.tc new file mode 100644 index 000000000000..ab41d2b29841 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_ftrace.tc @@ -0,0 +1,55 @@ +#!/bin/sh +# description: Kprobe dynamic event with function tracer + +[ -f kprobe_events ] || exit_unsupported # this is configurable +grep function available_tracers || exit_unsupported # this is configurable + +# prepare +echo nop > current_tracer +echo do_fork > set_ftrace_filter +echo 0 > events/enable +echo > kprobe_events +echo 'p:testprobe do_fork' > kprobe_events + +# kprobe on / ftrace off +echo 1 > events/kprobes/testprobe/enable +echo > trace +( echo "forked") +grep testprobe trace +! grep 'do_fork <-' trace + +# kprobe on / ftrace on +echo function > current_tracer +echo > trace +( echo "forked") +grep testprobe trace +grep 'do_fork <-' trace + +# kprobe off / ftrace on +echo 0 > events/kprobes/testprobe/enable +echo > trace +( echo "forked") +! grep testprobe trace +grep 'do_fork <-' trace + +# kprobe on / ftrace on +echo 1 > events/kprobes/testprobe/enable +echo function > current_tracer +echo > trace +( echo "forked") +grep testprobe trace +grep 'do_fork <-' trace + +# kprobe on / ftrace off +echo nop > current_tracer +echo > trace +( echo "forked") +grep testprobe trace +! grep 'do_fork <-' trace + +# cleanup +echo nop > current_tracer +echo > set_ftrace_filter +echo 0 > events/kprobes/testprobe/enable +echo > kprobe_events +echo > trace diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc index 283c29e7f7c4..31717985acc7 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc @@ -12,4 +12,5 @@ echo 1 > events/kprobes/testprobe2/enable ( echo "forked") echo 0 > events/kprobes/testprobe2/enable echo '-:testprobe2' >> kprobe_events +clear_trace test -d events/kprobes/testprobe2 && exit 1 || exit 0 diff --git a/tools/testing/selftests/ipc/msgque.c b/tools/testing/selftests/ipc/msgque.c index 552f0810bffb..1b2ce334bb3f 100644 --- a/tools/testing/selftests/ipc/msgque.c +++ b/tools/testing/selftests/ipc/msgque.c @@ -5,6 +5,8 @@ #include <linux/msg.h> #include <fcntl.h> +#include "../kselftest.h" + #define MAX_MSG_SIZE 32 struct msg1 { @@ -195,58 +197,58 @@ int main(int argc, char **argv) if (getuid() != 0) { printf("Please run the test as root - Exiting.\n"); - exit(1); + return ksft_exit_fail(); } msgque.key = ftok(argv[0], 822155650); if (msgque.key == -1) { - printf("Can't make key\n"); - return -errno; + printf("Can't make key: %d\n", -errno); + return ksft_exit_fail(); } msgque.msq_id = msgget(msgque.key, IPC_CREAT | IPC_EXCL | 0666); if (msgque.msq_id == -1) { err = -errno; - printf("Can't create queue\n"); + printf("Can't create queue: %d\n", err); goto err_out; } err = fill_msgque(&msgque); if (err) { - printf("Failed to fill queue\n"); + printf("Failed to fill queue: %d\n", err); goto err_destroy; } err = dump_queue(&msgque); if (err) { - printf("Failed to dump queue\n"); + printf("Failed to dump queue: %d\n", err); goto err_destroy; } err = check_and_destroy_queue(&msgque); if (err) { - printf("Failed to check and destroy queue\n"); + printf("Failed to check and destroy queue: %d\n", err); goto err_out; } err = restore_queue(&msgque); if (err) { - printf("Failed to restore queue\n"); + printf("Failed to restore queue: %d\n", err); goto err_destroy; } err = check_and_destroy_queue(&msgque); if (err) { - printf("Failed to test queue\n"); + printf("Failed to test queue: %d\n", err); goto err_out; } - return 0; + return ksft_exit_pass(); err_destroy: if (msgctl(msgque.msq_id, IPC_RMID, 0)) { printf("Failed to destroy queue: %d\n", -errno); - return -errno; + return ksft_exit_fail(); } err_out: - return err; + return ksft_exit_fail(); } diff --git a/tools/testing/selftests/kcmp/Makefile b/tools/testing/selftests/kcmp/Makefile index 8aabd82db9e4..ff0eefdc6ceb 100644 --- a/tools/testing/selftests/kcmp/Makefile +++ b/tools/testing/selftests/kcmp/Makefile @@ -1,25 +1,7 @@ -uname_M := $(shell uname -m 2>/dev/null || echo not) -ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/) -ifeq ($(ARCH),i386) - ARCH := x86 - CFLAGS := -DCONFIG_X86_32 -D__i386__ -endif -ifeq ($(ARCH),x86_64) - ARCH := x86 - CFLAGS := -DCONFIG_X86_64 -D__x86_64__ -endif - -CFLAGS += -I../../../../arch/x86/include/generated/ -CFLAGS += -I../../../../include/ +CC := $(CROSS_COMPILE)$(CC) CFLAGS += -I../../../../usr/include/ -CFLAGS += -I../../../../arch/x86/include/ -all: -ifeq ($(ARCH),x86) - gcc $(CFLAGS) kcmp_test.c -o kcmp_test -else - echo "Not an x86 target, can't build kcmp selftest" -endif +all: kcmp_test run_tests: all @./kcmp_test || echo "kcmp_test: [FAIL]" diff --git a/tools/testing/selftests/kcmp/kcmp_test.c b/tools/testing/selftests/kcmp/kcmp_test.c index dbba4084869c..a5a4da856dfe 100644 --- a/tools/testing/selftests/kcmp/kcmp_test.c +++ b/tools/testing/selftests/kcmp/kcmp_test.c @@ -17,6 +17,8 @@ #include <sys/stat.h> #include <sys/wait.h> +#include "../kselftest.h" + static long sys_kcmp(int pid1, int pid2, int type, int fd1, int fd2) { return syscall(__NR_kcmp, pid1, pid2, type, fd1, fd2); @@ -34,13 +36,13 @@ int main(int argc, char **argv) if (fd1 < 0) { perror("Can't create file"); - exit(1); + ksft_exit_fail(); } pid2 = fork(); if (pid2 < 0) { perror("fork failed"); - exit(1); + ksft_exit_fail(); } if (!pid2) { @@ -50,7 +52,7 @@ int main(int argc, char **argv) fd2 = open(kpath, O_RDWR, 0644); if (fd2 < 0) { perror("Can't open file"); - exit(1); + ksft_exit_fail(); } /* An example of output and arguments */ @@ -74,23 +76,34 @@ int main(int argc, char **argv) if (ret) { printf("FAIL: 0 expected but %d returned (%s)\n", ret, strerror(errno)); + ksft_inc_fail_cnt(); ret = -1; - } else + } else { printf("PASS: 0 returned as expected\n"); + ksft_inc_pass_cnt(); + } /* Compare with self */ ret = sys_kcmp(pid1, pid1, KCMP_VM, 0, 0); if (ret) { printf("FAIL: 0 expected but %d returned (%s)\n", ret, strerror(errno)); + ksft_inc_fail_cnt(); ret = -1; - } else + } else { printf("PASS: 0 returned as expected\n"); + ksft_inc_pass_cnt(); + } + + ksft_print_cnts(); - exit(ret); + if (ret) + ksft_exit_fail(); + else + ksft_exit_pass(); } waitpid(pid2, &status, P_ALL); - return 0; + return ksft_exit_pass(); } diff --git a/tools/testing/selftests/kselftest.h b/tools/testing/selftests/kselftest.h new file mode 100644 index 000000000000..572c8888167a --- /dev/null +++ b/tools/testing/selftests/kselftest.h @@ -0,0 +1,62 @@ +/* + * kselftest.h: kselftest framework return codes to include from + * selftests. + * + * Copyright (c) 2014 Shuah Khan <shuahkh@osg.samsung.com> + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * This file is released under the GPLv2. + */ +#ifndef __KSELFTEST_H +#define __KSELFTEST_H + +#include <stdlib.h> +#include <unistd.h> + +/* counters */ +struct ksft_count { + unsigned int ksft_pass; + unsigned int ksft_fail; + unsigned int ksft_xfail; + unsigned int ksft_xpass; + unsigned int ksft_xskip; +}; + +static struct ksft_count ksft_cnt; + +static inline void ksft_inc_pass_cnt(void) { ksft_cnt.ksft_pass++; } +static inline void ksft_inc_fail_cnt(void) { ksft_cnt.ksft_fail++; } +static inline void ksft_inc_xfail_cnt(void) { ksft_cnt.ksft_xfail++; } +static inline void ksft_inc_xpass_cnt(void) { ksft_cnt.ksft_xpass++; } +static inline void ksft_inc_xskip_cnt(void) { ksft_cnt.ksft_xskip++; } + +static inline void ksft_print_cnts(void) +{ + printf("Pass: %d Fail: %d Xfail: %d Xpass: %d, Xskip: %d\n", + ksft_cnt.ksft_pass, ksft_cnt.ksft_fail, + ksft_cnt.ksft_xfail, ksft_cnt.ksft_xpass, + ksft_cnt.ksft_xskip); +} + +static inline int ksft_exit_pass(void) +{ + exit(0); +} +static inline int ksft_exit_fail(void) +{ + exit(1); +} +static inline int ksft_exit_xfail(void) +{ + exit(2); +} +static inline int ksft_exit_xpass(void) +{ + exit(3); +} +static inline int ksft_exit_skip(void) +{ + exit(4); +} + +#endif /* __KSELFTEST_H */ diff --git a/tools/testing/selftests/mount/unprivileged-remount-test.c b/tools/testing/selftests/mount/unprivileged-remount-test.c index 1b3ff2fda4d0..517785052f1c 100644 --- a/tools/testing/selftests/mount/unprivileged-remount-test.c +++ b/tools/testing/selftests/mount/unprivileged-remount-test.c @@ -6,6 +6,8 @@ #include <sys/types.h> #include <sys/mount.h> #include <sys/wait.h> +#include <sys/vfs.h> +#include <sys/statvfs.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> @@ -32,11 +34,14 @@ # define CLONE_NEWPID 0x20000000 #endif +#ifndef MS_REC +# define MS_REC 16384 +#endif #ifndef MS_RELATIME -#define MS_RELATIME (1 << 21) +# define MS_RELATIME (1 << 21) #endif #ifndef MS_STRICTATIME -#define MS_STRICTATIME (1 << 24) +# define MS_STRICTATIME (1 << 24) #endif static void die(char *fmt, ...) @@ -48,17 +53,14 @@ static void die(char *fmt, ...) exit(EXIT_FAILURE); } -static void write_file(char *filename, char *fmt, ...) +static void vmaybe_write_file(bool enoent_ok, char *filename, char *fmt, va_list ap) { char buf[4096]; int fd; ssize_t written; int buf_len; - va_list ap; - va_start(ap, fmt); buf_len = vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); if (buf_len < 0) { die("vsnprintf failed: %s\n", strerror(errno)); @@ -69,6 +71,8 @@ static void write_file(char *filename, char *fmt, ...) fd = open(filename, O_WRONLY); if (fd < 0) { + if ((errno == ENOENT) && enoent_ok) + return; die("open of %s failed: %s\n", filename, strerror(errno)); } @@ -87,6 +91,65 @@ static void write_file(char *filename, char *fmt, ...) } } +static void maybe_write_file(char *filename, char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vmaybe_write_file(true, filename, fmt, ap); + va_end(ap); + +} + +static void write_file(char *filename, char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vmaybe_write_file(false, filename, fmt, ap); + va_end(ap); + +} + +static int read_mnt_flags(const char *path) +{ + int ret; + struct statvfs stat; + int mnt_flags; + + ret = statvfs(path, &stat); + if (ret != 0) { + die("statvfs of %s failed: %s\n", + path, strerror(errno)); + } + if (stat.f_flag & ~(ST_RDONLY | ST_NOSUID | ST_NODEV | \ + ST_NOEXEC | ST_NOATIME | ST_NODIRATIME | ST_RELATIME | \ + ST_SYNCHRONOUS | ST_MANDLOCK)) { + die("Unrecognized mount flags\n"); + } + mnt_flags = 0; + if (stat.f_flag & ST_RDONLY) + mnt_flags |= MS_RDONLY; + if (stat.f_flag & ST_NOSUID) + mnt_flags |= MS_NOSUID; + if (stat.f_flag & ST_NODEV) + mnt_flags |= MS_NODEV; + if (stat.f_flag & ST_NOEXEC) + mnt_flags |= MS_NOEXEC; + if (stat.f_flag & ST_NOATIME) + mnt_flags |= MS_NOATIME; + if (stat.f_flag & ST_NODIRATIME) + mnt_flags |= MS_NODIRATIME; + if (stat.f_flag & ST_RELATIME) + mnt_flags |= MS_RELATIME; + if (stat.f_flag & ST_SYNCHRONOUS) + mnt_flags |= MS_SYNCHRONOUS; + if (stat.f_flag & ST_MANDLOCK) + mnt_flags |= ST_MANDLOCK; + + return mnt_flags; +} + static void create_and_enter_userns(void) { uid_t uid; @@ -100,13 +163,10 @@ static void create_and_enter_userns(void) strerror(errno)); } + maybe_write_file("/proc/self/setgroups", "deny"); write_file("/proc/self/uid_map", "0 %d 1", uid); write_file("/proc/self/gid_map", "0 %d 1", gid); - if (setgroups(0, NULL) != 0) { - die("setgroups failed: %s\n", - strerror(errno)); - } if (setgid(0) != 0) { die ("setgid(0) failed %s\n", strerror(errno)); @@ -118,7 +178,8 @@ static void create_and_enter_userns(void) } static -bool test_unpriv_remount(int mount_flags, int remount_flags, int invalid_flags) +bool test_unpriv_remount(const char *fstype, const char *mount_options, + int mount_flags, int remount_flags, int invalid_flags) { pid_t child; @@ -151,9 +212,11 @@ bool test_unpriv_remount(int mount_flags, int remount_flags, int invalid_flags) strerror(errno)); } - if (mount("testing", "/tmp", "ramfs", mount_flags, NULL) != 0) { - die("mount of /tmp failed: %s\n", - strerror(errno)); + if (mount("testing", "/tmp", fstype, mount_flags, mount_options) != 0) { + die("mount of %s with options '%s' on /tmp failed: %s\n", + fstype, + mount_options? mount_options : "", + strerror(errno)); } create_and_enter_userns(); @@ -181,62 +244,127 @@ bool test_unpriv_remount(int mount_flags, int remount_flags, int invalid_flags) static bool test_unpriv_remount_simple(int mount_flags) { - return test_unpriv_remount(mount_flags, mount_flags, 0); + return test_unpriv_remount("ramfs", NULL, mount_flags, mount_flags, 0); } static bool test_unpriv_remount_atime(int mount_flags, int invalid_flags) { - return test_unpriv_remount(mount_flags, mount_flags, invalid_flags); + return test_unpriv_remount("ramfs", NULL, mount_flags, mount_flags, + invalid_flags); +} + +static bool test_priv_mount_unpriv_remount(void) +{ + pid_t child; + int ret; + const char *orig_path = "/dev"; + const char *dest_path = "/tmp"; + int orig_mnt_flags, remount_mnt_flags; + + child = fork(); + if (child == -1) { + die("fork failed: %s\n", + strerror(errno)); + } + if (child != 0) { /* parent */ + pid_t pid; + int status; + pid = waitpid(child, &status, 0); + if (pid == -1) { + die("waitpid failed: %s\n", + strerror(errno)); + } + if (pid != child) { + die("waited for %d got %d\n", + child, pid); + } + if (!WIFEXITED(status)) { + die("child did not terminate cleanly\n"); + } + return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false; + } + + orig_mnt_flags = read_mnt_flags(orig_path); + + create_and_enter_userns(); + ret = unshare(CLONE_NEWNS); + if (ret != 0) { + die("unshare(CLONE_NEWNS) failed: %s\n", + strerror(errno)); + } + + ret = mount(orig_path, dest_path, "bind", MS_BIND | MS_REC, NULL); + if (ret != 0) { + die("recursive bind mount of %s onto %s failed: %s\n", + orig_path, dest_path, strerror(errno)); + } + + ret = mount(dest_path, dest_path, "none", + MS_REMOUNT | MS_BIND | orig_mnt_flags , NULL); + if (ret != 0) { + /* system("cat /proc/self/mounts"); */ + die("remount of /tmp failed: %s\n", + strerror(errno)); + } + + remount_mnt_flags = read_mnt_flags(dest_path); + if (orig_mnt_flags != remount_mnt_flags) { + die("Mount flags unexpectedly changed during remount of %s originally mounted on %s\n", + dest_path, orig_path); + } + exit(EXIT_SUCCESS); } int main(int argc, char **argv) { - if (!test_unpriv_remount_simple(MS_RDONLY|MS_NODEV)) { + if (!test_unpriv_remount_simple(MS_RDONLY)) { die("MS_RDONLY malfunctions\n"); } - if (!test_unpriv_remount_simple(MS_NODEV)) { + if (!test_unpriv_remount("devpts", "newinstance", MS_NODEV, MS_NODEV, 0)) { die("MS_NODEV malfunctions\n"); } - if (!test_unpriv_remount_simple(MS_NOSUID|MS_NODEV)) { + if (!test_unpriv_remount_simple(MS_NOSUID)) { die("MS_NOSUID malfunctions\n"); } - if (!test_unpriv_remount_simple(MS_NOEXEC|MS_NODEV)) { + if (!test_unpriv_remount_simple(MS_NOEXEC)) { die("MS_NOEXEC malfunctions\n"); } - if (!test_unpriv_remount_atime(MS_RELATIME|MS_NODEV, - MS_NOATIME|MS_NODEV)) + if (!test_unpriv_remount_atime(MS_RELATIME, + MS_NOATIME)) { die("MS_RELATIME malfunctions\n"); } - if (!test_unpriv_remount_atime(MS_STRICTATIME|MS_NODEV, - MS_NOATIME|MS_NODEV)) + if (!test_unpriv_remount_atime(MS_STRICTATIME, + MS_NOATIME)) { die("MS_STRICTATIME malfunctions\n"); } - if (!test_unpriv_remount_atime(MS_NOATIME|MS_NODEV, - MS_STRICTATIME|MS_NODEV)) + if (!test_unpriv_remount_atime(MS_NOATIME, + MS_STRICTATIME)) { - die("MS_RELATIME malfunctions\n"); + die("MS_NOATIME malfunctions\n"); } - if (!test_unpriv_remount_atime(MS_RELATIME|MS_NODIRATIME|MS_NODEV, - MS_NOATIME|MS_NODEV)) + if (!test_unpriv_remount_atime(MS_RELATIME|MS_NODIRATIME, + MS_NOATIME)) { - die("MS_RELATIME malfunctions\n"); + die("MS_RELATIME|MS_NODIRATIME malfunctions\n"); } - if (!test_unpriv_remount_atime(MS_STRICTATIME|MS_NODIRATIME|MS_NODEV, - MS_NOATIME|MS_NODEV)) + if (!test_unpriv_remount_atime(MS_STRICTATIME|MS_NODIRATIME, + MS_NOATIME)) { - die("MS_RELATIME malfunctions\n"); + die("MS_STRICTATIME|MS_NODIRATIME malfunctions\n"); } - if (!test_unpriv_remount_atime(MS_NOATIME|MS_NODIRATIME|MS_NODEV, - MS_STRICTATIME|MS_NODEV)) + if (!test_unpriv_remount_atime(MS_NOATIME|MS_NODIRATIME, + MS_STRICTATIME)) { - die("MS_RELATIME malfunctions\n"); + die("MS_NOATIME|MS_DIRATIME malfunctions\n"); } - if (!test_unpriv_remount(MS_STRICTATIME|MS_NODEV, MS_NODEV, - MS_NOATIME|MS_NODEV)) + if (!test_unpriv_remount("ramfs", NULL, MS_STRICTATIME, 0, MS_NOATIME)) { die("Default atime malfunctions\n"); } + if (!test_priv_mount_unpriv_remount()) { + die("Mount flags unexpectedly changed after remount\n"); + } return EXIT_SUCCESS; } diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index c7493b8f9b0e..62f22cc9941c 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -14,12 +14,6 @@ all: $(NET_PROGS) run_tests: all @/bin/sh ./run_netsocktests || echo "sockettests: [FAIL]" @/bin/sh ./run_afpackettests || echo "afpackettests: [FAIL]" - @if /sbin/modprobe test_bpf ; then \ - /sbin/rmmod test_bpf; \ - echo "test_bpf: ok"; \ - else \ - echo "test_bpf: [FAIL]"; \ - exit 1; \ - fi + ./test_bpf.sh clean: $(RM) $(NET_PROGS) diff --git a/tools/testing/selftests/net/test_bpf.sh b/tools/testing/selftests/net/test_bpf.sh new file mode 100755 index 000000000000..8b29796d46aa --- /dev/null +++ b/tools/testing/selftests/net/test_bpf.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# Runs bpf test using test_bpf kernel module + +if /sbin/modprobe -q test_bpf ; then + /sbin/modprobe -q -r test_bpf; + echo "test_bpf: ok"; +else + echo "test_bpf: [FAIL]"; + exit 1; +fi diff --git a/tools/testing/selftests/size/.gitignore b/tools/testing/selftests/size/.gitignore new file mode 100644 index 000000000000..189b7818de34 --- /dev/null +++ b/tools/testing/selftests/size/.gitignore @@ -0,0 +1 @@ +get_size diff --git a/tools/testing/selftests/size/Makefile b/tools/testing/selftests/size/Makefile new file mode 100644 index 000000000000..04dc25e4fa92 --- /dev/null +++ b/tools/testing/selftests/size/Makefile @@ -0,0 +1,12 @@ +CC = $(CROSS_COMPILE)gcc + +all: get_size + +get_size: get_size.c + $(CC) -static -ffreestanding -nostartfiles -s $< -o $@ + +run_tests: all + ./get_size + +clean: + $(RM) get_size diff --git a/tools/testing/selftests/size/get_size.c b/tools/testing/selftests/size/get_size.c new file mode 100644 index 000000000000..2d1af7cca463 --- /dev/null +++ b/tools/testing/selftests/size/get_size.c @@ -0,0 +1,100 @@ +/* + * Copyright 2014 Sony Mobile Communications Inc. + * + * Licensed under the terms of the GNU GPL License version 2 + * + * Selftest for runtime system size + * + * Prints the amount of RAM that the currently running system is using. + * + * This program tries to be as small as possible itself, to + * avoid perturbing the system memory utilization with its + * own execution. It also attempts to have as few dependencies + * on kernel features as possible. + * + * It should be statically linked, with startup libs avoided. + * It uses no library calls, and only the following 3 syscalls: + * sysinfo(), write(), and _exit() + * + * For output, it avoids printf (which in some C libraries + * has large external dependencies) by implementing it's own + * number output and print routines, and using __builtin_strlen() + */ + +#include <sys/sysinfo.h> +#include <unistd.h> + +#define STDOUT_FILENO 1 + +static int print(const char *s) +{ + return write(STDOUT_FILENO, s, __builtin_strlen(s)); +} + +static inline char *num_to_str(unsigned long num, char *buf, int len) +{ + unsigned int digit; + + /* put digits in buffer from back to front */ + buf += len - 1; + *buf = 0; + do { + digit = num % 10; + *(--buf) = digit + '0'; + num /= 10; + } while (num > 0); + + return buf; +} + +static int print_num(unsigned long num) +{ + char num_buf[30]; + + return print(num_to_str(num, num_buf, sizeof(num_buf))); +} + +static int print_k_value(const char *s, unsigned long num, unsigned long units) +{ + unsigned long long temp; + int ccode; + + print(s); + + temp = num; + temp = (temp * units)/1024; + num = temp; + ccode = print_num(num); + print("\n"); + return ccode; +} + +/* this program has no main(), as startup libraries are not used */ +void _start(void) +{ + int ccode; + struct sysinfo info; + unsigned long used; + + print("Testing system size.\n"); + print("1..1\n"); + + ccode = sysinfo(&info); + if (ccode < 0) { + print("not ok 1 get runtime memory use\n"); + print("# could not get sysinfo\n"); + _exit(ccode); + } + /* ignore cache complexities for now */ + used = info.totalram - info.freeram - info.bufferram; + print_k_value("ok 1 get runtime memory use # size = ", used, + info.mem_unit); + + print("# System runtime memory report (units in Kilobytes):\n"); + print_k_value("# Total: ", info.totalram, info.mem_unit); + print_k_value("# Free: ", info.freeram, info.mem_unit); + print_k_value("# Buffer: ", info.bufferram, info.mem_unit); + print_k_value("# In use: ", used, info.mem_unit); + + _exit(0); +} diff --git a/tools/testing/selftests/timers/posix_timers.c b/tools/testing/selftests/timers/posix_timers.c index 41bd85559d4b..f87d970a485c 100644 --- a/tools/testing/selftests/timers/posix_timers.c +++ b/tools/testing/selftests/timers/posix_timers.c @@ -15,6 +15,8 @@ #include <time.h> #include <pthread.h> +#include "../kselftest.h" + #define DELAY 2 #define USECS_PER_SEC 1000000 @@ -194,16 +196,16 @@ int main(int argc, char **argv) printf("based timers if other threads run on the CPU...\n"); if (check_itimer(ITIMER_VIRTUAL) < 0) - return -1; + return ksft_exit_fail(); if (check_itimer(ITIMER_PROF) < 0) - return -1; + return ksft_exit_fail(); if (check_itimer(ITIMER_REAL) < 0) - return -1; + return ksft_exit_fail(); if (check_timer_create(CLOCK_THREAD_CPUTIME_ID) < 0) - return -1; + return ksft_exit_fail(); /* * It's unfortunately hard to reliably test a timer expiration @@ -215,7 +217,7 @@ int main(int argc, char **argv) * find a better solution. */ if (check_timer_create(CLOCK_PROCESS_CPUTIME_ID) < 0) - return -1; + return ksft_exit_fail(); - return 0; + return ksft_exit_pass(); } diff --git a/tools/testing/selftests/user/Makefile b/tools/testing/selftests/user/Makefile index 396255bd720e..12c9d15bab07 100644 --- a/tools/testing/selftests/user/Makefile +++ b/tools/testing/selftests/user/Makefile @@ -4,10 +4,4 @@ all: run_tests: all - @if /sbin/modprobe test_user_copy ; then \ - rmmod test_user_copy; \ - echo "user_copy: ok"; \ - else \ - echo "user_copy: [FAIL]"; \ - exit 1; \ - fi + ./test_user_copy.sh diff --git a/tools/testing/selftests/user/test_user_copy.sh b/tools/testing/selftests/user/test_user_copy.sh new file mode 100755 index 000000000000..350107f40c1d --- /dev/null +++ b/tools/testing/selftests/user/test_user_copy.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# Runs copy_to/from_user infrastructure using test_user_copy kernel module + +if /sbin/modprobe -q test_user_copy; then + /sbin/modprobe -q -r test_user_copy + echo "user_copy: ok" +else + echo "user_copy: [FAIL]" + exit 1 +fi diff --git a/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c b/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c index af4b0508be77..aaca1f44e788 100644 --- a/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c +++ b/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c @@ -342,7 +342,7 @@ int main(int argc, char *argv[]) iobuf[i].requested = ret; printf("submit: %d requests buf: %d\n", ret, i); } else - perror("unable to submit reqests"); + perror("unable to submit requests"); } /* if event is ready to read */ diff --git a/tools/usb/usbip/libsrc/list.h b/tools/usb/usbip/libsrc/list.h index 8d0c936e184f..5eaaa78e2c6a 100644 --- a/tools/usb/usbip/libsrc/list.h +++ b/tools/usb/usbip/libsrc/list.h @@ -98,7 +98,7 @@ static inline void list_del(struct list_head *entry) * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. - * @member: the name of the list_struct within the struct. + * @member: the name of the list_head within the struct. */ #define list_entry(ptr, type, member) \ container_of(ptr, type, member) diff --git a/tools/usb/usbip/src/usbipd.c b/tools/usb/usbip/src/usbipd.c index 2f87f2d348ba..2a7cd2b8d966 100644 --- a/tools/usb/usbip/src/usbipd.c +++ b/tools/usb/usbip/src/usbipd.c @@ -91,7 +91,6 @@ static void usbipd_help(void) static int recv_request_import(int sockfd) { struct op_import_request req; - struct op_common reply; struct usbip_exported_device *edev; struct usbip_usb_device pdu_udev; struct list_head *i; @@ -100,7 +99,6 @@ static int recv_request_import(int sockfd) int rc; memset(&req, 0, sizeof(req)); - memset(&reply, 0, sizeof(reply)); rc = usbip_net_recv(sockfd, &req, sizeof(req)); if (rc < 0) { diff --git a/tools/virtio/Makefile b/tools/virtio/Makefile index 9325f4693821..505ad51b3b51 100644 --- a/tools/virtio/Makefile +++ b/tools/virtio/Makefile @@ -3,7 +3,7 @@ test: virtio_test vringh_test virtio_test: virtio_ring.o virtio_test.o vringh_test: vringh_test.o vringh.o virtio_ring.o -CFLAGS += -g -O2 -Wall -I. -I../include/ -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE +CFLAGS += -g -O2 -Werror -Wall -I. -I../include/ -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE vpath %.c ../../drivers/virtio ../../drivers/vhost mod: ${MAKE} -C `pwd`/../.. M=`pwd`/vhost_test diff --git a/tools/virtio/linux/virtio.h b/tools/virtio/linux/virtio.h index 5a2d1f0f6bc7..a3e07016a440 100644 --- a/tools/virtio/linux/virtio.h +++ b/tools/virtio/linux/virtio.h @@ -6,31 +6,12 @@ /* TODO: empty stubs for now. Broken but enough for virtio_ring.c */ #define list_add_tail(a, b) do {} while (0) #define list_del(a) do {} while (0) - -#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) -#define BITS_PER_BYTE 8 -#define BITS_PER_LONG (sizeof(long) * BITS_PER_BYTE) -#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) - -/* TODO: Not atomic as it should be: - * we don't use this for anything important. */ -static inline void clear_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = BIT_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); - - *p &= ~mask; -} - -static inline int test_bit(int nr, const volatile unsigned long *addr) -{ - return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); -} +#define list_for_each_entry(a, b, c) while (0) /* end of stubs */ struct virtio_device { void *dev; - unsigned long features[1]; + u64 features; }; struct virtqueue { diff --git a/tools/virtio/linux/virtio_byteorder.h b/tools/virtio/linux/virtio_byteorder.h new file mode 100644 index 000000000000..9de9e6ac1d10 --- /dev/null +++ b/tools/virtio/linux/virtio_byteorder.h @@ -0,0 +1,8 @@ +#ifndef _LINUX_VIRTIO_BYTEORDER_STUB_H +#define _LINUX_VIRTIO_BYTEORDER_STUB_H + +#include <asm/byteorder.h> +#include "../../include/linux/byteorder/generic.h" +#include "../../include/linux/virtio_byteorder.h" + +#endif diff --git a/tools/virtio/linux/virtio_config.h b/tools/virtio/linux/virtio_config.h index 5049967f99f7..806d683ab107 100644 --- a/tools/virtio/linux/virtio_config.h +++ b/tools/virtio/linux/virtio_config.h @@ -1,6 +1,72 @@ -#define VIRTIO_TRANSPORT_F_START 28 -#define VIRTIO_TRANSPORT_F_END 32 +#include <linux/virtio_byteorder.h> +#include <linux/virtio.h> +#include <uapi/linux/virtio_config.h> + +/* + * __virtio_test_bit - helper to test feature bits. For use by transports. + * Devices should normally use virtio_has_feature, + * which includes more checks. + * @vdev: the device + * @fbit: the feature bit + */ +static inline bool __virtio_test_bit(const struct virtio_device *vdev, + unsigned int fbit) +{ + return vdev->features & (1ULL << fbit); +} + +/** + * __virtio_set_bit - helper to set feature bits. For use by transports. + * @vdev: the device + * @fbit: the feature bit + */ +static inline void __virtio_set_bit(struct virtio_device *vdev, + unsigned int fbit) +{ + vdev->features |= (1ULL << fbit); +} + +/** + * __virtio_clear_bit - helper to clear feature bits. For use by transports. + * @vdev: the device + * @fbit: the feature bit + */ +static inline void __virtio_clear_bit(struct virtio_device *vdev, + unsigned int fbit) +{ + vdev->features &= ~(1ULL << fbit); +} #define virtio_has_feature(dev, feature) \ - test_bit((feature), (dev)->features) + (__virtio_test_bit((dev), feature)) + +static inline u16 virtio16_to_cpu(struct virtio_device *vdev, __virtio16 val) +{ + return __virtio16_to_cpu(virtio_has_feature(vdev, VIRTIO_F_VERSION_1), val); +} + +static inline __virtio16 cpu_to_virtio16(struct virtio_device *vdev, u16 val) +{ + return __cpu_to_virtio16(virtio_has_feature(vdev, VIRTIO_F_VERSION_1), val); +} + +static inline u32 virtio32_to_cpu(struct virtio_device *vdev, __virtio32 val) +{ + return __virtio32_to_cpu(virtio_has_feature(vdev, VIRTIO_F_VERSION_1), val); +} + +static inline __virtio32 cpu_to_virtio32(struct virtio_device *vdev, u32 val) +{ + return __cpu_to_virtio32(virtio_has_feature(vdev, VIRTIO_F_VERSION_1), val); +} + +static inline u64 virtio64_to_cpu(struct virtio_device *vdev, __virtio64 val) +{ + return __virtio64_to_cpu(virtio_has_feature(vdev, VIRTIO_F_VERSION_1), val); +} + +static inline __virtio64 cpu_to_virtio64(struct virtio_device *vdev, u64 val) +{ + return __cpu_to_virtio64(virtio_has_feature(vdev, VIRTIO_F_VERSION_1), val); +} diff --git a/tools/virtio/uapi/linux/virtio_types.h b/tools/virtio/uapi/linux/virtio_types.h new file mode 100644 index 000000000000..e7a1096e7c97 --- /dev/null +++ b/tools/virtio/uapi/linux/virtio_types.h @@ -0,0 +1 @@ +#include "../../include/uapi/linux/virtio_types.h" diff --git a/tools/virtio/virtio_test.c b/tools/virtio/virtio_test.c index 00ea679b3826..e0445898f08f 100644 --- a/tools/virtio/virtio_test.c +++ b/tools/virtio/virtio_test.c @@ -11,6 +11,7 @@ #include <sys/types.h> #include <fcntl.h> #include <stdbool.h> +#include <linux/virtio_types.h> #include <linux/vhost.h> #include <linux/virtio.h> #include <linux/virtio_ring.h> @@ -60,7 +61,7 @@ void vhost_vq_setup(struct vdev_info *dev, struct vq_info *info) { struct vhost_vring_state state = { .index = info->idx }; struct vhost_vring_file file = { .index = info->idx }; - unsigned long long features = dev->vdev.features[0]; + unsigned long long features = dev->vdev.features; struct vhost_vring_addr addr = { .index = info->idx, .desc_user_addr = (uint64_t)(unsigned long)info->vring.desc, @@ -113,8 +114,7 @@ static void vdev_info_init(struct vdev_info* dev, unsigned long long features) { int r; memset(dev, 0, sizeof *dev); - dev->vdev.features[0] = features; - dev->vdev.features[1] = features >> 32; + dev->vdev.features = features; dev->buf_size = 1024; dev->buf = malloc(dev->buf_size); assert(dev->buf); @@ -228,6 +228,14 @@ const struct option longopts[] = { .val = 'i', }, { + .name = "virtio-1", + .val = '1', + }, + { + .name = "no-virtio-1", + .val = '0', + }, + { .name = "delayed-interrupt", .val = 'D', }, @@ -244,6 +252,7 @@ static void help(void) fprintf(stderr, "Usage: virtio_test [--help]" " [--no-indirect]" " [--no-event-idx]" + " [--no-virtio-1]" " [--delayed-interrupt]" "\n"); } @@ -252,7 +261,7 @@ int main(int argc, char **argv) { struct vdev_info dev; unsigned long long features = (1ULL << VIRTIO_RING_F_INDIRECT_DESC) | - (1ULL << VIRTIO_RING_F_EVENT_IDX); + (1ULL << VIRTIO_RING_F_EVENT_IDX) | (1ULL << VIRTIO_F_VERSION_1); int o; bool delayed = false; @@ -273,6 +282,9 @@ int main(int argc, char **argv) case 'i': features &= ~(1ULL << VIRTIO_RING_F_INDIRECT_DESC); break; + case '0': + features &= ~(1ULL << VIRTIO_F_VERSION_1); + break; case 'D': delayed = true; break; diff --git a/tools/virtio/vringh_test.c b/tools/virtio/vringh_test.c index 14a4f4cab5b9..5f94f5105678 100644 --- a/tools/virtio/vringh_test.c +++ b/tools/virtio/vringh_test.c @@ -7,6 +7,7 @@ #include <linux/virtio.h> #include <linux/vringh.h> #include <linux/virtio_ring.h> +#include <linux/virtio_config.h> #include <linux/uaccess.h> #include <sys/types.h> #include <sys/stat.h> @@ -131,7 +132,7 @@ static inline int vringh_get_head(struct vringh *vrh, u16 *head) return 1; } -static int parallel_test(unsigned long features, +static int parallel_test(u64 features, bool (*getrange)(struct vringh *vrh, u64 addr, struct vringh_range *r), bool fast_vringh) @@ -304,7 +305,7 @@ static int parallel_test(unsigned long features, close(to_guest[1]); close(to_host[0]); - gvdev.vdev.features[0] = features; + gvdev.vdev.features = features; gvdev.to_host_fd = to_host[1]; gvdev.notifies = 0; @@ -449,13 +450,15 @@ int main(int argc, char *argv[]) bool fast_vringh = false, parallel = false; getrange = getrange_iov; - vdev.features[0] = 0; + vdev.features = 0; while (argv[1]) { if (strcmp(argv[1], "--indirect") == 0) - vdev.features[0] |= (1 << VIRTIO_RING_F_INDIRECT_DESC); + __virtio_set_bit(&vdev, VIRTIO_RING_F_INDIRECT_DESC); else if (strcmp(argv[1], "--eventidx") == 0) - vdev.features[0] |= (1 << VIRTIO_RING_F_EVENT_IDX); + __virtio_set_bit(&vdev, VIRTIO_RING_F_EVENT_IDX); + else if (strcmp(argv[1], "--virtio-1") == 0) + __virtio_set_bit(&vdev, VIRTIO_F_VERSION_1); else if (strcmp(argv[1], "--slow-range") == 0) getrange = getrange_slow; else if (strcmp(argv[1], "--fast-vringh") == 0) @@ -468,7 +471,7 @@ int main(int argc, char *argv[]) } if (parallel) - return parallel_test(vdev.features[0], getrange, fast_vringh); + return parallel_test(vdev.features, getrange, fast_vringh); if (posix_memalign(&__user_addr_min, PAGE_SIZE, USER_MEM) != 0) abort(); @@ -483,7 +486,7 @@ int main(int argc, char *argv[]) /* Set up host side. */ vring_init(&vrh.vring, RINGSIZE, __user_addr_min, ALIGN); - vringh_init_user(&vrh, vdev.features[0], RINGSIZE, true, + vringh_init_user(&vrh, vdev.features, RINGSIZE, true, vrh.vring.desc, vrh.vring.avail, vrh.vring.used); /* No descriptor to get yet... */ @@ -652,13 +655,13 @@ int main(int argc, char *argv[]) } /* Test weird (but legal!) indirect. */ - if (vdev.features[0] & (1 << VIRTIO_RING_F_INDIRECT_DESC)) { + if (__virtio_test_bit(&vdev, VIRTIO_RING_F_INDIRECT_DESC)) { char *data = __user_addr_max - USER_MEM/4; struct vring_desc *d = __user_addr_max - USER_MEM/2; struct vring vring; /* Force creation of direct, which we modify. */ - vdev.features[0] &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC); + __virtio_clear_bit(&vdev, VIRTIO_RING_F_INDIRECT_DESC); vq = vring_new_virtqueue(0, RINGSIZE, ALIGN, &vdev, true, __user_addr_min, never_notify_host, diff --git a/tools/vm/Makefile b/tools/vm/Makefile index 3d907dacf2ac..ac884b65a072 100644 --- a/tools/vm/Makefile +++ b/tools/vm/Makefile @@ -1,6 +1,6 @@ # Makefile for vm tools # -TARGETS=page-types slabinfo +TARGETS=page-types slabinfo page_owner_sort LIB_DIR = ../lib/api LIBS = $(LIB_DIR)/libapikfs.a @@ -18,5 +18,5 @@ $(LIBS): $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) clean: - $(RM) page-types slabinfo + $(RM) page-types slabinfo page_owner_sort make -C $(LIB_DIR) clean diff --git a/tools/vm/page_owner_sort.c b/tools/vm/page_owner_sort.c new file mode 100644 index 000000000000..77147b42d598 --- /dev/null +++ b/tools/vm/page_owner_sort.c @@ -0,0 +1,144 @@ +/* + * User-space helper to sort the output of /sys/kernel/debug/page_owner + * + * Example use: + * cat /sys/kernel/debug/page_owner > page_owner_full.txt + * grep -v ^PFN page_owner_full.txt > page_owner.txt + * ./sort page_owner.txt sorted_page_owner.txt +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> + +struct block_list { + char *txt; + int len; + int num; +}; + + +static struct block_list *list; +static int list_size; +static int max_size; + +struct block_list *block_head; + +int read_block(char *buf, int buf_size, FILE *fin) +{ + char *curr = buf, *const buf_end = buf + buf_size; + + while (buf_end - curr > 1 && fgets(curr, buf_end - curr, fin)) { + if (*curr == '\n') /* empty line */ + return curr - buf; + curr += strlen(curr); + } + + return -1; /* EOF or no space left in buf. */ +} + +static int compare_txt(const void *p1, const void *p2) +{ + const struct block_list *l1 = p1, *l2 = p2; + + return strcmp(l1->txt, l2->txt); +} + +static int compare_num(const void *p1, const void *p2) +{ + const struct block_list *l1 = p1, *l2 = p2; + + return l2->num - l1->num; +} + +static void add_list(char *buf, int len) +{ + if (list_size != 0 && + len == list[list_size-1].len && + memcmp(buf, list[list_size-1].txt, len) == 0) { + list[list_size-1].num++; + return; + } + if (list_size == max_size) { + printf("max_size too small??\n"); + exit(1); + } + list[list_size].txt = malloc(len+1); + list[list_size].len = len; + list[list_size].num = 1; + memcpy(list[list_size].txt, buf, len); + list[list_size].txt[len] = 0; + list_size++; + if (list_size % 1000 == 0) { + printf("loaded %d\r", list_size); + fflush(stdout); + } +} + +#define BUF_SIZE 1024 + +int main(int argc, char **argv) +{ + FILE *fin, *fout; + char buf[BUF_SIZE]; + int ret, i, count; + struct block_list *list2; + struct stat st; + + if (argc < 3) { + printf("Usage: ./program <input> <output>\n"); + perror("open: "); + exit(1); + } + + fin = fopen(argv[1], "r"); + fout = fopen(argv[2], "w"); + if (!fin || !fout) { + printf("Usage: ./program <input> <output>\n"); + perror("open: "); + exit(1); + } + + fstat(fileno(fin), &st); + max_size = st.st_size / 100; /* hack ... */ + + list = malloc(max_size * sizeof(*list)); + + for ( ; ; ) { + ret = read_block(buf, BUF_SIZE, fin); + if (ret < 0) + break; + + add_list(buf, ret); + } + + printf("loaded %d\n", list_size); + + printf("sorting ....\n"); + + qsort(list, list_size, sizeof(list[0]), compare_txt); + + list2 = malloc(sizeof(*list) * list_size); + + printf("culling\n"); + + for (i = count = 0; i < list_size; i++) { + if (count == 0 || + strcmp(list2[count-1].txt, list[i].txt) != 0) { + list2[count++] = list[i]; + } else { + list2[count-1].num += list[i].num; + } + } + + qsort(list2, count, sizeof(list[0]), compare_num); + + for (i = 0; i < count; i++) + fprintf(fout, "%d times:\n%s\n", list2[i].num, list2[i].txt); + + return 0; +} |