diff options
Diffstat (limited to '')
75 files changed, 2028 insertions, 157 deletions
diff --git a/tools/include/linux/bitmap.h b/tools/include/linux/bitmap.h index 28f5493da491..43c1c5021e4b 100644 --- a/tools/include/linux/bitmap.h +++ b/tools/include/linux/bitmap.h @@ -3,6 +3,7 @@ #include <string.h> #include <linux/bitops.h> +#include <stdlib.h> #define DECLARE_BITMAP(name,bits) \ unsigned long name[BITS_TO_LONGS(bits)] @@ -10,6 +11,8 @@ int __bitmap_weight(const unsigned long *bitmap, int bits); void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, const unsigned long *bitmap2, int bits); +int __bitmap_and(unsigned long *dst, const unsigned long *bitmap1, + const unsigned long *bitmap2, unsigned int bits); #define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1))) @@ -65,4 +68,38 @@ static inline int test_and_set_bit(int nr, unsigned long *addr) return (old & mask) != 0; } +/** + * bitmap_alloc - Allocate bitmap + * @nr: Bit to set + */ +static inline unsigned long *bitmap_alloc(int nbits) +{ + return calloc(1, BITS_TO_LONGS(nbits) * sizeof(unsigned long)); +} + +/* + * bitmap_scnprintf - print bitmap list into buffer + * @bitmap: bitmap + * @nbits: size of bitmap + * @buf: buffer to store output + * @size: size of @buf + */ +size_t bitmap_scnprintf(unsigned long *bitmap, int nbits, + char *buf, size_t size); + +/** + * bitmap_and - Do logical and on bitmaps + * @dst: resulting bitmap + * @src1: operand 1 + * @src2: operand 2 + * @nbits: size of bitmap + */ +static inline int bitmap_and(unsigned long *dst, const unsigned long *src1, + const unsigned long *src2, unsigned int nbits) +{ + if (small_const_nbits(nbits)) + return (*dst = *src1 & *src2 & BITMAP_LAST_WORD_MASK(nbits)) != 0; + return __bitmap_and(dst, src1, src2, nbits); +} + #endif /* _PERF_BITOPS_H */ diff --git a/tools/lib/bitmap.c b/tools/lib/bitmap.c index 0a1adc1111fd..38748b0e342f 100644 --- a/tools/lib/bitmap.c +++ b/tools/lib/bitmap.c @@ -29,3 +29,47 @@ void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, for (k = 0; k < nr; k++) dst[k] = bitmap1[k] | bitmap2[k]; } + +size_t bitmap_scnprintf(unsigned long *bitmap, int nbits, + char *buf, size_t size) +{ + /* current bit is 'cur', most recently seen range is [rbot, rtop] */ + int cur, rbot, rtop; + bool first = true; + size_t ret = 0; + + rbot = cur = find_first_bit(bitmap, nbits); + while (cur < nbits) { + rtop = cur; + cur = find_next_bit(bitmap, nbits, cur + 1); + if (cur < nbits && cur <= rtop + 1) + continue; + + if (!first) + ret += scnprintf(buf + ret, size - ret, ","); + + first = false; + + ret += scnprintf(buf + ret, size - ret, "%d", rbot); + if (rbot < rtop) + ret += scnprintf(buf + ret, size - ret, "-%d", rtop); + + rbot = cur; + } + return ret; +} + +int __bitmap_and(unsigned long *dst, const unsigned long *bitmap1, + const unsigned long *bitmap2, unsigned int bits) +{ + unsigned int k; + unsigned int lim = bits/BITS_PER_LONG; + unsigned long result = 0; + + for (k = 0; k < lim; k++) + result |= (dst[k] = bitmap1[k] & bitmap2[k]); + if (bits % BITS_PER_LONG) + result |= (dst[k] = bitmap1[k] & bitmap2[k] & + BITMAP_LAST_WORD_MASK(bits)); + return result != 0; +} diff --git a/tools/lib/traceevent/.gitignore b/tools/lib/traceevent/.gitignore index 3c60335fe7be..9e9f25fb1922 100644 --- a/tools/lib/traceevent/.gitignore +++ b/tools/lib/traceevent/.gitignore @@ -1,2 +1,3 @@ TRACEEVENT-CFLAGS libtraceevent-dynamic-list +libtraceevent.so.* diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 69966abf65d1..379a2bed07c0 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -192,6 +192,9 @@ OPTIONS --period:: Record the sample period. +--sample-cpu:: + Record the sample cpu. + -n:: --no-samples:: Don't sample. diff --git a/tools/perf/config/Makefile b/tools/perf/Makefile.config index 24803c58049a..24803c58049a 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/Makefile.config diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 6641abb97f0a..2d9087501633 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -161,7 +161,7 @@ TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/ BPF_DIR = $(srctree)/tools/lib/bpf/ SUBCMD_DIR = $(srctree)/tools/lib/subcmd/ -# include config/Makefile by default and rule out +# include Makefile.config by default and rule out # non-config cases config := 1 @@ -183,7 +183,7 @@ ifeq ($(filter feature-dump,$(MAKECMDGOALS)),feature-dump) FEATURE_TESTS := all endif endif -include config/Makefile +include Makefile.config endif ifeq ($(config),0) @@ -706,7 +706,7 @@ $(INSTALL_DOC_TARGETS): ### Cleaning rules # -# This is here, not in config/Makefile, because config/Makefile does +# This is here, not in Makefile.config, because Makefile.config does # not get included for the clean target: # config-clean: diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 8f2c16d9275f..6355902fbfc8 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -1434,6 +1434,7 @@ struct option __record_options[] = { OPT_BOOLEAN('s', "stat", &record.opts.inherit_stat, "per thread counts"), OPT_BOOLEAN('d', "data", &record.opts.sample_address, "Record the sample addresses"), + OPT_BOOLEAN(0, "sample-cpu", &record.opts.sample_cpu, "Record the sample cpu"), OPT_BOOLEAN_SET('T', "timestamp", &record.opts.sample_time, &record.opts.sample_time_set, "Record the sample timestamps"), diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index bd108683fcb8..418ed94756d3 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -128,10 +128,14 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he) return err; } - err = symbol__annotate(sym, map, 0); + err = symbol__disassemble(sym, map, 0); if (err == 0) { out_assign: top->sym_filter_entry = he; + } else { + char msg[BUFSIZ]; + symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg)); + pr_err("Couldn't annotate %s: %s\n", sym->name, msg); } pthread_mutex_unlock(¬es->lock); diff --git a/tools/perf/perf.h b/tools/perf/perf.h index a7e0f1497244..cb0f1356ff81 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -52,6 +52,7 @@ struct record_opts { bool sample_weight; bool sample_time; bool sample_time_set; + bool sample_cpu; bool period; bool running_time; bool full_auxtrace; diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Build b/tools/perf/scripts/perl/Perf-Trace-Util/Build index 928e110179cb..34faecf774ae 100644 --- a/tools/perf/scripts/perl/Perf-Trace-Util/Build +++ b/tools/perf/scripts/perl/Perf-Trace-Util/Build @@ -1,3 +1,5 @@ libperf-y += Context.o -CFLAGS_Context.o += $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs -Wno-undef -Wno-switch-default +CFLAGS_Context.o += $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes +CFLAGS_Context.o += -Wno-unused-parameter -Wno-nested-externs -Wno-undef +CFLAGS_Context.o += -Wno-switch-default -Wno-shadow diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index cb20ae1c0d35..dc51bc570e51 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -41,6 +41,7 @@ perf-y += event-times.o perf-y += backward-ring-buffer.o perf-y += sdt.o perf-y += is_printable_array.o +perf-y += bitmap.o $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build $(call rule_mkdir) diff --git a/tools/perf/tests/bitmap.c b/tools/perf/tests/bitmap.c new file mode 100644 index 000000000000..9abe6c13090f --- /dev/null +++ b/tools/perf/tests/bitmap.c @@ -0,0 +1,53 @@ +#include <linux/compiler.h> +#include <linux/bitmap.h> +#include "tests.h" +#include "cpumap.h" +#include "debug.h" + +#define NBITS 100 + +static unsigned long *get_bitmap(const char *str, int nbits) +{ + struct cpu_map *map = cpu_map__new(str); + unsigned long *bm = NULL; + int i; + + bm = bitmap_alloc(nbits); + + if (map && bm) { + bitmap_zero(bm, nbits); + + for (i = 0; i < map->nr; i++) + set_bit(map->map[i], bm); + } + + if (map) + cpu_map__put(map); + return bm; +} + +static int test_bitmap(const char *str) +{ + unsigned long *bm = get_bitmap(str, NBITS); + char buf[100]; + int ret; + + bitmap_scnprintf(bm, NBITS, buf, sizeof(buf)); + pr_debug("bitmap: %s\n", buf); + + ret = !strcmp(buf, str); + free(bm); + return ret; +} + +int test__bitmap_print(int subtest __maybe_unused) +{ + TEST_ASSERT_VAL("failed to convert map", test_bitmap("1")); + TEST_ASSERT_VAL("failed to convert map", test_bitmap("1,5")); + TEST_ASSERT_VAL("failed to convert map", test_bitmap("1,3,5,7,9,11,13,15,17,19,21-40")); + TEST_ASSERT_VAL("failed to convert map", test_bitmap("2-5")); + TEST_ASSERT_VAL("failed to convert map", test_bitmap("1,3-6,8-10,24,35-37")); + TEST_ASSERT_VAL("failed to convert map", test_bitmap("1,3-6,8-10,24,35-37")); + TEST_ASSERT_VAL("failed to convert map", test_bitmap("1-10,12-20,22-30,32-40")); + return 0; +} diff --git a/tools/perf/tests/bpf-script-example.c b/tools/perf/tests/bpf-script-example.c index e53bc91fa260..268e5f8e4aa2 100644 --- a/tools/perf/tests/bpf-script-example.c +++ b/tools/perf/tests/bpf-script-example.c @@ -31,8 +31,8 @@ struct bpf_map_def SEC("maps") flip_table = { .max_entries = 1, }; -SEC("func=sys_epoll_wait") -int bpf_func__sys_epoll_wait(void *ctx) +SEC("func=SyS_epoll_wait") +int bpf_func__SyS_epoll_wait(void *ctx) { int ind =0; int *flag = bpf_map_lookup_elem(&flip_table, &ind); diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 10eb30686c9c..778668a2a966 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -226,6 +226,10 @@ static struct test generic_tests[] = { .func = test__is_printable_array, }, { + .desc = "Test bitmap print", + .func = test__bitmap_print, + }, + { .func = NULL, }, }; diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 68a69a195545..2af156a8d4e5 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -33,44 +33,86 @@ static unsigned int hex(char c) return c - 'A' + 10; } -static size_t read_objdump_line(const char *line, size_t line_len, void *buf, - size_t len) +static size_t read_objdump_chunk(const char **line, unsigned char **buf, + size_t *buf_len) { - const char *p; - size_t i, j = 0; - - /* Skip to a colon */ - p = strchr(line, ':'); - if (!p) - return 0; - i = p + 1 - line; + size_t bytes_read = 0; + unsigned char *chunk_start = *buf; /* Read bytes */ - while (j < len) { + while (*buf_len > 0) { char c1, c2; - /* Skip spaces */ - for (; i < line_len; i++) { - if (!isspace(line[i])) - break; - } /* Get 2 hex digits */ - if (i >= line_len || !isxdigit(line[i])) + c1 = *(*line)++; + if (!isxdigit(c1)) break; - c1 = line[i++]; - if (i >= line_len || !isxdigit(line[i])) + c2 = *(*line)++; + if (!isxdigit(c2)) break; - c2 = line[i++]; - /* Followed by a space */ - if (i < line_len && line[i] && !isspace(line[i])) + + /* Store byte and advance buf */ + **buf = (hex(c1) << 4) | hex(c2); + (*buf)++; + (*buf_len)--; + bytes_read++; + + /* End of chunk? */ + if (isspace(**line)) break; - /* Store byte */ - *(unsigned char *)buf = (hex(c1) << 4) | hex(c2); - buf += 1; - j++; } + + /* + * objdump will display raw insn as LE if code endian + * is LE and bytes_per_chunk > 1. In that case reverse + * the chunk we just read. + * + * see disassemble_bytes() at binutils/objdump.c for details + * how objdump chooses display endian) + */ + if (bytes_read > 1 && !bigendian()) { + unsigned char *chunk_end = chunk_start + bytes_read - 1; + unsigned char tmp; + + while (chunk_start < chunk_end) { + tmp = *chunk_start; + *chunk_start = *chunk_end; + *chunk_end = tmp; + chunk_start++; + chunk_end--; + } + } + + return bytes_read; +} + +static size_t read_objdump_line(const char *line, unsigned char *buf, + size_t buf_len) +{ + const char *p; + size_t ret, bytes_read = 0; + + /* Skip to a colon */ + p = strchr(line, ':'); + if (!p) + return 0; + p++; + + /* Skip initial spaces */ + while (*p) { + if (!isspace(*p)) + break; + p++; + } + + do { + ret = read_objdump_chunk(&p, &buf, &buf_len); + bytes_read += ret; + p++; + } while (ret > 0); + /* return number of successfully read bytes */ - return j; + return bytes_read; } static int read_objdump_output(FILE *f, void *buf, size_t *len, u64 start_addr) @@ -95,7 +137,7 @@ static int read_objdump_output(FILE *f, void *buf, size_t *len, u64 start_addr) } /* read objdump data into temporary buffer */ - read_bytes = read_objdump_line(line, ret, tmp, sizeof(tmp)); + read_bytes = read_objdump_line(line, tmp, sizeof(tmp)); if (!read_bytes) continue; @@ -152,7 +194,7 @@ static int read_via_objdump(const char *filename, u64 addr, void *buf, ret = read_objdump_output(f, buf, &len, addr); if (len) { - pr_debug("objdump read too few bytes\n"); + pr_debug("objdump read too few bytes: %zd\n", len); if (!ret) ret = len; } diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 9bfc0e06c61a..7c196c585472 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -90,6 +90,7 @@ int test__backward_ring_buffer(int subtest); int test__cpu_map_print(int subtest); int test__sdt_event(int subtest); int test__is_printable_array(int subtest); +int test__bitmap_print(int subtest); #if defined(__arm__) || defined(__aarch64__) #ifdef HAVE_DWARF_UNWIND_SUPPORT diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 29dc6d20364e..2e2d10022355 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -1026,7 +1026,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, .use_navkeypressed = true, }, }; - int ret = -1; + int ret = -1, err; int nr_pcnt = 1; size_t sizeof_bdl = sizeof(struct browser_disasm_line); @@ -1050,8 +1050,11 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, (nr_pcnt - 1); } - if (symbol__annotate(sym, map, sizeof_bdl) < 0) { - ui__error("%s", ui_helpline__last_msg); + err = symbol__disassemble(sym, map, sizeof_bdl); + if (err) { + char msg[BUFSIZ]; + symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg)); + ui__error("Couldn't annotate %s:\n%s", sym->name, msg); goto out_free_offsets; } diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c index 9c7ff8d31b27..42d319927762 100644 --- a/tools/perf/ui/gtk/annotate.c +++ b/tools/perf/ui/gtk/annotate.c @@ -162,12 +162,16 @@ static int symbol__gtk_annotate(struct symbol *sym, struct map *map, GtkWidget *notebook; GtkWidget *scrolled_window; GtkWidget *tab_label; + int err; if (map->dso->annotate_warned) return -1; - if (symbol__annotate(sym, map, 0) < 0) { - ui__error("%s", ui_helpline__current); + err = symbol__disassemble(sym, map, 0); + if (err) { + char msg[BUFSIZ]; + symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg)); + ui__error("Couldn't annotate %s: %s\n", sym->name, msg); return -1; } diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index e9825fe825fd..4024d309bb00 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1123,7 +1123,46 @@ static void delete_last_nop(struct symbol *sym) } } -int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) +int symbol__strerror_disassemble(struct symbol *sym __maybe_unused, struct map *map, + int errnum, char *buf, size_t buflen) +{ + struct dso *dso = map->dso; + + BUG_ON(buflen == 0); + + if (errnum >= 0) { + str_error_r(errnum, buf, buflen); + return 0; + } + + switch (errnum) { + case SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX: { + char bf[SBUILD_ID_SIZE + 15] = " with build id "; + char *build_id_msg = NULL; + + if (dso->has_build_id) { + build_id__sprintf(dso->build_id, + sizeof(dso->build_id), bf + 15); + build_id_msg = bf; + } + scnprintf(buf, buflen, + "No vmlinux file%s\nwas found in the path.\n\n" + "Note that annotation using /proc/kcore requires CAP_SYS_RAWIO capability.\n\n" + "Please use:\n\n" + " perf buildid-cache -vu vmlinux\n\n" + "or:\n\n" + " --vmlinux vmlinux\n", build_id_msg ?: ""); + } + break; + default: + scnprintf(buf, buflen, "Internal error: Invalid %d error code\n", errnum); + break; + } + + return 0; +} + +int symbol__disassemble(struct symbol *sym, struct map *map, size_t privsize) { struct dso *dso = map->dso; char *filename = dso__build_id_filename(dso, NULL, 0); @@ -1134,22 +1173,20 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) char symfs_filename[PATH_MAX]; struct kcore_extract kce; bool delete_extract = false; + int stdout_fd[2]; int lineno = 0; int nline; + pid_t pid; if (filename) symbol__join_symfs(symfs_filename, filename); if (filename == NULL) { - if (dso->has_build_id) { - pr_err("Can't annotate %s: not enough memory\n", - sym->name); - return -ENOMEM; - } + if (dso->has_build_id) + return ENOMEM; goto fallback; - } else if (dso__is_kcore(dso)) { - goto fallback; - } else if (readlink(symfs_filename, command, sizeof(command)) < 0 || + } else if (dso__is_kcore(dso) || + readlink(symfs_filename, command, sizeof(command)) < 0 || strstr(command, DSO__NAME_KALLSYMS) || access(symfs_filename, R_OK)) { free(filename); @@ -1166,27 +1203,7 @@ fallback: if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS && !dso__is_kcore(dso)) { - char bf[SBUILD_ID_SIZE + 15] = " with build id "; - char *build_id_msg = NULL; - - if (dso->annotate_warned) - goto out_free_filename; - - if (dso->has_build_id) { - build_id__sprintf(dso->build_id, - sizeof(dso->build_id), bf + 15); - build_id_msg = bf; - } - err = -ENOENT; - dso->annotate_warned = 1; - pr_err("Can't annotate %s:\n\n" - "No vmlinux file%s\nwas found in the path.\n\n" - "Note that annotation using /proc/kcore requires CAP_SYS_RAWIO capability.\n\n" - "Please use:\n\n" - " perf buildid-cache -vu vmlinux\n\n" - "or:\n\n" - " --vmlinux vmlinux\n", - sym->name, build_id_msg ?: ""); + err = SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX; goto out_free_filename; } @@ -1258,9 +1275,32 @@ fallback: pr_debug("Executing: %s\n", command); - file = popen(command, "r"); + err = -1; + if (pipe(stdout_fd) < 0) { + pr_err("Failure creating the pipe to run %s\n", command); + goto out_remove_tmp; + } + + pid = fork(); + if (pid < 0) { + pr_err("Failure forking to run %s\n", command); + goto out_close_stdout; + } + + if (pid == 0) { + close(stdout_fd[0]); + dup2(stdout_fd[1], 1); + close(stdout_fd[1]); + execl("/bin/sh", "sh", "-c", command, NULL); + perror(command); + exit(-1); + } + + close(stdout_fd[1]); + + file = fdopen(stdout_fd[0], "r"); if (!file) { - pr_err("Failure running %s\n", command); + pr_err("Failure creating FILE stream for %s\n", command); /* * If we were using debug info should retry with * original binary. @@ -1286,9 +1326,11 @@ fallback: if (dso__is_kcore(dso)) delete_last_nop(sym); - pclose(file); - + fclose(file); + err = 0; out_remove_tmp: + close(stdout_fd[0]); + if (dso__needs_decompress(dso)) unlink(symfs_filename); out_free_filename: @@ -1297,6 +1339,10 @@ out_free_filename: if (free_filename) free(filename); return err; + +out_close_stdout: + close(stdout_fd[1]); + goto out_remove_tmp; } static void insert_source_line(struct rb_root *root, struct source_line *src_line) @@ -1663,7 +1709,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, struct rb_root source_line = RB_ROOT; u64 len; - if (symbol__annotate(sym, map, 0) < 0) + if (symbol__disassemble(sym, map, 0) < 0) return -1; len = symbol__size(sym); diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index a23084f54128..f67ccb027561 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -155,7 +155,27 @@ int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 addr); int symbol__alloc_hist(struct symbol *sym); void symbol__annotate_zero_histograms(struct symbol *sym); -int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize); +int symbol__disassemble(struct symbol *sym, struct map *map, size_t privsize); + +enum symbol_disassemble_errno { + SYMBOL_ANNOTATE_ERRNO__SUCCESS = 0, + + /* + * Choose an arbitrary negative big number not to clash with standard + * errno since SUS requires the errno has distinct positive values. + * See 'Issue 6' in the link below. + * + * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html + */ + __SYMBOL_ANNOTATE_ERRNO__START = -10000, + + SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX = __SYMBOL_ANNOTATE_ERRNO__START, + + __SYMBOL_ANNOTATE_ERRNO__END, +}; + +int symbol__strerror_disassemble(struct symbol *sym, struct map *map, + int errnum, char *buf, size_t buflen); int symbol__annotate_init(struct map *map, struct symbol *sym); int symbol__annotate_printf(struct symbol *sym, struct map *map, diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 2a40b8e1def7..097b3ed77fdd 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -239,31 +239,13 @@ void perf_event_attr__set_max_precise_ip(struct perf_event_attr *attr) int perf_evlist__add_default(struct perf_evlist *evlist) { - struct perf_event_attr attr = { - .type = PERF_TYPE_HARDWARE, - .config = PERF_COUNT_HW_CPU_CYCLES, - }; - struct perf_evsel *evsel; - - event_attr_init(&attr); + struct perf_evsel *evsel = perf_evsel__new_cycles(); - perf_event_attr__set_max_precise_ip(&attr); - - evsel = perf_evsel__new(&attr); if (evsel == NULL) - goto error; - - /* use asprintf() because free(evsel) assumes name is allocated */ - if (asprintf(&evsel->name, "cycles%.*s", - attr.precise_ip ? attr.precise_ip + 1 : 0, ":ppp") < 0) - goto error_free; + return -ENOMEM; perf_evlist__add(evlist, evsel); return 0; -error_free: - perf_evsel__delete(evsel); -error: - return -ENOMEM; } int perf_evlist__add_dummy(struct perf_evlist *evlist) diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 8c54df61fe64..d9b80ef881cd 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -253,6 +253,34 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) return evsel; } +struct perf_evsel *perf_evsel__new_cycles(void) +{ + struct perf_event_attr attr = { + .type = PERF_TYPE_HARDWARE, + .config = PERF_COUNT_HW_CPU_CYCLES, + }; + struct perf_evsel *evsel; + + event_attr_init(&attr); + + perf_event_attr__set_max_precise_ip(&attr); + + evsel = perf_evsel__new(&attr); + if (evsel == NULL) + goto out; + + /* use asprintf() because free(evsel) assumes name is allocated */ + if (asprintf(&evsel->name, "cycles%.*s", + attr.precise_ip ? attr.precise_ip + 1 : 0, ":ppp") < 0) + goto error_free; +out: + return evsel; +error_free: + perf_evsel__delete(evsel); + evsel = NULL; + goto out; +} + /* * Returns pointer with encoded error via <linux/err.h> interface. */ @@ -854,7 +882,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts, perf_evsel__set_sample_bit(evsel, REGS_INTR); } - if (target__has_cpu(&opts->target)) + if (target__has_cpu(&opts->target) || opts->sample_cpu) perf_evsel__set_sample_bit(evsel, CPU); if (opts->period) diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 8a4a6c9f1480..4d44129e050b 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -175,6 +175,8 @@ static inline struct perf_evsel *perf_evsel__newtp(const char *sys, const char * return perf_evsel__newtp_idx(sys, name, 0); } +struct perf_evsel *perf_evsel__new_cycles(void); + struct event_format *event_format__new(const char *sys, const char *name); void perf_evsel__init(struct perf_evsel *evsel, diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index a18d142cdca3..de15dbcdcecf 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1672,7 +1672,7 @@ static void __hists__insert_output_entry(struct rb_root *entries, } static void output_resort(struct hists *hists, struct ui_progress *prog, - bool use_callchain) + bool use_callchain, hists__resort_cb_t cb) { struct rb_root *root; struct rb_node *next; @@ -1711,6 +1711,9 @@ static void output_resort(struct hists *hists, struct ui_progress *prog, n = rb_entry(next, struct hist_entry, rb_node_in); next = rb_next(&n->rb_node_in); + if (cb && cb(n)) + continue; + __hists__insert_output_entry(&hists->entries, n, min_callchain_hits, use_callchain); hists__inc_stats(hists, n); @@ -1731,12 +1734,18 @@ void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *pro else use_callchain = symbol_conf.use_callchain; - output_resort(evsel__hists(evsel), prog, use_callchain); + output_resort(evsel__hists(evsel), prog, use_callchain, NULL); } void hists__output_resort(struct hists *hists, struct ui_progress *prog) { - output_resort(hists, prog, symbol_conf.use_callchain); + output_resort(hists, prog, symbol_conf.use_callchain, NULL); +} + +void hists__output_resort_cb(struct hists *hists, struct ui_progress *prog, + hists__resort_cb_t cb) +{ + output_resort(hists, prog, symbol_conf.use_callchain, cb); } static bool can_goto_child(struct hist_entry *he, enum hierarchy_move_dir hmd) diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 49aa4fac148f..0a1edf1ab450 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -153,8 +153,12 @@ int hist_entry__snprintf_alignment(struct hist_entry *he, struct perf_hpp *hpp, struct perf_hpp_fmt *fmt, int printed); void hist_entry__delete(struct hist_entry *he); +typedef int (*hists__resort_cb_t)(struct hist_entry *he); + void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *prog); void hists__output_resort(struct hists *hists, struct ui_progress *prog); +void hists__output_resort_cb(struct hists *hists, struct ui_progress *prog, + hists__resort_cb_t cb); int hists__collapse_resort(struct hists *hists, struct ui_progress *prog); void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c index 8cdcf4641c51..21c4d9b23c24 100644 --- a/tools/perf/util/target.c +++ b/tools/perf/util/target.c @@ -122,11 +122,7 @@ int target__strerror(struct target *target, int errnum, BUG_ON(buflen == 0); if (errnum >= 0) { - const char *err = str_error_r(errnum, buf, buflen); - - if (err != buf) - scnprintf(buf, buflen, "%s", err); - + str_error_r(errnum, buf, buflen); return 0; } diff --git a/tools/testing/radix-tree/linux/gfp.h b/tools/testing/radix-tree/linux/gfp.h index 0e37f7a760eb..5201b915f631 100644 --- a/tools/testing/radix-tree/linux/gfp.h +++ b/tools/testing/radix-tree/linux/gfp.h @@ -1,7 +1,7 @@ #ifndef _GFP_H #define _GFP_H -#define __GFP_BITS_SHIFT 22 +#define __GFP_BITS_SHIFT 26 #define __GFP_BITS_MASK ((gfp_t)((1 << __GFP_BITS_SHIFT) - 1)) #define __GFP_WAIT 1 #define __GFP_ACCOUNT 0 diff --git a/tools/testing/selftests/exec/Makefile b/tools/testing/selftests/exec/Makefile index 4e400eb83657..d4300602bf37 100644 --- a/tools/testing/selftests/exec/Makefile +++ b/tools/testing/selftests/exec/Makefile @@ -18,7 +18,8 @@ execveat.denatured: execveat $(CC) $(CFLAGS) -o $@ $^ TEST_PROGS := execveat -TEST_FILES := $(DEPS) +# Makefile is a run-time dependency, since it's accessed by the execveat test +TEST_FILES := $(DEPS) Makefile include ../lib.mk diff --git a/tools/testing/selftests/lib/printf.sh b/tools/testing/selftests/lib/printf.sh index 4fdc70fe6980..4fdc70fe6980 100644..100755 --- a/tools/testing/selftests/lib/printf.sh +++ b/tools/testing/selftests/lib/printf.sh diff --git a/tools/testing/selftests/media_tests/.gitignore b/tools/testing/selftests/media_tests/.gitignore index 1c0711708b98..8745eba39012 100644 --- a/tools/testing/selftests/media_tests/.gitignore +++ b/tools/testing/selftests/media_tests/.gitignore @@ -1 +1,3 @@ media_device_test +media_device_open +video_device_test diff --git a/tools/testing/selftests/media_tests/Makefile b/tools/testing/selftests/media_tests/Makefile index 7071bcc1d066..6b34a0199468 100644 --- a/tools/testing/selftests/media_tests/Makefile +++ b/tools/testing/selftests/media_tests/Makefile @@ -1,7 +1,7 @@ -TEST_PROGS := media_device_test +TEST_PROGS := media_device_test media_device_open video_device_test all: $(TEST_PROGS) include ../lib.mk clean: - rm -fr media_device_test + rm -fr media_device_test media_device_open video_device_test diff --git a/tools/testing/selftests/media_tests/bind_unbind_sample.sh b/tools/testing/selftests/media_tests/bind_unbind_sample.sh new file mode 100755 index 000000000000..9f362f10631a --- /dev/null +++ b/tools/testing/selftests/media_tests/bind_unbind_sample.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# Find device number in /sys/bus/usb/drivers/drivername +# Edit this file to update the driver numer and name +# Example test for uvcvideo driver +#i=0 +# while :; do +# i=$((i+1)) +# echo 1-5:1.0 > /sys/bus/usb/drivers/uvcvideo/unbind; +# echo 1-5:1.0 > /sys/bus/usb/drivers/uvcvideo/bind; +# clear +# echo $i +#done diff --git a/tools/testing/selftests/media_tests/media_device_open.c b/tools/testing/selftests/media_tests/media_device_open.c new file mode 100644 index 000000000000..44343c091a20 --- /dev/null +++ b/tools/testing/selftests/media_tests/media_device_open.c @@ -0,0 +1,81 @@ +/* + * media_device_open.c - Media Controller Device Open Test + * + * Copyright (c) 2016 Shuah Khan <shuahkh@osg.samsung.com> + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * This file is released under the GPLv2. + */ + +/* + * This file adds a test for Media Controller API. + * This test should be run as root and should not be + * included in the Kselftest run. This test should be + * run when hardware and driver that makes use Media + * Controller API are present in the system. + * + * This test opens user specified Media Device and calls + * MEDIA_IOC_DEVICE_INFO ioctl, closes the file, and exits. + * + * Usage: + * sudo ./media_device_open -d /dev/mediaX + * + * Run this test is a loop and run bind/unbind on the driver. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <linux/media.h> + +int main(int argc, char **argv) +{ + int opt; + char media_device[256]; + int count = 0; + struct media_device_info mdi; + int ret; + int fd; + + if (argc < 2) { + printf("Usage: %s [-d </dev/mediaX>]\n", argv[0]); + exit(-1); + } + + /* Process arguments */ + while ((opt = getopt(argc, argv, "d:")) != -1) { + switch (opt) { + case 'd': + strncpy(media_device, optarg, sizeof(media_device) - 1); + media_device[sizeof(media_device)-1] = '\0'; + break; + default: + printf("Usage: %s [-d </dev/mediaX>]\n", argv[0]); + exit(-1); + } + } + + if (getuid() != 0) { + printf("Please run the test as root - Exiting.\n"); + exit(-1); + } + + /* Open Media device and keep it open */ + fd = open(media_device, O_RDWR); + if (fd == -1) { + printf("Media Device open errno %s\n", strerror(errno)); + exit(-1); + } + + ret = ioctl(fd, MEDIA_IOC_DEVICE_INFO, &mdi); + if (ret < 0) + printf("Media Device Info errno %s\n", strerror(errno)); + else + printf("Media device model %s driver %s\n", + mdi.model, mdi.driver); +} diff --git a/tools/testing/selftests/media_tests/media_device_test.c b/tools/testing/selftests/media_tests/media_device_test.c index cbf53a032ab5..5d49943e77d0 100644 --- a/tools/testing/selftests/media_tests/media_device_test.c +++ b/tools/testing/selftests/media_tests/media_device_test.c @@ -1,5 +1,5 @@ /* - * media_devkref_test.c - Media Controller Device Kref API Test + * media_device_test.c - Media Controller Device ioctl loop Test * * Copyright (c) 2016 Shuah Khan <shuahkh@osg.samsung.com> * Copyright (c) 2016 Samsung Electronics Co., Ltd. @@ -35,13 +35,14 @@ #include <fcntl.h> #include <sys/ioctl.h> #include <sys/stat.h> +#include <time.h> #include <linux/media.h> int main(int argc, char **argv) { int opt; char media_device[256]; - int count = 0; + int count; struct media_device_info mdi; int ret; int fd; @@ -69,6 +70,10 @@ int main(int argc, char **argv) exit(-1); } + /* Generate random number of interations */ + srand((unsigned int) time(NULL)); + count = rand(); + /* Open Media device and keep it open */ fd = open(media_device, O_RDWR); if (fd == -1) { @@ -82,14 +87,16 @@ int main(int argc, char **argv) "other Oops in the dmesg. Enable KaSan kernel\n" "config option for use-after-free error detection.\n\n"); - while (count < 100) { + printf("Running test for %d iternations\n", count); + + while (count > 0) { ret = ioctl(fd, MEDIA_IOC_DEVICE_INFO, &mdi); if (ret < 0) printf("Media Device Info errno %s\n", strerror(errno)); else - printf("Media device model %s driver %s\n", - mdi.model, mdi.driver); + printf("Media device model %s driver %s - count %d\n", + mdi.model, mdi.driver, count); sleep(10); - count++; + count--; } } diff --git a/tools/testing/selftests/media_tests/open_loop_test.sh b/tools/testing/selftests/media_tests/open_loop_test.sh new file mode 100755 index 000000000000..dcd3c17efc17 --- /dev/null +++ b/tools/testing/selftests/media_tests/open_loop_test.sh @@ -0,0 +1,10 @@ +#!/bin/bash + i=0 +file=/dev/media$1 + while :; do + echo $file + i=$((i+1)) + R=$(./media_device_open -d $file); + # clear + echo -e "Loop $i\n$R" + done diff --git a/tools/testing/selftests/media_tests/regression_test.txt b/tools/testing/selftests/media_tests/regression_test.txt new file mode 100644 index 000000000000..2627367681f7 --- /dev/null +++ b/tools/testing/selftests/media_tests/regression_test.txt @@ -0,0 +1,43 @@ +Testing for regressions in Media Controller API register, ioctl, syscall, +and unregister paths. There have a few problems that result in user-after +free on media_device, media_devnode, and cdev pointers when the driver is +unbound while ioctl is in progress. + +Test Procedure: + +Run bin/unbind loop while ioctls are in progress. +Run rmmod and modprobe. +Disconnect the device. + +Setup: + +Build media_device_test +cd tools/testing/selftests/media_tests +make + +Regressions test for cdev user-after free error on /dev/mediaX when driver +is unbound: + +Start media_device_test to regression test media devnode dynamic alloc +and cdev user-after-free fixes. This opens media dev files and sits in +a loop running media ioctl MEDIA_IOC_DEVICE_INFO command once every 10 +seconds. The idea is when device file goes away, media devnode and cdev +should stick around until this test exits. + +The test for a random number of iterations or until user kills it with a +sleep 10 in between the ioctl calls. + +sudo ./media_device_test -d /dev/mediaX + +Regression test for media_devnode unregister race with ioctl_syscall: + +Start 6 open_loop_test.sh tests with different /dev/mediaX files. When +device file goes away after unbind, device file name changes. Start the +test with possible device names. If we start with /dev/media0 for example, +after unbind, /dev/media1 or /dev/media2 could get created. The idea is +keep ioctls going while bind/unbind runs. + +Copy bind_unbind_sample.txt and make changes to specify the driver name +and number to run bind and unbind. Start the bind_unbind.sh + +Run dmesg looking for any user-after free errors or mutex lock errors. diff --git a/tools/testing/selftests/media_tests/video_device_test.c b/tools/testing/selftests/media_tests/video_device_test.c new file mode 100644 index 000000000000..66d419c28653 --- /dev/null +++ b/tools/testing/selftests/media_tests/video_device_test.c @@ -0,0 +1,100 @@ +/* + * video_device_test - Video Device Test + * + * Copyright (c) 2016 Shuah Khan <shuahkh@osg.samsung.com> + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * This file is released under the GPLv2. + */ + +/* + * This file adds a test for Video Device. This test should not be included + * in the Kselftest run. This test should be run when hardware and driver + * that makes use of V4L2 API is present. + * + * This test opens user specified Video Device and calls video ioctls in a + * loop once every 10 seconds. + * + * Usage: + * sudo ./video_device_test -d /dev/videoX + * + * While test is running, remove the device or unbind the driver and + * ensure there are no use after free errors and other Oops in the + * dmesg. + * When possible, enable KaSan kernel config option for use-after-free + * error detection. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <time.h> +#include <linux/videodev2.h> + +int main(int argc, char **argv) +{ + int opt; + char video_dev[256]; + int count; + struct v4l2_tuner vtuner; + struct v4l2_capability vcap; + int ret; + int fd; + + if (argc < 2) { + printf("Usage: %s [-d </dev/videoX>]\n", argv[0]); + exit(-1); + } + + /* Process arguments */ + while ((opt = getopt(argc, argv, "d:")) != -1) { + switch (opt) { + case 'd': + strncpy(video_dev, optarg, sizeof(video_dev) - 1); + video_dev[sizeof(video_dev)-1] = '\0'; + break; + default: + printf("Usage: %s [-d </dev/videoX>]\n", argv[0]); + exit(-1); + } + } + + /* Generate random number of interations */ + srand((unsigned int) time(NULL)); + count = rand(); + + /* Open Video device and keep it open */ + fd = open(video_dev, O_RDWR); + if (fd == -1) { + printf("Video Device open errno %s\n", strerror(errno)); + exit(-1); + } + + printf("\nNote:\n" + "While test is running, remove the device or unbind\n" + "driver and ensure there are no use after free errors\n" + "and other Oops in the dmesg. When possible, enable KaSan\n" + "kernel config option for use-after-free error detection.\n\n"); + + while (count > 0) { + ret = ioctl(fd, VIDIOC_QUERYCAP, &vcap); + if (ret < 0) + printf("VIDIOC_QUERYCAP errno %s\n", strerror(errno)); + else + printf("Video device driver %s\n", vcap.driver); + + ret = ioctl(fd, VIDIOC_G_TUNER, &vtuner); + if (ret < 0) + printf("VIDIOC_G_TUNER, errno %s\n", strerror(errno)); + else + printf("type %d rangelow %d rangehigh %d\n", + vtuner.type, vtuner.rangelow, vtuner.rangehigh); + sleep(10); + count--; + } +} diff --git a/tools/testing/selftests/ntb/ntb_test.sh b/tools/testing/selftests/ntb/ntb_test.sh new file mode 100755 index 000000000000..a676d3eefefb --- /dev/null +++ b/tools/testing/selftests/ntb/ntb_test.sh @@ -0,0 +1,422 @@ +#!/bin/bash +# Copyright (c) 2016 Microsemi. All Rights Reserved. +# +# 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. +# +# This program is distributed in the hope that it would be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# Author: Logan Gunthorpe <logang@deltatee.com> + +REMOTE_HOST= +LIST_DEVS=FALSE + +DEBUGFS=${DEBUGFS-/sys/kernel/debug} + +PERF_RUN_ORDER=32 +MAX_MW_SIZE=0 +RUN_DMA_TESTS= +DONT_CLEANUP= +MW_SIZE=65536 + +function show_help() +{ + echo "Usage: $0 [OPTIONS] LOCAL_DEV REMOTE_DEV" + echo "Run tests on a pair of NTB endpoints." + echo + echo "If the NTB device loops back to the same host then," + echo "just specifying the two PCI ids on the command line is" + echo "sufficient. Otherwise, if the NTB link spans two hosts" + echo "use the -r option to specify the hostname for the remote" + echo "device. SSH will then be used to test the remote side." + echo "An SSH key between the root users of the host would then" + echo "be highly recommended." + echo + echo "Options:" + echo " -C don't cleanup ntb modules on exit" + echo " -d run dma tests" + echo " -h show this help message" + echo " -l list available local and remote PCI ids" + echo " -r REMOTE_HOST specify the remote's hostname to connect" + echo " to for the test (using ssh)" + echo " -p NUM ntb_perf run order (default: $PERF_RUN_ORDER)" + echo " -w max_mw_size maxmium memory window size" + echo +} + +function parse_args() +{ + OPTIND=0 + while getopts "Cdhlm:r:p:w:" opt; do + case "$opt" in + C) DONT_CLEANUP=1 ;; + d) RUN_DMA_TESTS=1 ;; + h) show_help; exit 0 ;; + l) LIST_DEVS=TRUE ;; + m) MW_SIZE=${OPTARG} ;; + r) REMOTE_HOST=${OPTARG} ;; + p) PERF_RUN_ORDER=${OPTARG} ;; + w) MAX_MW_SIZE=${OPTARG} ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + esac + done +} + +parse_args "$@" +shift $((OPTIND-1)) +LOCAL_DEV=$1 +shift +parse_args "$@" +shift $((OPTIND-1)) +REMOTE_DEV=$1 +shift +parse_args "$@" + +set -e + +function _modprobe() +{ + modprobe "$@" +} + +function split_remote() +{ + VPATH=$1 + REMOTE= + + if [[ "$VPATH" == *":/"* ]]; then + REMOTE=${VPATH%%:*} + VPATH=${VPATH#*:} + fi +} + +function read_file() +{ + split_remote $1 + if [[ "$REMOTE" != "" ]]; then + ssh "$REMOTE" cat "$VPATH" + else + cat "$VPATH" + fi +} + +function write_file() +{ + split_remote $2 + VALUE=$1 + + if [[ "$REMOTE" != "" ]]; then + ssh "$REMOTE" "echo \"$VALUE\" > \"$VPATH\"" + else + echo "$VALUE" > "$VPATH" + fi +} + +function link_test() +{ + LOC=$1 + REM=$2 + EXP=0 + + echo "Running link tests on: $(basename $LOC) / $(basename $REM)" + + if ! write_file "N" "$LOC/link" 2> /dev/null; then + echo " Unsupported" + return + fi + + write_file "N" "$LOC/link_event" + + if [[ $(read_file "$REM/link") != "N" ]]; then + echo "Expected remote link to be down in $REM/link" >&2 + exit -1 + fi + + write_file "Y" "$LOC/link" + write_file "Y" "$LOC/link_event" + + echo " Passed" +} + +function doorbell_test() +{ + LOC=$1 + REM=$2 + EXP=0 + + echo "Running db tests on: $(basename $LOC) / $(basename $REM)" + + write_file "c 0xFFFFFFFF" "$REM/db" + + for ((i=1; i <= 8; i++)); do + let DB=$(read_file "$REM/db") || true + if [[ "$DB" != "$EXP" ]]; then + echo "Doorbell doesn't match expected value $EXP " \ + "in $REM/db" >&2 + exit -1 + fi + + let "MASK=1 << ($i-1)" || true + let "EXP=$EXP | $MASK" || true + write_file "s $MASK" "$LOC/peer_db" + done + + echo " Passed" +} + +function read_spad() +{ + VPATH=$1 + IDX=$2 + + ROW=($(read_file "$VPATH" | grep -e "^$IDX")) + let VAL=${ROW[1]} || true + echo $VAL +} + +function scratchpad_test() +{ + LOC=$1 + REM=$2 + CNT=$(read_file "$LOC/spad" | wc -l) + + echo "Running spad tests on: $(basename $LOC) / $(basename $REM)" + + for ((i = 0; i < $CNT; i++)); do + VAL=$RANDOM + write_file "$i $VAL" "$LOC/peer_spad" + RVAL=$(read_spad "$REM/spad" $i) + + if [[ "$VAL" != "$RVAL" ]]; then + echo "Scratchpad doesn't match expected value $VAL " \ + "in $REM/spad, got $RVAL" >&2 + exit -1 + fi + + done + + echo " Passed" +} + +function write_mw() +{ + split_remote $2 + + if [[ "$REMOTE" != "" ]]; then + ssh "$REMOTE" \ + dd if=/dev/urandom "of=$VPATH" 2> /dev/null || true + else + dd if=/dev/urandom "of=$VPATH" 2> /dev/null || true + fi +} + +function mw_test() +{ + IDX=$1 + LOC=$2 + REM=$3 + + echo "Running $IDX tests on: $(basename $LOC) / $(basename $REM)" + + write_mw "$LOC/$IDX" + + split_remote "$LOC/$IDX" + if [[ "$REMOTE" == "" ]]; then + A=$VPATH + else + A=/tmp/ntb_test.$$.A + ssh "$REMOTE" cat "$VPATH" > "$A" + fi + + split_remote "$REM/peer_$IDX" + if [[ "$REMOTE" == "" ]]; then + B=$VPATH + else + B=/tmp/ntb_test.$$.B + ssh "$REMOTE" cat "$VPATH" > "$B" + fi + + cmp -n $MW_SIZE "$A" "$B" + if [[ $? != 0 ]]; then + echo "Memory window $MW did not match!" >&2 + fi + + if [[ "$A" == "/tmp/*" ]]; then + rm "$A" + fi + + if [[ "$B" == "/tmp/*" ]]; then + rm "$B" + fi + + echo " Passed" +} + +function pingpong_test() +{ + LOC=$1 + REM=$2 + + echo "Running ping pong tests on: $(basename $LOC) / $(basename $REM)" + + LOC_START=$(read_file $LOC/count) + REM_START=$(read_file $REM/count) + + sleep 7 + + LOC_END=$(read_file $LOC/count) + REM_END=$(read_file $REM/count) + + if [[ $LOC_START == $LOC_END ]] || [[ $REM_START == $REM_END ]]; then + echo "Ping pong counter not incrementing!" >&2 + exit 1 + fi + + echo " Passed" +} + +function perf_test() +{ + USE_DMA=$1 + + if [[ $USE_DMA == "1" ]]; then + WITH="with" + else + WITH="without" + fi + + _modprobe ntb_perf run_order=$PERF_RUN_ORDER \ + max_mw_size=$MAX_MW_SIZE use_dma=$USE_DMA + + echo "Running local perf test $WITH DMA" + write_file "" $LOCAL_PERF/run + echo -n " " + read_file $LOCAL_PERF/run + echo " Passed" + + echo "Running remote perf test $WITH DMA" + write_file "" $REMOTE_PERF/run + echo -n " " + read_file $LOCAL_PERF/run + echo " Passed" + + _modprobe -r ntb_perf +} + +function ntb_tool_tests() +{ + LOCAL_TOOL=$DEBUGFS/ntb_tool/$LOCAL_DEV + REMOTE_TOOL=$REMOTE_HOST:$DEBUGFS/ntb_tool/$REMOTE_DEV + + echo "Starting ntb_tool tests..." + + _modprobe ntb_tool + + write_file Y $LOCAL_TOOL/link_event + write_file Y $REMOTE_TOOL/link_event + + link_test $LOCAL_TOOL $REMOTE_TOOL + link_test $REMOTE_TOOL $LOCAL_TOOL + + for PEER_TRANS in $(ls $LOCAL_TOOL/peer_trans*); do + PT=$(basename $PEER_TRANS) + write_file $MW_SIZE $LOCAL_TOOL/$PT + write_file $MW_SIZE $REMOTE_TOOL/$PT + done + + doorbell_test $LOCAL_TOOL $REMOTE_TOOL + doorbell_test $REMOTE_TOOL $LOCAL_TOOL + scratchpad_test $LOCAL_TOOL $REMOTE_TOOL + scratchpad_test $REMOTE_TOOL $LOCAL_TOOL + + for MW in $(ls $LOCAL_TOOL/mw*); do + MW=$(basename $MW) + + mw_test $MW $LOCAL_TOOL $REMOTE_TOOL + mw_test $MW $REMOTE_TOOL $LOCAL_TOOL + done + + _modprobe -r ntb_tool +} + +function ntb_pingpong_tests() +{ + LOCAL_PP=$DEBUGFS/ntb_pingpong/$LOCAL_DEV + REMOTE_PP=$REMOTE_HOST:$DEBUGFS/ntb_pingpong/$REMOTE_DEV + + echo "Starting ntb_pingpong tests..." + + _modprobe ntb_pingpong + + pingpong_test $LOCAL_PP $REMOTE_PP + + _modprobe -r ntb_pingpong +} + +function ntb_perf_tests() +{ + LOCAL_PERF=$DEBUGFS/ntb_perf/$LOCAL_DEV + REMOTE_PERF=$REMOTE_HOST:$DEBUGFS/ntb_perf/$REMOTE_DEV + + echo "Starting ntb_perf tests..." + + perf_test 0 + + if [[ $RUN_DMA_TESTS ]]; then + perf_test 1 + fi +} + +function cleanup() +{ + set +e + _modprobe -r ntb_tool 2> /dev/null + _modprobe -r ntb_perf 2> /dev/null + _modprobe -r ntb_pingpong 2> /dev/null + _modprobe -r ntb_transport 2> /dev/null + set -e +} + +cleanup + +if ! [[ $$DONT_CLEANUP ]]; then + trap cleanup EXIT +fi + +if [ "$(id -u)" != "0" ]; then + echo "This script must be run as root" 1>&2 + exit 1 +fi + +if [[ "$LIST_DEVS" == TRUE ]]; then + echo "Local Devices:" + ls -1 /sys/bus/ntb/devices + echo + + if [[ "$REMOTE_HOST" != "" ]]; then + echo "Remote Devices:" + ssh $REMOTE_HOST ls -1 /sys/bus/ntb/devices + fi + + exit 0 +fi + +if [[ "$LOCAL_DEV" == $"" ]] || [[ "$REMOTE_DEV" == $"" ]]; then + show_help + exit 1 +fi + +ntb_tool_tests +echo +ntb_pingpong_tests +echo +ntb_perf_tests +echo diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile index 4ca83fe80654..3c40c9d0e6c7 100644 --- a/tools/testing/selftests/powerpc/Makefile +++ b/tools/testing/selftests/powerpc/Makefile @@ -12,7 +12,8 @@ CFLAGS := -Wall -O2 -Wall -Werror -DGIT_VERSION='"$(GIT_VERSION)"' -I$(CURDIR) $ export CFLAGS -SUB_DIRS = benchmarks \ +SUB_DIRS = alignment \ + benchmarks \ copyloops \ context_switch \ dscr \ diff --git a/tools/testing/selftests/powerpc/alignment/.gitignore b/tools/testing/selftests/powerpc/alignment/.gitignore new file mode 100644 index 000000000000..1d980e3d7039 --- /dev/null +++ b/tools/testing/selftests/powerpc/alignment/.gitignore @@ -0,0 +1,5 @@ +copy_unaligned +copy_first_unaligned +paste_unaligned +paste_last_unaligned +copy_paste_unaligned_common diff --git a/tools/testing/selftests/powerpc/alignment/Makefile b/tools/testing/selftests/powerpc/alignment/Makefile new file mode 100644 index 000000000000..ad6a4e49da91 --- /dev/null +++ b/tools/testing/selftests/powerpc/alignment/Makefile @@ -0,0 +1,10 @@ +TEST_PROGS := copy_unaligned copy_first_unaligned paste_unaligned paste_last_unaligned + +all: $(TEST_PROGS) + +$(TEST_PROGS): ../harness.c ../utils.c copy_paste_unaligned_common.c + +include ../../lib.mk + +clean: + rm -f $(TEST_PROGS) diff --git a/tools/testing/selftests/powerpc/alignment/copy_first_unaligned.c b/tools/testing/selftests/powerpc/alignment/copy_first_unaligned.c new file mode 100644 index 000000000000..47b73b3a08bd --- /dev/null +++ b/tools/testing/selftests/powerpc/alignment/copy_first_unaligned.c @@ -0,0 +1,41 @@ +/* + * Copyright 2016, Chris Smart, IBM Corporation. + * + * 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. + * + * Calls to copy_first which are not 128-byte aligned should be + * caught and sent a SIGBUS. + * + */ + +#include <string.h> +#include <unistd.h> +#include "utils.h" +#include "instructions.h" +#include "copy_paste_unaligned_common.h" + +unsigned int expected_instruction = PPC_INST_COPY_FIRST; +unsigned int instruction_mask = 0xfc2007fe; + +int test_copy_first_unaligned(void) +{ + /* Only run this test on a P9 or later */ + SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_00)); + + /* Register our signal handler with SIGBUS */ + setup_signal_handler(); + + /* +1 makes buf unaligned */ + copy_first(cacheline_buf+1); + + /* We should not get here */ + return 1; +} + +int main(int argc, char *argv[]) +{ + return test_harness(test_copy_first_unaligned, "test_copy_first_unaligned"); +} diff --git a/tools/testing/selftests/powerpc/alignment/copy_paste_unaligned_common.c b/tools/testing/selftests/powerpc/alignment/copy_paste_unaligned_common.c new file mode 100644 index 000000000000..d35fa5f5d2d3 --- /dev/null +++ b/tools/testing/selftests/powerpc/alignment/copy_paste_unaligned_common.c @@ -0,0 +1,53 @@ +/* + * Copyright 2016, Chris Smart, IBM Corporation. + * + * 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. + * + * Common code for copy, copy_first, paste and paste_last unaligned + * tests. + * + */ + +#include <signal.h> +#include <string.h> +#include <unistd.h> +#include "utils.h" +#include "instructions.h" +#include "copy_paste_unaligned_common.h" + +unsigned int expected_instruction; +unsigned int instruction_mask; + +char cacheline_buf[128] __cacheline_aligned; + +void signal_action_handler(int signal_num, siginfo_t *info, void *ptr) +{ + ucontext_t *ctx = ptr; +#if defined(__powerpc64__) + unsigned int *pc = (unsigned int *)ctx->uc_mcontext.gp_regs[PT_NIP]; +#else + unsigned int *pc = (unsigned int *)ctx->uc_mcontext.uc_regs->gregs[PT_NIP]; +#endif + + /* + * Check that the signal was on the correct instruction, using a + * mask because the compiler assigns the register at RB. + */ + if ((*pc & instruction_mask) == expected_instruction) + _exit(0); /* We hit the right instruction */ + + _exit(1); +} + +void setup_signal_handler(void) +{ + struct sigaction signal_action; + + memset(&signal_action, 0, sizeof(signal_action)); + signal_action.sa_sigaction = signal_action_handler; + signal_action.sa_flags = SA_SIGINFO; + sigaction(SIGBUS, &signal_action, NULL); +} diff --git a/tools/testing/selftests/powerpc/alignment/copy_paste_unaligned_common.h b/tools/testing/selftests/powerpc/alignment/copy_paste_unaligned_common.h new file mode 100644 index 000000000000..053899fe506e --- /dev/null +++ b/tools/testing/selftests/powerpc/alignment/copy_paste_unaligned_common.h @@ -0,0 +1,26 @@ +/* + * Copyright 2016, Chris Smart, IBM Corporation. + * + * 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. + * + * Declarations for common code for copy, copy_first, paste and + * paste_last unaligned tests. + * + */ + +#ifndef _SELFTESTS_POWERPC_COPY_PASTE_H +#define _SELFTESTS_POWERPC_COPY_PASTE_H + +#include <signal.h> + +int main(int argc, char *argv[]); +void signal_action_handler(int signal_num, siginfo_t *info, void *ptr); +void setup_signal_handler(void); +extern char cacheline_buf[128] __cacheline_aligned; +extern unsigned int expected_instruction; +extern unsigned int instruction_mask; + +#endif /* _SELFTESTS_POWERPC_COPY_PASTE_H */ diff --git a/tools/testing/selftests/powerpc/alignment/copy_unaligned.c b/tools/testing/selftests/powerpc/alignment/copy_unaligned.c new file mode 100644 index 000000000000..3a4e26461554 --- /dev/null +++ b/tools/testing/selftests/powerpc/alignment/copy_unaligned.c @@ -0,0 +1,41 @@ +/* + * Copyright 2016, Chris Smart, IBM Corporation. + * + * 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. + * + * Calls to copy which are not 128-byte aligned should be caught + * and sent a SIGBUS. + * + */ + +#include <string.h> +#include <unistd.h> +#include "utils.h" +#include "instructions.h" +#include "copy_paste_unaligned_common.h" + +unsigned int expected_instruction = PPC_INST_COPY; +unsigned int instruction_mask = 0xfc0007fe; + +int test_copy_unaligned(void) +{ + /* Only run this test on a P9 or later */ + SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_00)); + + /* Register our signal handler with SIGBUS */ + setup_signal_handler(); + + /* +1 makes buf unaligned */ + copy(cacheline_buf+1); + + /* We should not get here */ + return 1; +} + +int main(int argc, char *argv[]) +{ + return test_harness(test_copy_unaligned, "test_copy_unaligned"); +} diff --git a/tools/testing/selftests/powerpc/alignment/paste_last_unaligned.c b/tools/testing/selftests/powerpc/alignment/paste_last_unaligned.c new file mode 100644 index 000000000000..6e0ad045fcc3 --- /dev/null +++ b/tools/testing/selftests/powerpc/alignment/paste_last_unaligned.c @@ -0,0 +1,43 @@ +/* + * Copyright 2016, Chris Smart, IBM Corporation. + * + * 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. + * + * Calls to paste_last which are not 128-byte aligned should be + * caught and sent a SIGBUS. + * + */ + +#include <string.h> +#include <unistd.h> +#include "utils.h" +#include "instructions.h" +#include "copy_paste_unaligned_common.h" + +unsigned int expected_instruction = PPC_INST_PASTE_LAST; +unsigned int instruction_mask = 0xfc2007ff; + +int test_paste_last_unaligned(void) +{ + /* Only run this test on a P9 or later */ + SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_00)); + + /* Register our signal handler with SIGBUS */ + setup_signal_handler(); + + copy(cacheline_buf); + + /* +1 makes buf unaligned */ + paste_last(cacheline_buf+1); + + /* We should not get here */ + return 1; +} + +int main(int argc, char *argv[]) +{ + return test_harness(test_paste_last_unaligned, "test_paste_last_unaligned"); +} diff --git a/tools/testing/selftests/powerpc/alignment/paste_unaligned.c b/tools/testing/selftests/powerpc/alignment/paste_unaligned.c new file mode 100644 index 000000000000..6f982b45e4bd --- /dev/null +++ b/tools/testing/selftests/powerpc/alignment/paste_unaligned.c @@ -0,0 +1,43 @@ +/* + * Copyright 2016, Chris Smart, IBM Corporation. + * + * 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. + * + * Calls to paste which are not 128-byte aligned should be caught + * and sent a SIGBUS. + * + */ + +#include <string.h> +#include <unistd.h> +#include "utils.h" +#include "instructions.h" +#include "copy_paste_unaligned_common.h" + +unsigned int expected_instruction = PPC_INST_PASTE; +unsigned int instruction_mask = 0xfc0007fe; + +int test_paste_unaligned(void) +{ + /* Only run this test on a P9 or later */ + SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_00)); + + /* Register our signal handler with SIGBUS */ + setup_signal_handler(); + + copy(cacheline_buf); + + /* +1 makes buf unaligned */ + paste(cacheline_buf+1); + + /* We should not get here */ + return 1; +} + +int main(int argc, char *argv[]) +{ + return test_harness(test_paste_unaligned, "test_paste_unaligned"); +} diff --git a/tools/testing/selftests/powerpc/benchmarks/.gitignore b/tools/testing/selftests/powerpc/benchmarks/.gitignore index 6fa673316ac2..bce49ebd869e 100644 --- a/tools/testing/selftests/powerpc/benchmarks/.gitignore +++ b/tools/testing/selftests/powerpc/benchmarks/.gitignore @@ -1,2 +1,4 @@ gettimeofday context_switch +mmap_bench +futex_bench
\ No newline at end of file diff --git a/tools/testing/selftests/powerpc/benchmarks/Makefile b/tools/testing/selftests/powerpc/benchmarks/Makefile index 912445ff7ce7..a9adfb7de78f 100644 --- a/tools/testing/selftests/powerpc/benchmarks/Makefile +++ b/tools/testing/selftests/powerpc/benchmarks/Makefile @@ -1,4 +1,4 @@ -TEST_PROGS := gettimeofday context_switch +TEST_PROGS := gettimeofday context_switch mmap_bench futex_bench CFLAGS += -O2 @@ -7,6 +7,7 @@ all: $(TEST_PROGS) $(TEST_PROGS): ../harness.c context_switch: ../utils.c +context_switch: CFLAGS += -maltivec -mvsx -mabi=altivec context_switch: LDLIBS += -lpthread include ../../lib.mk diff --git a/tools/testing/selftests/powerpc/benchmarks/context_switch.c b/tools/testing/selftests/powerpc/benchmarks/context_switch.c index 7b785941adec..a36883ad48a4 100644 --- a/tools/testing/selftests/powerpc/benchmarks/context_switch.c +++ b/tools/testing/selftests/powerpc/benchmarks/context_switch.c @@ -25,7 +25,9 @@ #include <sys/types.h> #include <sys/shm.h> #include <linux/futex.h> - +#ifdef __powerpc__ +#include <altivec.h> +#endif #include "../utils.h" static unsigned int timeout = 30; @@ -37,12 +39,15 @@ static int touch_fp = 1; double fp; static int touch_vector = 1; -typedef int v4si __attribute__ ((vector_size (16))); -v4si a, b, c; +vector int a, b, c; #ifdef __powerpc__ static int touch_altivec = 1; +/* + * Note: LTO (Link Time Optimisation) doesn't play well with this function + * attribute. Be very careful enabling LTO for this test. + */ static void __attribute__((__target__("no-vsx"))) altivec_touch_fn(void) { c = a + b; @@ -369,11 +374,11 @@ static void usage(void) fprintf(stderr, "\t\t--process\tUse processes (default threads)\n"); fprintf(stderr, "\t\t--timeout=X\tDuration in seconds to run (default 30)\n"); fprintf(stderr, "\t\t--vdso\t\ttouch VDSO\n"); - fprintf(stderr, "\t\t--fp\t\ttouch FP\n"); + fprintf(stderr, "\t\t--no-fp\t\tDon't touch FP\n"); #ifdef __powerpc__ - fprintf(stderr, "\t\t--altivec\ttouch altivec\n"); + fprintf(stderr, "\t\t--no-altivec\tDon't touch altivec\n"); #endif - fprintf(stderr, "\t\t--vector\ttouch vector\n"); + fprintf(stderr, "\t\t--no-vector\tDon't touch vector\n"); } int main(int argc, char *argv[]) diff --git a/tools/testing/selftests/powerpc/benchmarks/futex_bench.c b/tools/testing/selftests/powerpc/benchmarks/futex_bench.c new file mode 100644 index 000000000000..2fc711d9150d --- /dev/null +++ b/tools/testing/selftests/powerpc/benchmarks/futex_bench.c @@ -0,0 +1,42 @@ +/* + * Copyright 2016, Anton Blanchard, Michael Ellerman, IBM Corp. + * Licensed under GPLv2. + */ + +#define _GNU_SOURCE + +#include <stdio.h> +#include <sys/syscall.h> +#include <time.h> +#include <unistd.h> +#include <linux/futex.h> + +#include "utils.h" + +#define ITERATIONS 100000000 + +#define futex(A, B, C, D, E, F) syscall(__NR_futex, A, B, C, D, E, F) + +int test_futex(void) +{ + struct timespec ts_start, ts_end; + unsigned long i = ITERATIONS; + + clock_gettime(CLOCK_MONOTONIC, &ts_start); + + while (i--) { + unsigned int addr = 0; + futex(&addr, FUTEX_WAKE, 1, NULL, NULL, 0); + } + + clock_gettime(CLOCK_MONOTONIC, &ts_end); + + printf("time = %.6f\n", ts_end.tv_sec - ts_start.tv_sec + (ts_end.tv_nsec - ts_start.tv_nsec) / 1e9); + + return 0; +} + +int main(void) +{ + return test_harness(test_futex, "futex_bench"); +} diff --git a/tools/testing/selftests/powerpc/benchmarks/mmap_bench.c b/tools/testing/selftests/powerpc/benchmarks/mmap_bench.c new file mode 100644 index 000000000000..8d084a2d6e74 --- /dev/null +++ b/tools/testing/selftests/powerpc/benchmarks/mmap_bench.c @@ -0,0 +1,41 @@ +/* + * Copyright 2016, Anton Blanchard, Michael Ellerman, IBM Corp. + * Licensed under GPLv2. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <time.h> + +#include "utils.h" + +#define ITERATIONS 5000000 + +#define MEMSIZE (128 * 1024 * 1024) + +int test_mmap(void) +{ + struct timespec ts_start, ts_end; + unsigned long i = ITERATIONS; + + clock_gettime(CLOCK_MONOTONIC, &ts_start); + + while (i--) { + char *c = mmap(NULL, MEMSIZE, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + FAIL_IF(c == MAP_FAILED); + munmap(c, MEMSIZE); + } + + clock_gettime(CLOCK_MONOTONIC, &ts_end); + + printf("time = %.6f\n", ts_end.tv_sec - ts_start.tv_sec + (ts_end.tv_nsec - ts_start.tv_nsec) / 1e9); + + return 0; +} + +int main(void) +{ + return test_harness(test_mmap, "mmap_bench"); +} diff --git a/tools/testing/selftests/powerpc/instructions.h b/tools/testing/selftests/powerpc/instructions.h new file mode 100644 index 000000000000..0fb0bd3b28c9 --- /dev/null +++ b/tools/testing/selftests/powerpc/instructions.h @@ -0,0 +1,68 @@ +#ifndef _SELFTESTS_POWERPC_INSTRUCTIONS_H +#define _SELFTESTS_POWERPC_INSTRUCTIONS_H + +#include <stdio.h> +#include <stdlib.h> + +/* This defines the "copy" instruction from Power ISA 3.0 Book II, section 4.4. */ +#define __COPY(RA, RB, L) \ + (0x7c00060c | (RA) << (31-15) | (RB) << (31-20) | (L) << (31-10)) +#define COPY(RA, RB, L) \ + .long __COPY((RA), (RB), (L)) + +static inline void copy(void *i) +{ + asm volatile(str(COPY(0, %0, 0))";" + : + : "b" (i) + : "memory" + ); +} + +static inline void copy_first(void *i) +{ + asm volatile(str(COPY(0, %0, 1))";" + : + : "b" (i) + : "memory" + ); +} + +/* This defines the "paste" instruction from Power ISA 3.0 Book II, section 4.4. */ +#define __PASTE(RA, RB, L, RC) \ + (0x7c00070c | (RA) << (31-15) | (RB) << (31-20) | (L) << (31-10) | (RC) << (31-31)) +#define PASTE(RA, RB, L, RC) \ + .long __PASTE((RA), (RB), (L), (RC)) + +static inline int paste(void *i) +{ + int cr; + + asm volatile(str(PASTE(0, %1, 0, 0))";" + "mfcr %0;" + : "=r" (cr) + : "b" (i) + : "memory" + ); + return cr; +} + +static inline int paste_last(void *i) +{ + int cr; + + asm volatile(str(PASTE(0, %1, 1, 1))";" + "mfcr %0;" + : "=r" (cr) + : "b" (i) + : "memory" + ); + return cr; +} + +#define PPC_INST_COPY __COPY(0, 0, 0) +#define PPC_INST_COPY_FIRST __COPY(0, 0, 1) +#define PPC_INST_PASTE __PASTE(0, 0, 0, 0) +#define PPC_INST_PASTE_LAST __PASTE(0, 0, 1, 1) + +#endif /* _SELFTESTS_POWERPC_INSTRUCTIONS_H */ diff --git a/tools/testing/selftests/powerpc/mm/.gitignore b/tools/testing/selftests/powerpc/mm/.gitignore index b43ade0ec861..e715a3f2fbf4 100644 --- a/tools/testing/selftests/powerpc/mm/.gitignore +++ b/tools/testing/selftests/powerpc/mm/.gitignore @@ -1,3 +1,4 @@ hugetlb_vs_thp_test subpage_prot tempfile +prot_sao
\ No newline at end of file diff --git a/tools/testing/selftests/powerpc/mm/Makefile b/tools/testing/selftests/powerpc/mm/Makefile index ee179e22308c..3bdb96eae558 100644 --- a/tools/testing/selftests/powerpc/mm/Makefile +++ b/tools/testing/selftests/powerpc/mm/Makefile @@ -1,13 +1,15 @@ noarg: $(MAKE) -C ../ -TEST_PROGS := hugetlb_vs_thp_test subpage_prot +TEST_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao TEST_FILES := tempfile all: $(TEST_PROGS) $(TEST_FILES) $(TEST_PROGS): ../harness.c +prot_sao: ../utils.c + include ../../lib.mk tempfile: diff --git a/tools/testing/selftests/powerpc/mm/prot_sao.c b/tools/testing/selftests/powerpc/mm/prot_sao.c new file mode 100644 index 000000000000..611530d43fa9 --- /dev/null +++ b/tools/testing/selftests/powerpc/mm/prot_sao.c @@ -0,0 +1,42 @@ +/* + * Copyright 2016, Michael Ellerman, IBM Corp. + * Licensed under GPLv2. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> + +#include <asm/cputable.h> + +#include "utils.h" + +#define SIZE (64 * 1024) + +int test_prot_sao(void) +{ + char *p; + + /* 2.06 or later should support SAO */ + SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06)); + + /* + * Ensure we can ask for PROT_SAO. + * We can't really verify that it does the right thing, but at least we + * confirm the kernel will accept it. + */ + p = mmap(NULL, SIZE, PROT_READ | PROT_WRITE | PROT_SAO, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + FAIL_IF(p == MAP_FAILED); + + /* Write to the mapping, to at least cause a fault */ + memset(p, 0xaa, SIZE); + + return 0; +} + +int main(void) +{ + return test_harness(test_prot_sao, "prot-sao"); +} diff --git a/tools/testing/selftests/powerpc/pmu/ebb/.gitignore b/tools/testing/selftests/powerpc/pmu/ebb/.gitignore index 42bddbed8b64..44b7df14a936 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/.gitignore +++ b/tools/testing/selftests/powerpc/pmu/ebb/.gitignore @@ -20,3 +20,5 @@ back_to_back_ebbs_test lost_exception_test no_handler_test cycles_with_mmcr2_test +ebb_lmr +ebb_lmr_regs
\ No newline at end of file diff --git a/tools/testing/selftests/powerpc/pmu/ebb/Makefile b/tools/testing/selftests/powerpc/pmu/ebb/Makefile index 8d2279c4bb4b..6b0453e60d53 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/Makefile +++ b/tools/testing/selftests/powerpc/pmu/ebb/Makefile @@ -14,7 +14,7 @@ TEST_PROGS := reg_access_test event_attributes_test cycles_test \ fork_cleanup_test ebb_on_child_test \ ebb_on_willing_child_test back_to_back_ebbs_test \ lost_exception_test no_handler_test \ - cycles_with_mmcr2_test + cycles_with_mmcr2_test ebb_lmr ebb_lmr_regs all: $(TEST_PROGS) diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr.c new file mode 100644 index 000000000000..c47ebd55ba4d --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr.c @@ -0,0 +1,143 @@ +/* + * Copyright 2016, Jack Miller, IBM Corp. + * Licensed under GPLv2. + */ + +#include <stdlib.h> +#include <stdio.h> + +#include "ebb.h" +#include "ebb_lmr.h" + +#define SIZE (32 * 1024 * 1024) /* 32M */ +#define LM_SIZE 0 /* Smallest encoding, 32M */ + +#define SECTIONS 64 /* 1 per bit in LMSER */ +#define SECTION_SIZE (SIZE / SECTIONS) +#define SECTION_LONGS (SECTION_SIZE / sizeof(long)) + +static unsigned long *test_mem; + +static int lmr_count = 0; + +void ebb_lmr_handler(void) +{ + lmr_count++; +} + +void ldmx_full_section(unsigned long *mem, int section) +{ + unsigned long *ptr; + int i; + + for (i = 0; i < SECTION_LONGS; i++) { + ptr = &mem[(SECTION_LONGS * section) + i]; + ldmx((unsigned long) &ptr); + ebb_lmr_reset(); + } +} + +unsigned long section_masks[] = { + 0x8000000000000000, + 0xFF00000000000000, + 0x0000000F70000000, + 0x8000000000000001, + 0xF0F0F0F0F0F0F0F0, + 0x0F0F0F0F0F0F0F0F, + 0x0 +}; + +int ebb_lmr_section_test(unsigned long *mem) +{ + unsigned long *mask = section_masks; + int i; + + for (; *mask; mask++) { + mtspr(SPRN_LMSER, *mask); + printf("Testing mask 0x%016lx\n", mfspr(SPRN_LMSER)); + + for (i = 0; i < 64; i++) { + lmr_count = 0; + ldmx_full_section(mem, i); + if (*mask & (1UL << (63 - i))) + FAIL_IF(lmr_count != SECTION_LONGS); + else + FAIL_IF(lmr_count); + } + } + + return 0; +} + +int ebb_lmr(void) +{ + int i; + + SKIP_IF(!lmr_is_supported()); + + setup_ebb_handler(ebb_lmr_handler); + + ebb_global_enable(); + + FAIL_IF(posix_memalign((void **)&test_mem, SIZE, SIZE) != 0); + + mtspr(SPRN_LMSER, 0); + + FAIL_IF(mfspr(SPRN_LMSER) != 0); + + mtspr(SPRN_LMRR, ((unsigned long)test_mem | LM_SIZE)); + + FAIL_IF(mfspr(SPRN_LMRR) != ((unsigned long)test_mem | LM_SIZE)); + + /* Read every single byte to ensure we get no false positives */ + for (i = 0; i < SECTIONS; i++) + ldmx_full_section(test_mem, i); + + FAIL_IF(lmr_count != 0); + + /* Turn on the first section */ + + mtspr(SPRN_LMSER, (1UL << 63)); + FAIL_IF(mfspr(SPRN_LMSER) != (1UL << 63)); + + /* Enable LM (BESCR) */ + + mtspr(SPRN_BESCR, mfspr(SPRN_BESCR) | BESCR_LME); + FAIL_IF(!(mfspr(SPRN_BESCR) & BESCR_LME)); + + ldmx((unsigned long)&test_mem); + + FAIL_IF(lmr_count != 1); // exactly one exception + FAIL_IF(mfspr(SPRN_BESCR) & BESCR_LME); // LM now disabled + FAIL_IF(!(mfspr(SPRN_BESCR) & BESCR_LMEO)); // occurred bit set + + printf("Simple LMR EBB OK\n"); + + /* This shouldn't cause an EBB since it's been disabled */ + ldmx((unsigned long)&test_mem); + FAIL_IF(lmr_count != 1); + + printf("LMR disable on EBB OK\n"); + + ebb_lmr_reset(); + + /* This should cause an EBB or reset is broken */ + ldmx((unsigned long)&test_mem); + FAIL_IF(lmr_count != 2); + + printf("LMR reset EBB OK\n"); + + ebb_lmr_reset(); + + return ebb_lmr_section_test(test_mem); +} + +int main(void) +{ + int ret = test_harness(ebb_lmr, "ebb_lmr"); + + if (test_mem) + free(test_mem); + + return ret; +} diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr.h b/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr.h new file mode 100644 index 000000000000..ef50abd557cd --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr.h @@ -0,0 +1,39 @@ +#ifndef _SELFTESTS_POWERPC_PMU_EBB_LMR_H +#define _SELFTESTS_POWERPC_PMU_EBB_LMR_H + +#include "reg.h" + +#ifndef PPC_FEATURE2_ARCH_3_00 +#define PPC_FEATURE2_ARCH_3_00 0x00800000 +#endif + +#define lmr_is_supported() have_hwcap2(PPC_FEATURE2_ARCH_3_00) + +static inline void ebb_lmr_reset(void) +{ + unsigned long bescr = mfspr(SPRN_BESCR); + bescr &= ~(BESCR_LMEO); + bescr |= BESCR_LME; + mtspr(SPRN_BESCR, bescr); +} + +#define LDMX(t, a, b)\ + (0x7c00026a | \ + (((t) & 0x1f) << 21) | \ + (((a) & 0x1f) << 16) | \ + (((b) & 0x1f) << 11)) + +static inline unsigned long ldmx(unsigned long address) +{ + unsigned long ret; + + asm volatile ("mr 9, %1\r\n" + ".long " __stringify(LDMX(9, 0, 9)) "\r\n" + "mr %0, 9\r\n":"=r"(ret) + :"r"(address) + :"r9"); + + return ret; +} + +#endif diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr_regs.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr_regs.c new file mode 100644 index 000000000000..aff4241fd88a --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr_regs.c @@ -0,0 +1,37 @@ +/* + * Copyright 2016, Jack Miller, IBM Corp. + * Licensed under GPLv2. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +#include "ebb.h" +#include "ebb_lmr.h" + +#define CHECKS 10000 + +int ebb_lmr_regs(void) +{ + int i; + + SKIP_IF(!lmr_is_supported()); + + ebb_global_enable(); + + for (i = 0; i < CHECKS; i++) { + mtspr(SPRN_LMRR, i << 25); // skip size and rsvd bits + mtspr(SPRN_LMSER, i); + + FAIL_IF(mfspr(SPRN_LMRR) != (i << 25)); + FAIL_IF(mfspr(SPRN_LMSER) != i); + } + + return 0; +} + +int main(void) +{ + return test_harness(ebb_lmr_regs, "ebb_lmr_regs"); +} diff --git a/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c b/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c index 5da355135df2..ae9a79086111 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c +++ b/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c @@ -51,7 +51,7 @@ static int do_count_loop(struct event *event, uint64_t instructions, printf("Looped for %lu instructions, overhead %lu\n", instructions, overhead); printf("Expected %lu\n", expected); printf("Actual %llu\n", event->result.value); - printf("Error %ld, %f%%\n", difference, percentage); + printf("Delta %ld, %f%%\n", difference, percentage); printf("Took %d EBBs\n", ebb_state.stats.ebb_count); } diff --git a/tools/testing/selftests/powerpc/pmu/lib.c b/tools/testing/selftests/powerpc/pmu/lib.c index a361ad3334ce..8b992fa5b478 100644 --- a/tools/testing/selftests/powerpc/pmu/lib.c +++ b/tools/testing/selftests/powerpc/pmu/lib.c @@ -190,7 +190,7 @@ int parse_proc_maps(void) bool require_paranoia_below(int level) { - unsigned long current; + long current; char *end, buf[16]; FILE *f; int rc; @@ -208,7 +208,7 @@ bool require_paranoia_below(int level) goto out_close; } - current = strtoul(buf, &end, 10); + current = strtol(buf, &end, 10); if (end == buf) { printf("Couldn't parse " PARANOID_PATH "?\n"); @@ -216,7 +216,7 @@ bool require_paranoia_below(int level) } if (current >= level) - goto out; + goto out_close; rc = 0; out_close: diff --git a/tools/testing/selftests/powerpc/reg.h b/tools/testing/selftests/powerpc/reg.h index 65bfdeeebdee..fddf368ed82f 100644 --- a/tools/testing/selftests/powerpc/reg.h +++ b/tools/testing/selftests/powerpc/reg.h @@ -34,6 +34,11 @@ #define BESCR_PMEO 0x1 /* PMU Event-based exception Occurred */ #define BESCR_PME (0x1ul << 32) /* PMU Event-based exception Enable */ +#define BESCR_LME (0x1ul << 34) /* Load Monitor Enable */ +#define BESCR_LMEO (0x1ul << 2) /* Load Monitor Exception Occurred */ + +#define SPRN_LMRR 813 /* Load Monitor Region Register */ +#define SPRN_LMSER 814 /* Load Monitor Section Enable Register */ #define SPRN_PMC1 771 #define SPRN_PMC2 772 diff --git a/tools/testing/selftests/powerpc/tm/.gitignore b/tools/testing/selftests/powerpc/tm/.gitignore index bb942db845bf..82c0a9ce6e74 100644 --- a/tools/testing/selftests/powerpc/tm/.gitignore +++ b/tools/testing/selftests/powerpc/tm/.gitignore @@ -6,3 +6,4 @@ tm-vmxcopy tm-fork tm-tar tm-tmspr +tm-exec diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile index d0505dbd22d5..9d301d785d9e 100644 --- a/tools/testing/selftests/powerpc/tm/Makefile +++ b/tools/testing/selftests/powerpc/tm/Makefile @@ -1,11 +1,14 @@ -TEST_PROGS := tm-resched-dscr tm-syscall tm-signal-msr-resv tm-signal-stack tm-vmxcopy tm-fork tm-tar tm-tmspr +TEST_PROGS := tm-resched-dscr tm-syscall tm-signal-msr-resv tm-signal-stack \ + tm-vmxcopy tm-fork tm-tar tm-tmspr tm-exec tm-execed all: $(TEST_PROGS) $(TEST_PROGS): ../harness.c ../utils.c +CFLAGS += -mhtm + tm-syscall: tm-syscall-asm.S -tm-syscall: CFLAGS += -mhtm -I../../../../../usr/include +tm-syscall: CFLAGS += -I../../../../../usr/include tm-tmspr: CFLAGS += -pthread include ../../lib.mk diff --git a/tools/testing/selftests/powerpc/tm/tm-exec.c b/tools/testing/selftests/powerpc/tm/tm-exec.c new file mode 100644 index 000000000000..3d27fa0ece04 --- /dev/null +++ b/tools/testing/selftests/powerpc/tm/tm-exec.c @@ -0,0 +1,70 @@ +/* + * Copyright 2016, Cyril Bur, IBM Corp. + * + * 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. + * + * Syscalls can be performed provided the transactions are suspended. + * The exec() class of syscall is unique as a new process is loaded. + * + * It makes little sense for after an exec() call for the previously + * suspended transaction to still exist. + */ + +#define _GNU_SOURCE +#include <errno.h> +#include <inttypes.h> +#include <libgen.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "utils.h" +#include "tm.h" + +static char *path; + +static int test_exec(void) +{ + SKIP_IF(!have_htm()); + + asm __volatile__( + "tbegin.;" + "blt 1f; " + "tsuspend.;" + "1: ;" + : : : "memory"); + + execl(path, "tm-exec", "--child", NULL); + + /* Shouldn't get here */ + perror("execl() failed"); + return 1; +} + +static int after_exec(void) +{ + asm __volatile__( + "tbegin.;" + "blt 1f;" + "tsuspend.;" + "1: ;" + : : : "memory"); + + FAIL_IF(failure_is_nesting()); + return 0; +} + +int main(int argc, char *argv[]) +{ + path = argv[0]; + + if (argc > 1 && strcmp(argv[1], "--child") == 0) + return after_exec(); + + return test_harness(test_exec, "tm_exec"); +} diff --git a/tools/testing/selftests/powerpc/tm/tm-syscall.c b/tools/testing/selftests/powerpc/tm/tm-syscall.c index 60560cb20e38..454b965a2db3 100644 --- a/tools/testing/selftests/powerpc/tm/tm-syscall.c +++ b/tools/testing/selftests/powerpc/tm/tm-syscall.c @@ -27,21 +27,6 @@ unsigned retries = 0; #define TEST_DURATION 10 /* seconds */ #define TM_RETRIES 100 -long failure_code(void) -{ - return __builtin_get_texasru() >> 24; -} - -bool failure_is_persistent(void) -{ - return (failure_code() & TM_CAUSE_PERSISTENT) == TM_CAUSE_PERSISTENT; -} - -bool failure_is_syscall(void) -{ - return (failure_code() & TM_CAUSE_SYSCALL) == TM_CAUSE_SYSCALL; -} - pid_t getppid_tm(bool suspend) { int i; diff --git a/tools/testing/selftests/powerpc/tm/tm.h b/tools/testing/selftests/powerpc/tm/tm.h index 24144b25772c..60318bad7d7a 100644 --- a/tools/testing/selftests/powerpc/tm/tm.h +++ b/tools/testing/selftests/powerpc/tm/tm.h @@ -6,8 +6,9 @@ #ifndef _SELFTESTS_POWERPC_TM_TM_H #define _SELFTESTS_POWERPC_TM_TM_H -#include <stdbool.h> +#include <asm/tm.h> #include <asm/cputable.h> +#include <stdbool.h> #include "../utils.h" @@ -31,4 +32,24 @@ static inline bool have_htm_nosc(void) #endif } +static inline long failure_code(void) +{ + return __builtin_get_texasru() >> 24; +} + +static inline bool failure_is_persistent(void) +{ + return (failure_code() & TM_CAUSE_PERSISTENT) == TM_CAUSE_PERSISTENT; +} + +static inline bool failure_is_syscall(void) +{ + return (failure_code() & TM_CAUSE_SYSCALL) == TM_CAUSE_SYSCALL; +} + +static inline bool failure_is_nesting(void) +{ + return (__builtin_get_texasru() & 0x400000); +} + #endif /* _SELFTESTS_POWERPC_TM_TM_H */ diff --git a/tools/testing/selftests/powerpc/utils.h b/tools/testing/selftests/powerpc/utils.h index a985cfaa535e..fbd33e52ef8f 100644 --- a/tools/testing/selftests/powerpc/utils.h +++ b/tools/testing/selftests/powerpc/utils.h @@ -27,6 +27,11 @@ int test_harness(int (test_function)(void), char *name); extern void *get_auxv_entry(int type); int pick_online_cpu(void); +static inline bool have_hwcap(unsigned long ftr) +{ + return ((unsigned long)get_auxv_entry(AT_HWCAP) & ftr) == ftr; +} + static inline bool have_hwcap2(unsigned long ftr2) { return ((unsigned long)get_auxv_entry(AT_HWCAP2) & ftr2) == ftr2; diff --git a/tools/testing/selftests/timers/Makefile b/tools/testing/selftests/timers/Makefile index 4a1be1b75a7f..1d5556869137 100644 --- a/tools/testing/selftests/timers/Makefile +++ b/tools/testing/selftests/timers/Makefile @@ -10,7 +10,7 @@ TEST_PROGS = posix_timers nanosleep nsleep-lat set-timer-lat mqueue-lat \ TEST_PROGS_EXTENDED = alarmtimer-suspend valid-adjtimex adjtick change_skew \ skew_consistency clocksource-switch leap-a-day \ - leapcrash set-tai set-2038 + leapcrash set-tai set-2038 set-tz bins = $(TEST_PROGS) $(TEST_PROGS_EXTENDED) @@ -30,6 +30,7 @@ run_destructive_tests: run_tests ./clocksource-switch ./leap-a-day -s -i 10 ./leapcrash + ./set-tz ./set-tai ./set-2038 diff --git a/tools/testing/selftests/timers/rtctest.c b/tools/testing/selftests/timers/rtctest.c index 624bce51b27d..4230d3052e5d 100644 --- a/tools/testing/selftests/timers/rtctest.c +++ b/tools/testing/selftests/timers/rtctest.c @@ -144,11 +144,12 @@ test_READ: retval = ioctl(fd, RTC_ALM_SET, &rtc_tm); if (retval == -1) { - if (errno == ENOTTY) { + if (errno == EINVAL) { fprintf(stderr, "\n...Alarm IRQs not supported.\n"); goto test_PIE; } + perror("RTC_ALM_SET ioctl"); exit(errno); } @@ -166,6 +167,12 @@ test_READ: /* Enable alarm interrupts */ retval = ioctl(fd, RTC_AIE_ON, 0); if (retval == -1) { + if (errno == EINVAL) { + fprintf(stderr, + "\n...Alarm IRQs not supported.\n"); + goto test_PIE; + } + perror("RTC_AIE_ON ioctl"); exit(errno); } @@ -193,7 +200,7 @@ test_PIE: retval = ioctl(fd, RTC_IRQP_READ, &tmp); if (retval == -1) { /* not all RTCs support periodic IRQs */ - if (errno == ENOTTY) { + if (errno == EINVAL) { fprintf(stderr, "\nNo periodic IRQ support\n"); goto done; } @@ -211,7 +218,7 @@ test_PIE: retval = ioctl(fd, RTC_IRQP_SET, tmp); if (retval == -1) { /* not all RTCs can change their periodic IRQ rate */ - if (errno == ENOTTY) { + if (errno == EINVAL) { fprintf(stderr, "\n...Periodic IRQ rate is fixed\n"); goto done; diff --git a/tools/testing/selftests/timers/set-tz.c b/tools/testing/selftests/timers/set-tz.c new file mode 100644 index 000000000000..f4184928b16b --- /dev/null +++ b/tools/testing/selftests/timers/set-tz.c @@ -0,0 +1,119 @@ +/* Set tz value + * by: John Stultz <john.stultz@linaro.org> + * (C) Copyright Linaro 2016 + * Licensed under the GPLv2 + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <sys/time.h> +#include <sys/timex.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> +#ifdef KTEST +#include "../kselftest.h" +#else +static inline int ksft_exit_pass(void) +{ + exit(0); +} +static inline int ksft_exit_fail(void) +{ + exit(1); +} +#endif + +int set_tz(int min, int dst) +{ + struct timezone tz; + + tz.tz_minuteswest = min; + tz.tz_dsttime = dst; + + return settimeofday(0, &tz); +} + +int get_tz_min(void) +{ + struct timezone tz; + struct timeval tv; + + memset(&tz, 0, sizeof(tz)); + gettimeofday(&tv, &tz); + return tz.tz_minuteswest; +} + +int get_tz_dst(void) +{ + struct timezone tz; + struct timeval tv; + + memset(&tz, 0, sizeof(tz)); + gettimeofday(&tv, &tz); + return tz.tz_dsttime; +} + +int main(int argc, char **argv) +{ + int i, ret; + int min, dst; + + min = get_tz_min(); + dst = get_tz_dst(); + printf("tz_minuteswest started at %i, dst at %i\n", min, dst); + + printf("Checking tz_minuteswest can be properly set: "); + for (i = -15*60; i < 15*60; i += 30) { + ret = set_tz(i, dst); + ret = get_tz_min(); + if (ret != i) { + printf("[FAILED] expected: %i got %i\n", i, ret); + goto err; + } + } + printf("[OK]\n"); + + printf("Checking invalid tz_minuteswest values are caught: "); + + if (!set_tz(-15*60-1, dst)) { + printf("[FAILED] %i didn't return failure!\n", -15*60-1); + goto err; + } + + if (!set_tz(15*60+1, dst)) { + printf("[FAILED] %i didn't return failure!\n", 15*60+1); + goto err; + } + + if (!set_tz(-24*60, dst)) { + printf("[FAILED] %i didn't return failure!\n", -24*60); + goto err; + } + + if (!set_tz(24*60, dst)) { + printf("[FAILED] %i didn't return failure!\n", 24*60); + goto err; + } + + printf("[OK]\n"); + + set_tz(min, dst); + return ksft_exit_pass(); + +err: + set_tz(min, dst); + return ksft_exit_fail(); +} diff --git a/tools/testing/selftests/vm/compaction_test.c b/tools/testing/selftests/vm/compaction_test.c index 00c4f65d12da..6d1437f895b8 100644 --- a/tools/testing/selftests/vm/compaction_test.c +++ b/tools/testing/selftests/vm/compaction_test.c @@ -101,7 +101,7 @@ int check_compaction(unsigned long mem_free, unsigned int hugepage_size) /* Start with the initial condition of 0 huge pages*/ if (write(fd, "0", sizeof(char)) != sizeof(char)) { - perror("Failed to write to /proc/sys/vm/nr_hugepages\n"); + perror("Failed to write 0 to /proc/sys/vm/nr_hugepages\n"); goto close_fd; } @@ -110,14 +110,14 @@ int check_compaction(unsigned long mem_free, unsigned int hugepage_size) /* Request a large number of huge pages. The Kernel will allocate as much as it can */ if (write(fd, "100000", (6*sizeof(char))) != (6*sizeof(char))) { - perror("Failed to write to /proc/sys/vm/nr_hugepages\n"); + perror("Failed to write 100000 to /proc/sys/vm/nr_hugepages\n"); goto close_fd; } lseek(fd, 0, SEEK_SET); if (read(fd, nr_hugepages, sizeof(nr_hugepages)) <= 0) { - perror("Failed to read from /proc/sys/vm/nr_hugepages\n"); + perror("Failed to re-read from /proc/sys/vm/nr_hugepages\n"); goto close_fd; } @@ -138,7 +138,7 @@ int check_compaction(unsigned long mem_free, unsigned int hugepage_size) if (write(fd, initial_nr_hugepages, strlen(initial_nr_hugepages)) != strlen(initial_nr_hugepages)) { - perror("Failed to write to /proc/sys/vm/nr_hugepages\n"); + perror("Failed to write value to /proc/sys/vm/nr_hugepages\n"); goto close_fd; } diff --git a/tools/testing/selftests/vm/on-fault-limit.c b/tools/testing/selftests/vm/on-fault-limit.c index 245acccce42d..0ae458f32fdb 100644 --- a/tools/testing/selftests/vm/on-fault-limit.c +++ b/tools/testing/selftests/vm/on-fault-limit.c @@ -20,7 +20,7 @@ static int test_limit(void) return ret; } - if (mlockall(MCL_CURRENT | MCL_ONFAULT | MCL_FUTURE)) { + if (mlockall(MCL_ONFAULT | MCL_FUTURE)) { perror("mlockall"); return ret; } |