diff options
Diffstat (limited to 'tools')
132 files changed, 6889 insertions, 774 deletions
diff --git a/tools/Makefile b/tools/Makefile index daa8fb3e4363..00caacd3ed92 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -17,6 +17,7 @@ help: @echo ' hv - tools used when in Hyper-V clients' @echo ' iio - IIO tools' @echo ' kvm_stat - top-like utility for displaying kvm statistics' + @echo ' leds - LEDs tools' @echo ' lguest - a minimal 32-bit x86 hypervisor' @echo ' net - misc networking tools' @echo ' perf - Linux performance measurement and analysis tool' @@ -56,7 +57,7 @@ acpi: FORCE cpupower: FORCE $(call descend,power/$@) -cgroup firewire hv guest spi usb virtio vm net iio gpio objtool: FORCE +cgroup firewire hv guest spi usb virtio vm net iio gpio objtool leds: FORCE $(call descend,$@) liblockdep: FORCE @@ -126,7 +127,7 @@ acpi_clean: cpupower_clean: $(call descend,power/cpupower,clean) -cgroup_clean hv_clean firewire_clean lguest_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean gpio_clean objtool_clean: +cgroup_clean hv_clean firewire_clean lguest_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean gpio_clean objtool_clean leds_clean: $(call descend,$(@:_clean=),clean) liblockdep_clean: @@ -164,6 +165,6 @@ clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_cle perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \ vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \ - gpio_clean objtool_clean + gpio_clean objtool_clean leds_clean .PHONY: FORCE diff --git a/tools/gpio/gpio-hammer.c b/tools/gpio/gpio-hammer.c index 37b3f141053d..f1eab587dfea 100644 --- a/tools/gpio/gpio-hammer.c +++ b/tools/gpio/gpio-hammer.c @@ -23,54 +23,31 @@ #include <getopt.h> #include <sys/ioctl.h> #include <linux/gpio.h> +#include "gpio-utils.h" int hammer_device(const char *device_name, unsigned int *lines, int nlines, unsigned int loops) { - struct gpiohandle_request req; struct gpiohandle_data data; - char *chrdev_name; char swirr[] = "-\\|/"; int fd; int ret; int i, j; unsigned int iteration = 0; - ret = asprintf(&chrdev_name, "/dev/%s", device_name); + memset(&data.values, 0, sizeof(data.values)); + ret = gpiotools_request_linehandle(device_name, lines, nlines, + GPIOHANDLE_REQUEST_OUTPUT, &data, + "gpio-hammler"); if (ret < 0) - return -ENOMEM; + goto exit_error; + else + fd = ret; - fd = open(chrdev_name, 0); - if (fd == -1) { - ret = -errno; - fprintf(stderr, "Failed to open %s\n", chrdev_name); - goto exit_close_error; - } - - /* Request lines as output */ - for (i = 0; i < nlines; i++) - req.lineoffsets[i] = lines[i]; - req.flags = GPIOHANDLE_REQUEST_OUTPUT; /* Request as output */ - strcpy(req.consumer_label, "gpio-hammer"); - req.lines = nlines; - ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req); - if (ret == -1) { - ret = -errno; - fprintf(stderr, "Failed to issue GET LINEHANDLE " - "IOCTL (%d)\n", - ret); + ret = gpiotools_get_values(fd, &data); + if (ret < 0) goto exit_close_error; - } - /* Read initial states */ - ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data); - if (ret == -1) { - ret = -errno; - fprintf(stderr, "Failed to issue GPIOHANDLE GET LINE " - "VALUES IOCTL (%d)\n", - ret); - goto exit_close_error; - } fprintf(stdout, "Hammer lines ["); for (i = 0; i < nlines; i++) { fprintf(stdout, "%d", lines[i]); @@ -92,23 +69,14 @@ int hammer_device(const char *device_name, unsigned int *lines, int nlines, for (i = 0; i < nlines; i++) data.values[i] = !data.values[i]; - ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data); - if (ret == -1) { - ret = -errno; - fprintf(stderr, "Failed to issue GPIOHANDLE SET LINE " - "VALUES IOCTL (%d)\n", - ret); + ret = gpiotools_set_values(fd, &data); + if (ret < 0) goto exit_close_error; - } + /* Re-read values to get status */ - ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data); - if (ret == -1) { - ret = -errno; - fprintf(stderr, "Failed to issue GPIOHANDLE GET LINE " - "VALUES IOCTL (%d)\n", - ret); + ret = gpiotools_get_values(fd, &data); + if (ret < 0) goto exit_close_error; - } fprintf(stdout, "[%c] ", swirr[j]); j++; @@ -132,9 +100,8 @@ int hammer_device(const char *device_name, unsigned int *lines, int nlines, ret = 0; exit_close_error: - if (close(fd) == -1) - perror("Failed to close GPIO character device file"); - free(chrdev_name); + gpiotools_release_linehandle(fd); +exit_error: return ret; } diff --git a/tools/gpio/gpio-utils.c b/tools/gpio/gpio-utils.c index 8208718f2c99..b86a32d90d88 100644 --- a/tools/gpio/gpio-utils.c +++ b/tools/gpio/gpio-utils.c @@ -2,10 +2,266 @@ * GPIO tools - helpers library for the GPIO tools * * Copyright (C) 2015 Linus Walleij + * Copyright (C) 2016 Bamvor Jian Zhang * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. */ +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <getopt.h> +#include <sys/ioctl.h> +#include <linux/gpio.h> #include "gpio-utils.h" + +#define COMSUMER "gpio-utils" + +/** + * doc: Operation of gpio + * + * Provide the api of gpiochip for chardev interface. There are two + * types of api. The first one provide as same function as each + * ioctl, including request and release for lines of gpio, read/write + * the value of gpio. If the user want to do lots of read and write of + * lines of gpio, user should use this type of api. + * + * The second one provide the easy to use api for user. Each of the + * following api will request gpio lines, do the operation and then + * release these lines. + */ +/** + * gpiotools_request_linehandle() - request gpio lines in a gpiochip + * @device_name: The name of gpiochip without prefix "/dev/", + * such as "gpiochip0" + * @lines: An array desired lines, specified by offset + * index for the associated GPIO device. + * @nline: The number of lines to request. + * @flag: The new flag for requsted gpio. Reference + * "linux/gpio.h" for the meaning of flag. + * @data: Default value will be set to gpio when flag is + * GPIOHANDLE_REQUEST_OUTPUT. + * @consumer_label: The name of consumer, such as "sysfs", + * "powerkey". This is useful for other users to + * know who is using. + * + * Request gpio lines through the ioctl provided by chardev. User + * could call gpiotools_set_values() and gpiotools_get_values() to + * read and write respectively through the returned fd. Call + * gpiotools_release_linehandle() to release these lines after that. + * + * Return: On success return the fd; + * On failure return the errno. + */ +int gpiotools_request_linehandle(const char *device_name, unsigned int *lines, + unsigned int nlines, unsigned int flag, + struct gpiohandle_data *data, + const char *consumer_label) +{ + struct gpiohandle_request req; + char *chrdev_name; + int fd; + int i; + int ret; + + ret = asprintf(&chrdev_name, "/dev/%s", device_name); + if (ret < 0) + return -ENOMEM; + + fd = open(chrdev_name, 0); + if (fd == -1) { + ret = -errno; + fprintf(stderr, "Failed to open %s\n", chrdev_name); + goto exit_close_error; + } + + for (i = 0; i < nlines; i++) + req.lineoffsets[i] = lines[i]; + + req.flags = flag; + strcpy(req.consumer_label, consumer_label); + req.lines = nlines; + if (flag & GPIOHANDLE_REQUEST_OUTPUT) + memcpy(req.default_values, data, sizeof(req.default_values)); + + ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req); + if (ret == -1) { + ret = -errno; + fprintf(stderr, "Failed to issue GET LINEHANDLE IOCTL (%d)\n", + ret); + } + +exit_close_error: + if (close(fd) == -1) + perror("Failed to close GPIO character device file"); + free(chrdev_name); + return ret < 0 ? ret : req.fd; +} +/** + * gpiotools_set_values(): Set the value of gpio(s) + * @fd: The fd returned by + * gpiotools_request_linehandle(). + * @data: The array of values want to set. + * + * Return: On success return 0; + * On failure return the errno. + */ +int gpiotools_set_values(const int fd, struct gpiohandle_data *data) +{ + int ret; + + ret = ioctl(fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, data); + if (ret == -1) { + ret = -errno; + fprintf(stderr, "Failed to issue %s (%d)\n", + "GPIOHANDLE_SET_LINE_VALUES_IOCTL", ret); + } + + return ret; +} + +/** + * gpiotools_get_values(): Get the value of gpio(s) + * @fd: The fd returned by + * gpiotools_request_linehandle(). + * @data: The array of values get from hardware. + * + * Return: On success return 0; + * On failure return the errno. + */ +int gpiotools_get_values(const int fd, struct gpiohandle_data *data) +{ + int ret; + + ret = ioctl(fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, data); + if (ret == -1) { + ret = -errno; + fprintf(stderr, "Failed to issue %s (%d)\n", + "GPIOHANDLE_GET_LINE_VALUES_IOCTL", ret); + } + + return ret; +} + +/** + * gpiotools_release_linehandle(): Release the line(s) of gpiochip + * @fd: The fd returned by + * gpiotools_request_linehandle(). + * + * Return: On success return 0; + * On failure return the errno. + */ +int gpiotools_release_linehandle(const int fd) +{ + int ret; + + ret = close(fd); + if (ret == -1) { + perror("Failed to close GPIO LINEHANDLE device file"); + ret = -errno; + } + + return ret; +} + +/** + * gpiotools_get(): Get value from specific line + * @device_name: The name of gpiochip without prefix "/dev/", + * such as "gpiochip0" + * @line: number of line, such as 2. + * + * Return: On success return 0; + * On failure return the errno. + */ +int gpiotools_get(const char *device_name, unsigned int line) +{ + struct gpiohandle_data data; + unsigned int lines[] = {line}; + + gpiotools_gets(device_name, lines, 1, &data); + return data.values[0]; +} + + +/** + * gpiotools_gets(): Get values from specific lines. + * @device_name: The name of gpiochip without prefix "/dev/", + * such as "gpiochip0". + * @lines: An array desired lines, specified by offset + * index for the associated GPIO device. + * @nline: The number of lines to request. + * @data: The array of values get from gpiochip. + * + * Return: On success return 0; + * On failure return the errno. + */ +int gpiotools_gets(const char *device_name, unsigned int *lines, + unsigned int nlines, struct gpiohandle_data *data) +{ + int fd; + int ret; + int ret_close; + + ret = gpiotools_request_linehandle(device_name, lines, nlines, + GPIOHANDLE_REQUEST_INPUT, data, + COMSUMER); + if (ret < 0) + return ret; + + fd = ret; + ret = gpiotools_get_values(fd, data); + ret_close = gpiotools_release_linehandle(fd); + return ret < 0 ? ret : ret_close; +} + +/** + * gpiotools_set(): Set value to specific line + * @device_name: The name of gpiochip without prefix "/dev/", + * such as "gpiochip0" + * @line: number of line, such as 2. + * @value: The value of gpio, must be 0(low) or 1(high). + * + * Return: On success return 0; + * On failure return the errno. + */ +int gpiotools_set(const char *device_name, unsigned int line, + unsigned int value) +{ + struct gpiohandle_data data; + unsigned int lines[] = {line}; + + data.values[0] = value; + return gpiotools_sets(device_name, lines, 1, &data); +} + +/** + * gpiotools_sets(): Set values to specific lines. + * @device_name: The name of gpiochip without prefix "/dev/", + * such as "gpiochip0". + * @lines: An array desired lines, specified by offset + * index for the associated GPIO device. + * @nline: The number of lines to request. + * @data: The array of values set to gpiochip, must be + * 0(low) or 1(high). + * + * Return: On success return 0; + * On failure return the errno. + */ +int gpiotools_sets(const char *device_name, unsigned int *lines, + unsigned int nlines, struct gpiohandle_data *data) +{ + int ret; + + ret = gpiotools_request_linehandle(device_name, lines, nlines, + GPIOHANDLE_REQUEST_OUTPUT, data, + COMSUMER); + if (ret < 0) + return ret; + + return gpiotools_release_linehandle(ret); +} diff --git a/tools/gpio/gpio-utils.h b/tools/gpio/gpio-utils.h index 5f57133b8c04..344ea041f8d4 100644 --- a/tools/gpio/gpio-utils.h +++ b/tools/gpio/gpio-utils.h @@ -24,4 +24,20 @@ static inline int check_prefix(const char *str, const char *prefix) strncmp(str, prefix, strlen(prefix)) == 0; } +int gpiotools_request_linehandle(const char *device_name, unsigned int *lines, + unsigned int nlines, unsigned int flag, + struct gpiohandle_data *data, + const char *consumer_label); +int gpiotools_set_values(const int fd, struct gpiohandle_data *data); +int gpiotools_get_values(const int fd, struct gpiohandle_data *data); +int gpiotools_release_linehandle(const int fd); + +int gpiotools_get(const char *device_name, unsigned int line); +int gpiotools_gets(const char *device_name, unsigned int *lines, + unsigned int nlines, struct gpiohandle_data *data); +int gpiotools_set(const char *device_name, unsigned int line, + unsigned int value); +int gpiotools_sets(const char *device_name, unsigned int *lines, + unsigned int nlines, struct gpiohandle_data *data); + #endif /* _GPIO_UTILS_H_ */ diff --git a/tools/hv/Makefile b/tools/hv/Makefile index a8c4644022a6..0d1e61b81844 100644 --- a/tools/hv/Makefile +++ b/tools/hv/Makefile @@ -1,9 +1,8 @@ # Makefile for Hyper-V tools CC = $(CROSS_COMPILE)gcc -PTHREAD_LIBS = -lpthread WARNINGS = -Wall -Wextra -CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS) $(shell getconf LFS_CFLAGS) +CFLAGS = $(WARNINGS) -g $(shell getconf LFS_CFLAGS) CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include diff --git a/tools/hv/hv_fcopy_daemon.c b/tools/hv/hv_fcopy_daemon.c index fdc9ca4c0356..26ae609a9448 100644 --- a/tools/hv/hv_fcopy_daemon.c +++ b/tools/hv/hv_fcopy_daemon.c @@ -18,21 +18,14 @@ #include <sys/types.h> -#include <sys/socket.h> -#include <sys/poll.h> -#include <linux/types.h> -#include <linux/kdev_t.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> -#include <string.h> -#include <ctype.h> #include <errno.h> #include <linux/hyperv.h> #include <syslog.h> #include <sys/stat.h> #include <fcntl.h> -#include <dirent.h> #include <getopt.h> static int target_fd; diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index bc7adb84e679..f1758fcbc37d 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -22,8 +22,6 @@ */ -#include <sys/types.h> -#include <sys/socket.h> #include <sys/poll.h> #include <sys/utsname.h> #include <stdio.h> @@ -34,7 +32,6 @@ #include <errno.h> #include <arpa/inet.h> #include <linux/hyperv.h> -#include <linux/netlink.h> #include <ifaddrs.h> #include <netdb.h> #include <syslog.h> @@ -96,13 +93,13 @@ static struct utsname uts_buf; #define KVP_CONFIG_LOC "/var/lib/hyperv" +#ifndef KVP_SCRIPTS_PATH +#define KVP_SCRIPTS_PATH "/usr/libexec/hypervkvpd/" +#endif + #define MAX_FILE_NAME 100 #define ENTRIES_PER_BLOCK 50 -#ifndef SOL_NETLINK -#define SOL_NETLINK 270 -#endif - struct kvp_record { char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; char value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; @@ -702,7 +699,7 @@ static char *kvp_mac_to_if_name(char *mac) if (dir == NULL) return NULL; - snprintf(dev_id, sizeof(dev_id), kvp_net_dir); + snprintf(dev_id, sizeof(dev_id), "%s", kvp_net_dir); q = dev_id + strlen(kvp_net_dir); while ((entry = readdir(dir)) != NULL) { @@ -825,7 +822,7 @@ static void kvp_get_ipconfig_info(char *if_name, * . */ - sprintf(cmd, "%s", "hv_get_dns_info"); + sprintf(cmd, KVP_SCRIPTS_PATH "%s", "hv_get_dns_info"); /* * Execute the command to gather DNS info. @@ -842,7 +839,7 @@ static void kvp_get_ipconfig_info(char *if_name, * Enabled: DHCP enabled. */ - sprintf(cmd, "%s %s", "hv_get_dhcp_info", if_name); + sprintf(cmd, KVP_SCRIPTS_PATH "%s %s", "hv_get_dhcp_info", if_name); file = popen(cmd, "r"); if (file == NULL) @@ -1348,7 +1345,8 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) * invoke the external script to do its magic. */ - snprintf(cmd, sizeof(cmd), "%s %s", "hv_set_ifconfig", if_file); + snprintf(cmd, sizeof(cmd), KVP_SCRIPTS_PATH "%s %s", + "hv_set_ifconfig", if_file); if (system(cmd)) { syslog(LOG_ERR, "Failed to execute cmd '%s'; error: %d %s", cmd, errno, strerror(errno)); diff --git a/tools/iio/iio_generic_buffer.c b/tools/iio/iio_generic_buffer.c index f39c0e9c0d5c..f0c6f54a8b2f 100644 --- a/tools/iio/iio_generic_buffer.c +++ b/tools/iio/iio_generic_buffer.c @@ -247,6 +247,7 @@ void print_usage(void) fprintf(stderr, "Usage: generic_buffer [options]...\n" "Capture, convert and output data from IIO device buffer\n" " -a Auto-activate all available channels\n" + " -A Force-activate ALL channels\n" " -c <n> Do n conversions\n" " -e Disable wait for event (new data)\n" " -g Use trigger-less mode\n" @@ -347,16 +348,22 @@ int main(int argc, char **argv) int noevents = 0; int notrigger = 0; char *dummy; + bool force_autochannels = false; struct iio_channel_info *channels = NULL; register_cleanup(); - while ((c = getopt_long(argc, argv, "ac:egl:n:N:t:T:w:", longopts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "aAc:egl:n:N:t:T:w:?", longopts, + NULL)) != -1) { switch (c) { case 'a': autochannels = AUTOCHANNELS_ENABLED; break; + case 'A': + autochannels = AUTOCHANNELS_ENABLED; + force_autochannels = true; + break; case 'c': errno = 0; num_loops = strtoul(optarg, &dummy, 10); @@ -519,15 +526,16 @@ int main(int argc, char **argv) "diag %s\n", dev_dir_name); goto error; } - if (num_channels && autochannels == AUTOCHANNELS_ENABLED) { + if (num_channels && autochannels == AUTOCHANNELS_ENABLED && + !force_autochannels) { fprintf(stderr, "Auto-channels selected but some channels " "are already activated in sysfs\n"); fprintf(stderr, "Proceeding without activating any channels\n"); } - if (!num_channels && autochannels == AUTOCHANNELS_ENABLED) { - fprintf(stderr, - "No channels are enabled, enabling all channels\n"); + if ((!num_channels && autochannels == AUTOCHANNELS_ENABLED) || + (autochannels == AUTOCHANNELS_ENABLED && force_autochannels)) { + fprintf(stderr, "Enabling all channels\n"); ret = enable_disable_all_channels(dev_dir_name, 1); if (ret) { diff --git a/tools/include/asm/bug.h b/tools/include/asm/bug.h index 9e5f4846967f..beda1a884b50 100644 --- a/tools/include/asm/bug.h +++ b/tools/include/asm/bug.h @@ -12,6 +12,17 @@ unlikely(__ret_warn_on); \ }) +#define WARN_ON_ONCE(condition) ({ \ + static int __warned; \ + int __ret_warn_once = !!(condition); \ + \ + if (unlikely(__ret_warn_once && !__warned)) { \ + __warned = true; \ + WARN_ON(1); \ + } \ + unlikely(__ret_warn_once); \ +}) + #define WARN_ONCE(condition, format...) ({ \ static int __warned; \ int __ret_warn_once = !!(condition); \ diff --git a/tools/include/linux/bitmap.h b/tools/include/linux/bitmap.h index 43c1c5021e4b..eef41d500e9e 100644 --- a/tools/include/linux/bitmap.h +++ b/tools/include/linux/bitmap.h @@ -35,6 +35,32 @@ static inline void bitmap_zero(unsigned long *dst, int nbits) } } +static inline void bitmap_fill(unsigned long *dst, unsigned int nbits) +{ + unsigned int nlongs = BITS_TO_LONGS(nbits); + if (!small_const_nbits(nbits)) { + unsigned int len = (nlongs - 1) * sizeof(unsigned long); + memset(dst, 0xff, len); + } + dst[nlongs - 1] = BITMAP_LAST_WORD_MASK(nbits); +} + +static inline int bitmap_empty(const unsigned long *src, unsigned nbits) +{ + if (small_const_nbits(nbits)) + return ! (*src & BITMAP_LAST_WORD_MASK(nbits)); + + return find_first_bit(src, nbits) == nbits; +} + +static inline int bitmap_full(const unsigned long *src, unsigned int nbits) +{ + if (small_const_nbits(nbits)) + return ! (~(*src) & BITMAP_LAST_WORD_MASK(nbits)); + + return find_first_zero_bit(src, nbits) == nbits; +} + static inline int bitmap_weight(const unsigned long *src, int nbits) { if (small_const_nbits(nbits)) diff --git a/tools/include/linux/types.h b/tools/include/linux/types.h index 8ebf6278b2ef..c24b3e3ae296 100644 --- a/tools/include/linux/types.h +++ b/tools/include/linux/types.h @@ -42,11 +42,7 @@ typedef __s8 s8; #else #define __bitwise__ #endif -#ifdef __CHECK_ENDIAN__ #define __bitwise __bitwise__ -#else -#define __bitwise -#endif #define __force #define __user diff --git a/tools/include/uapi/linux/hw_breakpoint.h b/tools/include/uapi/linux/hw_breakpoint.h index b04000a2296a..2b65efd19a46 100644 --- a/tools/include/uapi/linux/hw_breakpoint.h +++ b/tools/include/uapi/linux/hw_breakpoint.h @@ -4,7 +4,11 @@ enum { HW_BREAKPOINT_LEN_1 = 1, HW_BREAKPOINT_LEN_2 = 2, + HW_BREAKPOINT_LEN_3 = 3, HW_BREAKPOINT_LEN_4 = 4, + HW_BREAKPOINT_LEN_5 = 5, + HW_BREAKPOINT_LEN_6 = 6, + HW_BREAKPOINT_LEN_7 = 7, HW_BREAKPOINT_LEN_8 = 8, }; diff --git a/tools/leds/.gitignore b/tools/leds/.gitignore new file mode 100644 index 000000000000..ac96d9f53dfc --- /dev/null +++ b/tools/leds/.gitignore @@ -0,0 +1 @@ +uledmon diff --git a/tools/leds/Makefile b/tools/leds/Makefile new file mode 100644 index 000000000000..c03a79ebf9c8 --- /dev/null +++ b/tools/leds/Makefile @@ -0,0 +1,13 @@ +# Makefile for LEDs tools + +CC = $(CROSS_COMPILE)gcc +CFLAGS = -Wall -Wextra -g -I../../include/uapi + +all: uledmon +%: %.c + $(CC) $(CFLAGS) -o $@ $^ + +clean: + $(RM) uledmon + +.PHONY: all clean diff --git a/tools/leds/uledmon.c b/tools/leds/uledmon.c new file mode 100644 index 000000000000..25cbc7acf50a --- /dev/null +++ b/tools/leds/uledmon.c @@ -0,0 +1,63 @@ +/* + * uledmon.c + * + * This program creates a new userspace LED class device and monitors it. A + * timestamp and brightness value is printed each time the brightness changes. + * + * Usage: uledmon <device-name> + * + * <device-name> is the name of the LED class device to be created. Pressing + * CTRL+C will exit. + */ + +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <linux/uleds.h> + +int main(int argc, char const *argv[]) +{ + struct uleds_user_dev uleds_dev; + int fd, ret; + int brightness; + struct timespec ts; + + if (argc != 2) { + fprintf(stderr, "Requires <device-name> argument\n"); + return 1; + } + + strncpy(uleds_dev.name, argv[1], LED_MAX_NAME_SIZE); + uleds_dev.max_brightness = 100; + + fd = open("/dev/uleds", O_RDWR); + if (fd == -1) { + perror("Failed to open /dev/uleds"); + return 1; + } + + ret = write(fd, &uleds_dev, sizeof(uleds_dev)); + if (ret == -1) { + perror("Failed to write to /dev/uleds"); + close(fd); + return 1; + } + + while (1) { + ret = read(fd, &brightness, sizeof(brightness)); + if (ret == -1) { + perror("Failed to read from /dev/uleds"); + close(fd); + return 1; + } + clock_gettime(CLOCK_MONOTONIC, &ts); + printf("[%ld.%09ld] %u\n", ts.tv_sec, ts.tv_nsec, brightness); + } + + close(fd); + + return 0; +} diff --git a/tools/power/acpi/os_specific/service_layers/osunixxf.c b/tools/power/acpi/os_specific/service_layers/osunixxf.c index 8d8003c919d4..10648aaf6164 100644 --- a/tools/power/acpi/os_specific/service_layers/osunixxf.c +++ b/tools/power/acpi/os_specific/service_layers/osunixxf.c @@ -646,8 +646,12 @@ acpi_os_create_semaphore(u32 max_units, } #ifdef __APPLE__ { - char *semaphore_name = tmpnam(NULL); + static int semaphore_count = 0; + char semaphore_name[32]; + snprintf(semaphore_name, sizeof(semaphore_name), "acpi_sem_%d", + semaphore_count++); + printf("%s\n", semaphore_name); sem = sem_open(semaphore_name, O_EXCL | O_CREAT, 0755, initial_units); @@ -692,10 +696,15 @@ acpi_status acpi_os_delete_semaphore(acpi_handle handle) if (!sem) { return (AE_BAD_PARAMETER); } - +#ifdef __APPLE__ + if (sem_close(sem) == -1) { + return (AE_BAD_PARAMETER); + } +#else if (sem_destroy(sem) == -1) { return (AE_BAD_PARAMETER); } +#endif return (AE_OK); } diff --git a/tools/power/acpi/tools/ec/ec_access.c b/tools/power/acpi/tools/ec/ec_access.c index 6b8aaed44f2c..5f50642386db 100644 --- a/tools/power/acpi/tools/ec/ec_access.c +++ b/tools/power/acpi/tools/ec/ec_access.c @@ -46,7 +46,7 @@ void usage(char progname[], int exit_status) puts("\t-b offset : Read value at byte_offset (in hex)"); puts("\t-w offset -v value : Write value at byte_offset"); puts("\t-h : Print this help\n\n"); - puts("Offsets and values are in hexadecimal number sytem."); + puts("Offsets and values are in hexadecimal number system."); puts("The offset and value must be between 0 and 0xff."); exit(exit_status); } diff --git a/tools/spi/spidev_test.c b/tools/spi/spidev_test.c index f046b77cfefe..816f119c9b7b 100644 --- a/tools/spi/spidev_test.c +++ b/tools/spi/spidev_test.c @@ -315,7 +315,7 @@ static void transfer_file(int fd, char *filename) pabort("can't stat input file"); tx_fd = open(filename, O_RDONLY); - if (fd < 0) + if (tx_fd < 0) pabort("can't open input file"); tx = malloc(sb.st_size); diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index d08e214ec6e7..be93ab02b490 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -719,14 +719,14 @@ sub set_value { if ($buildonly && $lvalue =~ /^TEST_TYPE(\[.*\])?$/ && $prvalue ne "build") { # Note if a test is something other than build, then we - # will need other manditory options. + # will need other mandatory options. if ($prvalue ne "install") { # for bisect, we need to check BISECT_TYPE if ($prvalue ne "bisect") { $buildonly = 0; } } else { - # install still limits some manditory options. + # install still limits some mandatory options. $buildonly = 2; } } @@ -735,7 +735,7 @@ sub set_value { if ($prvalue ne "install") { $buildonly = 0; } else { - # install still limits some manditory options. + # install still limits some mandatory options. $buildonly = 2; } } @@ -3989,7 +3989,7 @@ sub make_min_config { } } - # Save off all the current mandidory configs + # Save off all the current mandatory configs open (OUT, ">$temp_config") or die "Can't write to $temp_config"; foreach my $config (keys %keep_configs) { diff --git a/tools/testing/radix-tree/Makefile b/tools/testing/radix-tree/Makefile index f2e07f2fd4b4..3635e4d3eca7 100644 --- a/tools/testing/radix-tree/Makefile +++ b/tools/testing/radix-tree/Makefile @@ -1,10 +1,14 @@ -CFLAGS += -I. -g -O2 -Wall -D_LGPL_SOURCE +CFLAGS += -I. -I../../include -g -O2 -Wall -D_LGPL_SOURCE LDFLAGS += -lpthread -lurcu TARGETS = main OFILES = main.o radix-tree.o linux.o test.o tag_check.o find_next_bit.o \ regression1.o regression2.o regression3.o multiorder.o \ - iteration_check.o + iteration_check.o benchmark.o + +ifdef BENCHMARK + CFLAGS += -DBENCHMARK=1 +endif targets: $(TARGETS) @@ -14,7 +18,12 @@ main: $(OFILES) clean: $(RM) -f $(TARGETS) *.o radix-tree.c -$(OFILES): *.h */*.h ../../../include/linux/radix-tree.h ../../include/linux/*.h +find_next_bit.o: ../../lib/find_bit.c + $(CC) $(CFLAGS) -c -o $@ $< + +$(OFILES): *.h */*.h \ + ../../include/linux/*.h \ + ../../../include/linux/radix-tree.h radix-tree.c: ../../../lib/radix-tree.c sed -e 's/^static //' -e 's/__always_inline //' -e 's/inline //' < $< > $@ diff --git a/tools/testing/radix-tree/benchmark.c b/tools/testing/radix-tree/benchmark.c new file mode 100644 index 000000000000..215ca86c7605 --- /dev/null +++ b/tools/testing/radix-tree/benchmark.c @@ -0,0 +1,98 @@ +/* + * benchmark.c: + * Author: Konstantin Khlebnikov <koct9i@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 <linux/radix-tree.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <time.h> +#include "test.h" + +#define NSEC_PER_SEC 1000000000L + +static long long benchmark_iter(struct radix_tree_root *root, bool tagged) +{ + volatile unsigned long sink = 0; + struct radix_tree_iter iter; + struct timespec start, finish; + long long nsec; + int l, loops = 1; + void **slot; + +#ifdef BENCHMARK +again: +#endif + clock_gettime(CLOCK_MONOTONIC, &start); + for (l = 0; l < loops; l++) { + if (tagged) { + radix_tree_for_each_tagged(slot, root, &iter, 0, 0) + sink ^= (unsigned long)slot; + } else { + radix_tree_for_each_slot(slot, root, &iter, 0) + sink ^= (unsigned long)slot; + } + } + clock_gettime(CLOCK_MONOTONIC, &finish); + + nsec = (finish.tv_sec - start.tv_sec) * NSEC_PER_SEC + + (finish.tv_nsec - start.tv_nsec); + +#ifdef BENCHMARK + if (loops == 1 && nsec * 5 < NSEC_PER_SEC) { + loops = NSEC_PER_SEC / nsec / 4 + 1; + goto again; + } +#endif + + nsec /= loops; + return nsec; +} + +static void benchmark_size(unsigned long size, unsigned long step, int order) +{ + RADIX_TREE(tree, GFP_KERNEL); + long long normal, tagged; + unsigned long index; + + for (index = 0 ; index < size ; index += step) { + item_insert_order(&tree, index, order); + radix_tree_tag_set(&tree, index, 0); + } + + tagged = benchmark_iter(&tree, true); + normal = benchmark_iter(&tree, false); + + printf("Size %ld, step %6ld, order %d tagged %10lld ns, normal %10lld ns\n", + size, step, order, tagged, normal); + + item_kill_tree(&tree); + rcu_barrier(); +} + +void benchmark(void) +{ + unsigned long size[] = {1 << 10, 1 << 20, 0}; + unsigned long step[] = {1, 2, 7, 15, 63, 64, 65, + 128, 256, 512, 12345, 0}; + int c, s; + + printf("starting benchmarks\n"); + printf("RADIX_TREE_MAP_SHIFT = %d\n", RADIX_TREE_MAP_SHIFT); + + for (c = 0; size[c]; c++) + for (s = 0; step[s]; s++) + benchmark_size(size[c], step[s], 0); + + for (c = 0; size[c]; c++) + for (s = 0; step[s]; s++) + benchmark_size(size[c], step[s] << 9, 9); +} diff --git a/tools/testing/radix-tree/find_next_bit.c b/tools/testing/radix-tree/find_next_bit.c deleted file mode 100644 index d1c2178bb2d4..000000000000 --- a/tools/testing/radix-tree/find_next_bit.c +++ /dev/null @@ -1,57 +0,0 @@ -/* find_next_bit.c: fallback find next bit implementation - * - * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * 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. - */ - -#include <linux/types.h> -#include <linux/bitops.h> - -#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) - -/* - * Find the next set bit in a memory region. - */ -unsigned long find_next_bit(const unsigned long *addr, unsigned long size, - unsigned long offset) -{ - const unsigned long *p = addr + BITOP_WORD(offset); - unsigned long result = offset & ~(BITS_PER_LONG-1); - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset %= BITS_PER_LONG; - if (offset) { - tmp = *(p++); - tmp &= (~0UL << offset); - if (size < BITS_PER_LONG) - goto found_first; - if (tmp) - goto found_middle; - size -= BITS_PER_LONG; - result += BITS_PER_LONG; - } - while (size & ~(BITS_PER_LONG-1)) { - if ((tmp = *(p++))) - goto found_middle; - result += BITS_PER_LONG; - size -= BITS_PER_LONG; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp &= (~0UL >> (BITS_PER_LONG - size)); - if (tmp == 0UL) /* Are any bits set? */ - return result + size; /* Nope. */ -found_middle: - return result + __ffs(tmp); -} diff --git a/tools/testing/radix-tree/iteration_check.c b/tools/testing/radix-tree/iteration_check.c index 9adb8e7415a6..7572b7ed930e 100644 --- a/tools/testing/radix-tree/iteration_check.c +++ b/tools/testing/radix-tree/iteration_check.c @@ -16,35 +16,50 @@ #include <pthread.h> #include "test.h" -#define NUM_THREADS 4 -#define TAG 0 +#define NUM_THREADS 5 +#define MAX_IDX 100 +#define TAG 0 +#define NEW_TAG 1 + static pthread_mutex_t tree_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_t threads[NUM_THREADS]; -RADIX_TREE(tree, GFP_KERNEL); -bool test_complete; +static unsigned int seeds[3]; +static RADIX_TREE(tree, GFP_KERNEL); +static bool test_complete; +static int max_order; /* relentlessly fill the tree with tagged entries */ static void *add_entries_fn(void *arg) { - int pgoff; + rcu_register_thread(); while (!test_complete) { - for (pgoff = 0; pgoff < 100; pgoff++) { + unsigned long pgoff; + int order; + + for (pgoff = 0; pgoff < MAX_IDX; pgoff++) { pthread_mutex_lock(&tree_lock); - if (item_insert(&tree, pgoff) == 0) - item_tag_set(&tree, pgoff, TAG); + for (order = max_order; order >= 0; order--) { + if (item_insert_order(&tree, pgoff, order) + == 0) { + item_tag_set(&tree, pgoff, TAG); + break; + } + } pthread_mutex_unlock(&tree_lock); } } + rcu_unregister_thread(); + return NULL; } /* * Iterate over the tagged entries, doing a radix_tree_iter_retry() as we find * things that have been removed and randomly resetting our iteration to the - * next chunk with radix_tree_iter_next(). Both radix_tree_iter_retry() and - * radix_tree_iter_next() cause radix_tree_next_slot() to be called with a + * next chunk with radix_tree_iter_resume(). Both radix_tree_iter_retry() and + * radix_tree_iter_resume() cause radix_tree_next_slot() to be called with a * NULL 'slot' variable. */ static void *tagged_iteration_fn(void *arg) @@ -52,17 +67,12 @@ static void *tagged_iteration_fn(void *arg) struct radix_tree_iter iter; void **slot; + rcu_register_thread(); + while (!test_complete) { rcu_read_lock(); radix_tree_for_each_tagged(slot, &tree, &iter, 0, TAG) { - void *entry; - int i; - - /* busy wait to let removals happen */ - for (i = 0; i < 1000000; i++) - ; - - entry = radix_tree_deref_slot(slot); + void *entry = radix_tree_deref_slot(slot); if (unlikely(!entry)) continue; @@ -71,20 +81,26 @@ static void *tagged_iteration_fn(void *arg) continue; } - if (rand() % 50 == 0) - slot = radix_tree_iter_next(&iter); + if (rand_r(&seeds[0]) % 50 == 0) { + slot = radix_tree_iter_resume(slot, &iter); + rcu_read_unlock(); + rcu_barrier(); + rcu_read_lock(); + } } rcu_read_unlock(); } + rcu_unregister_thread(); + return NULL; } /* * Iterate over the entries, doing a radix_tree_iter_retry() as we find things * that have been removed and randomly resetting our iteration to the next - * chunk with radix_tree_iter_next(). Both radix_tree_iter_retry() and - * radix_tree_iter_next() cause radix_tree_next_slot() to be called with a + * chunk with radix_tree_iter_resume(). Both radix_tree_iter_retry() and + * radix_tree_iter_resume() cause radix_tree_next_slot() to be called with a * NULL 'slot' variable. */ static void *untagged_iteration_fn(void *arg) @@ -92,17 +108,12 @@ static void *untagged_iteration_fn(void *arg) struct radix_tree_iter iter; void **slot; + rcu_register_thread(); + while (!test_complete) { rcu_read_lock(); radix_tree_for_each_slot(slot, &tree, &iter, 0) { - void *entry; - int i; - - /* busy wait to let removals happen */ - for (i = 0; i < 1000000; i++) - ; - - entry = radix_tree_deref_slot(slot); + void *entry = radix_tree_deref_slot(slot); if (unlikely(!entry)) continue; @@ -111,12 +122,18 @@ static void *untagged_iteration_fn(void *arg) continue; } - if (rand() % 50 == 0) - slot = radix_tree_iter_next(&iter); + if (rand_r(&seeds[1]) % 50 == 0) { + slot = radix_tree_iter_resume(slot, &iter); + rcu_read_unlock(); + rcu_barrier(); + rcu_read_lock(); + } } rcu_read_unlock(); } + rcu_unregister_thread(); + return NULL; } @@ -126,47 +143,71 @@ static void *untagged_iteration_fn(void *arg) */ static void *remove_entries_fn(void *arg) { + rcu_register_thread(); + while (!test_complete) { int pgoff; - pgoff = rand() % 100; + pgoff = rand_r(&seeds[2]) % MAX_IDX; pthread_mutex_lock(&tree_lock); item_delete(&tree, pgoff); pthread_mutex_unlock(&tree_lock); } + rcu_unregister_thread(); + + return NULL; +} + +static void *tag_entries_fn(void *arg) +{ + rcu_register_thread(); + + while (!test_complete) { + tag_tagged_items(&tree, &tree_lock, 0, MAX_IDX, 10, TAG, + NEW_TAG); + } + rcu_unregister_thread(); return NULL; } /* This is a unit test for a bug found by the syzkaller tester */ -void iteration_test(void) +void iteration_test(unsigned order, unsigned test_duration) { int i; - printf("Running iteration tests for 10 seconds\n"); + printf("Running %siteration tests for %d seconds\n", + order > 0 ? "multiorder " : "", test_duration); - srand(time(0)); + max_order = order; test_complete = false; + for (i = 0; i < 3; i++) + seeds[i] = rand(); + if (pthread_create(&threads[0], NULL, tagged_iteration_fn, NULL)) { - perror("pthread_create"); + perror("create tagged iteration thread"); exit(1); } if (pthread_create(&threads[1], NULL, untagged_iteration_fn, NULL)) { - perror("pthread_create"); + perror("create untagged iteration thread"); exit(1); } if (pthread_create(&threads[2], NULL, add_entries_fn, NULL)) { - perror("pthread_create"); + perror("create add entry thread"); exit(1); } if (pthread_create(&threads[3], NULL, remove_entries_fn, NULL)) { - perror("pthread_create"); + perror("create remove entry thread"); + exit(1); + } + if (pthread_create(&threads[4], NULL, tag_entries_fn, NULL)) { + perror("create tag entry thread"); exit(1); } - sleep(10); + sleep(test_duration); test_complete = true; for (i = 0; i < NUM_THREADS; i++) { diff --git a/tools/testing/radix-tree/linux.c b/tools/testing/radix-tree/linux.c index 154823737b20..d31ea7c9abec 100644 --- a/tools/testing/radix-tree/linux.c +++ b/tools/testing/radix-tree/linux.c @@ -1,14 +1,26 @@ #include <stdlib.h> #include <string.h> #include <malloc.h> +#include <pthread.h> #include <unistd.h> #include <assert.h> #include <linux/mempool.h> +#include <linux/poison.h> #include <linux/slab.h> +#include <linux/radix-tree.h> #include <urcu/uatomic.h> int nr_allocated; +int preempt_count; + +struct kmem_cache { + pthread_mutex_t lock; + int size; + int nr_objs; + void *objs; + void (*ctor)(void *); +}; void *mempool_alloc(mempool_t *pool, int gfp_mask) { @@ -33,19 +45,59 @@ mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, void *kmem_cache_alloc(struct kmem_cache *cachep, int flags) { - void *ret = malloc(cachep->size); - if (cachep->ctor) - cachep->ctor(ret); + struct radix_tree_node *node; + + if (flags & __GFP_NOWARN) + return NULL; + + pthread_mutex_lock(&cachep->lock); + if (cachep->nr_objs) { + cachep->nr_objs--; + node = cachep->objs; + cachep->objs = node->private_data; + pthread_mutex_unlock(&cachep->lock); + node->private_data = NULL; + } else { + pthread_mutex_unlock(&cachep->lock); + node = malloc(cachep->size); + if (cachep->ctor) + cachep->ctor(node); + } + uatomic_inc(&nr_allocated); - return ret; + return node; } void kmem_cache_free(struct kmem_cache *cachep, void *objp) { assert(objp); uatomic_dec(&nr_allocated); - memset(objp, 0, cachep->size); - free(objp); + pthread_mutex_lock(&cachep->lock); + if (cachep->nr_objs > 10) { + memset(objp, POISON_FREE, cachep->size); + free(objp); + } else { + struct radix_tree_node *node = objp; + cachep->nr_objs++; + node->private_data = cachep->objs; + cachep->objs = node; + } + pthread_mutex_unlock(&cachep->lock); +} + +void *kmalloc(size_t size, gfp_t gfp) +{ + void *ret = malloc(size); + uatomic_inc(&nr_allocated); + return ret; +} + +void kfree(void *p) +{ + if (!p) + return; + uatomic_dec(&nr_allocated); + free(p); } struct kmem_cache * @@ -54,7 +106,10 @@ kmem_cache_create(const char *name, size_t size, size_t offset, { struct kmem_cache *ret = malloc(sizeof(*ret)); + pthread_mutex_init(&ret->lock, NULL); ret->size = size; + ret->nr_objs = 0; + ret->objs = NULL; ret->ctor = ctor; return ret; } diff --git a/tools/testing/radix-tree/linux/bitops.h b/tools/testing/radix-tree/linux/bitops.h index 71d58427ab60..a13e9bc76eec 100644 --- a/tools/testing/radix-tree/linux/bitops.h +++ b/tools/testing/radix-tree/linux/bitops.h @@ -2,9 +2,14 @@ #define _ASM_GENERIC_BITOPS_NON_ATOMIC_H_ #include <linux/types.h> +#include <linux/bitops/find.h> +#include <linux/bitops/hweight.h> +#include <linux/kernel.h> -#define BITOP_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) -#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) +#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) +#define BITS_PER_BYTE 8 +#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) /** * __set_bit - Set a bit in memory @@ -17,16 +22,16 @@ */ static inline void __set_bit(int nr, volatile unsigned long *addr) { - unsigned long mask = BITOP_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); *p |= mask; } static inline void __clear_bit(int nr, volatile unsigned long *addr) { - unsigned long mask = BITOP_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); *p &= ~mask; } @@ -42,8 +47,8 @@ static inline void __clear_bit(int nr, volatile unsigned long *addr) */ static inline void __change_bit(int nr, volatile unsigned long *addr) { - unsigned long mask = BITOP_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); *p ^= mask; } @@ -59,8 +64,8 @@ static inline void __change_bit(int nr, volatile unsigned long *addr) */ static inline int __test_and_set_bit(int nr, volatile unsigned long *addr) { - unsigned long mask = BITOP_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); unsigned long old = *p; *p = old | mask; @@ -78,8 +83,8 @@ static inline int __test_and_set_bit(int nr, volatile unsigned long *addr) */ static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr) { - unsigned long mask = BITOP_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); unsigned long old = *p; *p = old & ~mask; @@ -90,8 +95,8 @@ static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr) static inline int __test_and_change_bit(int nr, volatile unsigned long *addr) { - unsigned long mask = BITOP_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); unsigned long old = *p; *p = old ^ mask; @@ -105,7 +110,7 @@ static inline int __test_and_change_bit(int nr, */ static inline int test_bit(int nr, const volatile unsigned long *addr) { - return 1UL & (addr[BITOP_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); + return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); } /** @@ -147,4 +152,9 @@ unsigned long find_next_bit(const unsigned long *addr, unsigned long size, unsigned long offset); +static inline unsigned long hweight_long(unsigned long w) +{ + return sizeof(w) == 4 ? hweight32(w) : hweight64(w); +} + #endif /* _ASM_GENERIC_BITOPS_NON_ATOMIC_H_ */ diff --git a/tools/testing/radix-tree/linux/bitops/non-atomic.h b/tools/testing/radix-tree/linux/bitops/non-atomic.h index 46a825cf2ae1..6a1bcb9d2c4a 100644 --- a/tools/testing/radix-tree/linux/bitops/non-atomic.h +++ b/tools/testing/radix-tree/linux/bitops/non-atomic.h @@ -3,7 +3,6 @@ #include <asm/types.h> -#define BITOP_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) #define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) /** @@ -17,7 +16,7 @@ */ static inline void __set_bit(int nr, volatile unsigned long *addr) { - unsigned long mask = BITOP_MASK(nr); + unsigned long mask = BIT_MASK(nr); unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); *p |= mask; @@ -25,7 +24,7 @@ static inline void __set_bit(int nr, volatile unsigned long *addr) static inline void __clear_bit(int nr, volatile unsigned long *addr) { - unsigned long mask = BITOP_MASK(nr); + unsigned long mask = BIT_MASK(nr); unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); *p &= ~mask; @@ -42,7 +41,7 @@ static inline void __clear_bit(int nr, volatile unsigned long *addr) */ static inline void __change_bit(int nr, volatile unsigned long *addr) { - unsigned long mask = BITOP_MASK(nr); + unsigned long mask = BIT_MASK(nr); unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); *p ^= mask; @@ -59,7 +58,7 @@ static inline void __change_bit(int nr, volatile unsigned long *addr) */ static inline int __test_and_set_bit(int nr, volatile unsigned long *addr) { - unsigned long mask = BITOP_MASK(nr); + unsigned long mask = BIT_MASK(nr); unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); unsigned long old = *p; @@ -78,7 +77,7 @@ static inline int __test_and_set_bit(int nr, volatile unsigned long *addr) */ static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr) { - unsigned long mask = BITOP_MASK(nr); + unsigned long mask = BIT_MASK(nr); unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); unsigned long old = *p; @@ -90,7 +89,7 @@ static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr) static inline int __test_and_change_bit(int nr, volatile unsigned long *addr) { - unsigned long mask = BITOP_MASK(nr); + unsigned long mask = BIT_MASK(nr); unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); unsigned long old = *p; diff --git a/tools/testing/radix-tree/linux/bug.h b/tools/testing/radix-tree/linux/bug.h index ccbe444977df..23b8ed52f8c8 100644 --- a/tools/testing/radix-tree/linux/bug.h +++ b/tools/testing/radix-tree/linux/bug.h @@ -1 +1 @@ -#define WARN_ON_ONCE(x) assert(x) +#include "asm/bug.h" diff --git a/tools/testing/radix-tree/linux/cpu.h b/tools/testing/radix-tree/linux/cpu.h index 7cf412103205..a45530d78107 100644 --- a/tools/testing/radix-tree/linux/cpu.h +++ b/tools/testing/radix-tree/linux/cpu.h @@ -1,21 +1 @@ - -#define hotcpu_notifier(a, b) - -#define CPU_ONLINE 0x0002 /* CPU (unsigned)v is up */ -#define CPU_UP_PREPARE 0x0003 /* CPU (unsigned)v coming up */ -#define CPU_UP_CANCELED 0x0004 /* CPU (unsigned)v NOT coming up */ -#define CPU_DOWN_PREPARE 0x0005 /* CPU (unsigned)v going down */ -#define CPU_DOWN_FAILED 0x0006 /* CPU (unsigned)v NOT going down */ -#define CPU_DEAD 0x0007 /* CPU (unsigned)v dead */ -#define CPU_POST_DEAD 0x0009 /* CPU (unsigned)v dead, cpu_hotplug - * lock is dropped */ -#define CPU_BROKEN 0x000C /* CPU (unsigned)v did not die properly, - * perhaps due to preemption. */ -#define CPU_TASKS_FROZEN 0x0010 - -#define CPU_ONLINE_FROZEN (CPU_ONLINE | CPU_TASKS_FROZEN) -#define CPU_UP_PREPARE_FROZEN (CPU_UP_PREPARE | CPU_TASKS_FROZEN) -#define CPU_UP_CANCELED_FROZEN (CPU_UP_CANCELED | CPU_TASKS_FROZEN) -#define CPU_DOWN_PREPARE_FROZEN (CPU_DOWN_PREPARE | CPU_TASKS_FROZEN) -#define CPU_DOWN_FAILED_FROZEN (CPU_DOWN_FAILED | CPU_TASKS_FROZEN) -#define CPU_DEAD_FROZEN (CPU_DEAD | CPU_TASKS_FROZEN) +#define cpuhp_setup_state_nocalls(a, b, c, d) (0) diff --git a/tools/testing/radix-tree/linux/gfp.h b/tools/testing/radix-tree/linux/gfp.h index 5201b915f631..5b09b2ce6c33 100644 --- a/tools/testing/radix-tree/linux/gfp.h +++ b/tools/testing/radix-tree/linux/gfp.h @@ -3,8 +3,24 @@ #define __GFP_BITS_SHIFT 26 #define __GFP_BITS_MASK ((gfp_t)((1 << __GFP_BITS_SHIFT) - 1)) -#define __GFP_WAIT 1 -#define __GFP_ACCOUNT 0 -#define __GFP_NOWARN 0 + +#define __GFP_HIGH 0x20u +#define __GFP_IO 0x40u +#define __GFP_FS 0x80u +#define __GFP_NOWARN 0x200u +#define __GFP_ATOMIC 0x80000u +#define __GFP_ACCOUNT 0x100000u +#define __GFP_DIRECT_RECLAIM 0x400000u +#define __GFP_KSWAPD_RECLAIM 0x2000000u + +#define __GFP_RECLAIM (__GFP_DIRECT_RECLAIM|__GFP_KSWAPD_RECLAIM) + +#define GFP_ATOMIC (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM) +#define GFP_KERNEL (__GFP_RECLAIM | __GFP_IO | __GFP_FS) + +static inline bool gfpflags_allow_blocking(const gfp_t gfp_flags) +{ + return !!(gfp_flags & __GFP_DIRECT_RECLAIM); +} #endif diff --git a/tools/testing/radix-tree/linux/kernel.h b/tools/testing/radix-tree/linux/kernel.h index be98a47b4e1b..9b43b4975d83 100644 --- a/tools/testing/radix-tree/linux/kernel.h +++ b/tools/testing/radix-tree/linux/kernel.h @@ -8,9 +8,14 @@ #include <limits.h> #include "../../include/linux/compiler.h" +#include "../../include/linux/err.h" #include "../../../include/linux/kconfig.h" +#ifdef BENCHMARK +#define RADIX_TREE_MAP_SHIFT 6 +#else #define RADIX_TREE_MAP_SHIFT 3 +#endif #ifndef NULL #define NULL 0 @@ -43,4 +48,17 @@ static inline int in_interrupt(void) { return 0; } + +/* + * This looks more complex than it should be. But we need to + * get the type for the ~ right in round_down (it needs to be + * as wide as the result!), and we want to evaluate the macro + * arguments just once each. + */ +#define __round_mask(x, y) ((__typeof__(x))((y)-1)) +#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) +#define round_down(x, y) ((x) & ~__round_mask(x, y)) + +#define xchg(ptr, x) uatomic_xchg(ptr, x) + #endif /* _KERNEL_H */ diff --git a/tools/testing/radix-tree/linux/notifier.h b/tools/testing/radix-tree/linux/notifier.h deleted file mode 100644 index 70e4797d5a46..000000000000 --- a/tools/testing/radix-tree/linux/notifier.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _NOTIFIER_H -#define _NOTIFIER_H - -struct notifier_block; - -#define NOTIFY_OK 0x0001 /* Suits me */ - -#endif diff --git a/tools/testing/radix-tree/linux/preempt.h b/tools/testing/radix-tree/linux/preempt.h index 6210672e3baa..65c04c226965 100644 --- a/tools/testing/radix-tree/linux/preempt.h +++ b/tools/testing/radix-tree/linux/preempt.h @@ -1,4 +1,4 @@ -/* */ +extern int preempt_count; -#define preempt_disable() do { } while (0) -#define preempt_enable() do { } while (0) +#define preempt_disable() uatomic_inc(&preempt_count) +#define preempt_enable() uatomic_dec(&preempt_count) diff --git a/tools/testing/radix-tree/linux/slab.h b/tools/testing/radix-tree/linux/slab.h index 6d5a34770fd4..e40337f41a38 100644 --- a/tools/testing/radix-tree/linux/slab.h +++ b/tools/testing/radix-tree/linux/slab.h @@ -7,15 +7,8 @@ #define SLAB_PANIC 2 #define SLAB_RECLAIM_ACCOUNT 0x00020000UL /* Objects are reclaimable */ -static inline int gfpflags_allow_blocking(gfp_t mask) -{ - return 1; -} - -struct kmem_cache { - int size; - void (*ctor)(void *); -}; +void *kmalloc(size_t size, gfp_t); +void kfree(void *); void *kmem_cache_alloc(struct kmem_cache *cachep, int flags); void kmem_cache_free(struct kmem_cache *cachep, void *objp); diff --git a/tools/testing/radix-tree/linux/types.h b/tools/testing/radix-tree/linux/types.h index faa0b6ff9ca8..8491d89873bb 100644 --- a/tools/testing/radix-tree/linux/types.h +++ b/tools/testing/radix-tree/linux/types.h @@ -6,8 +6,6 @@ #define __rcu #define __read_mostly -#define BITS_PER_LONG (sizeof(long) * 8) - static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; diff --git a/tools/testing/radix-tree/main.c b/tools/testing/radix-tree/main.c index daa9010693e8..f7e9801a6754 100644 --- a/tools/testing/radix-tree/main.c +++ b/tools/testing/radix-tree/main.c @@ -67,7 +67,6 @@ void big_gang_check(bool long_run) for (i = 0; i < (long_run ? 1000 : 3); i++) { __big_gang_check(); - srand(time(0)); printf("%d ", i); fflush(stdout); } @@ -206,8 +205,7 @@ void copy_tag_check(void) } // printf("\ncopying tags...\n"); - cur = start; - tagged = radix_tree_range_tag_if_tagged(&tree, &cur, end, ITEMS, 0, 1); + tagged = tag_tagged_items(&tree, NULL, start, end, ITEMS, 0, 1); // printf("checking copied tags\n"); assert(tagged == count); @@ -215,16 +213,13 @@ void copy_tag_check(void) /* Copy tags in several rounds */ // printf("\ncopying tags...\n"); - cur = start; - do { - tmp = rand() % (count/10+2); - tagged = radix_tree_range_tag_if_tagged(&tree, &cur, end, tmp, 0, 2); - } while (tmp == tagged); + tmp = rand() % (count / 10 + 2); + tagged = tag_tagged_items(&tree, NULL, start, end, tmp, 0, 2); + assert(tagged == count); // printf("%lu %lu %lu\n", tagged, tmp, count); // printf("checking copied tags\n"); check_copied_tags(&tree, start, end, idx, ITEMS, 0, 2); - assert(tagged < tmp); verify_tag_consistency(&tree, 0); verify_tag_consistency(&tree, 1); verify_tag_consistency(&tree, 2); @@ -240,7 +235,7 @@ static void __locate_check(struct radix_tree_root *tree, unsigned long index, item_insert_order(tree, index, order); item = item_lookup(tree, index); - index2 = radix_tree_locate_item(tree, item); + index2 = find_item(tree, item); if (index != index2) { printf("index %ld order %d inserted; found %ld\n", index, order, index2); @@ -274,17 +269,17 @@ static void locate_check(void) index += (1UL << order)) { __locate_check(&tree, index + offset, order); } - if (radix_tree_locate_item(&tree, &tree) != -1) + if (find_item(&tree, &tree) != -1) abort(); item_kill_tree(&tree); } } - if (radix_tree_locate_item(&tree, &tree) != -1) + if (find_item(&tree, &tree) != -1) abort(); __locate_check(&tree, -1, 0); - if (radix_tree_locate_item(&tree, &tree) != -1) + if (find_item(&tree, &tree) != -1) abort(); item_kill_tree(&tree); } @@ -293,50 +288,80 @@ static void single_thread_tests(bool long_run) { int i; - printf("starting single_thread_tests: %d allocated\n", nr_allocated); + printf("starting single_thread_tests: %d allocated, preempt %d\n", + nr_allocated, preempt_count); multiorder_checks(); - printf("after multiorder_check: %d allocated\n", nr_allocated); + rcu_barrier(); + printf("after multiorder_check: %d allocated, preempt %d\n", + nr_allocated, preempt_count); locate_check(); - printf("after locate_check: %d allocated\n", nr_allocated); + rcu_barrier(); + printf("after locate_check: %d allocated, preempt %d\n", + nr_allocated, preempt_count); tag_check(); - printf("after tag_check: %d allocated\n", nr_allocated); + rcu_barrier(); + printf("after tag_check: %d allocated, preempt %d\n", + nr_allocated, preempt_count); gang_check(); - printf("after gang_check: %d allocated\n", nr_allocated); + rcu_barrier(); + printf("after gang_check: %d allocated, preempt %d\n", + nr_allocated, preempt_count); add_and_check(); - printf("after add_and_check: %d allocated\n", nr_allocated); + rcu_barrier(); + printf("after add_and_check: %d allocated, preempt %d\n", + nr_allocated, preempt_count); dynamic_height_check(); - printf("after dynamic_height_check: %d allocated\n", nr_allocated); + rcu_barrier(); + printf("after dynamic_height_check: %d allocated, preempt %d\n", + nr_allocated, preempt_count); big_gang_check(long_run); - printf("after big_gang_check: %d allocated\n", nr_allocated); + rcu_barrier(); + printf("after big_gang_check: %d allocated, preempt %d\n", + nr_allocated, preempt_count); for (i = 0; i < (long_run ? 2000 : 3); i++) { copy_tag_check(); printf("%d ", i); fflush(stdout); } - printf("after copy_tag_check: %d allocated\n", nr_allocated); + rcu_barrier(); + printf("after copy_tag_check: %d allocated, preempt %d\n", + nr_allocated, preempt_count); } int main(int argc, char **argv) { bool long_run = false; int opt; + unsigned int seed = time(NULL); - while ((opt = getopt(argc, argv, "l")) != -1) { + while ((opt = getopt(argc, argv, "ls:")) != -1) { if (opt == 'l') long_run = true; + else if (opt == 's') + seed = strtoul(optarg, NULL, 0); } + printf("random seed %u\n", seed); + srand(seed); + rcu_register_thread(); radix_tree_init(); regression1_test(); regression2_test(); regression3_test(); - iteration_test(); + iteration_test(0, 10); + iteration_test(7, 20); single_thread_tests(long_run); - sleep(1); - printf("after sleep(1): %d allocated\n", nr_allocated); + /* Free any remaining preallocated nodes */ + radix_tree_cpu_dead(0); + + benchmark(); + + rcu_barrier(); + printf("after rcu_barrier: %d allocated, preempt %d\n", + nr_allocated, preempt_count); rcu_unregister_thread(); exit(0); diff --git a/tools/testing/radix-tree/multiorder.c b/tools/testing/radix-tree/multiorder.c index d1be94667a30..f79812a5e070 100644 --- a/tools/testing/radix-tree/multiorder.c +++ b/tools/testing/radix-tree/multiorder.c @@ -26,7 +26,6 @@ static void __multiorder_tag_test(int index, int order) { RADIX_TREE(tree, GFP_KERNEL); int base, err, i; - unsigned long first = 0; /* our canonical entry */ base = index & ~((1 << order) - 1); @@ -60,7 +59,7 @@ static void __multiorder_tag_test(int index, int order) assert(!radix_tree_tag_get(&tree, i, 1)); } - assert(radix_tree_range_tag_if_tagged(&tree, &first, ~0UL, 10, 0, 1) == 1); + assert(tag_tagged_items(&tree, NULL, 0, ~0UL, 10, 0, 1) == 1); assert(radix_tree_tag_clear(&tree, index, 0)); for_each_index(i, base, order) { @@ -76,8 +75,27 @@ static void __multiorder_tag_test(int index, int order) item_kill_tree(&tree); } +static void __multiorder_tag_test2(unsigned order, unsigned long index2) +{ + RADIX_TREE(tree, GFP_KERNEL); + unsigned long index = (1 << order); + index2 += index; + + assert(item_insert_order(&tree, 0, order) == 0); + assert(item_insert(&tree, index2) == 0); + + assert(radix_tree_tag_set(&tree, 0, 0)); + assert(radix_tree_tag_set(&tree, index2, 0)); + + assert(tag_tagged_items(&tree, NULL, 0, ~0UL, 10, 0, 1) == 2); + + item_kill_tree(&tree); +} + static void multiorder_tag_tests(void) { + int i, j; + /* test multi-order entry for indices 0-7 with no sibling pointers */ __multiorder_tag_test(0, 3); __multiorder_tag_test(5, 3); @@ -117,6 +135,10 @@ static void multiorder_tag_tests(void) __multiorder_tag_test(300, 8); __multiorder_tag_test(0x12345678UL, 8); + + for (i = 1; i < 10; i++) + for (j = 0; j < (10 << i); j++) + __multiorder_tag_test2(i, j); } static void multiorder_check(unsigned long index, int order) @@ -125,7 +147,7 @@ static void multiorder_check(unsigned long index, int order) unsigned long min = index & ~((1UL << order) - 1); unsigned long max = min + (1UL << order); void **slot; - struct item *item2 = item_create(min); + struct item *item2 = item_create(min, order); RADIX_TREE(tree, GFP_KERNEL); printf("Multiorder index %ld, order %d\n", index, order); @@ -231,11 +253,14 @@ void multiorder_iteration(void) radix_tree_for_each_slot(slot, &tree, &iter, j) { int height = order[i] / RADIX_TREE_MAP_SHIFT; int shift = height * RADIX_TREE_MAP_SHIFT; - int mask = (1 << order[i]) - 1; + unsigned long mask = (1UL << order[i]) - 1; + struct item *item = *slot; - assert(iter.index >= (index[i] &~ mask)); - assert(iter.index <= (index[i] | mask)); + assert((iter.index | mask) == (index[i] | mask)); assert(iter.shift == shift); + assert(!radix_tree_is_internal_node(item)); + assert((item->index | mask) == (index[i] | mask)); + assert(item->order == order[i]); i++; } } @@ -248,7 +273,6 @@ void multiorder_tagged_iteration(void) RADIX_TREE(tree, GFP_KERNEL); struct radix_tree_iter iter; void **slot; - unsigned long first = 0; int i, j; printf("Multiorder tagged iteration test\n"); @@ -269,7 +293,7 @@ void multiorder_tagged_iteration(void) assert(radix_tree_tag_set(&tree, tag_index[i], 1)); for (j = 0; j < 256; j++) { - int mask, k; + int k; for (i = 0; i < TAG_ENTRIES; i++) { for (k = i; index[k] < tag_index[i]; k++) @@ -279,18 +303,22 @@ void multiorder_tagged_iteration(void) } radix_tree_for_each_tagged(slot, &tree, &iter, j, 1) { + unsigned long mask; + struct item *item = *slot; for (k = i; index[k] < tag_index[i]; k++) ; - mask = (1 << order[k]) - 1; + mask = (1UL << order[k]) - 1; - assert(iter.index >= (tag_index[i] &~ mask)); - assert(iter.index <= (tag_index[i] | mask)); + assert((iter.index | mask) == (tag_index[i] | mask)); + assert(!radix_tree_is_internal_node(item)); + assert((item->index | mask) == (tag_index[i] | mask)); + assert(item->order == order[k]); i++; } } - radix_tree_range_tag_if_tagged(&tree, &first, ~0UL, - MT_NUM_ENTRIES, 1, 2); + assert(tag_tagged_items(&tree, NULL, 0, ~0UL, TAG_ENTRIES, 1, 2) == + TAG_ENTRIES); for (j = 0; j < 256; j++) { int mask, k; @@ -303,19 +331,21 @@ void multiorder_tagged_iteration(void) } radix_tree_for_each_tagged(slot, &tree, &iter, j, 2) { + struct item *item = *slot; for (k = i; index[k] < tag_index[i]; k++) ; mask = (1 << order[k]) - 1; - assert(iter.index >= (tag_index[i] &~ mask)); - assert(iter.index <= (tag_index[i] | mask)); + assert((iter.index | mask) == (tag_index[i] | mask)); + assert(!radix_tree_is_internal_node(item)); + assert((item->index | mask) == (tag_index[i] | mask)); + assert(item->order == order[k]); i++; } } - first = 1; - radix_tree_range_tag_if_tagged(&tree, &first, ~0UL, - MT_NUM_ENTRIES, 1, 0); + assert(tag_tagged_items(&tree, NULL, 1, ~0UL, MT_NUM_ENTRIES * 2, 1, 0) + == TAG_ENTRIES); i = 0; radix_tree_for_each_tagged(slot, &tree, &iter, 0, 0) { assert(iter.index == tag_index[i]); @@ -325,6 +355,261 @@ void multiorder_tagged_iteration(void) item_kill_tree(&tree); } +static void multiorder_join1(unsigned long index, + unsigned order1, unsigned order2) +{ + unsigned long loc; + void *item, *item2 = item_create(index + 1, order1); + RADIX_TREE(tree, GFP_KERNEL); + + item_insert_order(&tree, index, order2); + item = radix_tree_lookup(&tree, index); + radix_tree_join(&tree, index + 1, order1, item2); + loc = find_item(&tree, item); + if (loc == -1) + free(item); + item = radix_tree_lookup(&tree, index + 1); + assert(item == item2); + item_kill_tree(&tree); +} + +static void multiorder_join2(unsigned order1, unsigned order2) +{ + RADIX_TREE(tree, GFP_KERNEL); + struct radix_tree_node *node; + void *item1 = item_create(0, order1); + void *item2; + + item_insert_order(&tree, 0, order2); + radix_tree_insert(&tree, 1 << order2, (void *)0x12UL); + item2 = __radix_tree_lookup(&tree, 1 << order2, &node, NULL); + assert(item2 == (void *)0x12UL); + assert(node->exceptional == 1); + + radix_tree_join(&tree, 0, order1, item1); + item2 = __radix_tree_lookup(&tree, 1 << order2, &node, NULL); + assert(item2 == item1); + assert(node->exceptional == 0); + item_kill_tree(&tree); +} + +/* + * This test revealed an accounting bug for exceptional entries at one point. + * Nodes were being freed back into the pool with an elevated exception count + * by radix_tree_join() and then radix_tree_split() was failing to zero the + * count of exceptional entries. + */ +static void multiorder_join3(unsigned int order) +{ + RADIX_TREE(tree, GFP_KERNEL); + struct radix_tree_node *node; + void **slot; + struct radix_tree_iter iter; + unsigned long i; + + for (i = 0; i < (1 << order); i++) { + radix_tree_insert(&tree, i, (void *)0x12UL); + } + + radix_tree_join(&tree, 0, order, (void *)0x16UL); + rcu_barrier(); + + radix_tree_split(&tree, 0, 0); + + radix_tree_for_each_slot(slot, &tree, &iter, 0) { + radix_tree_iter_replace(&tree, &iter, slot, (void *)0x12UL); + } + + __radix_tree_lookup(&tree, 0, &node, NULL); + assert(node->exceptional == node->count); + + item_kill_tree(&tree); +} + +static void multiorder_join(void) +{ + int i, j, idx; + + for (idx = 0; idx < 1024; idx = idx * 2 + 3) { + for (i = 1; i < 15; i++) { + for (j = 0; j < i; j++) { + multiorder_join1(idx, i, j); + } + } + } + + for (i = 1; i < 15; i++) { + for (j = 0; j < i; j++) { + multiorder_join2(i, j); + } + } + + for (i = 3; i < 10; i++) { + multiorder_join3(i); + } +} + +static void check_mem(unsigned old_order, unsigned new_order, unsigned alloc) +{ + struct radix_tree_preload *rtp = &radix_tree_preloads; + if (rtp->nr != 0) + printf("split(%u %u) remaining %u\n", old_order, new_order, + rtp->nr); + /* + * Can't check for equality here as some nodes may have been + * RCU-freed while we ran. But we should never finish with more + * nodes allocated since they should have all been preloaded. + */ + if (nr_allocated > alloc) + printf("split(%u %u) allocated %u %u\n", old_order, new_order, + alloc, nr_allocated); +} + +static void __multiorder_split(int old_order, int new_order) +{ + RADIX_TREE(tree, GFP_ATOMIC); + void **slot; + struct radix_tree_iter iter; + unsigned alloc; + + radix_tree_preload(GFP_KERNEL); + assert(item_insert_order(&tree, 0, old_order) == 0); + radix_tree_preload_end(); + + /* Wipe out the preloaded cache or it'll confuse check_mem() */ + radix_tree_cpu_dead(0); + + radix_tree_tag_set(&tree, 0, 2); + + radix_tree_split_preload(old_order, new_order, GFP_KERNEL); + alloc = nr_allocated; + radix_tree_split(&tree, 0, new_order); + check_mem(old_order, new_order, alloc); + radix_tree_for_each_slot(slot, &tree, &iter, 0) { + radix_tree_iter_replace(&tree, &iter, slot, + item_create(iter.index, new_order)); + } + radix_tree_preload_end(); + + item_kill_tree(&tree); +} + +static void __multiorder_split2(int old_order, int new_order) +{ + RADIX_TREE(tree, GFP_KERNEL); + void **slot; + struct radix_tree_iter iter; + struct radix_tree_node *node; + void *item; + + __radix_tree_insert(&tree, 0, old_order, (void *)0x12); + + item = __radix_tree_lookup(&tree, 0, &node, NULL); + assert(item == (void *)0x12); + assert(node->exceptional > 0); + + radix_tree_split(&tree, 0, new_order); + radix_tree_for_each_slot(slot, &tree, &iter, 0) { + radix_tree_iter_replace(&tree, &iter, slot, + item_create(iter.index, new_order)); + } + + item = __radix_tree_lookup(&tree, 0, &node, NULL); + assert(item != (void *)0x12); + assert(node->exceptional == 0); + + item_kill_tree(&tree); +} + +static void __multiorder_split3(int old_order, int new_order) +{ + RADIX_TREE(tree, GFP_KERNEL); + void **slot; + struct radix_tree_iter iter; + struct radix_tree_node *node; + void *item; + + __radix_tree_insert(&tree, 0, old_order, (void *)0x12); + + item = __radix_tree_lookup(&tree, 0, &node, NULL); + assert(item == (void *)0x12); + assert(node->exceptional > 0); + + radix_tree_split(&tree, 0, new_order); + radix_tree_for_each_slot(slot, &tree, &iter, 0) { + radix_tree_iter_replace(&tree, &iter, slot, (void *)0x16); + } + + item = __radix_tree_lookup(&tree, 0, &node, NULL); + assert(item == (void *)0x16); + assert(node->exceptional > 0); + + item_kill_tree(&tree); + + __radix_tree_insert(&tree, 0, old_order, (void *)0x12); + + item = __radix_tree_lookup(&tree, 0, &node, NULL); + assert(item == (void *)0x12); + assert(node->exceptional > 0); + + radix_tree_split(&tree, 0, new_order); + radix_tree_for_each_slot(slot, &tree, &iter, 0) { + if (iter.index == (1 << new_order)) + radix_tree_iter_replace(&tree, &iter, slot, + (void *)0x16); + else + radix_tree_iter_replace(&tree, &iter, slot, NULL); + } + + item = __radix_tree_lookup(&tree, 1 << new_order, &node, NULL); + assert(item == (void *)0x16); + assert(node->count == node->exceptional); + do { + node = node->parent; + if (!node) + break; + assert(node->count == 1); + assert(node->exceptional == 0); + } while (1); + + item_kill_tree(&tree); +} + +static void multiorder_split(void) +{ + int i, j; + + for (i = 3; i < 11; i++) + for (j = 0; j < i; j++) { + __multiorder_split(i, j); + __multiorder_split2(i, j); + __multiorder_split3(i, j); + } +} + +static void multiorder_account(void) +{ + RADIX_TREE(tree, GFP_KERNEL); + struct radix_tree_node *node; + void **slot; + + item_insert_order(&tree, 0, 5); + + __radix_tree_insert(&tree, 1 << 5, 5, (void *)0x12); + __radix_tree_lookup(&tree, 0, &node, NULL); + assert(node->count == node->exceptional * 2); + radix_tree_delete(&tree, 1 << 5); + assert(node->exceptional == 0); + + __radix_tree_insert(&tree, 1 << 5, 5, (void *)0x12); + __radix_tree_lookup(&tree, 1 << 5, &node, &slot); + assert(node->count == node->exceptional * 2); + __radix_tree_replace(&tree, node, slot, NULL, NULL, NULL); + assert(node->exceptional == 0); + + item_kill_tree(&tree); +} + void multiorder_checks(void) { int i; @@ -342,4 +627,9 @@ void multiorder_checks(void) multiorder_tag_tests(); multiorder_iteration(); multiorder_tagged_iteration(); + multiorder_join(); + multiorder_split(); + multiorder_account(); + + radix_tree_cpu_dead(0); } diff --git a/tools/testing/radix-tree/rcupdate.c b/tools/testing/radix-tree/rcupdate.c deleted file mode 100644 index 31a2d14225d6..000000000000 --- a/tools/testing/radix-tree/rcupdate.c +++ /dev/null @@ -1,86 +0,0 @@ -#include <linux/rcupdate.h> -#include <pthread.h> -#include <stdio.h> -#include <assert.h> - -static pthread_mutex_t rculock = PTHREAD_MUTEX_INITIALIZER; -static struct rcu_head *rcuhead_global = NULL; -static __thread int nr_rcuhead = 0; -static __thread struct rcu_head *rcuhead = NULL; -static __thread struct rcu_head *rcutail = NULL; - -static pthread_cond_t rcu_worker_cond = PTHREAD_COND_INITIALIZER; - -/* switch to urcu implementation when it is merged. */ -void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *head)) -{ - head->func = func; - head->next = rcuhead; - rcuhead = head; - if (!rcutail) - rcutail = head; - nr_rcuhead++; - if (nr_rcuhead >= 1000) { - int signal = 0; - - pthread_mutex_lock(&rculock); - if (!rcuhead_global) - signal = 1; - rcutail->next = rcuhead_global; - rcuhead_global = head; - pthread_mutex_unlock(&rculock); - - nr_rcuhead = 0; - rcuhead = NULL; - rcutail = NULL; - - if (signal) { - pthread_cond_signal(&rcu_worker_cond); - } - } -} - -static void *rcu_worker(void *arg) -{ - struct rcu_head *r; - - rcupdate_thread_init(); - - while (1) { - pthread_mutex_lock(&rculock); - while (!rcuhead_global) { - pthread_cond_wait(&rcu_worker_cond, &rculock); - } - r = rcuhead_global; - rcuhead_global = NULL; - - pthread_mutex_unlock(&rculock); - - synchronize_rcu(); - - while (r) { - struct rcu_head *tmp = r->next; - r->func(r); - r = tmp; - } - } - - rcupdate_thread_exit(); - - return NULL; -} - -static pthread_t worker_thread; -void rcupdate_init(void) -{ - pthread_create(&worker_thread, NULL, rcu_worker, NULL); -} - -void rcupdate_thread_init(void) -{ - rcu_register_thread(); -} -void rcupdate_thread_exit(void) -{ - rcu_unregister_thread(); -} diff --git a/tools/testing/radix-tree/regression2.c b/tools/testing/radix-tree/regression2.c index 63bf347aaf33..a41325d7a170 100644 --- a/tools/testing/radix-tree/regression2.c +++ b/tools/testing/radix-tree/regression2.c @@ -50,6 +50,7 @@ #include <stdio.h> #include "regression.h" +#include "test.h" #define PAGECACHE_TAG_DIRTY 0 #define PAGECACHE_TAG_WRITEBACK 1 @@ -90,7 +91,7 @@ void regression2_test(void) /* 1. */ start = 0; end = max_slots - 2; - radix_tree_range_tag_if_tagged(&mt_tree, &start, end, 1, + tag_tagged_items(&mt_tree, NULL, start, end, 1, PAGECACHE_TAG_DIRTY, PAGECACHE_TAG_TOWRITE); /* 2. */ diff --git a/tools/testing/radix-tree/regression3.c b/tools/testing/radix-tree/regression3.c index 1f06ed73d0a8..b594841fae85 100644 --- a/tools/testing/radix-tree/regression3.c +++ b/tools/testing/radix-tree/regression3.c @@ -5,7 +5,7 @@ * In following radix_tree_next_slot current chunk size becomes zero. * This isn't checked and it tries to dereference null pointer in slot. * - * Helper radix_tree_iter_next reset slot to NULL and next_index to index + 1, + * Helper radix_tree_iter_resume reset slot to NULL and next_index to index + 1, * for tagger iteraction it also must reset cached tags in iterator to abort * next radix_tree_next_slot and go to slow-path into radix_tree_next_chunk. * @@ -88,7 +88,7 @@ void regression3_test(void) printf("slot %ld %p\n", iter.index, *slot); if (!iter.index) { printf("next at %ld\n", iter.index); - slot = radix_tree_iter_next(&iter); + slot = radix_tree_iter_resume(slot, &iter); } } @@ -96,7 +96,7 @@ void regression3_test(void) printf("contig %ld %p\n", iter.index, *slot); if (!iter.index) { printf("next at %ld\n", iter.index); - slot = radix_tree_iter_next(&iter); + slot = radix_tree_iter_resume(slot, &iter); } } @@ -106,7 +106,7 @@ void regression3_test(void) printf("tagged %ld %p\n", iter.index, *slot); if (!iter.index) { printf("next at %ld\n", iter.index); - slot = radix_tree_iter_next(&iter); + slot = radix_tree_iter_resume(slot, &iter); } } diff --git a/tools/testing/radix-tree/tag_check.c b/tools/testing/radix-tree/tag_check.c index b0ac05741750..fd98c132207a 100644 --- a/tools/testing/radix-tree/tag_check.c +++ b/tools/testing/radix-tree/tag_check.c @@ -23,7 +23,7 @@ __simple_checks(struct radix_tree_root *tree, unsigned long index, int tag) item_tag_set(tree, index, tag); ret = item_tag_get(tree, index, tag); assert(ret != 0); - ret = radix_tree_range_tag_if_tagged(tree, &first, ~0UL, 10, tag, !tag); + ret = tag_tagged_items(tree, NULL, first, ~0UL, 10, tag, !tag); assert(ret == 1); ret = item_tag_get(tree, index, !tag); assert(ret != 0); @@ -51,6 +51,7 @@ void simple_checks(void) verify_tag_consistency(&tree, 1); printf("before item_kill_tree: %d allocated\n", nr_allocated); item_kill_tree(&tree); + rcu_barrier(); printf("after item_kill_tree: %d allocated\n", nr_allocated); } @@ -319,10 +320,13 @@ static void single_check(void) assert(ret == 0); verify_tag_consistency(&tree, 0); verify_tag_consistency(&tree, 1); - ret = radix_tree_range_tag_if_tagged(&tree, &first, 10, 10, 0, 1); + ret = tag_tagged_items(&tree, NULL, first, 10, 10, 0, 1); assert(ret == 1); ret = radix_tree_gang_lookup_tag(&tree, (void **)items, 0, BATCH, 1); assert(ret == 1); + item_tag_clear(&tree, 0, 0); + ret = radix_tree_gang_lookup_tag(&tree, (void **)items, 0, BATCH, 0); + assert(ret == 0); item_kill_tree(&tree); } @@ -331,12 +335,16 @@ void tag_check(void) single_check(); extend_checks(); contract_checks(); + rcu_barrier(); printf("after extend_checks: %d allocated\n", nr_allocated); __leak_check(); leak_check(); + rcu_barrier(); printf("after leak_check: %d allocated\n", nr_allocated); simple_checks(); + rcu_barrier(); printf("after simple_checks: %d allocated\n", nr_allocated); thrash_tags(); + rcu_barrier(); printf("after thrash_tags: %d allocated\n", nr_allocated); } diff --git a/tools/testing/radix-tree/test.c b/tools/testing/radix-tree/test.c index a6e8099eaf4f..e5726e373646 100644 --- a/tools/testing/radix-tree/test.c +++ b/tools/testing/radix-tree/test.c @@ -24,21 +24,29 @@ int item_tag_get(struct radix_tree_root *root, unsigned long index, int tag) return radix_tree_tag_get(root, index, tag); } -int __item_insert(struct radix_tree_root *root, struct item *item, - unsigned order) +int __item_insert(struct radix_tree_root *root, struct item *item) { - return __radix_tree_insert(root, item->index, order, item); + return __radix_tree_insert(root, item->index, item->order, item); } int item_insert(struct radix_tree_root *root, unsigned long index) { - return __item_insert(root, item_create(index), 0); + return __item_insert(root, item_create(index, 0)); } int item_insert_order(struct radix_tree_root *root, unsigned long index, unsigned order) { - return __item_insert(root, item_create(index), order); + return __item_insert(root, item_create(index, order)); +} + +void item_sanity(struct item *item, unsigned long index) +{ + unsigned long mask; + assert(!radix_tree_is_internal_node(item)); + assert(item->order < BITS_PER_LONG); + mask = (1UL << item->order) - 1; + assert((item->index | mask) == (index | mask)); } int item_delete(struct radix_tree_root *root, unsigned long index) @@ -46,18 +54,19 @@ int item_delete(struct radix_tree_root *root, unsigned long index) struct item *item = radix_tree_delete(root, index); if (item) { - assert(item->index == index); + item_sanity(item, index); free(item); return 1; } return 0; } -struct item *item_create(unsigned long index) +struct item *item_create(unsigned long index, unsigned int order) { struct item *ret = malloc(sizeof(*ret)); ret->index = index; + ret->order = order; return ret; } @@ -66,8 +75,8 @@ void item_check_present(struct radix_tree_root *root, unsigned long index) struct item *item; item = radix_tree_lookup(root, index); - assert(item != 0); - assert(item->index == index); + assert(item != NULL); + item_sanity(item, index); } struct item *item_lookup(struct radix_tree_root *root, unsigned long index) @@ -80,7 +89,7 @@ void item_check_absent(struct radix_tree_root *root, unsigned long index) struct item *item; item = radix_tree_lookup(root, index); - assert(item == 0); + assert(item == NULL); } /* @@ -142,6 +151,62 @@ void item_full_scan(struct radix_tree_root *root, unsigned long start, assert(nfound == 0); } +/* Use the same pattern as tag_pages_for_writeback() in mm/page-writeback.c */ +int tag_tagged_items(struct radix_tree_root *root, pthread_mutex_t *lock, + unsigned long start, unsigned long end, unsigned batch, + unsigned iftag, unsigned thentag) +{ + unsigned long tagged = 0; + struct radix_tree_iter iter; + void **slot; + + if (batch == 0) + batch = 1; + + if (lock) + pthread_mutex_lock(lock); + radix_tree_for_each_tagged(slot, root, &iter, start, iftag) { + if (iter.index > end) + break; + radix_tree_iter_tag_set(root, &iter, thentag); + tagged++; + if ((tagged % batch) != 0) + continue; + slot = radix_tree_iter_resume(slot, &iter); + if (lock) { + pthread_mutex_unlock(lock); + rcu_barrier(); + pthread_mutex_lock(lock); + } + } + if (lock) + pthread_mutex_unlock(lock); + + return tagged; +} + +/* Use the same pattern as find_swap_entry() in mm/shmem.c */ +unsigned long find_item(struct radix_tree_root *root, void *item) +{ + struct radix_tree_iter iter; + void **slot; + unsigned long found = -1; + unsigned long checked = 0; + + radix_tree_for_each_slot(slot, root, &iter, 0) { + if (*slot == item) { + found = iter.index; + break; + } + checked++; + if ((checked % 4) != 0) + continue; + slot = radix_tree_iter_resume(slot, &iter); + } + + return found; +} + static int verify_node(struct radix_tree_node *slot, unsigned int tag, int tagged) { @@ -200,9 +265,16 @@ void verify_tag_consistency(struct radix_tree_root *root, unsigned int tag) void item_kill_tree(struct radix_tree_root *root) { + struct radix_tree_iter iter; + void **slot; struct item *items[32]; int nfound; + radix_tree_for_each_slot(slot, root, &iter, 0) { + if (radix_tree_exceptional_entry(*slot)) + radix_tree_delete(root, iter.index); + } + while ((nfound = radix_tree_gang_lookup(root, (void **)items, 0, 32))) { int i; diff --git a/tools/testing/radix-tree/test.h b/tools/testing/radix-tree/test.h index 217fb2403f09..056a23b56467 100644 --- a/tools/testing/radix-tree/test.h +++ b/tools/testing/radix-tree/test.h @@ -5,11 +5,11 @@ struct item { unsigned long index; + unsigned int order; }; -struct item *item_create(unsigned long index); -int __item_insert(struct radix_tree_root *root, struct item *item, - unsigned order); +struct item *item_create(unsigned long index, unsigned int order); +int __item_insert(struct radix_tree_root *root, struct item *item); int item_insert(struct radix_tree_root *root, unsigned long index); int item_insert_order(struct radix_tree_root *root, unsigned long index, unsigned order); @@ -25,9 +25,15 @@ void item_full_scan(struct radix_tree_root *root, unsigned long start, unsigned long nr, int chunk); void item_kill_tree(struct radix_tree_root *root); +int tag_tagged_items(struct radix_tree_root *, pthread_mutex_t *, + unsigned long start, unsigned long end, unsigned batch, + unsigned iftag, unsigned thentag); +unsigned long find_item(struct radix_tree_root *, void *item); + void tag_check(void); void multiorder_checks(void); -void iteration_test(void); +void iteration_test(unsigned order, unsigned duration); +void benchmark(void); struct item * item_tag_set(struct radix_tree_root *root, unsigned long index, int tag); @@ -40,7 +46,14 @@ void verify_tag_consistency(struct radix_tree_root *root, unsigned int tag); extern int nr_allocated; /* Normally private parts of lib/radix-tree.c */ +struct radix_tree_node *entry_to_node(void *ptr); void radix_tree_dump(struct radix_tree_root *root); int root_tag_get(struct radix_tree_root *root, unsigned int tag); unsigned long node_maxindex(struct radix_tree_node *); unsigned long shift_maxindex(unsigned int shift); +int radix_tree_cpu_dead(unsigned int cpu); +struct radix_tree_preload { + unsigned nr; + struct radix_tree_node *nodes; +}; +extern struct radix_tree_preload radix_tree_preloads; diff --git a/tools/testing/selftests/.gitignore b/tools/testing/selftests/.gitignore new file mode 100644 index 000000000000..f0600d20ce7d --- /dev/null +++ b/tools/testing/selftests/.gitignore @@ -0,0 +1 @@ +kselftest diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index a3144a3de3a8..71b05891a6a1 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -7,6 +7,7 @@ TARGETS += exec TARGETS += firmware TARGETS += ftrace TARGETS += futex +TARGETS += gpio TARGETS += ipc TARGETS += kcmp TARGETS += lib @@ -24,6 +25,7 @@ TARGETS += seccomp TARGETS += sigaltstack TARGETS += size TARGETS += static_keys +TARGETS += sync TARGETS += sysctl ifneq (1, $(quicktest)) TARGETS += timers diff --git a/tools/testing/selftests/breakpoints/Makefile b/tools/testing/selftests/breakpoints/Makefile index 74e533fd4bc5..61b79e8df1f4 100644 --- a/tools/testing/selftests/breakpoints/Makefile +++ b/tools/testing/selftests/breakpoints/Makefile @@ -5,6 +5,9 @@ ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/) ifeq ($(ARCH),x86) TEST_PROGS := breakpoint_test endif +ifeq ($(ARCH),aarch64) +TEST_PROGS := breakpoint_test_arm64 +endif TEST_PROGS += step_after_suspend_test @@ -13,4 +16,4 @@ all: $(TEST_PROGS) include ../lib.mk clean: - rm -fr breakpoint_test step_after_suspend_test + rm -fr breakpoint_test breakpoint_test_arm64 step_after_suspend_test diff --git a/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c b/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c new file mode 100644 index 000000000000..3897e996541e --- /dev/null +++ b/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2016 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * Original Code by Pavel Labath <labath@google.com> + * + * Code modified by Pratyush Anand <panand@redhat.com> + * for testing different byte select for each access size. + * + */ + +#define _GNU_SOURCE + +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/ptrace.h> +#include <sys/param.h> +#include <sys/uio.h> +#include <stdint.h> +#include <stdbool.h> +#include <stddef.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <elf.h> +#include <errno.h> +#include <signal.h> + +#include "../kselftest.h" + +static volatile uint8_t var[96] __attribute__((__aligned__(32))); + +static void child(int size, int wr) +{ + volatile uint8_t *addr = &var[32 + wr]; + + if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) { + perror("ptrace(PTRACE_TRACEME) failed"); + _exit(1); + } + + if (raise(SIGSTOP) != 0) { + perror("raise(SIGSTOP) failed"); + _exit(1); + } + + if ((uintptr_t) addr % size) { + perror("Wrong address write for the given size\n"); + _exit(1); + } + switch (size) { + case 1: + *addr = 47; + break; + case 2: + *(uint16_t *)addr = 47; + break; + case 4: + *(uint32_t *)addr = 47; + break; + case 8: + *(uint64_t *)addr = 47; + break; + case 16: + __asm__ volatile ("stp x29, x30, %0" : "=m" (addr[0])); + break; + case 32: + __asm__ volatile ("stp q29, q30, %0" : "=m" (addr[0])); + break; + } + + _exit(0); +} + +static bool set_watchpoint(pid_t pid, int size, int wp) +{ + const volatile uint8_t *addr = &var[32 + wp]; + const int offset = (uintptr_t)addr % 8; + const unsigned int byte_mask = ((1 << size) - 1) << offset; + const unsigned int type = 2; /* Write */ + const unsigned int enable = 1; + const unsigned int control = byte_mask << 5 | type << 3 | enable; + struct user_hwdebug_state dreg_state; + struct iovec iov; + + memset(&dreg_state, 0, sizeof(dreg_state)); + dreg_state.dbg_regs[0].addr = (uintptr_t)(addr - offset); + dreg_state.dbg_regs[0].ctrl = control; + iov.iov_base = &dreg_state; + iov.iov_len = offsetof(struct user_hwdebug_state, dbg_regs) + + sizeof(dreg_state.dbg_regs[0]); + if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_HW_WATCH, &iov) == 0) + return true; + + if (errno == EIO) { + printf("ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) " + "not supported on this hardware\n"); + ksft_exit_skip(); + } + perror("ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) failed"); + return false; +} + +static bool run_test(int wr_size, int wp_size, int wr, int wp) +{ + int status; + siginfo_t siginfo; + pid_t pid = fork(); + pid_t wpid; + + if (pid < 0) { + perror("fork() failed"); + return false; + } + if (pid == 0) + child(wr_size, wr); + + wpid = waitpid(pid, &status, __WALL); + if (wpid != pid) { + perror("waitpid() failed"); + return false; + } + if (!WIFSTOPPED(status)) { + printf("child did not stop\n"); + return false; + } + if (WSTOPSIG(status) != SIGSTOP) { + printf("child did not stop with SIGSTOP\n"); + return false; + } + + if (!set_watchpoint(pid, wp_size, wp)) + return false; + + if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) { + perror("ptrace(PTRACE_SINGLESTEP) failed"); + return false; + } + + alarm(3); + wpid = waitpid(pid, &status, __WALL); + if (wpid != pid) { + perror("waitpid() failed"); + return false; + } + alarm(0); + if (WIFEXITED(status)) { + printf("child did not single-step\t"); + return false; + } + if (!WIFSTOPPED(status)) { + printf("child did not stop\n"); + return false; + } + if (WSTOPSIG(status) != SIGTRAP) { + printf("child did not stop with SIGTRAP\n"); + return false; + } + if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0) { + perror("ptrace(PTRACE_GETSIGINFO)"); + return false; + } + if (siginfo.si_code != TRAP_HWBKPT) { + printf("Unexpected si_code %d\n", siginfo.si_code); + return false; + } + + kill(pid, SIGKILL); + wpid = waitpid(pid, &status, 0); + if (wpid != pid) { + perror("waitpid() failed"); + return false; + } + return true; +} + +static void sigalrm(int sig) +{ +} + +int main(int argc, char **argv) +{ + int opt; + bool succeeded = true; + struct sigaction act; + int wr, wp, size; + bool result; + + act.sa_handler = sigalrm; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + sigaction(SIGALRM, &act, NULL); + for (size = 1; size <= 32; size = size*2) { + for (wr = 0; wr <= 32; wr = wr + size) { + for (wp = wr - size; wp <= wr + size; wp = wp + size) { + printf("Test size = %d write offset = %d watchpoint offset = %d\t", size, wr, wp); + result = run_test(size, MIN(size, 8), wr, wp); + if ((result && wr == wp) || (!result && wr != wp)) { + printf("[OK]\n"); + ksft_inc_pass_cnt(); + } else { + printf("[FAILED]\n"); + ksft_inc_fail_cnt(); + succeeded = false; + } + } + } + } + + for (size = 1; size <= 32; size = size*2) { + printf("Test size = %d write offset = %d watchpoint offset = -8\t", size, -size); + + if (run_test(size, 8, -size, -8)) { + printf("[OK]\n"); + ksft_inc_pass_cnt(); + } else { + printf("[FAILED]\n"); + ksft_inc_fail_cnt(); + succeeded = false; + } + } + + ksft_print_cnts(); + if (succeeded) + ksft_exit_pass(); + else + ksft_exit_fail(); +} diff --git a/tools/testing/selftests/drivers/gpu/i915.sh b/tools/testing/selftests/drivers/gpu/i915.sh new file mode 100755 index 000000000000..d407f0fa1e3a --- /dev/null +++ b/tools/testing/selftests/drivers/gpu/i915.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# Runs hardware independent tests for i915 (drivers/gpu/drm/i915) + +if ! /sbin/modprobe -q -r i915; then + echo "drivers/gpu/i915: [SKIP]" + exit 77 +fi + +if /sbin/modprobe -q i915 mock_selftests=-1; then + echo "drivers/gpu/i915: ok" +else + echo "drivers/gpu/i915: [FAIL]" + exit 1 +fi diff --git a/tools/testing/selftests/ftrace/.gitignore b/tools/testing/selftests/ftrace/.gitignore new file mode 100644 index 000000000000..98d8a5a63049 --- /dev/null +++ b/tools/testing/selftests/ftrace/.gitignore @@ -0,0 +1 @@ +logs diff --git a/tools/testing/selftests/ftrace/ftracetest b/tools/testing/selftests/ftrace/ftracetest index 4c6a0bf8ba79..52e3c4df28d6 100755 --- a/tools/testing/selftests/ftrace/ftracetest +++ b/tools/testing/selftests/ftrace/ftracetest @@ -13,7 +13,8 @@ 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 " -v|--verbose Increase verbosity of test messages" +echo " -vv Alias of -v -v (Show all results in stdout)" echo " -d|--debug Debug mode (trace all shell commands)" exit $1 } @@ -54,8 +55,9 @@ parse_opts() { # opts KEEP_LOG=1 shift 1 ;; - --verbose|-v) - VERBOSE=1 + --verbose|-v|-vv) + VERBOSE=$((VERBOSE + 1)) + [ $1 == '-vv' ] && VERBOSE=$((VERBOSE + 1)) shift 1 ;; --debug|-d) @@ -228,7 +230,7 @@ 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) + (cd $TRACING_DIR; read PID _ < /proc/self/stat; set -e; set -x; initialize_ftrace; . $1) [ $? -ne 0 ] && kill -s $SIG_FAIL $SIG_PID } @@ -236,10 +238,11 @@ __run_test() { # testfile run_test() { # testfile local testname=`basename $1` local testlog=`mktemp $LOG_DIR/${testname}-log.XXXXXX` + export TMPDIR=`mktemp -d /tmp/ftracetest-dir.XXXXXX` testcase $1 echo "execute: "$1 > $testlog SIG_RESULT=0 - if [ $VERBOSE -ne 0 ]; then + if [ $VERBOSE -ge 2 ]; then __run_test $1 2>> $testlog | tee -a $testlog else __run_test $1 >> $testlog 2>&1 @@ -249,9 +252,10 @@ run_test() { # testfile # Remove test log if the test was done as it was expected. [ $KEEP_LOG -eq 0 ] && rm $testlog else - catlog $testlog + [ $VERBOSE -ge 1 ] && catlog $testlog TOTAL_RESULT=1 fi + rm -rf $TMPDIR } # load in the helper functions diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-glob.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-glob.tc new file mode 100644 index 000000000000..9dcd0ca1f49c --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-glob.tc @@ -0,0 +1,49 @@ +#!/bin/sh +# description: ftrace - function glob filters + +# Make sure that function glob matching filter works. + +if ! grep -q function available_tracers; then + echo "no function tracer configured" + exit_unsupported +fi + +disable_tracing +clear_trace + +# filter by ?, schedule is always good +if ! echo "sch?dule" > set_ftrace_filter; then + # test for powerpc 64 + if ! echo ".sch?dule" > set_ftrace_filter; then + fail "can not enable schedule filter" + fi + cat set_ftrace_filter | grep '^.schedule$' +else + cat set_ftrace_filter | grep '^schedule$' +fi + +ftrace_filter_check() { # glob grep + echo "$1" > set_ftrace_filter + cut -f1 -d" " set_ftrace_filter > $TMPDIR/actual + cut -f1 -d" " available_filter_functions | grep "$2" > $TMPDIR/expected + DIFF=`diff $TMPDIR/actual $TMPDIR/expected` + test -z "$DIFF" +} + +# filter by *, front match +ftrace_filter_check '*schedule' '^.*schedule$' + +# filter by *, middle match +ftrace_filter_check '*schedule*' '^.*schedule.*$' + +# filter by *, end match +ftrace_filter_check 'schedule*' '^schedule.*$' + +# filter by *, both side match +ftrace_filter_check 'sch*ule' '^sch.*ule$' + +# filter by char class. +ftrace_filter_check '[Ss]y[Ss]_*' '^[Ss]y[Ss]_.*$' + +echo > set_ftrace_filter +enable_tracing diff --git a/tools/testing/selftests/ftrace/test.d/functions b/tools/testing/selftests/ftrace/test.d/functions index c37262f6c269..91de1a8e4f19 100644 --- a/tools/testing/selftests/ftrace/test.d/functions +++ b/tools/testing/selftests/ftrace/test.d/functions @@ -23,3 +23,31 @@ reset_trigger() { # reset all current setting triggers done } +reset_events_filter() { # reset all current setting filters + grep -v ^none events/*/*/filter | + while read line; do + echo 0 > `echo $line | cut -f1 -d:` + done +} + +disable_events() { + echo 0 > events/enable +} + +initialize_ftrace() { # Reset ftrace to initial-state +# As the initial state, ftrace will be set to nop tracer, +# no events, no triggers, no filters, no function filters, +# no probes, and tracing on. + disable_tracing + reset_tracer + reset_trigger + reset_events_filter + disable_events + echo > set_event_pid # event tracer is always on + [ -f set_ftrace_filter ] && echo | tee set_ftrace_* + [ -f set_graph_function ] && echo | tee set_graph_* + [ -f stack_trace_filter ] && echo > stack_trace_filter + [ -f kprobe_events ] && echo > kprobe_events + [ -f uprobe_events ] && echo > uprobe_events + enable_tracing +} diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_type.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_type.tc new file mode 100644 index 000000000000..0a78705b43b2 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_type.tc @@ -0,0 +1,37 @@ +#!/bin/sh +# description: Kprobes event arguments with types + +[ -f kprobe_events ] || exit_unsupported # this is configurable + +grep "x8/16/32/64" README > /dev/null || exit_unsupported # version issue + +echo 0 > events/enable +echo > kprobe_events +enable_tracing + +echo 'p:testprobe _do_fork $stack0:s32 $stack0:u32 $stack0:x32 $stack0:b8@4/32' > kprobe_events +grep testprobe kprobe_events +test -d events/kprobes/testprobe + +echo 1 > events/kprobes/testprobe/enable +( echo "forked") +echo 0 > events/kprobes/testprobe/enable +ARGS=`tail -n 1 trace | sed -e 's/.* arg1=\(.*\) arg2=\(.*\) arg3=\(.*\) arg4=\(.*\)/\1 \2 \3 \4/'` + +check_types() { + X1=`printf "%x" $1 | tail -c 8` + X2=`printf "%x" $2` + X3=`printf "%x" $3` + test $X1 = $X2 + test $X2 = $X3 + test 0x$X3 = $3 + + B4=`printf "%x" $4` + B3=`echo -n $X3 | tail -c 3 | head -c 2` + test $B3 = $B4 +} +check_types $ARGS + +echo "-:testprobe" >> kprobe_events +clear_trace +test -d events/kprobes/testprobe && exit 1 || exit 0 diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-mod.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-mod.tc index 0bf5085281f3..400e98b64948 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-mod.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-mod.tc @@ -56,7 +56,7 @@ echo "Test histogram with syscall modifier" echo 'hist:keys=id.syscall' > events/raw_syscalls/sys_exit/trigger for i in `seq 1 10` ; do ( echo "forked" > /dev/null); done -grep "id: sys_" events/raw_syscalls/sys_exit/hist > /dev/null || \ +grep "id: \(unknown_\|sys_\)" events/raw_syscalls/sys_exit/hist > /dev/null || \ fail "syscall modifier on raw_syscalls/sys_exit did not work" diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-snapshot.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-snapshot.tc index f84b80d551a2..ed94f0c4e0e4 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-snapshot.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-snapshot.tc @@ -23,6 +23,11 @@ if [ ! -f events/sched/sched_process_fork/trigger ]; then exit_unsupported fi +if [ ! -f snapshot ]; then + echo "snapshot is not supported" + exit_unsupported +fi + reset_tracer do_reset diff --git a/tools/testing/selftests/gpio/Makefile b/tools/testing/selftests/gpio/Makefile new file mode 100644 index 000000000000..205e4d10e085 --- /dev/null +++ b/tools/testing/selftests/gpio/Makefile @@ -0,0 +1,23 @@ + +TEST_PROGS := gpio-mockup.sh +TEST_FILES := gpio-mockup-sysfs.sh $(BINARIES) +BINARIES := gpio-mockup-chardev + +include ../lib.mk + +all: $(BINARIES) + +clean: + $(RM) $(BINARIES) + +CFLAGS += -O2 -g -std=gnu99 -Wall -I../../../../usr/include/ +LDLIBS += -lmount -I/usr/include/libmount + +$(BINARIES): ../../../gpio/gpio-utils.o ../../../../usr/include/linux/gpio.h + +../../../gpio/gpio-utils.o: + make ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C ../../../gpio + +../../../../usr/include/linux/gpio.h: + make -C ../../../.. headers_install INSTALL_HDR_PATH=$(shell pwd)/../../../../usr/ + diff --git a/tools/testing/selftests/gpio/gpio-mockup-chardev.c b/tools/testing/selftests/gpio/gpio-mockup-chardev.c new file mode 100644 index 000000000000..667e916fa7cc --- /dev/null +++ b/tools/testing/selftests/gpio/gpio-mockup-chardev.c @@ -0,0 +1,324 @@ +/* + * GPIO chardev test helper + * + * Copyright (C) 2016 Bamvor Jian Zhang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#define _GNU_SOURCE +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <getopt.h> +#include <sys/ioctl.h> +#include <libmount.h> +#include <err.h> +#include <dirent.h> +#include <linux/gpio.h> +#include "../../../gpio/gpio-utils.h" + +#define CONSUMER "gpio-selftest" +#define GC_NUM 10 +enum direction { + OUT, + IN +}; + +static int get_debugfs(char **path) +{ + struct libmnt_context *cxt; + struct libmnt_table *tb; + struct libmnt_iter *itr = NULL; + struct libmnt_fs *fs; + int found = 0; + + cxt = mnt_new_context(); + if (!cxt) + err(EXIT_FAILURE, "libmount context allocation failed"); + + itr = mnt_new_iter(MNT_ITER_FORWARD); + if (!itr) + err(EXIT_FAILURE, "failed to initialize libmount iterator"); + + if (mnt_context_get_mtab(cxt, &tb)) + err(EXIT_FAILURE, "failed to read mtab"); + + while (mnt_table_next_fs(tb, itr, &fs) == 0) { + const char *type = mnt_fs_get_fstype(fs); + + if (!strcmp(type, "debugfs")) { + found = 1; + break; + } + } + if (found) + asprintf(path, "%s/gpio", mnt_fs_get_target(fs)); + + mnt_free_iter(itr); + mnt_free_context(cxt); + + if (!found) + return -1; + + return 0; +} + +static int gpio_debugfs_get(const char *consumer, int *dir, int *value) +{ + char *debugfs; + FILE *f; + char *line = NULL; + size_t len = 0; + char *cur; + int found = 0; + + if (get_debugfs(&debugfs) != 0) + err(EXIT_FAILURE, "debugfs is not mounted"); + + f = fopen(debugfs, "r"); + if (!f) + err(EXIT_FAILURE, "read from gpio debugfs failed"); + + /* + * gpio-2 ( |gpio-selftest ) in lo + */ + while (getline(&line, &len, f) != -1) { + cur = strstr(line, consumer); + if (cur == NULL) + continue; + + cur = strchr(line, ')'); + if (!cur) + continue; + + cur += 2; + if (!strncmp(cur, "out", 3)) { + *dir = OUT; + cur += 4; + } else if (!strncmp(cur, "in", 2)) { + *dir = IN; + cur += 4; + } + + if (!strncmp(cur, "hi", 2)) + *value = 1; + else if (!strncmp(cur, "lo", 2)) + *value = 0; + + found = 1; + break; + } + free(debugfs); + fclose(f); + free(line); + + if (!found) + return -1; + + return 0; +} + +static struct gpiochip_info *list_gpiochip(const char *gpiochip_name, int *ret) +{ + struct gpiochip_info *cinfo; + struct gpiochip_info *current; + const struct dirent *ent; + DIR *dp; + char *chrdev_name; + int fd; + int i = 0; + + cinfo = calloc(sizeof(struct gpiochip_info) * 4, GC_NUM + 1); + if (!cinfo) + err(EXIT_FAILURE, "gpiochip_info allocation failed"); + + current = cinfo; + dp = opendir("/dev"); + if (!dp) { + *ret = -errno; + goto error_out; + } else { + *ret = 0; + } + + while (ent = readdir(dp), ent) { + if (check_prefix(ent->d_name, "gpiochip")) { + *ret = asprintf(&chrdev_name, "/dev/%s", ent->d_name); + if (*ret < 0) + goto error_out; + + fd = open(chrdev_name, 0); + if (fd == -1) { + *ret = -errno; + fprintf(stderr, "Failed to open %s\n", + chrdev_name); + goto error_close_dir; + } + *ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, current); + if (*ret == -1) { + perror("Failed to issue CHIPINFO IOCTL\n"); + goto error_close_dir; + } + close(fd); + if (strcmp(current->label, gpiochip_name) == 0 + || check_prefix(current->label, gpiochip_name)) { + *ret = 0; + current++; + i++; + } + } + } + + if ((!*ret && i == 0) || *ret < 0) { + free(cinfo); + cinfo = NULL; + } + if (!*ret && i > 0) { + cinfo = realloc(cinfo, sizeof(struct gpiochip_info) * 4 * i); + *ret = i; + } + +error_close_dir: + closedir(dp); +error_out: + if (*ret < 0) + err(EXIT_FAILURE, "list gpiochip failed: %s", strerror(*ret)); + + return cinfo; +} + +int gpio_pin_test(struct gpiochip_info *cinfo, int line, int flag, int value) +{ + struct gpiohandle_data data; + unsigned int lines[] = {line}; + int fd; + int debugfs_dir = IN; + int debugfs_value = 0; + int ret; + + data.values[0] = value; + ret = gpiotools_request_linehandle(cinfo->name, lines, 1, flag, &data, + CONSUMER); + if (ret < 0) + goto fail_out; + else + fd = ret; + + ret = gpio_debugfs_get(CONSUMER, &debugfs_dir, &debugfs_value); + if (ret) { + ret = -EINVAL; + goto fail_out; + } + if (flag & GPIOHANDLE_REQUEST_INPUT) { + if (debugfs_dir != IN) { + errno = -EINVAL; + ret = -errno; + } + } else if (flag & GPIOHANDLE_REQUEST_OUTPUT) { + if (flag & GPIOHANDLE_REQUEST_ACTIVE_LOW) + debugfs_value = !debugfs_value; + + if (!(debugfs_dir == OUT && value == debugfs_value)) + errno = -EINVAL; + ret = -errno; + + } + gpiotools_release_linehandle(fd); + +fail_out: + if (ret) + err(EXIT_FAILURE, "gpio<%s> line<%d> test flag<0x%x> value<%d>", + cinfo->name, line, flag, value); + + return ret; +} + +void gpio_pin_tests(struct gpiochip_info *cinfo, unsigned int line) +{ + printf("line<%d>", line); + gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_OUTPUT, 0); + printf("."); + gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_OUTPUT, 1); + printf("."); + gpio_pin_test(cinfo, line, + GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_ACTIVE_LOW, + 0); + printf("."); + gpio_pin_test(cinfo, line, + GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_ACTIVE_LOW, + 1); + printf("."); + gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_INPUT, 0); + printf("."); +} + +/* + * ./gpio-mockup-chardev gpio_chip_name_prefix is_valid_gpio_chip + * Return 0 if successful or exit with EXIT_FAILURE if test failed. + * gpio_chip_name_prefix: The prefix of gpiochip you want to test. E.g. + * gpio-mockup + * is_valid_gpio_chip: Whether the gpio_chip is valid. 1 means valid, + * 0 means invalid which could not be found by + * list_gpiochip. + */ +int main(int argc, char *argv[]) +{ + char *prefix; + int valid; + struct gpiochip_info *cinfo; + struct gpiochip_info *current; + int i; + int ret; + + if (argc < 3) { + printf("Usage: %s prefix is_valid", argv[0]); + exit(EXIT_FAILURE); + } + + prefix = argv[1]; + valid = strcmp(argv[2], "true") == 0 ? 1 : 0; + + printf("Test gpiochip %s: ", prefix); + cinfo = list_gpiochip(prefix, &ret); + if (!cinfo) { + if (!valid && ret == 0) { + printf("Invalid test successful\n"); + ret = 0; + goto out; + } else { + ret = -EINVAL; + goto out; + } + } else if (cinfo && !valid) { + ret = -EINVAL; + goto out; + } + current = cinfo; + for (i = 0; i < ret; i++) { + gpio_pin_tests(current, 0); + gpio_pin_tests(current, current->lines - 1); + gpio_pin_tests(current, random() % current->lines); + current++; + } + ret = 0; + printf("successful\n"); + +out: + if (ret) + fprintf(stderr, "gpio<%s> test failed\n", prefix); + + if (cinfo) + free(cinfo); + + if (ret) + exit(EXIT_FAILURE); + + return ret; +} diff --git a/tools/testing/selftests/gpio/gpio-mockup-sysfs.sh b/tools/testing/selftests/gpio/gpio-mockup-sysfs.sh new file mode 100755 index 000000000000..085d7a39899c --- /dev/null +++ b/tools/testing/selftests/gpio/gpio-mockup-sysfs.sh @@ -0,0 +1,134 @@ + +is_consistent() +{ + val= + + active_low_sysfs=`cat $GPIO_SYSFS/gpio$nr/active_low` + val_sysfs=`cat $GPIO_SYSFS/gpio$nr/value` + dir_sysfs=`cat $GPIO_SYSFS/gpio$nr/direction` + + gpio_this_debugfs=`cat $GPIO_DEBUGFS |grep "gpio-$nr" | sed "s/(.*)//g"` + dir_debugfs=`echo $gpio_this_debugfs | awk '{print $2}'` + val_debugfs=`echo $gpio_this_debugfs | awk '{print $3}'` + if [ $val_debugfs = "lo" ]; then + val=0 + elif [ $val_debugfs = "hi" ]; then + val=1 + fi + + if [ $active_low_sysfs = "1" ]; then + if [ $val = "0" ]; then + val="1" + else + val="0" + fi + fi + + if [ $val_sysfs = $val ] && [ $dir_sysfs = $dir_debugfs ]; then + echo -n "." + else + echo "test fail, exit" + die + fi +} + +test_pin_logic() +{ + nr=$1 + direction=$2 + active_low=$3 + value=$4 + + echo $direction > $GPIO_SYSFS/gpio$nr/direction + echo $active_low > $GPIO_SYSFS/gpio$nr/active_low + if [ $direction = "out" ]; then + echo $value > $GPIO_SYSFS/gpio$nr/value + fi + is_consistent $nr +} + +test_one_pin() +{ + nr=$1 + + echo -n "test pin<$nr>" + + echo $nr > $GPIO_SYSFS/export 2>/dev/null + + if [ X$? != X0 ]; then + echo "test GPIO pin $nr failed" + die + fi + + #"Checking if the sysfs is consistent with debugfs: " + is_consistent $nr + + #"Checking the logic of active_low: " + test_pin_logic $nr out 1 1 + test_pin_logic $nr out 1 0 + test_pin_logic $nr out 0 1 + test_pin_logic $nr out 0 0 + + #"Checking the logic of direction: " + test_pin_logic $nr in 1 1 + test_pin_logic $nr out 1 0 + test_pin_logic $nr low 0 1 + test_pin_logic $nr high 0 0 + + echo $nr > $GPIO_SYSFS/unexport + + echo "successful" +} + +test_one_pin_fail() +{ + nr=$1 + + echo $nr > $GPIO_SYSFS/export 2>/dev/null + + if [ X$? != X0 ]; then + echo "test invalid pin $nr successful" + else + echo "test invalid pin $nr failed" + echo $nr > $GPIO_SYSFS/unexport 2>/dev/null + die + fi +} + +list_chip() +{ + echo `ls -d $GPIO_DRV_SYSFS/gpiochip* 2>/dev/null` +} + +test_chip() +{ + chip=$1 + name=`basename $chip` + base=`cat $chip/base` + ngpio=`cat $chip/ngpio` + printf "%-10s %-5s %-5s\n" $name $base $ngpio + if [ $ngpio = "0" ]; then + echo "number of gpio is zero is not allowed". + fi + test_one_pin $base + test_one_pin $(($base + $ngpio - 1)) + test_one_pin $((( RANDOM % $ngpio ) + $base )) +} + +test_chips_sysfs() +{ + gpiochip=`list_chip $module` + if [ X"$gpiochip" = X ]; then + if [ X"$valid" = Xfalse ]; then + echo "successful" + else + echo "fail" + die + fi + else + for chip in $gpiochip; do + test_chip $chip + done + fi +} + diff --git a/tools/testing/selftests/gpio/gpio-mockup.sh b/tools/testing/selftests/gpio/gpio-mockup.sh new file mode 100755 index 000000000000..b183439e058e --- /dev/null +++ b/tools/testing/selftests/gpio/gpio-mockup.sh @@ -0,0 +1,201 @@ +#!/bin/bash + +#exit status +#1: run as non-root user +#2: sysfs/debugfs not mount +#3: insert module fail when gpio-mockup is a module. +#4: other reason. + +SYSFS= +GPIO_SYSFS= +GPIO_DRV_SYSFS= +DEBUGFS= +GPIO_DEBUGFS= +dev_type= +module= + +usage() +{ + echo "Usage:" + echo "$0 [-f] [-m name] [-t type]" + echo "-f: full test. It maybe conflict with existence gpio device." + echo "-m: module name, default name is gpio-mockup. It could also test" + echo " other gpio device." + echo "-t: interface type: chardev(char device) and sysfs(being" + echo " deprecated). The first one is default" + echo "" + echo "$0 -h" + echo "This usage" +} + +prerequisite() +{ + msg="skip all tests:" + if [ $UID != 0 ]; then + echo $msg must be run as root >&2 + exit 1 + fi + SYSFS=`mount -t sysfs | head -1 | awk '{ print $3 }'` + if [ ! -d "$SYSFS" ]; then + echo $msg sysfs is not mounted >&2 + exit 2 + fi + GPIO_SYSFS=`echo $SYSFS/class/gpio` + GPIO_DRV_SYSFS=`echo $SYSFS/devices/platform/$module/gpio` + DEBUGFS=`mount -t debugfs | head -1 | awk '{ print $3 }'` + if [ ! -d "$DEBUGFS" ]; then + echo $msg debugfs is not mounted >&2 + exit 2 + fi + GPIO_DEBUGFS=`echo $DEBUGFS/gpio` + source gpio-mockup-sysfs.sh +} + +try_insert_module() +{ + if [ -d "$GPIO_DRV_SYSFS" ]; then + echo "$GPIO_DRV_SYSFS exist. Skip insert module" + else + modprobe -q $module $1 + if [ X$? != X0 ]; then + echo $msg insmod $module failed >&2 + exit 3 + fi + fi +} + +remove_module() +{ + modprobe -r -q $module +} + +die() +{ + remove_module + exit 4 +} + +test_chips() +{ + if [ X$dev_type = Xsysfs ]; then + echo "WARNING: sysfs ABI of gpio is going to deprecated." + test_chips_sysfs $* + else + $BASE/gpio-mockup-chardev $* + fi +} + +gpio_test() +{ + param=$1 + valid=$2 + + if [ X"$param" = X ]; then + die + fi + try_insert_module "gpio_mockup_ranges=$param" + echo -n "GPIO $module test with ranges: <" + echo "$param>: " + printf "%-10s %s\n" $param + test_chips $module $valid + remove_module +} + +BASE=`dirname $0` + +dev_type= +TEMP=`getopt -o fhm:t: -n '$0' -- "$@"` + +if [ "$?" != "0" ]; then + echo "Parameter process failed, Terminating..." >&2 + exit 1 +fi + +# Note the quotes around `$TEMP': they are essential! +eval set -- "$TEMP" + +while true; do + case $1 in + -f) + full_test=true + shift + ;; + -h) + usage + exit + ;; + -m) + module=$2 + shift 2 + ;; + -t) + dev_type=$2 + shift 2 + ;; + --) + shift + break + ;; + *) + echo "Internal error!" + exit 1 + ;; + esac +done + +if [ X"$module" = X ]; then + module="gpio-mockup" +fi + +if [ X$dev_type != Xsysfs ]; then + dev_type="chardev" +fi + +prerequisite + +echo "1. Test dynamic allocation of gpio successful means insert gpiochip and" +echo " manipulate gpio pin successful" +gpio_test "-1,32" true +gpio_test "-1,32,-1,32" true +gpio_test "-1,32,-1,32,-1,32" true +if [ X$full_test = Xtrue ]; then + gpio_test "-1,32,32,64" true + gpio_test "-1,32,40,64,-1,5" true + gpio_test "-1,32,32,64,-1,32" true + gpio_test "0,32,32,64,-1,32,-1,32" true + gpio_test "-1,32,-1,32,0,32,32,64" true + echo "2. Do basic test: successful means insert gpiochip and" + echo " manipulate gpio pin successful" + gpio_test "0,32" true + gpio_test "0,32,32,64" true + gpio_test "0,32,40,64,64,96" true +fi +echo "3. Error test: successful means insert gpiochip failed" +echo "3.1 Test number of gpio overflow" +#Currently: The max number of gpio(1024) is defined in arm architecture. +gpio_test "-1,32,-1,1024" false +if [ X$full_test = Xtrue ]; then + echo "3.2 Test zero line of gpio" + gpio_test "0,0" false + echo "3.3 Test range overlap" + echo "3.3.1 Test corner case" + gpio_test "0,32,0,1" false + gpio_test "0,32,32,64,32,40" false + gpio_test "0,32,35,64,35,45" false + gpio_test "0,32,31,32" false + gpio_test "0,32,32,64,36,37" false + gpio_test "0,32,35,64,34,36" false + echo "3.3.2 Test inserting invalid second gpiochip" + gpio_test "0,32,30,35" false + gpio_test "0,32,1,5" false + gpio_test "10,32,9,14" false + gpio_test "10,32,30,35" false + echo "3.3.3 Test others" + gpio_test "0,32,40,56,39,45" false + gpio_test "0,32,40,56,30,33" false + gpio_test "0,32,40,56,30,41" false + gpio_test "0,32,40,56,20,21" false +fi + +echo GPIO test PASS + diff --git a/tools/testing/selftests/nsfs/.gitignore b/tools/testing/selftests/nsfs/.gitignore new file mode 100644 index 000000000000..2ab2c824ce86 --- /dev/null +++ b/tools/testing/selftests/nsfs/.gitignore @@ -0,0 +1,2 @@ +owner +pidns diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile index db54a33f850f..c2c4211ba58b 100644 --- a/tools/testing/selftests/powerpc/Makefile +++ b/tools/testing/selftests/powerpc/Makefile @@ -8,7 +8,7 @@ ifeq ($(ARCH),powerpc) GIT_VERSION = $(shell git describe --always --long --dirty || echo "unknown") -CFLAGS := -std=gnu99 -Wall -O2 -Wall -Werror -DGIT_VERSION='"$(GIT_VERSION)"' -I$(CURDIR) $(CFLAGS) +CFLAGS := -std=gnu99 -Wall -O2 -Wall -Werror -DGIT_VERSION='"$(GIT_VERSION)"' -I$(CURDIR)/include $(CFLAGS) export CFLAGS @@ -26,7 +26,8 @@ SUB_DIRS = alignment \ syscalls \ tm \ vphn \ - math + math \ + ptrace endif diff --git a/tools/testing/selftests/powerpc/benchmarks/.gitignore b/tools/testing/selftests/powerpc/benchmarks/.gitignore index bce49ebd869e..04dc1e6ef2ce 100644 --- a/tools/testing/selftests/powerpc/benchmarks/.gitignore +++ b/tools/testing/selftests/powerpc/benchmarks/.gitignore @@ -1,4 +1,5 @@ gettimeofday context_switch mmap_bench -futex_bench
\ No newline at end of file +futex_bench +null_syscall diff --git a/tools/testing/selftests/powerpc/benchmarks/Makefile b/tools/testing/selftests/powerpc/benchmarks/Makefile index a9adfb7de78f..545077f98f72 100644 --- a/tools/testing/selftests/powerpc/benchmarks/Makefile +++ b/tools/testing/selftests/powerpc/benchmarks/Makefile @@ -1,4 +1,4 @@ -TEST_PROGS := gettimeofday context_switch mmap_bench futex_bench +TEST_PROGS := gettimeofday context_switch mmap_bench futex_bench null_syscall CFLAGS += -O2 diff --git a/tools/testing/selftests/powerpc/benchmarks/context_switch.c b/tools/testing/selftests/powerpc/benchmarks/context_switch.c index a36883ad48a4..778f5fbfd784 100644 --- a/tools/testing/selftests/powerpc/benchmarks/context_switch.c +++ b/tools/testing/selftests/powerpc/benchmarks/context_switch.c @@ -28,7 +28,7 @@ #ifdef __powerpc__ #include <altivec.h> #endif -#include "../utils.h" +#include "utils.h" static unsigned int timeout = 30; diff --git a/tools/testing/selftests/powerpc/benchmarks/null_syscall.c b/tools/testing/selftests/powerpc/benchmarks/null_syscall.c new file mode 100644 index 000000000000..ecc14d68e101 --- /dev/null +++ b/tools/testing/selftests/powerpc/benchmarks/null_syscall.c @@ -0,0 +1,157 @@ +/* + * Test null syscall performance + * + * Copyright (C) 2009-2015 Anton Blanchard, IBM + * + * 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. + */ + +#define NR_LOOPS 10000000 + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <sys/types.h> +#include <sys/time.h> +#include <signal.h> + +static volatile int soak_done; +unsigned long long clock_frequency; +unsigned long long timebase_frequency; +double timebase_multiplier; + +static inline unsigned long long mftb(void) +{ + unsigned long low; + + asm volatile("mftb %0" : "=r" (low)); + + return low; +} + +static void sigalrm_handler(int unused) +{ + soak_done = 1; +} + +/* + * Use a timer instead of busy looping on clock_gettime() so we don't + * pollute profiles with glibc and VDSO hits. + */ +static void cpu_soak_usecs(unsigned long usecs) +{ + struct itimerval val; + + memset(&val, 0, sizeof(val)); + val.it_value.tv_usec = usecs; + + signal(SIGALRM, sigalrm_handler); + setitimer(ITIMER_REAL, &val, NULL); + + while (1) { + if (soak_done) + break; + } + + signal(SIGALRM, SIG_DFL); +} + +/* + * This only works with recent kernels where cpufreq modifies + * /proc/cpuinfo dynamically. + */ +static void get_proc_frequency(void) +{ + FILE *f; + char line[128]; + char *p, *end; + unsigned long v; + double d; + char *override; + + /* Try to get out of low power/low frequency mode */ + cpu_soak_usecs(0.25 * 1000000); + + f = fopen("/proc/cpuinfo", "r"); + if (f == NULL) + return; + + timebase_frequency = 0; + + while (fgets(line, sizeof(line), f) != NULL) { + if (strncmp(line, "timebase", 8) == 0) { + p = strchr(line, ':'); + if (p != NULL) { + v = strtoull(p + 1, &end, 0); + if (end != p + 1) + timebase_frequency = v; + } + } + + if (((strncmp(line, "clock", 5) == 0) || + (strncmp(line, "cpu MHz", 7) == 0))) { + p = strchr(line, ':'); + if (p != NULL) { + d = strtod(p + 1, &end); + if (end != p + 1) { + /* Find fastest clock frequency */ + if ((d * 1000000ULL) > clock_frequency) + clock_frequency = d * 1000000ULL; + } + } + } + } + + fclose(f); + + override = getenv("FREQUENCY"); + if (override) + clock_frequency = strtoull(override, NULL, 10); + + if (timebase_frequency) + timebase_multiplier = (double)clock_frequency + / timebase_frequency; + else + timebase_multiplier = 1; +} + +static void do_null_syscall(unsigned long nr) +{ + unsigned long i; + + for (i = 0; i < nr; i++) + getppid(); +} + +#define TIME(A, STR) \ + +int main(void) +{ + unsigned long tb_start, tb_now; + struct timespec tv_start, tv_now; + unsigned long long elapsed_ns, elapsed_tb; + + get_proc_frequency(); + + clock_gettime(CLOCK_MONOTONIC, &tv_start); + tb_start = mftb(); + + do_null_syscall(NR_LOOPS); + + clock_gettime(CLOCK_MONOTONIC, &tv_now); + tb_now = mftb(); + + elapsed_ns = (tv_now.tv_sec - tv_start.tv_sec) * 1000000000ULL + + (tv_now.tv_nsec - tv_start.tv_nsec); + elapsed_tb = tb_now - tb_start; + + printf("%10.2f ns %10.2f cycles\n", (float)elapsed_ns / NR_LOOPS, + (float)elapsed_tb * timebase_multiplier / NR_LOOPS); + + return 0; +} diff --git a/tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h b/tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h index 50ae7d2091ce..80d34a9ffff4 100644 --- a/tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h +++ b/tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h @@ -25,6 +25,8 @@ #define PPC_MTOCRF(A, B) mtocrf A, B +#define EX_TABLE(x, y) + FUNC_START(enter_vmx_usercopy) li r3,1 blr diff --git a/tools/testing/selftests/powerpc/copyloops/validate.c b/tools/testing/selftests/powerpc/copyloops/validate.c index 1750ff57ee58..7fb436f82d16 100644 --- a/tools/testing/selftests/powerpc/copyloops/validate.c +++ b/tools/testing/selftests/powerpc/copyloops/validate.c @@ -3,7 +3,7 @@ #include <stdlib.h> #include <stdbool.h> -#include "../utils.h" +#include "utils.h" #define MAX_LEN 8192 #define MAX_OFFSET 16 diff --git a/tools/testing/selftests/powerpc/dscr/dscr.h b/tools/testing/selftests/powerpc/dscr/dscr.h index a36af1b2c2bb..18ea223bd398 100644 --- a/tools/testing/selftests/powerpc/dscr/dscr.h +++ b/tools/testing/selftests/powerpc/dscr/dscr.h @@ -28,8 +28,6 @@ #include "utils.h" -#define SPRN_DSCR 0x11 /* Privilege state SPR */ -#define SPRN_DSCR_USR 0x03 /* Problem state SPR */ #define THREADS 100 /* Max threads */ #define COUNT 100 /* Max iterations */ #define DSCR_MAX 16 /* Max DSCR value */ @@ -48,14 +46,14 @@ inline unsigned long get_dscr(void) { unsigned long ret; - asm volatile("mfspr %0,%1" : "=r" (ret): "i" (SPRN_DSCR)); + asm volatile("mfspr %0,%1" : "=r" (ret) : "i" (SPRN_DSCR_PRIV)); return ret; } inline void set_dscr(unsigned long val) { - asm volatile("mtspr %1,%0" : : "r" (val), "i" (SPRN_DSCR)); + asm volatile("mtspr %1,%0" : : "r" (val), "i" (SPRN_DSCR_PRIV)); } /* Problem state DSCR access */ @@ -63,14 +61,14 @@ inline unsigned long get_dscr_usr(void) { unsigned long ret; - asm volatile("mfspr %0,%1" : "=r" (ret): "i" (SPRN_DSCR_USR)); + asm volatile("mfspr %0,%1" : "=r" (ret) : "i" (SPRN_DSCR)); return ret; } inline void set_dscr_usr(unsigned long val) { - asm volatile("mtspr %1,%0" : : "r" (val), "i" (SPRN_DSCR_USR)); + asm volatile("mtspr %1,%0" : : "r" (val), "i" (SPRN_DSCR)); } /* Default DSCR access */ diff --git a/tools/testing/selftests/powerpc/basic_asm.h b/tools/testing/selftests/powerpc/include/basic_asm.h index 3349a0704d1a..12eaddf72e66 100644 --- a/tools/testing/selftests/powerpc/basic_asm.h +++ b/tools/testing/selftests/powerpc/include/basic_asm.h @@ -4,12 +4,12 @@ #include <ppc-asm.h> #include <asm/unistd.h> -#define LOAD_REG_IMMEDIATE(reg,expr) \ - lis reg,(expr)@highest; \ - ori reg,reg,(expr)@higher; \ - rldicr reg,reg,32,31; \ - oris reg,reg,(expr)@high; \ - ori reg,reg,(expr)@l; +#define LOAD_REG_IMMEDIATE(reg, expr) \ + lis reg, (expr)@highest; \ + ori reg, reg, (expr)@higher; \ + rldicr reg, reg, 32, 31; \ + oris reg, reg, (expr)@high; \ + ori reg, reg, (expr)@l; /* * Note: These macros assume that variables being stored on the stack are @@ -20,7 +20,8 @@ #define STACK_FRAME_MIN_SIZE 32 #define STACK_FRAME_TOC_POS 24 #define __STACK_FRAME_PARAM(_param) (32 + ((_param)*8)) -#define __STACK_FRAME_LOCAL(_num_params,_var_num) ((STACK_FRAME_PARAM(_num_params)) + ((_var_num)*8)) +#define __STACK_FRAME_LOCAL(_num_params, _var_num) \ + ((STACK_FRAME_PARAM(_num_params)) + ((_var_num)*8)) #else #define STACK_FRAME_MIN_SIZE 112 #define STACK_FRAME_TOC_POS 40 @@ -30,14 +31,16 @@ * Caveat: if a function passed more than 8 doublewords, the caller will have * made more space... which would render the 112 incorrect. */ -#define __STACK_FRAME_LOCAL(_num_params,_var_num) (112 + ((_var_num)*8)) +#define __STACK_FRAME_LOCAL(_num_params, _var_num) \ + (112 + ((_var_num)*8)) #endif /* Parameter x saved to the stack */ #define STACK_FRAME_PARAM(var) __STACK_FRAME_PARAM(var) /* Local variable x saved to the stack after x parameters */ -#define STACK_FRAME_LOCAL(num_params,var) __STACK_FRAME_LOCAL(num_params,var) +#define STACK_FRAME_LOCAL(num_params, var) \ + __STACK_FRAME_LOCAL(num_params, var) #define STACK_FRAME_LR_POS 16 #define STACK_FRAME_CR_POS 8 @@ -53,18 +56,18 @@ */ #define PUSH_BASIC_STACK(_extra) \ mflr r0; \ - std r0,STACK_FRAME_LR_POS(%r1); \ - stdu %r1,-(_extra + STACK_FRAME_MIN_SIZE)(%r1); \ + std r0, STACK_FRAME_LR_POS(%r1); \ + stdu %r1, -(_extra + STACK_FRAME_MIN_SIZE)(%r1); \ mfcr r0; \ - stw r0,STACK_FRAME_CR_POS(%r1); \ - std %r2,STACK_FRAME_TOC_POS(%r1); + stw r0, STACK_FRAME_CR_POS(%r1); \ + std %r2, STACK_FRAME_TOC_POS(%r1); #define POP_BASIC_STACK(_extra) \ - ld %r2,STACK_FRAME_TOC_POS(%r1); \ - lwz r0,STACK_FRAME_CR_POS(%r1); \ + ld %r2, STACK_FRAME_TOC_POS(%r1); \ + lwz r0, STACK_FRAME_CR_POS(%r1); \ mtcr r0; \ - addi %r1,%r1,(_extra + STACK_FRAME_MIN_SIZE); \ - ld r0,STACK_FRAME_LR_POS(%r1); \ + addi %r1, %r1, (_extra + STACK_FRAME_MIN_SIZE); \ + ld r0, STACK_FRAME_LR_POS(%r1); \ mtlr r0; #endif /* _SELFTESTS_POWERPC_BASIC_ASM_H */ diff --git a/tools/testing/selftests/powerpc/fpu_asm.h b/tools/testing/selftests/powerpc/include/fpu_asm.h index 6a387d255e27..6a387d255e27 100644 --- a/tools/testing/selftests/powerpc/fpu_asm.h +++ b/tools/testing/selftests/powerpc/include/fpu_asm.h diff --git a/tools/testing/selftests/powerpc/gpr_asm.h b/tools/testing/selftests/powerpc/include/gpr_asm.h index f6f38852d3a0..f6f38852d3a0 100644 --- a/tools/testing/selftests/powerpc/gpr_asm.h +++ b/tools/testing/selftests/powerpc/include/gpr_asm.h diff --git a/tools/testing/selftests/powerpc/instructions.h b/tools/testing/selftests/powerpc/include/instructions.h index 0fb0bd3b28c9..0fb0bd3b28c9 100644 --- a/tools/testing/selftests/powerpc/instructions.h +++ b/tools/testing/selftests/powerpc/include/instructions.h diff --git a/tools/testing/selftests/powerpc/include/reg.h b/tools/testing/selftests/powerpc/include/reg.h new file mode 100644 index 000000000000..4afdebcce4cd --- /dev/null +++ b/tools/testing/selftests/powerpc/include/reg.h @@ -0,0 +1,145 @@ +/* + * Copyright 2014, Michael Ellerman, IBM Corp. + * Licensed under GPLv2. + */ + +#ifndef _SELFTESTS_POWERPC_REG_H +#define _SELFTESTS_POWERPC_REG_H + +#define __stringify_1(x) #x +#define __stringify(x) __stringify_1(x) + +#define mfspr(rn) ({unsigned long rval; \ + asm volatile("mfspr %0," _str(rn) \ + : "=r" (rval)); rval; }) +#define mtspr(rn, v) asm volatile("mtspr " _str(rn) ",%0" : \ + : "r" ((unsigned long)(v)) \ + : "memory") + +#define mb() asm volatile("sync" : : : "memory"); + +#define SPRN_MMCR2 769 +#define SPRN_MMCRA 770 +#define SPRN_MMCR0 779 +#define MMCR0_PMAO 0x00000080 +#define MMCR0_PMAE 0x04000000 +#define MMCR0_FC 0x80000000 +#define SPRN_EBBHR 804 +#define SPRN_EBBRR 805 +#define SPRN_BESCR 806 /* Branch event status & control register */ +#define SPRN_BESCRS 800 /* Branch event status & control set (1 bits set to 1) */ +#define SPRN_BESCRSU 801 /* Branch event status & control set upper */ +#define SPRN_BESCRR 802 /* Branch event status & control REset (1 bits set to 0) */ +#define SPRN_BESCRRU 803 /* Branch event status & control REset upper */ + +#define BESCR_PMEO 0x1 /* PMU Event-based exception Occurred */ +#define BESCR_PME (0x1ul << 32) /* PMU Event-based exception Enable */ + +#define SPRN_PMC1 771 +#define SPRN_PMC2 772 +#define SPRN_PMC3 773 +#define SPRN_PMC4 774 +#define SPRN_PMC5 775 +#define SPRN_PMC6 776 + +#define SPRN_SIAR 780 +#define SPRN_SDAR 781 +#define SPRN_SIER 768 + +#define SPRN_TEXASR 0x82 /* Transaction Exception and Status Register */ +#define SPRN_TFIAR 0x81 /* Transaction Failure Inst Addr */ +#define SPRN_TFHAR 0x80 /* Transaction Failure Handler Addr */ +#define SPRN_TAR 0x32f /* Target Address Register */ + +#define SPRN_DSCR_PRIV 0x11 /* Privilege State DSCR */ +#define SPRN_DSCR 0x03 /* Data Stream Control Register */ +#define SPRN_PPR 896 /* Program Priority Register */ + +/* TEXASR register bits */ +#define TEXASR_FC 0xFE00000000000000 +#define TEXASR_FP 0x0100000000000000 +#define TEXASR_DA 0x0080000000000000 +#define TEXASR_NO 0x0040000000000000 +#define TEXASR_FO 0x0020000000000000 +#define TEXASR_SIC 0x0010000000000000 +#define TEXASR_NTC 0x0008000000000000 +#define TEXASR_TC 0x0004000000000000 +#define TEXASR_TIC 0x0002000000000000 +#define TEXASR_IC 0x0001000000000000 +#define TEXASR_IFC 0x0000800000000000 +#define TEXASR_ABT 0x0000000100000000 +#define TEXASR_SPD 0x0000000080000000 +#define TEXASR_HV 0x0000000020000000 +#define TEXASR_PR 0x0000000010000000 +#define TEXASR_FS 0x0000000008000000 +#define TEXASR_TE 0x0000000004000000 +#define TEXASR_ROT 0x0000000002000000 + +/* Vector Instructions */ +#define VSX_XX1(xs, ra, rb) (((xs) & 0x1f) << 21 | ((ra) << 16) | \ + ((rb) << 11) | (((xs) >> 5))) +#define STXVD2X(xs, ra, rb) .long (0x7c000798 | VSX_XX1((xs), (ra), (rb))) +#define LXVD2X(xs, ra, rb) .long (0x7c000698 | VSX_XX1((xs), (ra), (rb))) + +#define ASM_LOAD_GPR_IMMED(_asm_symbol_name_immed) \ + "li 14, %[" #_asm_symbol_name_immed "];" \ + "li 15, %[" #_asm_symbol_name_immed "];" \ + "li 16, %[" #_asm_symbol_name_immed "];" \ + "li 17, %[" #_asm_symbol_name_immed "];" \ + "li 18, %[" #_asm_symbol_name_immed "];" \ + "li 19, %[" #_asm_symbol_name_immed "];" \ + "li 20, %[" #_asm_symbol_name_immed "];" \ + "li 21, %[" #_asm_symbol_name_immed "];" \ + "li 22, %[" #_asm_symbol_name_immed "];" \ + "li 23, %[" #_asm_symbol_name_immed "];" \ + "li 24, %[" #_asm_symbol_name_immed "];" \ + "li 25, %[" #_asm_symbol_name_immed "];" \ + "li 26, %[" #_asm_symbol_name_immed "];" \ + "li 27, %[" #_asm_symbol_name_immed "];" \ + "li 28, %[" #_asm_symbol_name_immed "];" \ + "li 29, %[" #_asm_symbol_name_immed "];" \ + "li 30, %[" #_asm_symbol_name_immed "];" \ + "li 31, %[" #_asm_symbol_name_immed "];" + +#define ASM_LOAD_FPR_SINGLE_PRECISION(_asm_symbol_name_addr) \ + "lfs 0, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 1, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 2, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 3, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 4, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 5, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 6, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 7, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 8, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 9, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 10, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 11, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 12, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 13, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 14, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 15, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 16, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 17, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 18, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 19, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 20, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 21, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 22, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 23, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 24, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 25, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 26, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 27, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 28, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 29, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 30, 0(%[" #_asm_symbol_name_addr "]);" \ + "lfs 31, 0(%[" #_asm_symbol_name_addr "]);" + +#ifndef __ASSEMBLER__ +void store_gpr(unsigned long *addr); +void load_gpr(unsigned long *addr); +void load_fpr_single_precision(float *addr); +void store_fpr_single_precision(float *addr); +#endif /* end of __ASSEMBLER__ */ + +#endif /* _SELFTESTS_POWERPC_REG_H */ diff --git a/tools/testing/selftests/powerpc/subunit.h b/tools/testing/selftests/powerpc/include/subunit.h index 9c6c4e901ab6..9c6c4e901ab6 100644 --- a/tools/testing/selftests/powerpc/subunit.h +++ b/tools/testing/selftests/powerpc/include/subunit.h diff --git a/tools/testing/selftests/powerpc/utils.h b/tools/testing/selftests/powerpc/include/utils.h index 53405e8a52ab..53405e8a52ab 100644 --- a/tools/testing/selftests/powerpc/utils.h +++ b/tools/testing/selftests/powerpc/include/utils.h diff --git a/tools/testing/selftests/powerpc/vmx_asm.h b/tools/testing/selftests/powerpc/include/vmx_asm.h index 2eaaeca9cf1d..2eaaeca9cf1d 100644 --- a/tools/testing/selftests/powerpc/vmx_asm.h +++ b/tools/testing/selftests/powerpc/include/vmx_asm.h diff --git a/tools/testing/selftests/powerpc/vsx_asm.h b/tools/testing/selftests/powerpc/include/vsx_asm.h index d828bfb6ef2d..d828bfb6ef2d 100644 --- a/tools/testing/selftests/powerpc/vsx_asm.h +++ b/tools/testing/selftests/powerpc/include/vsx_asm.h diff --git a/tools/testing/selftests/powerpc/lib/reg.S b/tools/testing/selftests/powerpc/lib/reg.S new file mode 100644 index 000000000000..0dc44f0da065 --- /dev/null +++ b/tools/testing/selftests/powerpc/lib/reg.S @@ -0,0 +1,397 @@ +/* + * test helper assembly functions + * + * Copyright (C) 2016 Simon Guo, 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. + */ +#include <ppc-asm.h> +#include "reg.h" + + +/* Non volatile GPR - unsigned long buf[18] */ +FUNC_START(load_gpr) + ld 14, 0*8(3) + ld 15, 1*8(3) + ld 16, 2*8(3) + ld 17, 3*8(3) + ld 18, 4*8(3) + ld 19, 5*8(3) + ld 20, 6*8(3) + ld 21, 7*8(3) + ld 22, 8*8(3) + ld 23, 9*8(3) + ld 24, 10*8(3) + ld 25, 11*8(3) + ld 26, 12*8(3) + ld 27, 13*8(3) + ld 28, 14*8(3) + ld 29, 15*8(3) + ld 30, 16*8(3) + ld 31, 17*8(3) + blr +FUNC_END(load_gpr) + +FUNC_START(store_gpr) + std 14, 0*8(3) + std 15, 1*8(3) + std 16, 2*8(3) + std 17, 3*8(3) + std 18, 4*8(3) + std 19, 5*8(3) + std 20, 6*8(3) + std 21, 7*8(3) + std 22, 8*8(3) + std 23, 9*8(3) + std 24, 10*8(3) + std 25, 11*8(3) + std 26, 12*8(3) + std 27, 13*8(3) + std 28, 14*8(3) + std 29, 15*8(3) + std 30, 16*8(3) + std 31, 17*8(3) + blr +FUNC_END(store_gpr) + +/* Single Precision Float - float buf[32] */ +FUNC_START(load_fpr_single_precision) + lfs 0, 0*4(3) + lfs 1, 1*4(3) + lfs 2, 2*4(3) + lfs 3, 3*4(3) + lfs 4, 4*4(3) + lfs 5, 5*4(3) + lfs 6, 6*4(3) + lfs 7, 7*4(3) + lfs 8, 8*4(3) + lfs 9, 9*4(3) + lfs 10, 10*4(3) + lfs 11, 11*4(3) + lfs 12, 12*4(3) + lfs 13, 13*4(3) + lfs 14, 14*4(3) + lfs 15, 15*4(3) + lfs 16, 16*4(3) + lfs 17, 17*4(3) + lfs 18, 18*4(3) + lfs 19, 19*4(3) + lfs 20, 20*4(3) + lfs 21, 21*4(3) + lfs 22, 22*4(3) + lfs 23, 23*4(3) + lfs 24, 24*4(3) + lfs 25, 25*4(3) + lfs 26, 26*4(3) + lfs 27, 27*4(3) + lfs 28, 28*4(3) + lfs 29, 29*4(3) + lfs 30, 30*4(3) + lfs 31, 31*4(3) + blr +FUNC_END(load_fpr_single_precision) + +/* Single Precision Float - float buf[32] */ +FUNC_START(store_fpr_single_precision) + stfs 0, 0*4(3) + stfs 1, 1*4(3) + stfs 2, 2*4(3) + stfs 3, 3*4(3) + stfs 4, 4*4(3) + stfs 5, 5*4(3) + stfs 6, 6*4(3) + stfs 7, 7*4(3) + stfs 8, 8*4(3) + stfs 9, 9*4(3) + stfs 10, 10*4(3) + stfs 11, 11*4(3) + stfs 12, 12*4(3) + stfs 13, 13*4(3) + stfs 14, 14*4(3) + stfs 15, 15*4(3) + stfs 16, 16*4(3) + stfs 17, 17*4(3) + stfs 18, 18*4(3) + stfs 19, 19*4(3) + stfs 20, 20*4(3) + stfs 21, 21*4(3) + stfs 22, 22*4(3) + stfs 23, 23*4(3) + stfs 24, 24*4(3) + stfs 25, 25*4(3) + stfs 26, 26*4(3) + stfs 27, 27*4(3) + stfs 28, 28*4(3) + stfs 29, 29*4(3) + stfs 30, 30*4(3) + stfs 31, 31*4(3) + blr +FUNC_END(store_fpr_single_precision) + +/* VMX/VSX registers - unsigned long buf[128] */ +FUNC_START(loadvsx) + lis 4, 0 + LXVD2X (0,(4),(3)) + addi 4, 4, 16 + LXVD2X (1,(4),(3)) + addi 4, 4, 16 + LXVD2X (2,(4),(3)) + addi 4, 4, 16 + LXVD2X (3,(4),(3)) + addi 4, 4, 16 + LXVD2X (4,(4),(3)) + addi 4, 4, 16 + LXVD2X (5,(4),(3)) + addi 4, 4, 16 + LXVD2X (6,(4),(3)) + addi 4, 4, 16 + LXVD2X (7,(4),(3)) + addi 4, 4, 16 + LXVD2X (8,(4),(3)) + addi 4, 4, 16 + LXVD2X (9,(4),(3)) + addi 4, 4, 16 + LXVD2X (10,(4),(3)) + addi 4, 4, 16 + LXVD2X (11,(4),(3)) + addi 4, 4, 16 + LXVD2X (12,(4),(3)) + addi 4, 4, 16 + LXVD2X (13,(4),(3)) + addi 4, 4, 16 + LXVD2X (14,(4),(3)) + addi 4, 4, 16 + LXVD2X (15,(4),(3)) + addi 4, 4, 16 + LXVD2X (16,(4),(3)) + addi 4, 4, 16 + LXVD2X (17,(4),(3)) + addi 4, 4, 16 + LXVD2X (18,(4),(3)) + addi 4, 4, 16 + LXVD2X (19,(4),(3)) + addi 4, 4, 16 + LXVD2X (20,(4),(3)) + addi 4, 4, 16 + LXVD2X (21,(4),(3)) + addi 4, 4, 16 + LXVD2X (22,(4),(3)) + addi 4, 4, 16 + LXVD2X (23,(4),(3)) + addi 4, 4, 16 + LXVD2X (24,(4),(3)) + addi 4, 4, 16 + LXVD2X (25,(4),(3)) + addi 4, 4, 16 + LXVD2X (26,(4),(3)) + addi 4, 4, 16 + LXVD2X (27,(4),(3)) + addi 4, 4, 16 + LXVD2X (28,(4),(3)) + addi 4, 4, 16 + LXVD2X (29,(4),(3)) + addi 4, 4, 16 + LXVD2X (30,(4),(3)) + addi 4, 4, 16 + LXVD2X (31,(4),(3)) + addi 4, 4, 16 + LXVD2X (32,(4),(3)) + addi 4, 4, 16 + LXVD2X (33,(4),(3)) + addi 4, 4, 16 + LXVD2X (34,(4),(3)) + addi 4, 4, 16 + LXVD2X (35,(4),(3)) + addi 4, 4, 16 + LXVD2X (36,(4),(3)) + addi 4, 4, 16 + LXVD2X (37,(4),(3)) + addi 4, 4, 16 + LXVD2X (38,(4),(3)) + addi 4, 4, 16 + LXVD2X (39,(4),(3)) + addi 4, 4, 16 + LXVD2X (40,(4),(3)) + addi 4, 4, 16 + LXVD2X (41,(4),(3)) + addi 4, 4, 16 + LXVD2X (42,(4),(3)) + addi 4, 4, 16 + LXVD2X (43,(4),(3)) + addi 4, 4, 16 + LXVD2X (44,(4),(3)) + addi 4, 4, 16 + LXVD2X (45,(4),(3)) + addi 4, 4, 16 + LXVD2X (46,(4),(3)) + addi 4, 4, 16 + LXVD2X (47,(4),(3)) + addi 4, 4, 16 + LXVD2X (48,(4),(3)) + addi 4, 4, 16 + LXVD2X (49,(4),(3)) + addi 4, 4, 16 + LXVD2X (50,(4),(3)) + addi 4, 4, 16 + LXVD2X (51,(4),(3)) + addi 4, 4, 16 + LXVD2X (52,(4),(3)) + addi 4, 4, 16 + LXVD2X (53,(4),(3)) + addi 4, 4, 16 + LXVD2X (54,(4),(3)) + addi 4, 4, 16 + LXVD2X (55,(4),(3)) + addi 4, 4, 16 + LXVD2X (56,(4),(3)) + addi 4, 4, 16 + LXVD2X (57,(4),(3)) + addi 4, 4, 16 + LXVD2X (58,(4),(3)) + addi 4, 4, 16 + LXVD2X (59,(4),(3)) + addi 4, 4, 16 + LXVD2X (60,(4),(3)) + addi 4, 4, 16 + LXVD2X (61,(4),(3)) + addi 4, 4, 16 + LXVD2X (62,(4),(3)) + addi 4, 4, 16 + LXVD2X (63,(4),(3)) + blr +FUNC_END(loadvsx) + +FUNC_START(storevsx) + lis 4, 0 + STXVD2X (0,(4),(3)) + addi 4, 4, 16 + STXVD2X (1,(4),(3)) + addi 4, 4, 16 + STXVD2X (2,(4),(3)) + addi 4, 4, 16 + STXVD2X (3,(4),(3)) + addi 4, 4, 16 + STXVD2X (4,(4),(3)) + addi 4, 4, 16 + STXVD2X (5,(4),(3)) + addi 4, 4, 16 + STXVD2X (6,(4),(3)) + addi 4, 4, 16 + STXVD2X (7,(4),(3)) + addi 4, 4, 16 + STXVD2X (8,(4),(3)) + addi 4, 4, 16 + STXVD2X (9,(4),(3)) + addi 4, 4, 16 + STXVD2X (10,(4),(3)) + addi 4, 4, 16 + STXVD2X (11,(4),(3)) + addi 4, 4, 16 + STXVD2X (12,(4),(3)) + addi 4, 4, 16 + STXVD2X (13,(4),(3)) + addi 4, 4, 16 + STXVD2X (14,(4),(3)) + addi 4, 4, 16 + STXVD2X (15,(4),(3)) + addi 4, 4, 16 + STXVD2X (16,(4),(3)) + addi 4, 4, 16 + STXVD2X (17,(4),(3)) + addi 4, 4, 16 + STXVD2X (18,(4),(3)) + addi 4, 4, 16 + STXVD2X (19,(4),(3)) + addi 4, 4, 16 + STXVD2X (20,(4),(3)) + addi 4, 4, 16 + STXVD2X (21,(4),(3)) + addi 4, 4, 16 + STXVD2X (22,(4),(3)) + addi 4, 4, 16 + STXVD2X (23,(4),(3)) + addi 4, 4, 16 + STXVD2X (24,(4),(3)) + addi 4, 4, 16 + STXVD2X (25,(4),(3)) + addi 4, 4, 16 + STXVD2X (26,(4),(3)) + addi 4, 4, 16 + STXVD2X (27,(4),(3)) + addi 4, 4, 16 + STXVD2X (28,(4),(3)) + addi 4, 4, 16 + STXVD2X (29,(4),(3)) + addi 4, 4, 16 + STXVD2X (30,(4),(3)) + addi 4, 4, 16 + STXVD2X (31,(4),(3)) + addi 4, 4, 16 + STXVD2X (32,(4),(3)) + addi 4, 4, 16 + STXVD2X (33,(4),(3)) + addi 4, 4, 16 + STXVD2X (34,(4),(3)) + addi 4, 4, 16 + STXVD2X (35,(4),(3)) + addi 4, 4, 16 + STXVD2X (36,(4),(3)) + addi 4, 4, 16 + STXVD2X (37,(4),(3)) + addi 4, 4, 16 + STXVD2X (38,(4),(3)) + addi 4, 4, 16 + STXVD2X (39,(4),(3)) + addi 4, 4, 16 + STXVD2X (40,(4),(3)) + addi 4, 4, 16 + STXVD2X (41,(4),(3)) + addi 4, 4, 16 + STXVD2X (42,(4),(3)) + addi 4, 4, 16 + STXVD2X (43,(4),(3)) + addi 4, 4, 16 + STXVD2X (44,(4),(3)) + addi 4, 4, 16 + STXVD2X (45,(4),(3)) + addi 4, 4, 16 + STXVD2X (46,(4),(3)) + addi 4, 4, 16 + STXVD2X (47,(4),(3)) + addi 4, 4, 16 + STXVD2X (48,(4),(3)) + addi 4, 4, 16 + STXVD2X (49,(4),(3)) + addi 4, 4, 16 + STXVD2X (50,(4),(3)) + addi 4, 4, 16 + STXVD2X (51,(4),(3)) + addi 4, 4, 16 + STXVD2X (52,(4),(3)) + addi 4, 4, 16 + STXVD2X (53,(4),(3)) + addi 4, 4, 16 + STXVD2X (54,(4),(3)) + addi 4, 4, 16 + STXVD2X (55,(4),(3)) + addi 4, 4, 16 + STXVD2X (56,(4),(3)) + addi 4, 4, 16 + STXVD2X (57,(4),(3)) + addi 4, 4, 16 + STXVD2X (58,(4),(3)) + addi 4, 4, 16 + STXVD2X (59,(4),(3)) + addi 4, 4, 16 + STXVD2X (60,(4),(3)) + addi 4, 4, 16 + STXVD2X (61,(4),(3)) + addi 4, 4, 16 + STXVD2X (62,(4),(3)) + addi 4, 4, 16 + STXVD2X (63,(4),(3)) + blr +FUNC_END(storevsx) diff --git a/tools/testing/selftests/powerpc/math/fpu_asm.S b/tools/testing/selftests/powerpc/math/fpu_asm.S index 241f067a510f..8a04bb117b69 100644 --- a/tools/testing/selftests/powerpc/math/fpu_asm.S +++ b/tools/testing/selftests/powerpc/math/fpu_asm.S @@ -7,8 +7,8 @@ * 2 of the License, or (at your option) any later version. */ -#include "../basic_asm.h" -#include "../fpu_asm.h" +#include "basic_asm.h" +#include "fpu_asm.h" FUNC_START(check_fpu) mr r4,r3 diff --git a/tools/testing/selftests/powerpc/math/vmx_asm.S b/tools/testing/selftests/powerpc/math/vmx_asm.S index fd74da488625..cb1e5ae1be99 100644 --- a/tools/testing/selftests/powerpc/math/vmx_asm.S +++ b/tools/testing/selftests/powerpc/math/vmx_asm.S @@ -7,8 +7,8 @@ * 2 of the License, or (at your option) any later version. */ -#include "../basic_asm.h" -#include "../vmx_asm.h" +#include "basic_asm.h" +#include "vmx_asm.h" # Should be safe from C, only touches r4, r5 and v0,v1,v2 FUNC_START(check_vmx) diff --git a/tools/testing/selftests/powerpc/math/vsx_asm.S b/tools/testing/selftests/powerpc/math/vsx_asm.S index a110dd882d5e..8f431f6abc49 100644 --- a/tools/testing/selftests/powerpc/math/vsx_asm.S +++ b/tools/testing/selftests/powerpc/math/vsx_asm.S @@ -7,8 +7,8 @@ * 2 of the License, or (at your option) any later version. */ -#include "../basic_asm.h" -#include "../vsx_asm.h" +#include "basic_asm.h" +#include "vsx_asm.h" #long check_vsx(vector int *r3); #This function wraps storeing VSX regs to the end of an array and a diff --git a/tools/testing/selftests/powerpc/pmu/ebb/.gitignore b/tools/testing/selftests/powerpc/pmu/ebb/.gitignore index 44b7df14a936..42bddbed8b64 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/.gitignore +++ b/tools/testing/selftests/powerpc/pmu/ebb/.gitignore @@ -20,5 +20,3 @@ 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 6b0453e60d53..8d2279c4bb4b 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 ebb_lmr ebb_lmr_regs + cycles_with_mmcr2_test 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 deleted file mode 100644 index c47ebd55ba4d..000000000000 --- a/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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 deleted file mode 100644 index ef50abd557cd..000000000000 --- a/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr.h +++ /dev/null @@ -1,39 +0,0 @@ -#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 deleted file mode 100644 index aff4241fd88a..000000000000 --- a/tools/testing/selftests/powerpc/pmu/ebb/ebb_lmr_regs.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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/lib.c b/tools/testing/selftests/powerpc/pmu/lib.c index 8b992fa5b478..5bf5dd40822b 100644 --- a/tools/testing/selftests/powerpc/pmu/lib.c +++ b/tools/testing/selftests/powerpc/pmu/lib.c @@ -193,9 +193,9 @@ bool require_paranoia_below(int level) long current; char *end, buf[16]; FILE *f; - int rc; + bool rc; - rc = -1; + rc = false; f = fopen(PARANOID_PATH, "r"); if (!f) { @@ -218,7 +218,7 @@ bool require_paranoia_below(int level) if (current >= level) goto out_close; - rc = 0; + rc = true; out_close: fclose(f); out: diff --git a/tools/testing/selftests/powerpc/primitives/asm/firmware.h b/tools/testing/selftests/powerpc/primitives/asm/firmware.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/testing/selftests/powerpc/primitives/asm/firmware.h diff --git a/tools/testing/selftests/powerpc/primitives/asm/ppc_asm.h b/tools/testing/selftests/powerpc/primitives/asm/ppc_asm.h new file mode 120000 index 000000000000..66c8193224e9 --- /dev/null +++ b/tools/testing/selftests/powerpc/primitives/asm/ppc_asm.h @@ -0,0 +1 @@ +../../../../../../arch/powerpc/include/asm/ppc_asm.h
\ No newline at end of file diff --git a/tools/testing/selftests/powerpc/primitives/asm/processor.h b/tools/testing/selftests/powerpc/primitives/asm/processor.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/testing/selftests/powerpc/primitives/asm/processor.h diff --git a/tools/testing/selftests/powerpc/primitives/linux/stringify.h b/tools/testing/selftests/powerpc/primitives/linux/stringify.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/testing/selftests/powerpc/primitives/linux/stringify.h diff --git a/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c b/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c index 6cae06117b55..ed3239bbfae2 100644 --- a/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c +++ b/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c @@ -73,20 +73,23 @@ extern char __stop___ex_table[]; #error implement UCONTEXT_NIA #endif -static int segv_error; +struct extbl_entry { + int insn; + int fixup; +}; static void segv_handler(int signr, siginfo_t *info, void *ptr) { ucontext_t *uc = (ucontext_t *)ptr; unsigned long addr = (unsigned long)info->si_addr; unsigned long *ip = &UCONTEXT_NIA(uc); - unsigned long *ex_p = (unsigned long *)__start___ex_table; + struct extbl_entry *entry = (struct extbl_entry *)__start___ex_table; - while (ex_p < (unsigned long *)__stop___ex_table) { + while (entry < (struct extbl_entry *)__stop___ex_table) { unsigned long insn, fixup; - insn = *ex_p++; - fixup = *ex_p++; + insn = (unsigned long)&entry->insn + entry->insn; + fixup = (unsigned long)&entry->fixup + entry->fixup; if (insn == *ip) { *ip = fixup; @@ -95,7 +98,7 @@ static void segv_handler(int signr, siginfo_t *info, void *ptr) } printf("No exception table match for NIA %lx ADDR %lx\n", *ip, addr); - segv_error++; + abort(); } static void setup_segv_handler(void) @@ -119,8 +122,10 @@ static int do_one_test(char *p, int page_offset) got = load_unaligned_zeropad(p); - if (should != got) + if (should != got) { printf("offset %u load_unaligned_zeropad returned 0x%lx, should be 0x%lx\n", page_offset, got, should); + return 1; + } return 0; } @@ -145,8 +150,6 @@ static int test_body(void) for (i = 0; i < page_size; i++) FAIL_IF(do_one_test(mem_region+i, i)); - FAIL_IF(segv_error); - return 0; } diff --git a/tools/testing/selftests/powerpc/ptrace/.gitignore b/tools/testing/selftests/powerpc/ptrace/.gitignore new file mode 100644 index 000000000000..349acfafc95b --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/.gitignore @@ -0,0 +1,10 @@ +ptrace-gpr +ptrace-tm-gpr +ptrace-tm-spd-gpr +ptrace-tar +ptrace-tm-tar +ptrace-tm-spd-tar +ptrace-vsx +ptrace-tm-vsx +ptrace-tm-spd-vsx +ptrace-tm-spr diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile new file mode 100644 index 000000000000..fe6bc60dfc60 --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/Makefile @@ -0,0 +1,14 @@ +TEST_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \ + ptrace-tar ptrace-tm-tar ptrace-tm-spd-tar ptrace-vsx ptrace-tm-vsx \ + ptrace-tm-spd-vsx ptrace-tm-spr + +include ../../lib.mk + +all: $(TEST_PROGS) + +CFLAGS += -m64 -I../../../../../usr/include -I../tm -mhtm + +$(TEST_PROGS): ../harness.c ../utils.c ../lib/reg.S ptrace.h + +clean: + rm -f $(TEST_PROGS) *.o diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c new file mode 100644 index 000000000000..0b4ebcc2f485 --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c @@ -0,0 +1,123 @@ +/* + * Ptrace test for GPR/FPR registers + * + * Copyright (C) 2015 Anshuman Khandual, 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. + */ +#include "ptrace.h" +#include "ptrace-gpr.h" +#include "reg.h" + +/* Tracer and Tracee Shared Data */ +int shm_id; +int *cptr, *pptr; + +float a = FPR_1; +float b = FPR_2; +float c = FPR_3; + +void gpr(void) +{ + unsigned long gpr_buf[18]; + float fpr_buf[32]; + + cptr = (int *)shmat(shm_id, NULL, 0); + + asm __volatile__( + ASM_LOAD_GPR_IMMED(gpr_1) + ASM_LOAD_FPR_SINGLE_PRECISION(flt_1) + : + : [gpr_1]"i"(GPR_1), [flt_1] "r" (&a) + : "memory", "r6", "r7", "r8", "r9", "r10", + "r11", "r12", "r13", "r14", "r15", "r16", "r17", + "r18", "r19", "r20", "r21", "r22", "r23", "r24", + "r25", "r26", "r27", "r28", "r29", "r30", "r31" + ); + + cptr[1] = 1; + + while (!cptr[0]) + asm volatile("" : : : "memory"); + + shmdt((void *)cptr); + store_gpr(gpr_buf); + store_fpr_single_precision(fpr_buf); + + if (validate_gpr(gpr_buf, GPR_3)) + exit(1); + + if (validate_fpr_float(fpr_buf, c)) + exit(1); + + exit(0); +} + +int trace_gpr(pid_t child) +{ + unsigned long gpr[18]; + unsigned long fpr[32]; + + FAIL_IF(start_trace(child)); + FAIL_IF(show_gpr(child, gpr)); + FAIL_IF(validate_gpr(gpr, GPR_1)); + FAIL_IF(show_fpr(child, fpr)); + FAIL_IF(validate_fpr(fpr, FPR_1_REP)); + FAIL_IF(write_gpr(child, GPR_3)); + FAIL_IF(write_fpr(child, FPR_3_REP)); + FAIL_IF(stop_trace(child)); + + return TEST_PASS; +} + +int ptrace_gpr(void) +{ + pid_t pid; + int ret, status; + + shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT); + pid = fork(); + if (pid < 0) { + perror("fork() failed"); + return TEST_FAIL; + } + if (pid == 0) + gpr(); + + if (pid) { + pptr = (int *)shmat(shm_id, NULL, 0); + while (!pptr[1]) + asm volatile("" : : : "memory"); + + ret = trace_gpr(pid); + if (ret) { + kill(pid, SIGTERM); + shmdt((void *)pptr); + shmctl(shm_id, IPC_RMID, NULL); + return TEST_FAIL; + } + + pptr[0] = 1; + shmdt((void *)pptr); + + ret = wait(&status); + shmctl(shm_id, IPC_RMID, NULL); + if (ret != pid) { + printf("Child's exit status not captured\n"); + return TEST_FAIL; + } + + return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL : + TEST_PASS; + } + + return TEST_PASS; +} + +int main(int argc, char *argv[]) +{ + return test_harness(ptrace_gpr, "ptrace_gpr"); +} diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.h b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.h new file mode 100644 index 000000000000..e30fef63824c --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 Anshuman Khandual, 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. + */ +#define GPR_1 1 +#define GPR_2 2 +#define GPR_3 3 +#define GPR_4 4 + +#define FPR_1 0.001 +#define FPR_2 0.002 +#define FPR_3 0.003 +#define FPR_4 0.004 + +#define FPR_1_REP 0x3f50624de0000000 +#define FPR_2_REP 0x3f60624de0000000 +#define FPR_3_REP 0x3f689374c0000000 +#define FPR_4_REP 0x3f70624de0000000 + +/* Buffer must have 18 elements */ +int validate_gpr(unsigned long *gpr, unsigned long val) +{ + int i, found = 1; + + for (i = 0; i < 18; i++) { + if (gpr[i] != val) { + printf("GPR[%d]: %lx Expected: %lx\n", + i+14, gpr[i], val); + found = 0; + } + } + + if (!found) + return TEST_FAIL; + return TEST_PASS; +} + +/* Buffer must have 32 elements */ +int validate_fpr(unsigned long *fpr, unsigned long val) +{ + int i, found = 1; + + for (i = 0; i < 32; i++) { + if (fpr[i] != val) { + printf("FPR[%d]: %lx Expected: %lx\n", i, fpr[i], val); + found = 0; + } + } + + if (!found) + return TEST_FAIL; + return TEST_PASS; +} + +/* Buffer must have 32 elements */ +int validate_fpr_float(float *fpr, float val) +{ + int i, found = 1; + + for (i = 0; i < 32; i++) { + if (fpr[i] != val) { + printf("FPR[%d]: %f Expected: %f\n", i, fpr[i], val); + found = 0; + } + } + + if (!found) + return TEST_FAIL; + return TEST_PASS; +} diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tar.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tar.c new file mode 100644 index 000000000000..f9b5069db89b --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tar.c @@ -0,0 +1,135 @@ +/* + * Ptrace test for TAR, PPR, DSCR registers + * + * Copyright (C) 2015 Anshuman Khandual, 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. + */ +#include "ptrace.h" +#include "ptrace-tar.h" + +/* Tracer and Tracee Shared Data */ +int shm_id; +int *cptr; +int *pptr; + +void tar(void) +{ + unsigned long reg[3]; + int ret; + + cptr = (int *)shmat(shm_id, NULL, 0); + printf("%-30s TAR: %u PPR: %lx DSCR: %u\n", + user_write, TAR_1, PPR_1, DSCR_1); + + mtspr(SPRN_TAR, TAR_1); + mtspr(SPRN_PPR, PPR_1); + mtspr(SPRN_DSCR, DSCR_1); + + cptr[2] = 1; + + /* Wait on parent */ + while (!cptr[0]) + asm volatile("" : : : "memory"); + + reg[0] = mfspr(SPRN_TAR); + reg[1] = mfspr(SPRN_PPR); + reg[2] = mfspr(SPRN_DSCR); + + printf("%-30s TAR: %lu PPR: %lx DSCR: %lu\n", + user_read, reg[0], reg[1], reg[2]); + + /* Unblock the parent now */ + cptr[1] = 1; + shmdt((int *)cptr); + + ret = validate_tar_registers(reg, TAR_2, PPR_2, DSCR_2); + if (ret) + exit(1); + exit(0); +} + +int trace_tar(pid_t child) +{ + unsigned long reg[3]; + + FAIL_IF(start_trace(child)); + FAIL_IF(show_tar_registers(child, reg)); + printf("%-30s TAR: %lu PPR: %lx DSCR: %lu\n", + ptrace_read_running, reg[0], reg[1], reg[2]); + + FAIL_IF(validate_tar_registers(reg, TAR_1, PPR_1, DSCR_1)); + FAIL_IF(stop_trace(child)); + return TEST_PASS; +} + +int trace_tar_write(pid_t child) +{ + FAIL_IF(start_trace(child)); + FAIL_IF(write_tar_registers(child, TAR_2, PPR_2, DSCR_2)); + printf("%-30s TAR: %u PPR: %lx DSCR: %u\n", + ptrace_write_running, TAR_2, PPR_2, DSCR_2); + + FAIL_IF(stop_trace(child)); + return TEST_PASS; +} + +int ptrace_tar(void) +{ + pid_t pid; + int ret, status; + + shm_id = shmget(IPC_PRIVATE, sizeof(int) * 3, 0777|IPC_CREAT); + pid = fork(); + if (pid < 0) { + perror("fork() failed"); + return TEST_FAIL; + } + + if (pid == 0) + tar(); + + if (pid) { + pptr = (int *)shmat(shm_id, NULL, 0); + pptr[0] = 0; + pptr[1] = 0; + + while (!pptr[2]) + asm volatile("" : : : "memory"); + ret = trace_tar(pid); + if (ret) + return ret; + + ret = trace_tar_write(pid); + if (ret) + return ret; + + /* Unblock the child now */ + pptr[0] = 1; + + /* Wait on child */ + while (!pptr[1]) + asm volatile("" : : : "memory"); + + shmdt((int *)pptr); + + ret = wait(&status); + shmctl(shm_id, IPC_RMID, NULL); + if (ret != pid) { + printf("Child's exit status not captured\n"); + return TEST_PASS; + } + + return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL : + TEST_PASS; + } + return TEST_PASS; +} + +int main(int argc, char *argv[]) +{ + return test_harness(ptrace_tar, "ptrace_tar"); +} diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tar.h b/tools/testing/selftests/powerpc/ptrace/ptrace-tar.h new file mode 100644 index 000000000000..aed0aac716d2 --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tar.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 Anshuman Khandual, 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. + */ +#define TAR_1 10 +#define TAR_2 20 +#define TAR_3 30 +#define TAR_4 40 +#define TAR_5 50 + +#define DSCR_1 100 +#define DSCR_2 200 +#define DSCR_3 300 +#define DSCR_4 400 +#define DSCR_5 500 + +#define PPR_1 0x4000000000000 /* or 31,31,31*/ +#define PPR_2 0x8000000000000 /* or 1,1,1 */ +#define PPR_3 0xc000000000000 /* or 6,6,6 */ +#define PPR_4 0x10000000000000 /* or 2,2,2 */ + +char *user_read = "[User Read (Running)]"; +char *user_write = "[User Write (Running)]"; +char *ptrace_read_running = "[Ptrace Read (Running)]"; +char *ptrace_write_running = "[Ptrace Write (Running)]"; +char *ptrace_read_ckpt = "[Ptrace Read (Checkpointed)]"; +char *ptrace_write_ckpt = "[Ptrace Write (Checkpointed)]"; + +int validate_tar_registers(unsigned long *reg, unsigned long tar, + unsigned long ppr, unsigned long dscr) +{ + int match = 1; + + if (reg[0] != tar) + match = 0; + + if (reg[1] != ppr) + match = 0; + + if (reg[2] != dscr) + match = 0; + + if (!match) + return TEST_FAIL; + return TEST_PASS; +} diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c new file mode 100644 index 000000000000..59206b96e98a --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c @@ -0,0 +1,158 @@ +/* + * Ptrace test for GPR/FPR registers in TM context + * + * Copyright (C) 2015 Anshuman Khandual, 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. + */ +#include "ptrace.h" +#include "ptrace-gpr.h" +#include "tm.h" + +/* Tracer and Tracee Shared Data */ +int shm_id; +unsigned long *cptr, *pptr; + +float a = FPR_1; +float b = FPR_2; +float c = FPR_3; + +void tm_gpr(void) +{ + unsigned long gpr_buf[18]; + unsigned long result, texasr; + float fpr_buf[32]; + + printf("Starting the child\n"); + cptr = (unsigned long *)shmat(shm_id, NULL, 0); + +trans: + cptr[1] = 0; + asm __volatile__( + ASM_LOAD_GPR_IMMED(gpr_1) + ASM_LOAD_FPR_SINGLE_PRECISION(flt_1) + "1: ;" + "tbegin.;" + "beq 2f;" + ASM_LOAD_GPR_IMMED(gpr_2) + ASM_LOAD_FPR_SINGLE_PRECISION(flt_2) + "tsuspend.;" + "li 7, 1;" + "stw 7, 0(%[cptr1]);" + "tresume.;" + "b .;" + + "tend.;" + "li 0, 0;" + "ori %[res], 0, 0;" + "b 3f;" + + /* Transaction abort handler */ + "2: ;" + "li 0, 1;" + "ori %[res], 0, 0;" + "mfspr %[texasr], %[sprn_texasr];" + + "3: ;" + : [res] "=r" (result), [texasr] "=r" (texasr) + : [gpr_1]"i"(GPR_1), [gpr_2]"i"(GPR_2), + [sprn_texasr] "i" (SPRN_TEXASR), [flt_1] "r" (&a), + [flt_2] "r" (&b), [cptr1] "r" (&cptr[1]) + : "memory", "r7", "r8", "r9", "r10", + "r11", "r12", "r13", "r14", "r15", "r16", + "r17", "r18", "r19", "r20", "r21", "r22", + "r23", "r24", "r25", "r26", "r27", "r28", + "r29", "r30", "r31" + ); + + if (result) { + if (!cptr[0]) + goto trans; + + shmdt((void *)cptr); + store_gpr(gpr_buf); + store_fpr_single_precision(fpr_buf); + + if (validate_gpr(gpr_buf, GPR_3)) + exit(1); + + if (validate_fpr_float(fpr_buf, c)) + exit(1); + + exit(0); + } + shmdt((void *)cptr); + exit(1); +} + +int trace_tm_gpr(pid_t child) +{ + unsigned long gpr[18]; + unsigned long fpr[32]; + + FAIL_IF(start_trace(child)); + FAIL_IF(show_gpr(child, gpr)); + FAIL_IF(validate_gpr(gpr, GPR_2)); + FAIL_IF(show_fpr(child, fpr)); + FAIL_IF(validate_fpr(fpr, FPR_2_REP)); + FAIL_IF(show_ckpt_fpr(child, fpr)); + FAIL_IF(validate_fpr(fpr, FPR_1_REP)); + FAIL_IF(show_ckpt_gpr(child, gpr)); + FAIL_IF(validate_gpr(gpr, GPR_1)); + FAIL_IF(write_ckpt_gpr(child, GPR_3)); + FAIL_IF(write_ckpt_fpr(child, FPR_3_REP)); + + pptr[0] = 1; + FAIL_IF(stop_trace(child)); + + return TEST_PASS; +} + +int ptrace_tm_gpr(void) +{ + pid_t pid; + int ret, status; + + SKIP_IF(!have_htm()); + shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT); + pid = fork(); + if (pid < 0) { + perror("fork() failed"); + return TEST_FAIL; + } + if (pid == 0) + tm_gpr(); + + if (pid) { + pptr = (unsigned long *)shmat(shm_id, NULL, 0); + + while (!pptr[1]) + asm volatile("" : : : "memory"); + ret = trace_tm_gpr(pid); + if (ret) { + kill(pid, SIGTERM); + return TEST_FAIL; + } + + shmdt((void *)pptr); + + ret = wait(&status); + shmctl(shm_id, IPC_RMID, NULL); + if (ret != pid) { + printf("Child's exit status not captured\n"); + return TEST_FAIL; + } + + return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL : + TEST_PASS; + } + return TEST_PASS; +} + +int main(int argc, char *argv[]) +{ + return test_harness(ptrace_tm_gpr, "ptrace_tm_gpr"); +} diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-gpr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-gpr.c new file mode 100644 index 000000000000..327fa943c7f3 --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-gpr.c @@ -0,0 +1,169 @@ +/* + * Ptrace test for GPR/FPR registers in TM Suspend context + * + * Copyright (C) 2015 Anshuman Khandual, 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. + */ +#include "ptrace.h" +#include "ptrace-gpr.h" +#include "tm.h" + +/* Tracer and Tracee Shared Data */ +int shm_id; +int *cptr, *pptr; + +float a = FPR_1; +float b = FPR_2; +float c = FPR_3; +float d = FPR_4; + +__attribute__((used)) void wait_parent(void) +{ + cptr[2] = 1; + while (!cptr[1]) + asm volatile("" : : : "memory"); +} + +void tm_spd_gpr(void) +{ + unsigned long gpr_buf[18]; + unsigned long result, texasr; + float fpr_buf[32]; + + cptr = (int *)shmat(shm_id, NULL, 0); + +trans: + cptr[2] = 0; + asm __volatile__( + ASM_LOAD_GPR_IMMED(gpr_1) + ASM_LOAD_FPR_SINGLE_PRECISION(flt_1) + + "1: ;" + "tbegin.;" + "beq 2f;" + + ASM_LOAD_GPR_IMMED(gpr_2) + "tsuspend.;" + ASM_LOAD_GPR_IMMED(gpr_4) + ASM_LOAD_FPR_SINGLE_PRECISION(flt_4) + + "bl wait_parent;" + "tresume.;" + "tend.;" + "li 0, 0;" + "ori %[res], 0, 0;" + "b 3f;" + + /* Transaction abort handler */ + "2: ;" + "li 0, 1;" + "ori %[res], 0, 0;" + "mfspr %[texasr], %[sprn_texasr];" + + "3: ;" + : [res] "=r" (result), [texasr] "=r" (texasr) + : [gpr_1]"i"(GPR_1), [gpr_2]"i"(GPR_2), [gpr_4]"i"(GPR_4), + [sprn_texasr] "i" (SPRN_TEXASR), [flt_1] "r" (&a), + [flt_2] "r" (&b), [flt_4] "r" (&d) + : "memory", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31" + ); + + if (result) { + if (!cptr[0]) + goto trans; + + shmdt((void *)cptr); + store_gpr(gpr_buf); + store_fpr_single_precision(fpr_buf); + + if (validate_gpr(gpr_buf, GPR_3)) + exit(1); + + if (validate_fpr_float(fpr_buf, c)) + exit(1); + exit(0); + } + shmdt((void *)cptr); + exit(1); +} + +int trace_tm_spd_gpr(pid_t child) +{ + unsigned long gpr[18]; + unsigned long fpr[32]; + + FAIL_IF(start_trace(child)); + FAIL_IF(show_gpr(child, gpr)); + FAIL_IF(validate_gpr(gpr, GPR_4)); + FAIL_IF(show_fpr(child, fpr)); + FAIL_IF(validate_fpr(fpr, FPR_4_REP)); + FAIL_IF(show_ckpt_fpr(child, fpr)); + FAIL_IF(validate_fpr(fpr, FPR_1_REP)); + FAIL_IF(show_ckpt_gpr(child, gpr)); + FAIL_IF(validate_gpr(gpr, GPR_1)); + FAIL_IF(write_ckpt_gpr(child, GPR_3)); + FAIL_IF(write_ckpt_fpr(child, FPR_3_REP)); + + pptr[0] = 1; + pptr[1] = 1; + FAIL_IF(stop_trace(child)); + return TEST_PASS; +} + +int ptrace_tm_spd_gpr(void) +{ + pid_t pid; + int ret, status; + + SKIP_IF(!have_htm()); + shm_id = shmget(IPC_PRIVATE, sizeof(int) * 3, 0777|IPC_CREAT); + pid = fork(); + if (pid < 0) { + perror("fork() failed"); + return TEST_FAIL; + } + + if (pid == 0) + tm_spd_gpr(); + + if (pid) { + pptr = (int *)shmat(shm_id, NULL, 0); + pptr[0] = 0; + pptr[1] = 0; + + while (!pptr[2]) + asm volatile("" : : : "memory"); + ret = trace_tm_spd_gpr(pid); + if (ret) { + kill(pid, SIGTERM); + shmdt((void *)pptr); + shmctl(shm_id, IPC_RMID, NULL); + return TEST_FAIL; + } + + shmdt((void *)pptr); + + ret = wait(&status); + shmctl(shm_id, IPC_RMID, NULL); + if (ret != pid) { + printf("Child's exit status not captured\n"); + return TEST_FAIL; + } + + return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL : + TEST_PASS; + } + return TEST_PASS; +} + +int main(int argc, char *argv[]) +{ + return test_harness(ptrace_tm_spd_gpr, "ptrace_tm_spd_gpr"); +} diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c new file mode 100644 index 000000000000..b3c061dc9512 --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c @@ -0,0 +1,174 @@ +/* + * Ptrace test for TAR, PPR, DSCR registers in the TM Suspend context + * + * Copyright (C) 2015 Anshuman Khandual, 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. + */ +#include "ptrace.h" +#include "tm.h" +#include "ptrace-tar.h" + +int shm_id; +int *cptr, *pptr; + +__attribute__((used)) void wait_parent(void) +{ + cptr[2] = 1; + while (!cptr[1]) + asm volatile("" : : : "memory"); +} + +void tm_spd_tar(void) +{ + unsigned long result, texasr; + unsigned long regs[3]; + int ret; + + cptr = (int *)shmat(shm_id, NULL, 0); + +trans: + cptr[2] = 0; + asm __volatile__( + "li 4, %[tar_1];" + "mtspr %[sprn_tar], 4;" /* TAR_1 */ + "li 4, %[dscr_1];" + "mtspr %[sprn_dscr], 4;" /* DSCR_1 */ + "or 31,31,31;" /* PPR_1*/ + + "1: ;" + "tbegin.;" + "beq 2f;" + + "li 4, %[tar_2];" + "mtspr %[sprn_tar], 4;" /* TAR_2 */ + "li 4, %[dscr_2];" + "mtspr %[sprn_dscr], 4;" /* DSCR_2 */ + "or 1,1,1;" /* PPR_2 */ + + "tsuspend.;" + "li 4, %[tar_3];" + "mtspr %[sprn_tar], 4;" /* TAR_3 */ + "li 4, %[dscr_3];" + "mtspr %[sprn_dscr], 4;" /* DSCR_3 */ + "or 6,6,6;" /* PPR_3 */ + "bl wait_parent;" + "tresume.;" + + "tend.;" + "li 0, 0;" + "ori %[res], 0, 0;" + "b 3f;" + + /* Transaction abort handler */ + "2: ;" + "li 0, 1;" + "ori %[res], 0, 0;" + "mfspr %[texasr], %[sprn_texasr];" + + "3: ;" + + : [res] "=r" (result), [texasr] "=r" (texasr) + : [val] "r" (cptr[1]), [sprn_dscr]"i"(SPRN_DSCR), + [sprn_tar]"i"(SPRN_TAR), [sprn_ppr]"i"(SPRN_PPR), + [sprn_texasr]"i"(SPRN_TEXASR), [tar_1]"i"(TAR_1), + [dscr_1]"i"(DSCR_1), [tar_2]"i"(TAR_2), [dscr_2]"i"(DSCR_2), + [tar_3]"i"(TAR_3), [dscr_3]"i"(DSCR_3) + : "memory", "r0", "r1", "r3", "r4", "r5", "r6" + ); + + /* TM failed, analyse */ + if (result) { + if (!cptr[0]) + goto trans; + + regs[0] = mfspr(SPRN_TAR); + regs[1] = mfspr(SPRN_PPR); + regs[2] = mfspr(SPRN_DSCR); + + shmdt(&cptr); + printf("%-30s TAR: %lu PPR: %lx DSCR: %lu\n", + user_read, regs[0], regs[1], regs[2]); + + ret = validate_tar_registers(regs, TAR_4, PPR_4, DSCR_4); + if (ret) + exit(1); + exit(0); + } + shmdt(&cptr); + exit(1); +} + +int trace_tm_spd_tar(pid_t child) +{ + unsigned long regs[3]; + + FAIL_IF(start_trace(child)); + FAIL_IF(show_tar_registers(child, regs)); + printf("%-30s TAR: %lu PPR: %lx DSCR: %lu\n", + ptrace_read_running, regs[0], regs[1], regs[2]); + + FAIL_IF(validate_tar_registers(regs, TAR_3, PPR_3, DSCR_3)); + FAIL_IF(show_tm_checkpointed_state(child, regs)); + printf("%-30s TAR: %lu PPR: %lx DSCR: %lu\n", + ptrace_read_ckpt, regs[0], regs[1], regs[2]); + + FAIL_IF(validate_tar_registers(regs, TAR_1, PPR_1, DSCR_1)); + FAIL_IF(write_ckpt_tar_registers(child, TAR_4, PPR_4, DSCR_4)); + printf("%-30s TAR: %u PPR: %lx DSCR: %u\n", + ptrace_write_ckpt, TAR_4, PPR_4, DSCR_4); + + pptr[0] = 1; + pptr[1] = 1; + FAIL_IF(stop_trace(child)); + return TEST_PASS; +} + +int ptrace_tm_spd_tar(void) +{ + pid_t pid; + int ret, status; + + SKIP_IF(!have_htm()); + shm_id = shmget(IPC_PRIVATE, sizeof(int) * 3, 0777|IPC_CREAT); + pid = fork(); + if (pid == 0) + tm_spd_tar(); + + pptr = (int *)shmat(shm_id, NULL, 0); + pptr[0] = 0; + pptr[1] = 0; + + if (pid) { + while (!pptr[2]) + asm volatile("" : : : "memory"); + ret = trace_tm_spd_tar(pid); + if (ret) { + kill(pid, SIGTERM); + shmdt(&pptr); + shmctl(shm_id, IPC_RMID, NULL); + return TEST_FAIL; + } + + shmdt(&pptr); + + ret = wait(&status); + shmctl(shm_id, IPC_RMID, NULL); + if (ret != pid) { + printf("Child's exit status not captured\n"); + return TEST_FAIL; + } + + return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL : + TEST_PASS; + } + return TEST_PASS; +} + +int main(int argc, char *argv[]) +{ + return test_harness(ptrace_tm_spd_tar, "ptrace_tm_spd_tar"); +} diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c new file mode 100644 index 000000000000..0df3c23b7888 --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c @@ -0,0 +1,185 @@ +/* + * Ptrace test for VMX/VSX registers in the TM Suspend context + * + * Copyright (C) 2015 Anshuman Khandual, 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. + */ +#include "ptrace.h" +#include "tm.h" +#include "ptrace-vsx.h" + +int shm_id; +int *cptr, *pptr; + +unsigned long fp_load[VEC_MAX]; +unsigned long fp_load_new[VEC_MAX]; +unsigned long fp_store[VEC_MAX]; +unsigned long fp_load_ckpt[VEC_MAX]; +unsigned long fp_load_ckpt_new[VEC_MAX]; + +__attribute__((used)) void load_vsx(void) +{ + loadvsx(fp_load, 0); +} + +__attribute__((used)) void load_vsx_new(void) +{ + loadvsx(fp_load_new, 0); +} + +__attribute__((used)) void load_vsx_ckpt(void) +{ + loadvsx(fp_load_ckpt, 0); +} + +__attribute__((used)) void wait_parent(void) +{ + cptr[2] = 1; + while (!cptr[1]) + asm volatile("" : : : "memory"); +} + +void tm_spd_vsx(void) +{ + unsigned long result, texasr; + int ret; + + cptr = (int *)shmat(shm_id, NULL, 0); + +trans: + cptr[2] = 0; + asm __volatile__( + "bl load_vsx_ckpt;" + + "1: ;" + "tbegin.;" + "beq 2f;" + + "bl load_vsx_new;" + "tsuspend.;" + "bl load_vsx;" + "bl wait_parent;" + "tresume.;" + + "tend.;" + "li 0, 0;" + "ori %[res], 0, 0;" + "b 3f;" + + "2: ;" + "li 0, 1;" + "ori %[res], 0, 0;" + "mfspr %[texasr], %[sprn_texasr];" + + "3: ;" + : [res] "=r" (result), [texasr] "=r" (texasr) + : [fp_load] "r" (fp_load), [fp_load_ckpt] "r" (fp_load_ckpt), + [sprn_texasr] "i" (SPRN_TEXASR) + : "memory", "r0", "r1", "r2", "r3", "r4", + "r8", "r9", "r10", "r11" + ); + + if (result) { + if (!cptr[0]) + goto trans; + shmdt((void *)cptr); + + storevsx(fp_store, 0); + ret = compare_vsx_vmx(fp_store, fp_load_ckpt_new); + if (ret) + exit(1); + exit(0); + } + shmdt((void *)cptr); + exit(1); +} + +int trace_tm_spd_vsx(pid_t child) +{ + unsigned long vsx[VSX_MAX]; + unsigned long vmx[VMX_MAX + 2][2]; + + FAIL_IF(start_trace(child)); + FAIL_IF(show_vsx(child, vsx)); + FAIL_IF(validate_vsx(vsx, fp_load)); + FAIL_IF(show_vmx(child, vmx)); + FAIL_IF(validate_vmx(vmx, fp_load)); + FAIL_IF(show_vsx_ckpt(child, vsx)); + FAIL_IF(validate_vsx(vsx, fp_load_ckpt)); + FAIL_IF(show_vmx_ckpt(child, vmx)); + FAIL_IF(validate_vmx(vmx, fp_load_ckpt)); + + memset(vsx, 0, sizeof(vsx)); + memset(vmx, 0, sizeof(vmx)); + + load_vsx_vmx(fp_load_ckpt_new, vsx, vmx); + + FAIL_IF(write_vsx_ckpt(child, vsx)); + FAIL_IF(write_vmx_ckpt(child, vmx)); + + pptr[0] = 1; + pptr[1] = 1; + FAIL_IF(stop_trace(child)); + + return TEST_PASS; +} + +int ptrace_tm_spd_vsx(void) +{ + pid_t pid; + int ret, status, i; + + SKIP_IF(!have_htm()); + shm_id = shmget(IPC_PRIVATE, sizeof(int) * 3, 0777|IPC_CREAT); + + for (i = 0; i < 128; i++) { + fp_load[i] = 1 + rand(); + fp_load_new[i] = 1 + 2 * rand(); + fp_load_ckpt[i] = 1 + 3 * rand(); + fp_load_ckpt_new[i] = 1 + 4 * rand(); + } + + pid = fork(); + if (pid < 0) { + perror("fork() failed"); + return TEST_FAIL; + } + + if (pid == 0) + tm_spd_vsx(); + + if (pid) { + pptr = (int *)shmat(shm_id, NULL, 0); + while (!pptr[2]) + asm volatile("" : : : "memory"); + + ret = trace_tm_spd_vsx(pid); + if (ret) { + kill(pid, SIGKILL); + shmdt((void *)pptr); + shmctl(shm_id, IPC_RMID, NULL); + return TEST_FAIL; + } + + shmdt((void *)pptr); + ret = wait(&status); + shmctl(shm_id, IPC_RMID, NULL); + if (ret != pid) { + printf("Child's exit status not captured\n"); + return TEST_FAIL; + } + + return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL : + TEST_PASS; + } + return TEST_PASS; +} + +int main(int argc, char *argv[]) +{ + return test_harness(ptrace_tm_spd_vsx, "ptrace_tm_spd_vsx"); +} diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c new file mode 100644 index 000000000000..94e57cb89769 --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c @@ -0,0 +1,168 @@ +/* + * Ptrace test TM SPR registers + * + * Copyright (C) 2015 Anshuman Khandual, 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. + */ +#include "ptrace.h" +#include "tm.h" + +/* Tracee and tracer shared data */ +struct shared { + int flag; + struct tm_spr_regs regs; +}; +unsigned long tfhar; + +int shm_id; +struct shared *cptr, *pptr; + +int shm_id1; +int *cptr1, *pptr1; + +#define TM_KVM_SCHED 0xe0000001ac000001 +int validate_tm_spr(struct tm_spr_regs *regs) +{ + FAIL_IF(regs->tm_tfhar != tfhar); + FAIL_IF((regs->tm_texasr == TM_KVM_SCHED) && (regs->tm_tfiar != 0)); + + return TEST_PASS; +} + +void tm_spr(void) +{ + unsigned long result, texasr; + int ret; + + cptr = (struct shared *)shmat(shm_id, NULL, 0); + cptr1 = (int *)shmat(shm_id1, NULL, 0); + +trans: + cptr1[0] = 0; + asm __volatile__( + "1: ;" + /* TM failover handler should follow "tbegin.;" */ + "mflr 31;" + "bl 4f;" /* $ = TFHAR - 12 */ + "4: ;" + "mflr %[tfhar];" + "mtlr 31;" + + "tbegin.;" + "beq 2f;" + + "tsuspend.;" + "li 8, 1;" + "sth 8, 0(%[cptr1]);" + "tresume.;" + "b .;" + + "tend.;" + "li 0, 0;" + "ori %[res], 0, 0;" + "b 3f;" + + "2: ;" + + "li 0, 1;" + "ori %[res], 0, 0;" + "mfspr %[texasr], %[sprn_texasr];" + + "3: ;" + : [tfhar] "=r" (tfhar), [res] "=r" (result), + [texasr] "=r" (texasr), [cptr1] "=r" (cptr1) + : [sprn_texasr] "i" (SPRN_TEXASR) + : "memory", "r0", "r1", "r2", "r3", "r4", + "r8", "r9", "r10", "r11", "r31" + ); + + /* There are 2 32bit instructions before tbegin. */ + tfhar += 12; + + if (result) { + if (!cptr->flag) + goto trans; + + ret = validate_tm_spr((struct tm_spr_regs *)&cptr->regs); + shmdt((void *)cptr); + shmdt((void *)cptr1); + if (ret) + exit(1); + exit(0); + } + shmdt((void *)cptr); + shmdt((void *)cptr1); + exit(1); +} + +int trace_tm_spr(pid_t child) +{ + FAIL_IF(start_trace(child)); + FAIL_IF(show_tm_spr(child, (struct tm_spr_regs *)&pptr->regs)); + + printf("TFHAR: %lx TEXASR: %lx TFIAR: %lx\n", pptr->regs.tm_tfhar, + pptr->regs.tm_texasr, pptr->regs.tm_tfiar); + + pptr->flag = 1; + FAIL_IF(stop_trace(child)); + + return TEST_PASS; +} + +int ptrace_tm_spr(void) +{ + pid_t pid; + int ret, status; + + SKIP_IF(!have_htm()); + shm_id = shmget(IPC_PRIVATE, sizeof(struct shared), 0777|IPC_CREAT); + shm_id1 = shmget(IPC_PRIVATE, sizeof(int), 0777|IPC_CREAT); + pid = fork(); + if (pid < 0) { + perror("fork() failed"); + return TEST_FAIL; + } + + if (pid == 0) + tm_spr(); + + if (pid) { + pptr = (struct shared *)shmat(shm_id, NULL, 0); + pptr1 = (int *)shmat(shm_id1, NULL, 0); + + while (!pptr1[0]) + asm volatile("" : : : "memory"); + ret = trace_tm_spr(pid); + if (ret) { + kill(pid, SIGKILL); + shmdt((void *)pptr); + shmdt((void *)pptr1); + shmctl(shm_id, IPC_RMID, NULL); + shmctl(shm_id1, IPC_RMID, NULL); + return TEST_FAIL; + } + + shmdt((void *)pptr); + shmdt((void *)pptr1); + ret = wait(&status); + shmctl(shm_id, IPC_RMID, NULL); + shmctl(shm_id1, IPC_RMID, NULL); + if (ret != pid) { + printf("Child's exit status not captured\n"); + return TEST_FAIL; + } + + return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL : + TEST_PASS; + } + return TEST_PASS; +} + +int main(int argc, char *argv[]) +{ + return test_harness(ptrace_tm_spr, "ptrace_tm_spr"); +} diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c new file mode 100644 index 000000000000..48b462f75023 --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c @@ -0,0 +1,160 @@ +/* + * Ptrace test for TAR, PPR, DSCR registers in the TM context + * + * Copyright (C) 2015 Anshuman Khandual, 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. + */ +#include "ptrace.h" +#include "tm.h" +#include "ptrace-tar.h" + +int shm_id; +unsigned long *cptr, *pptr; + + +void tm_tar(void) +{ + unsigned long result, texasr; + unsigned long regs[3]; + int ret; + + cptr = (unsigned long *)shmat(shm_id, NULL, 0); + +trans: + cptr[1] = 0; + asm __volatile__( + "li 4, %[tar_1];" + "mtspr %[sprn_tar], 4;" /* TAR_1 */ + "li 4, %[dscr_1];" + "mtspr %[sprn_dscr], 4;" /* DSCR_1 */ + "or 31,31,31;" /* PPR_1*/ + + "1: ;" + "tbegin.;" + "beq 2f;" + + "li 4, %[tar_2];" + "mtspr %[sprn_tar], 4;" /* TAR_2 */ + "li 4, %[dscr_2];" + "mtspr %[sprn_dscr], 4;" /* DSCR_2 */ + "or 1,1,1;" /* PPR_2 */ + "tsuspend.;" + "li 0, 1;" + "stw 0, 0(%[cptr1]);" + "tresume.;" + "b .;" + + "tend.;" + "li 0, 0;" + "ori %[res], 0, 0;" + "b 3f;" + + /* Transaction abort handler */ + "2: ;" + "li 0, 1;" + "ori %[res], 0, 0;" + "mfspr %[texasr], %[sprn_texasr];" + + "3: ;" + + : [res] "=r" (result), [texasr] "=r" (texasr) + : [sprn_dscr]"i"(SPRN_DSCR), [sprn_tar]"i"(SPRN_TAR), + [sprn_ppr]"i"(SPRN_PPR), [sprn_texasr]"i"(SPRN_TEXASR), + [tar_1]"i"(TAR_1), [dscr_1]"i"(DSCR_1), [tar_2]"i"(TAR_2), + [dscr_2]"i"(DSCR_2), [cptr1] "r" (&cptr[1]) + : "memory", "r0", "r1", "r3", "r4", "r5", "r6" + ); + + /* TM failed, analyse */ + if (result) { + if (!cptr[0]) + goto trans; + + regs[0] = mfspr(SPRN_TAR); + regs[1] = mfspr(SPRN_PPR); + regs[2] = mfspr(SPRN_DSCR); + + shmdt(&cptr); + printf("%-30s TAR: %lu PPR: %lx DSCR: %lu\n", + user_read, regs[0], regs[1], regs[2]); + + ret = validate_tar_registers(regs, TAR_4, PPR_4, DSCR_4); + if (ret) + exit(1); + exit(0); + } + shmdt(&cptr); + exit(1); +} + +int trace_tm_tar(pid_t child) +{ + unsigned long regs[3]; + + FAIL_IF(start_trace(child)); + FAIL_IF(show_tar_registers(child, regs)); + printf("%-30s TAR: %lu PPR: %lx DSCR: %lu\n", + ptrace_read_running, regs[0], regs[1], regs[2]); + + FAIL_IF(validate_tar_registers(regs, TAR_2, PPR_2, DSCR_2)); + FAIL_IF(show_tm_checkpointed_state(child, regs)); + printf("%-30s TAR: %lu PPR: %lx DSCR: %lu\n", + ptrace_read_ckpt, regs[0], regs[1], regs[2]); + + FAIL_IF(validate_tar_registers(regs, TAR_1, PPR_1, DSCR_1)); + FAIL_IF(write_ckpt_tar_registers(child, TAR_4, PPR_4, DSCR_4)); + printf("%-30s TAR: %u PPR: %lx DSCR: %u\n", + ptrace_write_ckpt, TAR_4, PPR_4, DSCR_4); + + pptr[0] = 1; + FAIL_IF(stop_trace(child)); + return TEST_PASS; +} + +int ptrace_tm_tar(void) +{ + pid_t pid; + int ret, status; + + SKIP_IF(!have_htm()); + shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT); + pid = fork(); + if (pid == 0) + tm_tar(); + + pptr = (unsigned long *)shmat(shm_id, NULL, 0); + pptr[0] = 0; + + if (pid) { + while (!pptr[1]) + asm volatile("" : : : "memory"); + ret = trace_tm_tar(pid); + if (ret) { + kill(pid, SIGTERM); + shmdt(&pptr); + shmctl(shm_id, IPC_RMID, NULL); + return TEST_FAIL; + } + shmdt(&pptr); + + ret = wait(&status); + shmctl(shm_id, IPC_RMID, NULL); + if (ret != pid) { + printf("Child's exit status not captured\n"); + return TEST_FAIL; + } + + return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL : + TEST_PASS; + } + return TEST_PASS; +} + +int main(int argc, char *argv[]) +{ + return test_harness(ptrace_tm_tar, "ptrace_tm_tar"); +} diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c new file mode 100644 index 000000000000..b4081e2b22d5 --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c @@ -0,0 +1,168 @@ +/* + * Ptrace test for VMX/VSX registers in the TM context + * + * Copyright (C) 2015 Anshuman Khandual, 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. + */ +#include "ptrace.h" +#include "tm.h" +#include "ptrace-vsx.h" + +int shm_id; +unsigned long *cptr, *pptr; + +unsigned long fp_load[VEC_MAX]; +unsigned long fp_store[VEC_MAX]; +unsigned long fp_load_ckpt[VEC_MAX]; +unsigned long fp_load_ckpt_new[VEC_MAX]; + +__attribute__((used)) void load_vsx(void) +{ + loadvsx(fp_load, 0); +} + +__attribute__((used)) void load_vsx_ckpt(void) +{ + loadvsx(fp_load_ckpt, 0); +} + +void tm_vsx(void) +{ + unsigned long result, texasr; + int ret; + + cptr = (unsigned long *)shmat(shm_id, NULL, 0); + +trans: + cptr[1] = 0; + asm __volatile__( + "bl load_vsx_ckpt;" + + "1: ;" + "tbegin.;" + "beq 2f;" + + "bl load_vsx;" + "tsuspend.;" + "li 7, 1;" + "stw 7, 0(%[cptr1]);" + "tresume.;" + "b .;" + + "tend.;" + "li 0, 0;" + "ori %[res], 0, 0;" + "b 3f;" + + "2: ;" + "li 0, 1;" + "ori %[res], 0, 0;" + "mfspr %[texasr], %[sprn_texasr];" + + "3: ;" + : [res] "=r" (result), [texasr] "=r" (texasr) + : [fp_load] "r" (fp_load), [fp_load_ckpt] "r" (fp_load_ckpt), + [sprn_texasr] "i" (SPRN_TEXASR), [cptr1] "r" (&cptr[1]) + : "memory", "r0", "r1", "r2", "r3", "r4", + "r7", "r8", "r9", "r10", "r11" + ); + + if (result) { + if (!cptr[0]) + goto trans; + + shmdt((void *)cptr); + storevsx(fp_store, 0); + ret = compare_vsx_vmx(fp_store, fp_load_ckpt_new); + if (ret) + exit(1); + exit(0); + } + shmdt((void *)cptr); + exit(1); +} + +int trace_tm_vsx(pid_t child) +{ + unsigned long vsx[VSX_MAX]; + unsigned long vmx[VMX_MAX + 2][2]; + + FAIL_IF(start_trace(child)); + FAIL_IF(show_vsx(child, vsx)); + FAIL_IF(validate_vsx(vsx, fp_load)); + FAIL_IF(show_vmx(child, vmx)); + FAIL_IF(validate_vmx(vmx, fp_load)); + FAIL_IF(show_vsx_ckpt(child, vsx)); + FAIL_IF(validate_vsx(vsx, fp_load_ckpt)); + FAIL_IF(show_vmx_ckpt(child, vmx)); + FAIL_IF(validate_vmx(vmx, fp_load_ckpt)); + memset(vsx, 0, sizeof(vsx)); + memset(vmx, 0, sizeof(vmx)); + + load_vsx_vmx(fp_load_ckpt_new, vsx, vmx); + + FAIL_IF(write_vsx_ckpt(child, vsx)); + FAIL_IF(write_vmx_ckpt(child, vmx)); + pptr[0] = 1; + FAIL_IF(stop_trace(child)); + return TEST_PASS; +} + +int ptrace_tm_vsx(void) +{ + pid_t pid; + int ret, status, i; + + SKIP_IF(!have_htm()); + shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT); + + for (i = 0; i < 128; i++) { + fp_load[i] = 1 + rand(); + fp_load_ckpt[i] = 1 + 2 * rand(); + fp_load_ckpt_new[i] = 1 + 3 * rand(); + } + + pid = fork(); + if (pid < 0) { + perror("fork() failed"); + return TEST_FAIL; + } + + if (pid == 0) + tm_vsx(); + + if (pid) { + pptr = (unsigned long *)shmat(shm_id, NULL, 0); + while (!pptr[1]) + asm volatile("" : : : "memory"); + + ret = trace_tm_vsx(pid); + if (ret) { + kill(pid, SIGKILL); + shmdt((void *)pptr); + shmctl(shm_id, IPC_RMID, NULL); + return TEST_FAIL; + } + + shmdt((void *)pptr); + ret = wait(&status); + shmctl(shm_id, IPC_RMID, NULL); + if (ret != pid) { + printf("Child's exit status not captured\n"); + return TEST_FAIL; + } + + return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL : + TEST_PASS; + } + return TEST_PASS; +} + +int main(int argc, char *argv[]) +{ + return test_harness(ptrace_tm_vsx, "ptrace_tm_vsx"); +} diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c b/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c new file mode 100644 index 000000000000..04084ee7d27b --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c @@ -0,0 +1,117 @@ +/* + * Ptrace test for VMX/VSX registers + * + * Copyright (C) 2015 Anshuman Khandual, 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. + */ +#include "ptrace.h" +#include "ptrace-vsx.h" + +/* Tracer and Tracee Shared Data */ +int shm_id; +int *cptr, *pptr; + +unsigned long fp_load[VEC_MAX]; +unsigned long fp_load_new[VEC_MAX]; +unsigned long fp_store[VEC_MAX]; + +void vsx(void) +{ + int ret; + + cptr = (int *)shmat(shm_id, NULL, 0); + loadvsx(fp_load, 0); + cptr[1] = 1; + + while (!cptr[0]) + asm volatile("" : : : "memory"); + shmdt((void *) cptr); + + storevsx(fp_store, 0); + ret = compare_vsx_vmx(fp_store, fp_load_new); + if (ret) + exit(1); + exit(0); +} + +int trace_vsx(pid_t child) +{ + unsigned long vsx[VSX_MAX]; + unsigned long vmx[VMX_MAX + 2][2]; + + FAIL_IF(start_trace(child)); + FAIL_IF(show_vsx(child, vsx)); + FAIL_IF(validate_vsx(vsx, fp_load)); + FAIL_IF(show_vmx(child, vmx)); + FAIL_IF(validate_vmx(vmx, fp_load)); + + memset(vsx, 0, sizeof(vsx)); + memset(vmx, 0, sizeof(vmx)); + load_vsx_vmx(fp_load_new, vsx, vmx); + + FAIL_IF(write_vsx(child, vsx)); + FAIL_IF(write_vmx(child, vmx)); + FAIL_IF(stop_trace(child)); + + return TEST_PASS; +} + +int ptrace_vsx(void) +{ + pid_t pid; + int ret, status, i; + + shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT); + + for (i = 0; i < VEC_MAX; i++) + fp_load[i] = i + rand(); + + for (i = 0; i < VEC_MAX; i++) + fp_load_new[i] = i + 2 * rand(); + + pid = fork(); + if (pid < 0) { + perror("fork() failed"); + return TEST_FAIL; + } + + if (pid == 0) + vsx(); + + if (pid) { + pptr = (int *)shmat(shm_id, NULL, 0); + while (!pptr[1]) + asm volatile("" : : : "memory"); + + ret = trace_vsx(pid); + if (ret) { + kill(pid, SIGTERM); + shmdt((void *)pptr); + shmctl(shm_id, IPC_RMID, NULL); + return TEST_FAIL; + } + + pptr[0] = 1; + shmdt((void *)pptr); + + ret = wait(&status); + shmctl(shm_id, IPC_RMID, NULL); + if (ret != pid) { + printf("Child's exit status not captured\n"); + return TEST_FAIL; + } + + return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL : + TEST_PASS; + } + return TEST_PASS; +} + +int main(int argc, char *argv[]) +{ + return test_harness(ptrace_vsx, "ptrace_vsx"); +} diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.h b/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.h new file mode 100644 index 000000000000..f4e4b427c9d9 --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2015 Anshuman Khandual, 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. + */ +#define VEC_MAX 128 +#define VSX_MAX 32 +#define VMX_MAX 32 + +/* + * unsigned long vsx[32] + * unsigned long load[128] + */ +int validate_vsx(unsigned long *vsx, unsigned long *load) +{ + int i; + + for (i = 0; i < VSX_MAX; i++) { + if (vsx[i] != load[2 * i + 1]) { + printf("vsx[%d]: %lx load[%d] %lx\n", + i, vsx[i], 2 * i + 1, load[2 * i + 1]); + return TEST_FAIL; + } + } + return TEST_PASS; +} + +/* + * unsigned long vmx[32][2] + * unsigned long load[128] + */ +int validate_vmx(unsigned long vmx[][2], unsigned long *load) +{ + int i; + + for (i = 0; i < VMX_MAX; i++) { + #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + if ((vmx[i][0] != load[64 + 2 * i]) || + (vmx[i][1] != load[65 + 2 * i])) { + printf("vmx[%d][0]: %lx load[%d] %lx\n", + i, vmx[i][0], 64 + 2 * i, + load[64 + 2 * i]); + printf("vmx[%d][1]: %lx load[%d] %lx\n", + i, vmx[i][1], 65 + 2 * i, + load[65 + 2 * i]); + return TEST_FAIL; + } + #else /* + * In LE each value pair is stored in an + * alternate manner. + */ + if ((vmx[i][0] != load[65 + 2 * i]) || + (vmx[i][1] != load[64 + 2 * i])) { + printf("vmx[%d][0]: %lx load[%d] %lx\n", + i, vmx[i][0], 65 + 2 * i, + load[65 + 2 * i]); + printf("vmx[%d][1]: %lx load[%d] %lx\n", + i, vmx[i][1], 64 + 2 * i, + load[64 + 2 * i]); + return TEST_FAIL; + } + #endif + } + return TEST_PASS; +} + +/* + * unsigned long store[128] + * unsigned long load[128] + */ +int compare_vsx_vmx(unsigned long *store, unsigned long *load) +{ + int i; + + for (i = 0; i < VSX_MAX; i++) { + if (store[1 + 2 * i] != load[1 + 2 * i]) { + printf("store[%d]: %lx load[%d] %lx\n", + 1 + 2 * i, store[i], + 1 + 2 * i, load[i]); + return TEST_FAIL; + } + } + + #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + for (i = 64; i < VEC_MAX; i++) { + if (store[i] != load[i]) { + printf("store[%d]: %lx load[%d] %lx\n", + i, store[i], i, load[i]); + return TEST_FAIL; + } + } + #else /* In LE each value pair is stored in an alternate manner */ + for (i = 64; i < VEC_MAX; i++) { + if (!(i % 2) && (store[i] != load[i+1])) { + printf("store[%d]: %lx load[%d] %lx\n", + i, store[i], i+1, load[i+1]); + return TEST_FAIL; + } + if ((i % 2) && (store[i] != load[i-1])) { + printf("here store[%d]: %lx load[%d] %lx\n", + i, store[i], i-1, load[i-1]); + return TEST_FAIL; + } + } + #endif + return TEST_PASS; +} + +void load_vsx_vmx(unsigned long *load, unsigned long *vsx, + unsigned long vmx[][2]) +{ + int i; + + for (i = 0; i < VSX_MAX; i++) + vsx[i] = load[1 + 2 * i]; + + for (i = 0; i < VMX_MAX; i++) { + vmx[i][0] = load[64 + 2 * i]; + vmx[i][1] = load[65 + 2 * i]; + } +} + +void loadvsx(void *p, int tmp); +void storevsx(void *p, int tmp); diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace.h b/tools/testing/selftests/powerpc/ptrace/ptrace.h new file mode 100644 index 000000000000..19fb825270a1 --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace.h @@ -0,0 +1,711 @@ +/* + * Ptrace interface test helper functions + * + * Copyright (C) 2015 Anshuman Khandual, 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. + */ +#include <inttypes.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <malloc.h> +#include <errno.h> +#include <time.h> +#include <sys/ptrace.h> +#include <sys/ioctl.h> +#include <sys/uio.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/signal.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <sys/user.h> +#include <linux/elf.h> +#include <linux/types.h> +#include <linux/auxvec.h> +#include "reg.h" +#include "utils.h" + +#define TEST_PASS 0 +#define TEST_FAIL 1 + +struct fpr_regs { + unsigned long fpr[32]; + unsigned long fpscr; +}; + +struct tm_spr_regs { + unsigned long tm_tfhar; + unsigned long tm_texasr; + unsigned long tm_tfiar; +}; + +#ifndef NT_PPC_TAR +#define NT_PPC_TAR 0x103 +#define NT_PPC_PPR 0x104 +#define NT_PPC_DSCR 0x105 +#define NT_PPC_EBB 0x106 +#define NT_PPC_PMU 0x107 +#define NT_PPC_TM_CGPR 0x108 +#define NT_PPC_TM_CFPR 0x109 +#define NT_PPC_TM_CVMX 0x10a +#define NT_PPC_TM_CVSX 0x10b +#define NT_PPC_TM_SPR 0x10c +#define NT_PPC_TM_CTAR 0x10d +#define NT_PPC_TM_CPPR 0x10e +#define NT_PPC_TM_CDSCR 0x10f +#endif + +/* Basic ptrace operations */ +int start_trace(pid_t child) +{ + int ret; + + ret = ptrace(PTRACE_ATTACH, child, NULL, NULL); + if (ret) { + perror("ptrace(PTRACE_ATTACH) failed"); + return TEST_FAIL; + } + ret = waitpid(child, NULL, 0); + if (ret != child) { + perror("waitpid() failed"); + return TEST_FAIL; + } + return TEST_PASS; +} + +int stop_trace(pid_t child) +{ + int ret; + + ret = ptrace(PTRACE_DETACH, child, NULL, NULL); + if (ret) { + perror("ptrace(PTRACE_DETACH) failed"); + return TEST_FAIL; + } + return TEST_PASS; +} + +int cont_trace(pid_t child) +{ + int ret; + + ret = ptrace(PTRACE_CONT, child, NULL, NULL); + if (ret) { + perror("ptrace(PTRACE_CONT) failed"); + return TEST_FAIL; + } + return TEST_PASS; +} + +/* TAR, PPR, DSCR */ +int show_tar_registers(pid_t child, unsigned long *out) +{ + struct iovec iov; + unsigned long *reg; + int ret; + + reg = malloc(sizeof(unsigned long)); + if (!reg) { + perror("malloc() failed"); + return TEST_FAIL; + } + iov.iov_base = (u64 *) reg; + iov.iov_len = sizeof(unsigned long); + + ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TAR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + goto fail; + } + if (out) + out[0] = *reg; + + ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_PPR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + goto fail; + } + if (out) + out[1] = *reg; + + ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_DSCR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + goto fail; + } + if (out) + out[2] = *reg; + + free(reg); + return TEST_PASS; +fail: + free(reg); + return TEST_FAIL; +} + +int write_tar_registers(pid_t child, unsigned long tar, + unsigned long ppr, unsigned long dscr) +{ + struct iovec iov; + unsigned long *reg; + int ret; + + reg = malloc(sizeof(unsigned long)); + if (!reg) { + perror("malloc() failed"); + return TEST_FAIL; + } + + iov.iov_base = (u64 *) reg; + iov.iov_len = sizeof(unsigned long); + + *reg = tar; + ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TAR, &iov); + if (ret) { + perror("ptrace(PTRACE_SETREGSET) failed"); + goto fail; + } + + *reg = ppr; + ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_PPR, &iov); + if (ret) { + perror("ptrace(PTRACE_SETREGSET) failed"); + goto fail; + } + + *reg = dscr; + ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_DSCR, &iov); + if (ret) { + perror("ptrace(PTRACE_SETREGSET) failed"); + goto fail; + } + + free(reg); + return TEST_PASS; +fail: + free(reg); + return TEST_FAIL; +} + +int show_tm_checkpointed_state(pid_t child, unsigned long *out) +{ + struct iovec iov; + unsigned long *reg; + int ret; + + reg = malloc(sizeof(unsigned long)); + if (!reg) { + perror("malloc() failed"); + return TEST_FAIL; + } + + iov.iov_base = (u64 *) reg; + iov.iov_len = sizeof(unsigned long); + + ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CTAR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + goto fail; + } + if (out) + out[0] = *reg; + + ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CPPR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + goto fail; + } + if (out) + out[1] = *reg; + + ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CDSCR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + goto fail; + } + if (out) + out[2] = *reg; + + free(reg); + return TEST_PASS; + +fail: + free(reg); + return TEST_FAIL; +} + +int write_ckpt_tar_registers(pid_t child, unsigned long tar, + unsigned long ppr, unsigned long dscr) +{ + struct iovec iov; + unsigned long *reg; + int ret; + + reg = malloc(sizeof(unsigned long)); + if (!reg) { + perror("malloc() failed"); + return TEST_FAIL; + } + + iov.iov_base = (u64 *) reg; + iov.iov_len = sizeof(unsigned long); + + *reg = tar; + ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CTAR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + goto fail; + } + + *reg = ppr; + ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CPPR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + goto fail; + } + + *reg = dscr; + ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CDSCR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + goto fail; + } + + free(reg); + return TEST_PASS; +fail: + free(reg); + return TEST_FAIL; +} + +/* FPR */ +int show_fpr(pid_t child, unsigned long *fpr) +{ + struct fpr_regs *regs; + int ret, i; + + regs = (struct fpr_regs *) malloc(sizeof(struct fpr_regs)); + ret = ptrace(PTRACE_GETFPREGS, child, NULL, regs); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + + if (fpr) { + for (i = 0; i < 32; i++) + fpr[i] = regs->fpr[i]; + } + return TEST_PASS; +} + +int write_fpr(pid_t child, unsigned long val) +{ + struct fpr_regs *regs; + int ret, i; + + regs = (struct fpr_regs *) malloc(sizeof(struct fpr_regs)); + ret = ptrace(PTRACE_GETFPREGS, child, NULL, regs); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + + for (i = 0; i < 32; i++) + regs->fpr[i] = val; + + ret = ptrace(PTRACE_SETFPREGS, child, NULL, regs); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + return TEST_PASS; +} + +int show_ckpt_fpr(pid_t child, unsigned long *fpr) +{ + struct fpr_regs *regs; + struct iovec iov; + int ret, i; + + regs = (struct fpr_regs *) malloc(sizeof(struct fpr_regs)); + iov.iov_base = regs; + iov.iov_len = sizeof(struct fpr_regs); + + ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CFPR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + + if (fpr) { + for (i = 0; i < 32; i++) + fpr[i] = regs->fpr[i]; + } + + return TEST_PASS; +} + +int write_ckpt_fpr(pid_t child, unsigned long val) +{ + struct fpr_regs *regs; + struct iovec iov; + int ret, i; + + regs = (struct fpr_regs *) malloc(sizeof(struct fpr_regs)); + iov.iov_base = regs; + iov.iov_len = sizeof(struct fpr_regs); + + ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CFPR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + + for (i = 0; i < 32; i++) + regs->fpr[i] = val; + + ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CFPR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + return TEST_PASS; +} + +/* GPR */ +int show_gpr(pid_t child, unsigned long *gpr) +{ + struct pt_regs *regs; + int ret, i; + + regs = (struct pt_regs *) malloc(sizeof(struct pt_regs)); + if (!regs) { + perror("malloc() failed"); + return TEST_FAIL; + } + + ret = ptrace(PTRACE_GETREGS, child, NULL, regs); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + + if (gpr) { + for (i = 14; i < 32; i++) + gpr[i-14] = regs->gpr[i]; + } + + return TEST_PASS; +} + +int write_gpr(pid_t child, unsigned long val) +{ + struct pt_regs *regs; + int i, ret; + + regs = (struct pt_regs *) malloc(sizeof(struct pt_regs)); + if (!regs) { + perror("malloc() failed"); + return TEST_FAIL; + } + + ret = ptrace(PTRACE_GETREGS, child, NULL, regs); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + + for (i = 14; i < 32; i++) + regs->gpr[i] = val; + + ret = ptrace(PTRACE_SETREGS, child, NULL, regs); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + return TEST_PASS; +} + +int show_ckpt_gpr(pid_t child, unsigned long *gpr) +{ + struct pt_regs *regs; + struct iovec iov; + int ret, i; + + regs = (struct pt_regs *) malloc(sizeof(struct pt_regs)); + if (!regs) { + perror("malloc() failed"); + return TEST_FAIL; + } + + iov.iov_base = (u64 *) regs; + iov.iov_len = sizeof(struct pt_regs); + + ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CGPR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + + if (gpr) { + for (i = 14; i < 32; i++) + gpr[i-14] = regs->gpr[i]; + } + + return TEST_PASS; +} + +int write_ckpt_gpr(pid_t child, unsigned long val) +{ + struct pt_regs *regs; + struct iovec iov; + int ret, i; + + regs = (struct pt_regs *) malloc(sizeof(struct pt_regs)); + if (!regs) { + perror("malloc() failed\n"); + return TEST_FAIL; + } + iov.iov_base = (u64 *) regs; + iov.iov_len = sizeof(struct pt_regs); + + ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CGPR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + + for (i = 14; i < 32; i++) + regs->gpr[i] = val; + + ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CGPR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + return TEST_PASS; +} + +/* VMX */ +int show_vmx(pid_t child, unsigned long vmx[][2]) +{ + int ret; + + ret = ptrace(PTRACE_GETVRREGS, child, 0, vmx); + if (ret) { + perror("ptrace(PTRACE_GETVRREGS) failed"); + return TEST_FAIL; + } + return TEST_PASS; +} + +int show_vmx_ckpt(pid_t child, unsigned long vmx[][2]) +{ + unsigned long regs[34][2]; + struct iovec iov; + int ret; + + iov.iov_base = (u64 *) regs; + iov.iov_len = sizeof(regs); + ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CVMX, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET, NT_PPC_TM_CVMX) failed"); + return TEST_FAIL; + } + memcpy(vmx, regs, sizeof(regs)); + return TEST_PASS; +} + + +int write_vmx(pid_t child, unsigned long vmx[][2]) +{ + int ret; + + ret = ptrace(PTRACE_SETVRREGS, child, 0, vmx); + if (ret) { + perror("ptrace(PTRACE_SETVRREGS) failed"); + return TEST_FAIL; + } + return TEST_PASS; +} + +int write_vmx_ckpt(pid_t child, unsigned long vmx[][2]) +{ + unsigned long regs[34][2]; + struct iovec iov; + int ret; + + memcpy(regs, vmx, sizeof(regs)); + iov.iov_base = (u64 *) regs; + iov.iov_len = sizeof(regs); + ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CVMX, &iov); + if (ret) { + perror("ptrace(PTRACE_SETREGSET, NT_PPC_TM_CVMX) failed"); + return TEST_FAIL; + } + return TEST_PASS; +} + +/* VSX */ +int show_vsx(pid_t child, unsigned long *vsx) +{ + int ret; + + ret = ptrace(PTRACE_GETVSRREGS, child, 0, vsx); + if (ret) { + perror("ptrace(PTRACE_GETVSRREGS) failed"); + return TEST_FAIL; + } + return TEST_PASS; +} + +int show_vsx_ckpt(pid_t child, unsigned long *vsx) +{ + unsigned long regs[32]; + struct iovec iov; + int ret; + + iov.iov_base = (u64 *) regs; + iov.iov_len = sizeof(regs); + ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CVSX, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET, NT_PPC_TM_CVSX) failed"); + return TEST_FAIL; + } + memcpy(vsx, regs, sizeof(regs)); + return TEST_PASS; +} + +int write_vsx(pid_t child, unsigned long *vsx) +{ + int ret; + + ret = ptrace(PTRACE_SETVSRREGS, child, 0, vsx); + if (ret) { + perror("ptrace(PTRACE_SETVSRREGS) failed"); + return TEST_FAIL; + } + return TEST_PASS; +} + +int write_vsx_ckpt(pid_t child, unsigned long *vsx) +{ + unsigned long regs[32]; + struct iovec iov; + int ret; + + memcpy(regs, vsx, sizeof(regs)); + iov.iov_base = (u64 *) regs; + iov.iov_len = sizeof(regs); + ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CVSX, &iov); + if (ret) { + perror("ptrace(PTRACE_SETREGSET, NT_PPC_TM_CVSX) failed"); + return TEST_FAIL; + } + return TEST_PASS; +} + +/* TM SPR */ +int show_tm_spr(pid_t child, struct tm_spr_regs *out) +{ + struct tm_spr_regs *regs; + struct iovec iov; + int ret; + + regs = (struct tm_spr_regs *) malloc(sizeof(struct tm_spr_regs)); + if (!regs) { + perror("malloc() failed"); + return TEST_FAIL; + } + + iov.iov_base = (u64 *) regs; + iov.iov_len = sizeof(struct tm_spr_regs); + + ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_SPR, &iov); + if (ret) { + perror("ptrace(PTRACE_GETREGSET) failed"); + return TEST_FAIL; + } + + if (out) + memcpy(out, regs, sizeof(struct tm_spr_regs)); + + return TEST_PASS; +} + + + +/* Analyse TEXASR after TM failure */ +inline unsigned long get_tfiar(void) +{ + unsigned long ret; + + asm volatile("mfspr %0,%1" : "=r" (ret) : "i" (SPRN_TFIAR)); + return ret; +} + +void analyse_texasr(unsigned long texasr) +{ + printf("TEXASR: %16lx\t", texasr); + + if (texasr & TEXASR_FP) + printf("TEXASR_FP "); + + if (texasr & TEXASR_DA) + printf("TEXASR_DA "); + + if (texasr & TEXASR_NO) + printf("TEXASR_NO "); + + if (texasr & TEXASR_FO) + printf("TEXASR_FO "); + + if (texasr & TEXASR_SIC) + printf("TEXASR_SIC "); + + if (texasr & TEXASR_NTC) + printf("TEXASR_NTC "); + + if (texasr & TEXASR_TC) + printf("TEXASR_TC "); + + if (texasr & TEXASR_TIC) + printf("TEXASR_TIC "); + + if (texasr & TEXASR_IC) + printf("TEXASR_IC "); + + if (texasr & TEXASR_IFC) + printf("TEXASR_IFC "); + + if (texasr & TEXASR_ABT) + printf("TEXASR_ABT "); + + if (texasr & TEXASR_SPD) + printf("TEXASR_SPD "); + + if (texasr & TEXASR_HV) + printf("TEXASR_HV "); + + if (texasr & TEXASR_PR) + printf("TEXASR_PR "); + + if (texasr & TEXASR_FS) + printf("TEXASR_FS "); + + if (texasr & TEXASR_TE) + printf("TEXASR_TE "); + + if (texasr & TEXASR_ROT) + printf("TEXASR_ROT "); + + printf("TFIAR :%lx\n", get_tfiar()); +} + +void store_gpr(unsigned long *addr); +void store_fpr(float *addr); diff --git a/tools/testing/selftests/powerpc/reg.h b/tools/testing/selftests/powerpc/reg.h deleted file mode 100644 index fddf368ed82f..000000000000 --- a/tools/testing/selftests/powerpc/reg.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2014, Michael Ellerman, IBM Corp. - * Licensed under GPLv2. - */ - -#ifndef _SELFTESTS_POWERPC_REG_H -#define _SELFTESTS_POWERPC_REG_H - -#define __stringify_1(x) #x -#define __stringify(x) __stringify_1(x) - -#define mfspr(rn) ({unsigned long rval; \ - asm volatile("mfspr %0," _str(rn) \ - : "=r" (rval)); rval; }) -#define mtspr(rn, v) asm volatile("mtspr " _str(rn) ",%0" : \ - : "r" ((unsigned long)(v)) \ - : "memory") - -#define mb() asm volatile("sync" : : : "memory"); - -#define SPRN_MMCR2 769 -#define SPRN_MMCRA 770 -#define SPRN_MMCR0 779 -#define MMCR0_PMAO 0x00000080 -#define MMCR0_PMAE 0x04000000 -#define MMCR0_FC 0x80000000 -#define SPRN_EBBHR 804 -#define SPRN_EBBRR 805 -#define SPRN_BESCR 806 /* Branch event status & control register */ -#define SPRN_BESCRS 800 /* Branch event status & control set (1 bits set to 1) */ -#define SPRN_BESCRSU 801 /* Branch event status & control set upper */ -#define SPRN_BESCRR 802 /* Branch event status & control REset (1 bits set to 0) */ -#define SPRN_BESCRRU 803 /* Branch event status & control REset upper */ - -#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 -#define SPRN_PMC3 773 -#define SPRN_PMC4 774 -#define SPRN_PMC5 775 -#define SPRN_PMC6 776 - -#define SPRN_SIAR 780 -#define SPRN_SDAR 781 -#define SPRN_SIER 768 - -#define SPRN_TEXASR 0x82 -#define SPRN_TFIAR 0x81 /* Transaction Failure Inst Addr */ -#define SPRN_TFHAR 0x80 /* Transaction Failure Handler Addr */ -#define TEXASR_FS 0x08000000 -#define SPRN_TAR 0x32f - -#endif /* _SELFTESTS_POWERPC_REG_H */ diff --git a/tools/testing/selftests/powerpc/signal/signal.S b/tools/testing/selftests/powerpc/signal/signal.S index 7043d521df0a..322f2f1fc327 100644 --- a/tools/testing/selftests/powerpc/signal/signal.S +++ b/tools/testing/selftests/powerpc/signal/signal.S @@ -7,7 +7,7 @@ * 2 of the License, or (at your option) any later version. */ -#include "../basic_asm.h" +#include "basic_asm.h" /* long signal_self(pid_t pid, int sig); */ FUNC_START(signal_self) diff --git a/tools/testing/selftests/powerpc/stringloops/memcmp.c b/tools/testing/selftests/powerpc/stringloops/memcmp.c index 17417dd70708..30b1222380ca 100644 --- a/tools/testing/selftests/powerpc/stringloops/memcmp.c +++ b/tools/testing/selftests/powerpc/stringloops/memcmp.c @@ -1,7 +1,7 @@ #include <malloc.h> #include <stdlib.h> #include <string.h> -#include "../utils.h" +#include "utils.h" #define SIZE 256 #define ITERATIONS 10000 diff --git a/tools/testing/selftests/powerpc/tm/tm-signal.S b/tools/testing/selftests/powerpc/tm/tm-signal.S index 4e13e8b3a96f..506a4ebaf3ae 100644 --- a/tools/testing/selftests/powerpc/tm/tm-signal.S +++ b/tools/testing/selftests/powerpc/tm/tm-signal.S @@ -7,11 +7,11 @@ * 2 of the License, or (at your option) any later version. */ -#include "../basic_asm.h" -#include "../gpr_asm.h" -#include "../fpu_asm.h" -#include "../vmx_asm.h" -#include "../vsx_asm.h" +#include "basic_asm.h" +#include "gpr_asm.h" +#include "fpu_asm.h" +#include "vmx_asm.h" +#include "vsx_asm.h" /* * Large caveat here being that the caller cannot expect the diff --git a/tools/testing/selftests/powerpc/tm/tm.h b/tools/testing/selftests/powerpc/tm/tm.h index 2c8da74304e7..0ffff04433c5 100644 --- a/tools/testing/selftests/powerpc/tm/tm.h +++ b/tools/testing/selftests/powerpc/tm/tm.h @@ -10,7 +10,7 @@ #include <asm/cputable.h> #include <stdbool.h> -#include "../utils.h" +#include "utils.h" static inline bool have_htm(void) { diff --git a/tools/testing/selftests/sigaltstack/.gitignore b/tools/testing/selftests/sigaltstack/.gitignore new file mode 100644 index 000000000000..35897b0a3f44 --- /dev/null +++ b/tools/testing/selftests/sigaltstack/.gitignore @@ -0,0 +1 @@ +sas diff --git a/tools/testing/selftests/sync/.gitignore b/tools/testing/selftests/sync/.gitignore new file mode 100644 index 000000000000..f5091e7792f2 --- /dev/null +++ b/tools/testing/selftests/sync/.gitignore @@ -0,0 +1 @@ +sync_test diff --git a/tools/testing/selftests/sync/Makefile b/tools/testing/selftests/sync/Makefile new file mode 100644 index 000000000000..87ac400507c0 --- /dev/null +++ b/tools/testing/selftests/sync/Makefile @@ -0,0 +1,24 @@ +CFLAGS += -O2 -g -std=gnu89 -pthread -Wall -Wextra +CFLAGS += -I../../../../usr/include/ +LDFLAGS += -pthread + +TEST_PROGS = sync_test + +all: $(TEST_PROGS) + +include ../lib.mk + +OBJS = sync_test.o sync.o + +TESTS += sync_alloc.o +TESTS += sync_fence.o +TESTS += sync_merge.o +TESTS += sync_wait.o +TESTS += sync_stress_parallelism.o +TESTS += sync_stress_consumer.o +TESTS += sync_stress_merge.o + +sync_test: $(OBJS) $(TESTS) + +clean: + $(RM) sync_test $(OBJS) $(TESTS) diff --git a/tools/testing/selftests/sync/sw_sync.h b/tools/testing/selftests/sync/sw_sync.h new file mode 100644 index 000000000000..e2cfc6bad83e --- /dev/null +++ b/tools/testing/selftests/sync/sw_sync.h @@ -0,0 +1,46 @@ +/* + * sw_sync abstraction + * + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2013 Google, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SELFTESTS_SW_SYNC_H +#define SELFTESTS_SW_SYNC_H + +/* + * sw_sync is mainly intended for testing and should not be compiled into + * production kernels + */ + +int sw_sync_timeline_create(void); +int sw_sync_timeline_is_valid(int fd); +int sw_sync_timeline_inc(int fd, unsigned int count); +void sw_sync_timeline_destroy(int fd); + +int sw_sync_fence_create(int fd, const char *name, unsigned int value); +int sw_sync_fence_is_valid(int fd); +void sw_sync_fence_destroy(int fd); + +#endif diff --git a/tools/testing/selftests/sync/sync.c b/tools/testing/selftests/sync/sync.c new file mode 100644 index 000000000000..f3d599f249b9 --- /dev/null +++ b/tools/testing/selftests/sync/sync.c @@ -0,0 +1,221 @@ +/* + * sync / sw_sync abstraction + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <fcntl.h> +#include <malloc.h> +#include <poll.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> + +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "sync.h" +#include "sw_sync.h" + +#include <linux/sync_file.h> + + +/* SW_SYNC ioctls */ +struct sw_sync_create_fence_data { + __u32 value; + char name[32]; + __s32 fence; +}; + +#define SW_SYNC_IOC_MAGIC 'W' +#define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0,\ + struct sw_sync_create_fence_data) +#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32) + + +int sync_wait(int fd, int timeout) +{ + struct pollfd fds; + + fds.fd = fd; + fds.events = POLLIN | POLLERR; + + return poll(&fds, 1, timeout); +} + +int sync_merge(const char *name, int fd1, int fd2) +{ + struct sync_merge_data data = {}; + int err; + + data.fd2 = fd2; + strncpy(data.name, name, sizeof(data.name) - 1); + data.name[sizeof(data.name) - 1] = '\0'; + + err = ioctl(fd1, SYNC_IOC_MERGE, &data); + if (err < 0) + return err; + + return data.fence; +} + +static struct sync_file_info *sync_file_info(int fd) +{ + struct sync_file_info *info; + struct sync_fence_info *fence_info; + int err, num_fences; + + info = calloc(1, sizeof(*info)); + if (info == NULL) + return NULL; + + err = ioctl(fd, SYNC_IOC_FILE_INFO, info); + if (err < 0) { + free(info); + return NULL; + } + + num_fences = info->num_fences; + + if (num_fences) { + info->flags = 0; + info->num_fences = num_fences; + + fence_info = calloc(num_fences, sizeof(*fence_info)); + if (!fence_info) { + free(info); + return NULL; + } + + info->sync_fence_info = (uint64_t)fence_info; + + err = ioctl(fd, SYNC_IOC_FILE_INFO, info); + if (err < 0) { + free(fence_info); + free(info); + return NULL; + } + } + + return info; +} + +static void sync_file_info_free(struct sync_file_info *info) +{ + free((void *)info->sync_fence_info); + free(info); +} + +int sync_fence_size(int fd) +{ + int count; + struct sync_file_info *info = sync_file_info(fd); + + if (!info) + return 0; + + count = info->num_fences; + + sync_file_info_free(info); + + return count; +} + +int sync_fence_count_with_status(int fd, int status) +{ + unsigned int i, count = 0; + struct sync_fence_info *fence_info = NULL; + struct sync_file_info *info = sync_file_info(fd); + + if (!info) + return -1; + + fence_info = (struct sync_fence_info *)info->sync_fence_info; + for (i = 0 ; i < info->num_fences ; i++) { + if (fence_info[i].status == status) + count++; + } + + sync_file_info_free(info); + + return count; +} + +int sw_sync_timeline_create(void) +{ + return open("/sys/kernel/debug/sync/sw_sync", O_RDWR); +} + +int sw_sync_timeline_inc(int fd, unsigned int count) +{ + __u32 arg = count; + + return ioctl(fd, SW_SYNC_IOC_INC, &arg); +} + +int sw_sync_timeline_is_valid(int fd) +{ + int status; + + if (fd == -1) + return 0; + + status = fcntl(fd, F_GETFD, 0); + return (status >= 0); +} + +void sw_sync_timeline_destroy(int fd) +{ + if (sw_sync_timeline_is_valid(fd)) + close(fd); +} + +int sw_sync_fence_create(int fd, const char *name, unsigned int value) +{ + struct sw_sync_create_fence_data data = {}; + int err; + + data.value = value; + strncpy(data.name, name, sizeof(data.name) - 1); + data.name[sizeof(data.name) - 1] = '\0'; + + err = ioctl(fd, SW_SYNC_IOC_CREATE_FENCE, &data); + if (err < 0) + return err; + + return data.fence; +} + +int sw_sync_fence_is_valid(int fd) +{ + /* Same code! */ + return sw_sync_timeline_is_valid(fd); +} + +void sw_sync_fence_destroy(int fd) +{ + if (sw_sync_fence_is_valid(fd)) + close(fd); +} diff --git a/tools/testing/selftests/sync/sync.h b/tools/testing/selftests/sync/sync.h new file mode 100644 index 000000000000..fb7156148350 --- /dev/null +++ b/tools/testing/selftests/sync/sync.h @@ -0,0 +1,40 @@ +/* + * sync abstraction + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SELFTESTS_SYNC_H +#define SELFTESTS_SYNC_H + +#define FENCE_STATUS_ERROR (-1) +#define FENCE_STATUS_ACTIVE (0) +#define FENCE_STATUS_SIGNALED (1) + +int sync_wait(int fd, int timeout); +int sync_merge(const char *name, int fd1, int fd2); +int sync_fence_size(int fd); +int sync_fence_count_with_status(int fd, int status); + +#endif diff --git a/tools/testing/selftests/sync/sync_alloc.c b/tools/testing/selftests/sync/sync_alloc.c new file mode 100644 index 000000000000..66a28afc05dc --- /dev/null +++ b/tools/testing/selftests/sync/sync_alloc.c @@ -0,0 +1,74 @@ +/* + * sync allocation tests + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "sync.h" +#include "sw_sync.h" +#include "synctest.h" + +int test_alloc_timeline(void) +{ + int timeline, valid; + + timeline = sw_sync_timeline_create(); + valid = sw_sync_timeline_is_valid(timeline); + ASSERT(valid, "Failure allocating timeline\n"); + + sw_sync_timeline_destroy(timeline); + return 0; +} + +int test_alloc_fence(void) +{ + int timeline, fence, valid; + + timeline = sw_sync_timeline_create(); + valid = sw_sync_timeline_is_valid(timeline); + ASSERT(valid, "Failure allocating timeline\n"); + + fence = sw_sync_fence_create(timeline, "allocFence", 1); + valid = sw_sync_fence_is_valid(fence); + ASSERT(valid, "Failure allocating fence\n"); + + sw_sync_fence_destroy(fence); + sw_sync_timeline_destroy(timeline); + return 0; +} + +int test_alloc_fence_negative(void) +{ + int fence, timeline; + + timeline = sw_sync_timeline_create(); + ASSERT(timeline > 0, "Failure allocating timeline\n"); + + fence = sw_sync_fence_create(-1, "fence", 1); + ASSERT(fence < 0, "Success allocating negative fence\n"); + + sw_sync_fence_destroy(fence); + sw_sync_timeline_destroy(timeline); + return 0; +} diff --git a/tools/testing/selftests/sync/sync_fence.c b/tools/testing/selftests/sync/sync_fence.c new file mode 100644 index 000000000000..13f175287da3 --- /dev/null +++ b/tools/testing/selftests/sync/sync_fence.c @@ -0,0 +1,132 @@ +/* + * sync fence tests with one timeline + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "sync.h" +#include "sw_sync.h" +#include "synctest.h" + +int test_fence_one_timeline_wait(void) +{ + int fence, valid, ret; + int timeline = sw_sync_timeline_create(); + + valid = sw_sync_timeline_is_valid(timeline); + ASSERT(valid, "Failure allocating timeline\n"); + + fence = sw_sync_fence_create(timeline, "allocFence", 5); + valid = sw_sync_fence_is_valid(fence); + ASSERT(valid, "Failure allocating fence\n"); + + /* Wait on fence until timeout */ + ret = sync_wait(fence, 0); + ASSERT(ret == 0, "Failure waiting on fence until timeout\n"); + + /* Advance timeline from 0 -> 1 */ + ret = sw_sync_timeline_inc(timeline, 1); + ASSERT(ret == 0, "Failure advancing timeline\n"); + + /* Wait on fence until timeout */ + ret = sync_wait(fence, 0); + ASSERT(ret == 0, "Failure waiting on fence until timeout\n"); + + /* Signal the fence */ + ret = sw_sync_timeline_inc(timeline, 4); + ASSERT(ret == 0, "Failure signaling the fence\n"); + + /* Wait successfully */ + ret = sync_wait(fence, 0); + ASSERT(ret > 0, "Failure waiting on fence\n"); + + /* Go even further, and confirm wait still succeeds */ + ret = sw_sync_timeline_inc(timeline, 10); + ASSERT(ret == 0, "Failure going further\n"); + ret = sync_wait(fence, 0); + ASSERT(ret > 0, "Failure waiting ahead\n"); + + sw_sync_fence_destroy(fence); + sw_sync_timeline_destroy(timeline); + + return 0; +} + +int test_fence_one_timeline_merge(void) +{ + int a, b, c, d, valid; + int timeline = sw_sync_timeline_create(); + + /* create fence a,b,c and then merge them all into fence d */ + a = sw_sync_fence_create(timeline, "allocFence", 1); + b = sw_sync_fence_create(timeline, "allocFence", 2); + c = sw_sync_fence_create(timeline, "allocFence", 3); + + valid = sw_sync_fence_is_valid(a) && + sw_sync_fence_is_valid(b) && + sw_sync_fence_is_valid(c); + ASSERT(valid, "Failure allocating fences\n"); + + d = sync_merge("mergeFence", b, a); + d = sync_merge("mergeFence", c, d); + valid = sw_sync_fence_is_valid(d); + ASSERT(valid, "Failure merging fences\n"); + + /* confirm all fences have one active point (even d) */ + ASSERT(sync_fence_count_with_status(a, FENCE_STATUS_ACTIVE) == 1, + "a has too many active fences!\n"); + ASSERT(sync_fence_count_with_status(a, FENCE_STATUS_ACTIVE) == 1, + "b has too many active fences!\n"); + ASSERT(sync_fence_count_with_status(a, FENCE_STATUS_ACTIVE) == 1, + "c has too many active fences!\n"); + ASSERT(sync_fence_count_with_status(a, FENCE_STATUS_ACTIVE) == 1, + "d has too many active fences!\n"); + + /* confirm that d is not signaled until the max of a,b,c */ + sw_sync_timeline_inc(timeline, 1); + ASSERT(sync_fence_count_with_status(a, FENCE_STATUS_SIGNALED) == 1, + "a did not signal!\n"); + ASSERT(sync_fence_count_with_status(d, FENCE_STATUS_ACTIVE) == 1, + "d signaled too early!\n"); + + sw_sync_timeline_inc(timeline, 1); + ASSERT(sync_fence_count_with_status(b, FENCE_STATUS_SIGNALED) == 1, + "b did not signal!\n"); + ASSERT(sync_fence_count_with_status(d, FENCE_STATUS_ACTIVE) == 1, + "d signaled too early!\n"); + + sw_sync_timeline_inc(timeline, 1); + ASSERT(sync_fence_count_with_status(c, FENCE_STATUS_SIGNALED) == 1, + "c did not signal!\n"); + ASSERT(sync_fence_count_with_status(d, FENCE_STATUS_ACTIVE) == 0 && + sync_fence_count_with_status(d, FENCE_STATUS_SIGNALED) == 1, + "d did not signal!\n"); + + sw_sync_fence_destroy(d); + sw_sync_fence_destroy(c); + sw_sync_fence_destroy(b); + sw_sync_fence_destroy(a); + sw_sync_timeline_destroy(timeline); + return 0; +} diff --git a/tools/testing/selftests/sync/sync_merge.c b/tools/testing/selftests/sync/sync_merge.c new file mode 100644 index 000000000000..8914d43395c7 --- /dev/null +++ b/tools/testing/selftests/sync/sync_merge.c @@ -0,0 +1,60 @@ +/* + * sync fence merge tests + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "sync.h" +#include "sw_sync.h" +#include "synctest.h" + +int test_fence_merge_same_fence(void) +{ + int fence, valid, merged; + int timeline = sw_sync_timeline_create(); + + valid = sw_sync_timeline_is_valid(timeline); + ASSERT(valid, "Failure allocating timeline\n"); + + fence = sw_sync_fence_create(timeline, "allocFence", 5); + valid = sw_sync_fence_is_valid(fence); + ASSERT(valid, "Failure allocating fence\n"); + + merged = sync_merge("mergeFence", fence, fence); + valid = sw_sync_fence_is_valid(fence); + ASSERT(valid, "Failure merging fence\n"); + + ASSERT(sync_fence_count_with_status(merged, FENCE_STATUS_SIGNALED) == 0, + "fence signaled too early!\n"); + + sw_sync_timeline_inc(timeline, 5); + ASSERT(sync_fence_count_with_status(merged, FENCE_STATUS_SIGNALED) == 1, + "fence did not signal!\n"); + + sw_sync_fence_destroy(merged); + sw_sync_fence_destroy(fence); + sw_sync_timeline_destroy(timeline); + + return 0; +} diff --git a/tools/testing/selftests/sync/sync_stress_consumer.c b/tools/testing/selftests/sync/sync_stress_consumer.c new file mode 100644 index 000000000000..d9eff8d524f7 --- /dev/null +++ b/tools/testing/selftests/sync/sync_stress_consumer.c @@ -0,0 +1,185 @@ +/* + * sync stress test: producer/consumer + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <pthread.h> + +#include "sync.h" +#include "sw_sync.h" +#include "synctest.h" + +/* IMPORTANT NOTE: if you see this test failing on your system, it may be + * due to a shortage of file descriptors. Please ensure your system has + * a sensible limit for this test to finish correctly. + */ + +/* Returns 1 on error, 0 on success */ +static int busy_wait_on_fence(int fence) +{ + int error, active; + + do { + error = sync_fence_count_with_status(fence, FENCE_STATUS_ERROR); + ASSERT(error == 0, "Error occurred on fence\n"); + active = sync_fence_count_with_status(fence, + FENCE_STATUS_ACTIVE); + } while (active); + + return 0; +} + +static struct { + int iterations; + int threads; + int counter; + int consumer_timeline; + int *producer_timelines; + pthread_mutex_t lock; +} test_data_mpsc; + +static int mpsc_producer_thread(void *d) +{ + int id = (long)d; + int fence, valid, i; + int *producer_timelines = test_data_mpsc.producer_timelines; + int consumer_timeline = test_data_mpsc.consumer_timeline; + int iterations = test_data_mpsc.iterations; + + for (i = 0; i < iterations; i++) { + fence = sw_sync_fence_create(consumer_timeline, "fence", i); + valid = sw_sync_fence_is_valid(fence); + ASSERT(valid, "Failure creating fence\n"); + + /* + * Wait for the consumer to finish. Use alternate + * means of waiting on the fence + */ + + if ((iterations + id) % 8 != 0) { + ASSERT(sync_wait(fence, -1) > 0, + "Failure waiting on fence\n"); + } else { + ASSERT(busy_wait_on_fence(fence) == 0, + "Failure waiting on fence\n"); + } + + /* + * Every producer increments the counter, the consumer + * checks and erases it + */ + pthread_mutex_lock(&test_data_mpsc.lock); + test_data_mpsc.counter++; + pthread_mutex_unlock(&test_data_mpsc.lock); + + ASSERT(sw_sync_timeline_inc(producer_timelines[id], 1) == 0, + "Error advancing producer timeline\n"); + + sw_sync_fence_destroy(fence); + } + + return 0; +} + +static int mpcs_consumer_thread(void) +{ + int fence, merged, tmp, valid, it, i; + int *producer_timelines = test_data_mpsc.producer_timelines; + int consumer_timeline = test_data_mpsc.consumer_timeline; + int iterations = test_data_mpsc.iterations; + int n = test_data_mpsc.threads; + + for (it = 1; it <= iterations; it++) { + fence = sw_sync_fence_create(producer_timelines[0], "name", it); + for (i = 1; i < n; i++) { + tmp = sw_sync_fence_create(producer_timelines[i], + "name", it); + merged = sync_merge("name", tmp, fence); + sw_sync_fence_destroy(tmp); + sw_sync_fence_destroy(fence); + fence = merged; + } + + valid = sw_sync_fence_is_valid(fence); + ASSERT(valid, "Failure merging fences\n"); + + /* + * Make sure we see an increment from every producer thread. + * Vary the means by which we wait. + */ + if (iterations % 8 != 0) { + ASSERT(sync_wait(fence, -1) > 0, + "Producers did not increment as expected\n"); + } else { + ASSERT(busy_wait_on_fence(fence) == 0, + "Producers did not increment as expected\n"); + } + + ASSERT(test_data_mpsc.counter == n * it, + "Counter value mismatch!\n"); + + /* Release the producer threads */ + ASSERT(sw_sync_timeline_inc(consumer_timeline, 1) == 0, + "Failure releasing producer threads\n"); + + sw_sync_fence_destroy(fence); + } + + return 0; +} + +int test_consumer_stress_multi_producer_single_consumer(void) +{ + int iterations = 1 << 12; + int n = 5; + long i, ret; + int producer_timelines[n]; + int consumer_timeline; + pthread_t threads[n]; + + consumer_timeline = sw_sync_timeline_create(); + for (i = 0; i < n; i++) + producer_timelines[i] = sw_sync_timeline_create(); + + test_data_mpsc.producer_timelines = producer_timelines; + test_data_mpsc.consumer_timeline = consumer_timeline; + test_data_mpsc.iterations = iterations; + test_data_mpsc.threads = n; + test_data_mpsc.counter = 0; + pthread_mutex_init(&test_data_mpsc.lock, NULL); + + for (i = 0; i < n; i++) { + pthread_create(&threads[i], NULL, (void * (*)(void *)) + mpsc_producer_thread, (void *)i); + } + + /* Consumer thread runs here */ + ret = mpcs_consumer_thread(); + + for (i = 0; i < n; i++) + pthread_join(threads[i], NULL); + + return ret; +} diff --git a/tools/testing/selftests/sync/sync_stress_merge.c b/tools/testing/selftests/sync/sync_stress_merge.c new file mode 100644 index 000000000000..99e83ef45fbf --- /dev/null +++ b/tools/testing/selftests/sync/sync_stress_merge.c @@ -0,0 +1,115 @@ +/* + * sync stress test: merging + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "sync.h" +#include "sw_sync.h" +#include "synctest.h" + +int test_merge_stress_random_merge(void) +{ + int i, size, ret; + int timeline_count = 32; + int merge_count = 1024 * 32; + int timelines[timeline_count]; + int fence_map[timeline_count]; + int fence, tmpfence, merged, valid; + int timeline, timeline_offset, sync_point; + + srand(time(NULL)); + + for (i = 0; i < timeline_count; i++) + timelines[i] = sw_sync_timeline_create(); + + fence = sw_sync_fence_create(timelines[0], "fence", 0); + valid = sw_sync_fence_is_valid(fence); + ASSERT(valid, "Failure creating fence\n"); + + memset(fence_map, -1, sizeof(fence_map)); + fence_map[0] = 0; + + /* + * Randomly create sync_points out of a fixed set of timelines, + * and merge them together + */ + for (i = 0; i < merge_count; i++) { + /* Generate sync_point. */ + timeline_offset = rand() % timeline_count; + timeline = timelines[timeline_offset]; + sync_point = rand(); + + /* Keep track of the latest sync_point in each timeline. */ + if (fence_map[timeline_offset] == -1) + fence_map[timeline_offset] = sync_point; + else if (fence_map[timeline_offset] < sync_point) + fence_map[timeline_offset] = sync_point; + + /* Merge */ + tmpfence = sw_sync_fence_create(timeline, "fence", sync_point); + merged = sync_merge("merge", tmpfence, fence); + sw_sync_fence_destroy(tmpfence); + sw_sync_fence_destroy(fence); + fence = merged; + + valid = sw_sync_fence_is_valid(merged); + ASSERT(valid, "Failure creating fence i\n"); + } + + size = 0; + for (i = 0; i < timeline_count; i++) + if (fence_map[i] != -1) + size++; + + /* Confirm our map matches the fence. */ + ASSERT(sync_fence_size(fence) == size, + "Quantity of elements not matching\n"); + + /* Trigger the merged fence */ + for (i = 0; i < timeline_count; i++) { + if (fence_map[i] != -1) { + ret = sync_wait(fence, 0); + ASSERT(ret == 0, + "Failure waiting on fence until timeout\n"); + /* Increment the timeline to the last sync_point */ + sw_sync_timeline_inc(timelines[i], fence_map[i]); + } + } + + /* Check that the fence is triggered. */ + ret = sync_wait(fence, 0); + ASSERT(ret > 0, "Failure triggering fence\n"); + + sw_sync_fence_destroy(fence); + + for (i = 0; i < timeline_count; i++) + sw_sync_timeline_destroy(timelines[i]); + + return 0; +} diff --git a/tools/testing/selftests/sync/sync_stress_parallelism.c b/tools/testing/selftests/sync/sync_stress_parallelism.c new file mode 100644 index 000000000000..e6c9be671dfc --- /dev/null +++ b/tools/testing/selftests/sync/sync_stress_parallelism.c @@ -0,0 +1,111 @@ +/* + * sync stress test: parallelism + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <pthread.h> + +#include "sync.h" +#include "sw_sync.h" +#include "synctest.h" + +static struct { + int iterations; + int timeline; + int counter; +} test_data_two_threads; + +static int test_stress_two_threads_shared_timeline_thread(void *d) +{ + int thread_id = (long)d; + int timeline = test_data_two_threads.timeline; + int iterations = test_data_two_threads.iterations; + int fence, valid, ret, i; + + for (i = 0; i < iterations; i++) { + fence = sw_sync_fence_create(timeline, "fence", + i * 2 + thread_id); + valid = sw_sync_fence_is_valid(fence); + ASSERT(valid, "Failure allocating fence\n"); + + /* Wait on the prior thread to complete */ + ret = sync_wait(fence, -1); + ASSERT(ret > 0, "Problem occurred on prior thread\n"); + + /* + * Confirm the previous thread's writes are visible + * and then increment + */ + ASSERT(test_data_two_threads.counter == i * 2 + thread_id, + "Counter got damaged!\n"); + test_data_two_threads.counter++; + + /* Kick off the other thread */ + ret = sw_sync_timeline_inc(timeline, 1); + ASSERT(ret == 0, "Advancing timeline failed\n"); + + sw_sync_fence_destroy(fence); + } + + return 0; +} + +int test_stress_two_threads_shared_timeline(void) +{ + pthread_t a, b; + int valid; + int timeline = sw_sync_timeline_create(); + + valid = sw_sync_timeline_is_valid(timeline); + ASSERT(valid, "Failure allocating timeline\n"); + + test_data_two_threads.iterations = 1 << 16; + test_data_two_threads.counter = 0; + test_data_two_threads.timeline = timeline; + + /* + * Use a single timeline to synchronize two threads + * hammmering on the same counter. + */ + + pthread_create(&a, NULL, (void *(*)(void *)) + test_stress_two_threads_shared_timeline_thread, + (void *)0); + pthread_create(&b, NULL, (void *(*)(void *)) + test_stress_two_threads_shared_timeline_thread, + (void *)1); + + pthread_join(a, NULL); + pthread_join(b, NULL); + + /* make sure the threads did not trample on one another */ + ASSERT(test_data_two_threads.counter == + test_data_two_threads.iterations * 2, + "Counter has unexpected value\n"); + + sw_sync_timeline_destroy(timeline); + + return 0; +} diff --git a/tools/testing/selftests/sync/sync_test.c b/tools/testing/selftests/sync/sync_test.c new file mode 100644 index 000000000000..9ea08d9f0b13 --- /dev/null +++ b/tools/testing/selftests/sync/sync_test.c @@ -0,0 +1,79 @@ +/* + * sync test runner + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include "synctest.h" + +static int run_test(int (*test)(void), char *name) +{ + int result; + pid_t childpid; + + fflush(stdout); + childpid = fork(); + + if (childpid) { + waitpid(childpid, &result, 0); + if (WIFEXITED(result)) + return WEXITSTATUS(result); + return 1; + } + + printf("[RUN]\tExecuting %s\n", name); + exit(test()); +} + +int main(void) +{ + int err = 0; + + printf("[RUN]\tTesting sync framework\n"); + + err += RUN_TEST(test_alloc_timeline); + err += RUN_TEST(test_alloc_fence); + err += RUN_TEST(test_alloc_fence_negative); + + err += RUN_TEST(test_fence_one_timeline_wait); + err += RUN_TEST(test_fence_one_timeline_merge); + err += RUN_TEST(test_fence_merge_same_fence); + err += RUN_TEST(test_fence_multi_timeline_wait); + err += RUN_TEST(test_stress_two_threads_shared_timeline); + err += RUN_TEST(test_consumer_stress_multi_producer_single_consumer); + err += RUN_TEST(test_merge_stress_random_merge); + + if (err) + printf("[FAIL]\tsync errors: %d\n", err); + else + printf("[OK]\tsync\n"); + + return !!err; +} diff --git a/tools/testing/selftests/sync/sync_wait.c b/tools/testing/selftests/sync/sync_wait.c new file mode 100644 index 000000000000..d69b752f6550 --- /dev/null +++ b/tools/testing/selftests/sync/sync_wait.c @@ -0,0 +1,91 @@ +/* + * sync fence wait tests + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "sync.h" +#include "sw_sync.h" +#include "synctest.h" + +int test_fence_multi_timeline_wait(void) +{ + int timelineA, timelineB, timelineC; + int fenceA, fenceB, fenceC, merged; + int valid, active, signaled, ret; + + timelineA = sw_sync_timeline_create(); + timelineB = sw_sync_timeline_create(); + timelineC = sw_sync_timeline_create(); + + fenceA = sw_sync_fence_create(timelineA, "fenceA", 5); + fenceB = sw_sync_fence_create(timelineB, "fenceB", 5); + fenceC = sw_sync_fence_create(timelineC, "fenceC", 5); + + merged = sync_merge("mergeFence", fenceB, fenceA); + merged = sync_merge("mergeFence", fenceC, merged); + + valid = sw_sync_fence_is_valid(merged); + ASSERT(valid, "Failure merging fence from various timelines\n"); + + /* Confirm fence isn't signaled */ + active = sync_fence_count_with_status(merged, FENCE_STATUS_ACTIVE); + ASSERT(active == 3, "Fence signaled too early!\n"); + + ret = sync_wait(merged, 0); + ASSERT(ret == 0, + "Failure waiting on fence until timeout\n"); + + ret = sw_sync_timeline_inc(timelineA, 5); + active = sync_fence_count_with_status(merged, FENCE_STATUS_ACTIVE); + signaled = sync_fence_count_with_status(merged, FENCE_STATUS_SIGNALED); + ASSERT(active == 2 && signaled == 1, + "Fence did not signal properly!\n"); + + ret = sw_sync_timeline_inc(timelineB, 5); + active = sync_fence_count_with_status(merged, FENCE_STATUS_ACTIVE); + signaled = sync_fence_count_with_status(merged, FENCE_STATUS_SIGNALED); + ASSERT(active == 1 && signaled == 2, + "Fence did not signal properly!\n"); + + ret = sw_sync_timeline_inc(timelineC, 5); + active = sync_fence_count_with_status(merged, FENCE_STATUS_ACTIVE); + signaled = sync_fence_count_with_status(merged, FENCE_STATUS_SIGNALED); + ASSERT(active == 0 && signaled == 3, + "Fence did not signal properly!\n"); + + /* confirm you can successfully wait */ + ret = sync_wait(merged, 100); + ASSERT(ret > 0, "Failure waiting on signaled fence\n"); + + sw_sync_fence_destroy(merged); + sw_sync_fence_destroy(fenceC); + sw_sync_fence_destroy(fenceB); + sw_sync_fence_destroy(fenceA); + sw_sync_timeline_destroy(timelineC); + sw_sync_timeline_destroy(timelineB); + sw_sync_timeline_destroy(timelineA); + + return 0; +} diff --git a/tools/testing/selftests/sync/synctest.h b/tools/testing/selftests/sync/synctest.h new file mode 100644 index 000000000000..e7d1d57dba7a --- /dev/null +++ b/tools/testing/selftests/sync/synctest.h @@ -0,0 +1,66 @@ +/* + * sync tests + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SELFTESTS_SYNCTEST_H +#define SELFTESTS_SYNCTEST_H + +#include <stdio.h> + +#define ASSERT(cond, msg) do { \ + if (!(cond)) { \ + printf("[ERROR]\t%s", (msg)); \ + return 1; \ + } \ +} while (0) + +#define RUN_TEST(x) run_test((x), #x) + +/* Allocation tests */ +int test_alloc_timeline(void); +int test_alloc_fence(void); +int test_alloc_fence_negative(void); + +/* Fence tests with one timeline */ +int test_fence_one_timeline_wait(void); +int test_fence_one_timeline_merge(void); + +/* Fence merge tests */ +int test_fence_merge_same_fence(void); + +/* Fence wait tests */ +int test_fence_multi_timeline_wait(void); + +/* Stress test - parallelism */ +int test_stress_two_threads_shared_timeline(void); + +/* Stress test - consumer */ +int test_consumer_stress_multi_producer_single_consumer(void); + +/* Stress test - merging */ +int test_merge_stress_random_merge(void); + +#endif diff --git a/tools/testing/selftests/timers/.gitignore b/tools/testing/selftests/timers/.gitignore index 68f3fc71ac44..cc986621f512 100644 --- a/tools/testing/selftests/timers/.gitignore +++ b/tools/testing/selftests/timers/.gitignore @@ -17,3 +17,4 @@ skew_consistency threadtest valid-adjtimex adjtick +set-tz diff --git a/tools/usb/usbip/.gitignore b/tools/usb/usbip/.gitignore index 9aad9e30a8ba..03b892c8bd8c 100644 --- a/tools/usb/usbip/.gitignore +++ b/tools/usb/usbip/.gitignore @@ -2,6 +2,7 @@ Makefile Makefile.in aclocal.m4 autom4te.cache/ +compile config.guess config.h config.h.in @@ -21,7 +22,10 @@ src/Makefile.in stamp-h1 libsrc/libusbip.la libsrc/libusbip_la-names.lo +libsrc/libusbip_la-sysfs_utils.lo libsrc/libusbip_la-usbip_common.lo +libsrc/libusbip_la-usbip_device_driver.lo +libsrc/libusbip_la-usbip_host_common.lo libsrc/libusbip_la-usbip_host_driver.lo libsrc/libusbip_la-vhci_driver.lo src/usbip diff --git a/tools/usb/usbip/src/usbipd.c b/tools/usb/usbip/src/usbipd.c index a0972dea9e6c..009afb4a3aae 100644 --- a/tools/usb/usbip/src/usbipd.c +++ b/tools/usb/usbip/src/usbipd.c @@ -398,13 +398,6 @@ static int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[], * (see do_standalone_mode()) */ usbip_net_set_v6only(sock); - if (sock >= FD_SETSIZE) { - err("FD_SETSIZE: %s: sock=%d, max=%d", - ai_buf, sock, FD_SETSIZE); - close(sock); - continue; - } - ret = bind(sock, ai->ai_addr, ai->ai_addrlen); if (ret < 0) { err("bind: %s: %d (%s)", diff --git a/tools/virtio/linux/compiler.h b/tools/virtio/linux/compiler.h index 845960e1cbf2..c9ccfd42ec13 100644 --- a/tools/virtio/linux/compiler.h +++ b/tools/virtio/linux/compiler.h @@ -4,6 +4,6 @@ #define WRITE_ONCE(var, val) \ (*((volatile typeof(val) *)(&(var))) = (val)) -#define READ_ONCE(var) (*((volatile typeof(val) *)(&(var)))) +#define READ_ONCE(var) (*((volatile typeof(var) *)(&(var)))) #endif diff --git a/tools/virtio/linux/uaccess.h b/tools/virtio/linux/uaccess.h index 0a578fe18653..fa05d01b2c90 100644 --- a/tools/virtio/linux/uaccess.h +++ b/tools/virtio/linux/uaccess.h @@ -1,8 +1,9 @@ #ifndef UACCESS_H #define UACCESS_H -extern void *__user_addr_min, *__user_addr_max; -#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) +#include <linux/compiler.h> + +extern void *__user_addr_min, *__user_addr_max; static inline void __chk_user_ptr(const volatile void *p, size_t size) { @@ -13,7 +14,7 @@ static inline void __chk_user_ptr(const volatile void *p, size_t size) ({ \ typeof(ptr) __pu_ptr = (ptr); \ __chk_user_ptr(__pu_ptr, sizeof(*__pu_ptr)); \ - ACCESS_ONCE(*(__pu_ptr)) = x; \ + WRITE_ONCE(*(__pu_ptr), x); \ 0; \ }) @@ -21,7 +22,7 @@ static inline void __chk_user_ptr(const volatile void *p, size_t size) ({ \ typeof(ptr) __pu_ptr = (ptr); \ __chk_user_ptr(__pu_ptr, sizeof(*__pu_ptr)); \ - x = ACCESS_ONCE(*(__pu_ptr)); \ + x = READ_ONCE(*(__pu_ptr)); \ 0; \ }) |