diff options
Diffstat (limited to 'tools')
69 files changed, 4267 insertions, 854 deletions
diff --git a/tools/build/Makefile.build b/tools/build/Makefile.build index e279a71c650d..c46b20e4ad87 100644 --- a/tools/build/Makefile.build +++ b/tools/build/Makefile.build @@ -19,15 +19,9 @@ else Q=@ endif -ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4 -ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),) +ifneq ($(findstring s,$(filter-out --%,$(MAKEFLAGS))),) quiet=silent_ endif -else # make-3.8x -ifneq ($(filter s% -s%,$(MAKEFLAGS)),) - quiet=silent_ -endif -endif build-dir := $(srctree)/tools/build diff --git a/tools/hv/bondvf.sh b/tools/hv/bondvf.sh index d85968cb1bf2..89b25068cd98 100755 --- a/tools/hv/bondvf.sh +++ b/tools/hv/bondvf.sh @@ -102,15 +102,30 @@ function create_bond_cfg_redhat { } function del_eth_cfg_ubuntu { - local fn=$cfgdir/interfaces + local mainfn=$cfgdir/interfaces + local fnlist=( $mainfn ) + + local dirlist=(`awk '/^[ \t]*source/{print $2}' $mainfn`) + + local i + for i in "${dirlist[@]}" + do + fnlist+=(`ls $i 2>/dev/null`) + done + local tmpfl=$(mktemp) local nic_start='^[ \t]*(auto|iface|mapping|allow-.*)[ \t]+'$1 local nic_end='^[ \t]*(auto|iface|mapping|allow-.*|source)' - awk "/$nic_end/{x=0} x{next} /$nic_start/{x=1;next} 1" $fn >$tmpfl + local fn + for fn in "${fnlist[@]}" + do + awk "/$nic_end/{x=0} x{next} /$nic_start/{x=1;next} 1" \ + $fn >$tmpfl - cp $tmpfl $fn + cp $tmpfl $fn + done rm $tmpfl } @@ -119,7 +134,6 @@ function create_eth_cfg_ubuntu { local fn=$cfgdir/interfaces del_eth_cfg_ubuntu $1 - echo $'\n'auto $1 >>$fn echo iface $1 inet manual >>$fn echo bond-master $2 >>$fn @@ -128,7 +142,10 @@ function create_eth_cfg_ubuntu { function create_eth_cfg_pri_ubuntu { local fn=$cfgdir/interfaces - create_eth_cfg_ubuntu $1 $2 + del_eth_cfg_ubuntu $1 + echo $'\n'allow-hotplug $1 >>$fn + echo iface $1 inet manual >>$fn + echo bond-master $2 >>$fn echo bond-primary $1 >>$fn } @@ -153,7 +170,11 @@ function create_eth_cfg_suse { } function create_eth_cfg_pri_suse { - create_eth_cfg_suse $1 + local fn=$cfgdir/ifcfg-$1 + + rm -f $fn + echo BOOTPROTO=none >>$fn + echo STARTMODE=hotplug >>$fn } function create_bond_cfg_suse { diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 94dfa9def355..ce2988be4f0e 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -82,6 +82,11 @@ enum bpf_cmd { BPF_PROG_ATTACH, BPF_PROG_DETACH, BPF_PROG_TEST_RUN, + BPF_PROG_GET_NEXT_ID, + BPF_MAP_GET_NEXT_ID, + BPF_PROG_GET_FD_BY_ID, + BPF_MAP_GET_FD_BY_ID, + BPF_OBJ_GET_INFO_BY_FD, }; enum bpf_map_type { @@ -115,12 +120,14 @@ enum bpf_prog_type { BPF_PROG_TYPE_LWT_IN, BPF_PROG_TYPE_LWT_OUT, BPF_PROG_TYPE_LWT_XMIT, + BPF_PROG_TYPE_SOCK_OPS, }; enum bpf_attach_type { BPF_CGROUP_INET_INGRESS, BPF_CGROUP_INET_EGRESS, BPF_CGROUP_INET_SOCK_CREATE, + BPF_CGROUP_SOCK_OPS, __MAX_BPF_ATTACH_TYPE }; @@ -209,6 +216,21 @@ union bpf_attr { __u32 repeat; __u32 duration; } test; + + struct { /* anonymous struct used by BPF_*_GET_*_ID */ + union { + __u32 start_id; + __u32 prog_id; + __u32 map_id; + }; + __u32 next_id; + }; + + struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */ + __u32 bpf_fd; + __u32 info_len; + __aligned_u64 info; + } info; } __attribute__((aligned(8))); /* BPF helper function descriptions: @@ -313,8 +335,11 @@ union bpf_attr { * @flags: room for future extensions * Return: 0 on success or negative error * - * u64 bpf_perf_event_read(&map, index) - * Return: Number events read or error code + * u64 bpf_perf_event_read(map, flags) + * read perf event counter value + * @map: pointer to perf_event_array map + * @flags: index of event in the map or bitmask flags + * Return: value of perf event counter read or error code * * int bpf_redirect(ifindex, flags) * redirect to another netdev @@ -328,11 +353,11 @@ union bpf_attr { * @skb: pointer to skb * Return: realm if != 0 * - * int bpf_perf_event_output(ctx, map, index, data, size) + * int bpf_perf_event_output(ctx, map, flags, data, size) * output perf raw sample * @ctx: struct pt_regs* * @map: pointer to perf_event_array map - * @index: index of event in the map + * @flags: index of event in the map or bitmask flags * @data: data on stack to be output as raw data * @size: size of data * Return: 0 on success or negative error @@ -490,6 +515,30 @@ union bpf_attr { * Get the owner uid of the socket stored inside sk_buff. * @skb: pointer to skb * Return: uid of the socket owner on success or overflowuid if failed. + * + * u32 bpf_set_hash(skb, hash) + * Set full skb->hash. + * @skb: pointer to skb + * @hash: hash to set + * + * int bpf_setsockopt(bpf_socket, level, optname, optval, optlen) + * Calls setsockopt. Not all opts are available, only those with + * integer optvals plus TCP_CONGESTION. + * Supported levels: SOL_SOCKET and IPROTO_TCP + * @bpf_socket: pointer to bpf_socket + * @level: SOL_SOCKET or IPROTO_TCP + * @optname: option name + * @optval: pointer to option value + * @optlen: length of optval in byes + * Return: 0 or negative error + * + * int bpf_skb_adjust_room(skb, len_diff, mode, flags) + * Grow or shrink room in sk_buff. + * @skb: pointer to skb + * @len_diff: (signed) amount of room to grow/shrink + * @mode: operation mode (enum bpf_adj_room_mode) + * @flags: reserved for future use + * Return: 0 on success or negative error code */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -539,7 +588,10 @@ union bpf_attr { FN(xdp_adjust_head), \ FN(probe_read_str), \ FN(get_socket_cookie), \ - FN(get_socket_uid), + FN(get_socket_uid), \ + FN(set_hash), \ + FN(setsockopt), \ + FN(skb_adjust_room), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call @@ -589,6 +641,11 @@ enum bpf_func_id { /* BPF_FUNC_perf_event_output for sk_buff input context. */ #define BPF_F_CTXLEN_MASK (0xfffffULL << 32) +/* Mode for BPF_FUNC_skb_adjust_room helper. */ +enum bpf_adj_room_mode { + BPF_ADJ_ROOM_NET_OPTS, +}; + /* user accessible mirror of in-kernel sk_buff. * new fields can only be added to the end of this structure */ @@ -670,4 +727,75 @@ struct xdp_md { __u32 data_end; }; +#define BPF_TAG_SIZE 8 + +struct bpf_prog_info { + __u32 type; + __u32 id; + __u8 tag[BPF_TAG_SIZE]; + __u32 jited_prog_len; + __u32 xlated_prog_len; + __aligned_u64 jited_prog_insns; + __aligned_u64 xlated_prog_insns; +} __attribute__((aligned(8))); + +struct bpf_map_info { + __u32 type; + __u32 id; + __u32 key_size; + __u32 value_size; + __u32 max_entries; + __u32 map_flags; +} __attribute__((aligned(8))); + +/* User bpf_sock_ops struct to access socket values and specify request ops + * and their replies. + * New fields can only be added at the end of this structure + */ +struct bpf_sock_ops { + __u32 op; + union { + __u32 reply; + __u32 replylong[4]; + }; + __u32 family; + __u32 remote_ip4; + __u32 local_ip4; + __u32 remote_ip6[4]; + __u32 local_ip6[4]; + __u32 remote_port; + __u32 local_port; +}; + +/* List of known BPF sock_ops operators. + * New entries can only be added at the end + */ +enum { + BPF_SOCK_OPS_VOID, + BPF_SOCK_OPS_TIMEOUT_INIT, /* Should return SYN-RTO value to use or + * -1 if default value should be used + */ + BPF_SOCK_OPS_RWND_INIT, /* Should return initial advertized + * window (in packets) or -1 if default + * value should be used + */ + BPF_SOCK_OPS_TCP_CONNECT_CB, /* Calls BPF program right before an + * active connection is initialized + */ + BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB, /* Calls BPF program when an + * active connection is + * established + */ + BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB, /* Calls BPF program when a + * passive connection is + * established + */ + BPF_SOCK_OPS_NEEDS_ECN, /* If connection's congestion control + * needs ECN + */ +}; + +#define TCP_BPF_IW 1001 /* Set TCP initial congestion window */ +#define TCP_BPF_SNDCWND_CLAMP 1002 /* Set sndcwnd_clamp */ + #endif /* _UAPI__LINUX_BPF_H__ */ diff --git a/tools/kvm/kvm_stat/kvm_stat b/tools/kvm/kvm_stat/kvm_stat index 8f74ed8e7237..dd8f00cfb8b4 100755 --- a/tools/kvm/kvm_stat/kvm_stat +++ b/tools/kvm/kvm_stat/kvm_stat @@ -295,114 +295,6 @@ class ArchS390(Arch): ARCH = Arch.get_arch() -def walkdir(path): - """Returns os.walk() data for specified directory. - - As it is only a wrapper it returns the same 3-tuple of (dirpath, - dirnames, filenames). - """ - return next(os.walk(path)) - - -def parse_int_list(list_string): - """Returns an int list from a string of comma separated integers and - integer ranges.""" - integers = [] - members = list_string.split(',') - - for member in members: - if '-' not in member: - integers.append(int(member)) - else: - int_range = member.split('-') - integers.extend(range(int(int_range[0]), - int(int_range[1]) + 1)) - - return integers - - -def get_pid_from_gname(gname): - """Fuzzy function to convert guest name to QEMU process pid. - - Returns a list of potential pids, can be empty if no match found. - Throws an exception on processing errors. - - """ - pids = [] - try: - child = subprocess.Popen(['ps', '-A', '--format', 'pid,args'], - stdout=subprocess.PIPE) - except: - raise Exception - for line in child.stdout: - line = line.lstrip().split(' ', 1) - # perform a sanity check before calling the more expensive - # function to possibly extract the guest name - if ' -name ' in line[1] and gname == get_gname_from_pid(line[0]): - pids.append(int(line[0])) - child.stdout.close() - - return pids - - -def get_gname_from_pid(pid): - """Returns the guest name for a QEMU process pid. - - Extracts the guest name from the QEMU comma line by processing the '-name' - option. Will also handle names specified out of sequence. - - """ - name = '' - try: - line = open('/proc/{}/cmdline'.format(pid), 'rb').read().split('\0') - parms = line[line.index('-name') + 1].split(',') - while '' in parms: - # commas are escaped (i.e. ',,'), hence e.g. 'foo,bar' results in - # ['foo', '', 'bar'], which we revert here - idx = parms.index('') - parms[idx - 1] += ',' + parms[idx + 1] - del parms[idx:idx+2] - # the '-name' switch allows for two ways to specify the guest name, - # where the plain name overrides the name specified via 'guest=' - for arg in parms: - if '=' not in arg: - name = arg - break - if arg[:6] == 'guest=': - name = arg[6:] - except (ValueError, IOError, IndexError): - pass - - return name - - -def get_online_cpus(): - """Returns a list of cpu id integers.""" - with open('/sys/devices/system/cpu/online') as cpu_list: - cpu_string = cpu_list.readline() - return parse_int_list(cpu_string) - - -def get_filters(): - """Returns a dict of trace events, their filter ids and - the values that can be filtered. - - Trace events can be filtered for special values by setting a - filter string via an ioctl. The string normally has the format - identifier==value. For each filter a new event will be created, to - be able to distinguish the events. - - """ - filters = {} - filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS) - if ARCH.exit_reasons: - filters['kvm_exit'] = ('exit_reason', ARCH.exit_reasons) - return filters - -libc = ctypes.CDLL('libc.so.6', use_errno=True) -syscall = libc.syscall - - class perf_event_attr(ctypes.Structure): """Struct that holds the necessary data to set up a trace event. @@ -432,25 +324,6 @@ class perf_event_attr(ctypes.Structure): self.read_format = PERF_FORMAT_GROUP -def perf_event_open(attr, pid, cpu, group_fd, flags): - """Wrapper for the sys_perf_evt_open() syscall. - - Used to set up performance events, returns a file descriptor or -1 - on error. - - Attributes are: - - syscall number - - struct perf_event_attr * - - pid or -1 to monitor all pids - - cpu number or -1 to monitor all cpus - - The file descriptor of the group leader or -1 to create a group. - - flags - - """ - return syscall(ARCH.sc_perf_evt_open, ctypes.pointer(attr), - ctypes.c_int(pid), ctypes.c_int(cpu), - ctypes.c_int(group_fd), ctypes.c_long(flags)) - PERF_TYPE_TRACEPOINT = 2 PERF_FORMAT_GROUP = 1 << 3 @@ -495,6 +368,8 @@ class Event(object): """Represents a performance event and manages its life cycle.""" def __init__(self, name, group, trace_cpu, trace_pid, trace_point, trace_filter, trace_set='kvm'): + self.libc = ctypes.CDLL('libc.so.6', use_errno=True) + self.syscall = self.libc.syscall self.name = name self.fd = None self.setup_event(group, trace_cpu, trace_pid, trace_point, @@ -511,6 +386,25 @@ class Event(object): if self.fd: os.close(self.fd) + def perf_event_open(self, attr, pid, cpu, group_fd, flags): + """Wrapper for the sys_perf_evt_open() syscall. + + Used to set up performance events, returns a file descriptor or -1 + on error. + + Attributes are: + - syscall number + - struct perf_event_attr * + - pid or -1 to monitor all pids + - cpu number or -1 to monitor all cpus + - The file descriptor of the group leader or -1 to create a group. + - flags + + """ + return self.syscall(ARCH.sc_perf_evt_open, ctypes.pointer(attr), + ctypes.c_int(pid), ctypes.c_int(cpu), + ctypes.c_int(group_fd), ctypes.c_long(flags)) + def setup_event_attribute(self, trace_set, trace_point): """Returns an initialized ctype perf_event_attr struct.""" @@ -539,8 +433,8 @@ class Event(object): if group.events: group_leader = group.events[0].fd - fd = perf_event_open(event_attr, trace_pid, - trace_cpu, group_leader, 0) + fd = self.perf_event_open(event_attr, trace_pid, + trace_cpu, group_leader, 0) if fd == -1: err = ctypes.get_errno() raise OSError(err, os.strerror(err), @@ -575,17 +469,53 @@ class Event(object): fcntl.ioctl(self.fd, ARCH.ioctl_numbers['RESET'], 0) -class TracepointProvider(object): +class Provider(object): + """Encapsulates functionalities used by all providers.""" + @staticmethod + def is_field_wanted(fields_filter, field): + """Indicate whether field is valid according to fields_filter.""" + if not fields_filter: + return True + return re.match(fields_filter, field) is not None + + @staticmethod + def walkdir(path): + """Returns os.walk() data for specified directory. + + As it is only a wrapper it returns the same 3-tuple of (dirpath, + dirnames, filenames). + """ + return next(os.walk(path)) + + +class TracepointProvider(Provider): """Data provider for the stats class. Manages the events/groups from which it acquires its data. """ - def __init__(self): + def __init__(self, pid, fields_filter): self.group_leaders = [] - self.filters = get_filters() - self._fields = self.get_available_fields() - self._pid = 0 + self.filters = self.get_filters() + self.update_fields(fields_filter) + self.pid = pid + + @staticmethod + def get_filters(): + """Returns a dict of trace events, their filter ids and + the values that can be filtered. + + Trace events can be filtered for special values by setting a + filter string via an ioctl. The string normally has the format + identifier==value. For each filter a new event will be created, to + be able to distinguish the events. + + """ + filters = {} + filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS) + if ARCH.exit_reasons: + filters['kvm_exit'] = ('exit_reason', ARCH.exit_reasons) + return filters def get_available_fields(self): """Returns a list of available event's of format 'event name(filter @@ -603,7 +533,7 @@ class TracepointProvider(object): """ path = os.path.join(PATH_DEBUGFS_TRACING, 'events', 'kvm') - fields = walkdir(path)[1] + fields = self.walkdir(path)[1] extra = [] for field in fields: if field in self.filters: @@ -613,6 +543,34 @@ class TracepointProvider(object): fields += extra return fields + def update_fields(self, fields_filter): + """Refresh fields, applying fields_filter""" + self._fields = [field for field in self.get_available_fields() + if self.is_field_wanted(fields_filter, field)] + + @staticmethod + def get_online_cpus(): + """Returns a list of cpu id integers.""" + def parse_int_list(list_string): + """Returns an int list from a string of comma separated integers and + integer ranges.""" + integers = [] + members = list_string.split(',') + + for member in members: + if '-' not in member: + integers.append(int(member)) + else: + int_range = member.split('-') + integers.extend(range(int(int_range[0]), + int(int_range[1]) + 1)) + + return integers + + with open('/sys/devices/system/cpu/online') as cpu_list: + cpu_string = cpu_list.readline() + return parse_int_list(cpu_string) + def setup_traces(self): """Creates all event and group objects needed to be able to retrieve data.""" @@ -621,9 +579,9 @@ class TracepointProvider(object): # Fetch list of all threads of the monitored pid, as qemu # starts a thread for each vcpu. path = os.path.join('/proc', str(self._pid), 'task') - groupids = walkdir(path)[1] + groupids = self.walkdir(path)[1] else: - groupids = get_online_cpus() + groupids = self.get_online_cpus() # The constant is needed as a buffer for python libs, std # streams and other files that the script opens. @@ -671,9 +629,6 @@ class TracepointProvider(object): self.group_leaders.append(group) - def available_fields(self): - return self.get_available_fields() - @property def fields(self): return self._fields @@ -707,7 +662,7 @@ class TracepointProvider(object): self.setup_traces() self.fields = self._fields - def read(self): + def read(self, by_guest=0): """Returns 'event name: current value' for all enabled events.""" ret = defaultdict(int) for group in self.group_leaders: @@ -723,16 +678,17 @@ class TracepointProvider(object): event.reset() -class DebugfsProvider(object): +class DebugfsProvider(Provider): """Provides data from the files that KVM creates in the kvm debugfs folder.""" - def __init__(self): - self._fields = self.get_available_fields() + def __init__(self, pid, fields_filter, include_past): + self.update_fields(fields_filter) self._baseline = {} - self._pid = 0 self.do_read = True self.paths = [] - self.reset() + self.pid = pid + if include_past: + self.restore() def get_available_fields(self): """"Returns a list of available fields. @@ -740,7 +696,12 @@ class DebugfsProvider(object): The fields are all available KVM debugfs files """ - return walkdir(PATH_DEBUGFS_KVM)[2] + return self.walkdir(PATH_DEBUGFS_KVM)[2] + + def update_fields(self, fields_filter): + """Refresh fields, applying fields_filter""" + self._fields = [field for field in self.get_available_fields() + if self.is_field_wanted(fields_filter, field)] @property def fields(self): @@ -757,10 +718,9 @@ class DebugfsProvider(object): @pid.setter def pid(self, pid): + self._pid = pid if pid != 0: - self._pid = pid - - vms = walkdir(PATH_DEBUGFS_KVM)[1] + vms = self.walkdir(PATH_DEBUGFS_KVM)[1] if len(vms) == 0: self.do_read = False @@ -771,8 +731,15 @@ class DebugfsProvider(object): self.do_read = True self.reset() - def read(self, reset=0): - """Returns a dict with format:'file name / field -> current value'.""" + def read(self, reset=0, by_guest=0): + """Returns a dict with format:'file name / field -> current value'. + + Parameter 'reset': + 0 plain read + 1 reset field counts to 0 + 2 restore the original field counts + + """ results = {} # If no debugfs filtering support is available, then don't read. @@ -789,12 +756,22 @@ class DebugfsProvider(object): for field in self._fields: value = self.read_field(field, path) key = path + field - if reset: + if reset == 1: self._baseline[key] = value + if reset == 2: + self._baseline[key] = 0 if self._baseline.get(key, -1) == -1: self._baseline[key] = value - results[field] = (results.get(field, 0) + value - - self._baseline.get(key, 0)) + increment = (results.get(field, 0) + value - + self._baseline.get(key, 0)) + if by_guest: + pid = key.split('-')[0] + if pid in results: + results[pid] += increment + else: + results[pid] = increment + else: + results[field] = increment return results @@ -813,6 +790,11 @@ class DebugfsProvider(object): self._baseline = {} self.read(1) + def restore(self): + """Reset field counters""" + self._baseline = {} + self.read(2) + class Stats(object): """Manages the data providers and the data they provide. @@ -821,33 +803,32 @@ class Stats(object): provider data. """ - def __init__(self, providers, pid, fields=None): - self.providers = providers - self._pid_filter = pid - self._fields_filter = fields + def __init__(self, options): + self.providers = self.get_providers(options) + self._pid_filter = options.pid + self._fields_filter = options.fields self.values = {} - self.update_provider_pid() - self.update_provider_filters() + + @staticmethod + def get_providers(options): + """Returns a list of data providers depending on the passed options.""" + providers = [] + + if options.debugfs: + providers.append(DebugfsProvider(options.pid, options.fields, + options.dbgfs_include_past)) + if options.tracepoints or not providers: + providers.append(TracepointProvider(options.pid, options.fields)) + + return providers def update_provider_filters(self): """Propagates fields filters to providers.""" - def wanted(key): - if not self._fields_filter: - return True - return re.match(self._fields_filter, key) is not None - # As we reset the counters when updating the fields we can # also clear the cache of old values. self.values = {} for provider in self.providers: - provider_fields = [key for key in provider.get_available_fields() - if wanted(key)] - provider.fields = provider_fields - - def update_provider_pid(self): - """Propagates pid filters to providers.""" - for provider in self.providers: - provider.pid = self._pid_filter + provider.update_fields(self._fields_filter) def reset(self): self.values = {} @@ -873,27 +854,52 @@ class Stats(object): if pid != self._pid_filter: self._pid_filter = pid self.values = {} - self.update_provider_pid() + for provider in self.providers: + provider.pid = self._pid_filter - def get(self): + def get(self, by_guest=0): """Returns a dict with field -> (value, delta to last value) of all provider data.""" for provider in self.providers: - new = provider.read() - for key in provider.fields: + new = provider.read(by_guest=by_guest) + for key in new if by_guest else provider.fields: oldval = self.values.get(key, (0, 0))[0] newval = new.get(key, 0) newdelta = newval - oldval self.values[key] = (newval, newdelta) return self.values -LABEL_WIDTH = 40 -NUMBER_WIDTH = 10 -DELAY_INITIAL = 0.25 -DELAY_REGULAR = 3.0 + def toggle_display_guests(self, to_pid): + """Toggle between collection of stats by individual event and by + guest pid + + Events reported by DebugfsProvider change when switching to/from + reading by guest values. Hence we have to remove the excess event + names from self.values. + + """ + if any(isinstance(ins, TracepointProvider) for ins in self.providers): + return 1 + if to_pid: + for provider in self.providers: + if isinstance(provider, DebugfsProvider): + for key in provider.fields: + if key in self.values.keys(): + del self.values[key] + else: + oldvals = self.values.copy() + for key in oldvals: + if key.isdigit(): + del self.values[key] + # Update oldval (see get()) + self.get(to_pid) + return 0 + +DELAY_DEFAULT = 3.0 MAX_GUEST_NAME_LEN = 48 MAX_REGEX_LEN = 44 DEFAULT_REGEX = r'^[^\(]*$' +SORT_DEFAULT = 0 class Tui(object): @@ -901,7 +907,10 @@ class Tui(object): def __init__(self, stats): self.stats = stats self.screen = None - self.update_drilldown() + self._delay_initial = 0.25 + self._delay_regular = DELAY_DEFAULT + self._sorting = SORT_DEFAULT + self._display_guests = 0 def __enter__(self): """Initialises curses for later use. Based on curses.wrapper @@ -929,7 +938,7 @@ class Tui(object): return self def __exit__(self, *exception): - """Resets the terminal to its normal state. Based on curses.wrappre + """Resets the terminal to its normal state. Based on curses.wrapper implementation from the Python standard library.""" if self.screen: self.screen.keypad(0) @@ -937,6 +946,86 @@ class Tui(object): curses.nocbreak() curses.endwin() + def get_all_gnames(self): + """Returns a list of (pid, gname) tuples of all running guests""" + res = [] + try: + child = subprocess.Popen(['ps', '-A', '--format', 'pid,args'], + stdout=subprocess.PIPE) + except: + raise Exception + for line in child.stdout: + line = line.lstrip().split(' ', 1) + # perform a sanity check before calling the more expensive + # function to possibly extract the guest name + if ' -name ' in line[1]: + res.append((line[0], self.get_gname_from_pid(line[0]))) + child.stdout.close() + + return res + + def print_all_gnames(self, row): + """Print a list of all running guests along with their pids.""" + self.screen.addstr(row, 2, '%8s %-60s' % + ('Pid', 'Guest Name (fuzzy list, might be ' + 'inaccurate!)'), + curses.A_UNDERLINE) + row += 1 + try: + for line in self.get_all_gnames(): + self.screen.addstr(row, 2, '%8s %-60s' % (line[0], line[1])) + row += 1 + if row >= self.screen.getmaxyx()[0]: + break + except Exception: + self.screen.addstr(row + 1, 2, 'Not available') + + def get_pid_from_gname(self, gname): + """Fuzzy function to convert guest name to QEMU process pid. + + Returns a list of potential pids, can be empty if no match found. + Throws an exception on processing errors. + + """ + pids = [] + for line in self.get_all_gnames(): + if gname == line[1]: + pids.append(int(line[0])) + + return pids + + @staticmethod + def get_gname_from_pid(pid): + """Returns the guest name for a QEMU process pid. + + Extracts the guest name from the QEMU comma line by processing the + '-name' option. Will also handle names specified out of sequence. + + """ + name = '' + try: + line = open('/proc/{}/cmdline' + .format(pid), 'rb').read().split('\0') + parms = line[line.index('-name') + 1].split(',') + while '' in parms: + # commas are escaped (i.e. ',,'), hence e.g. 'foo,bar' results + # in # ['foo', '', 'bar'], which we revert here + idx = parms.index('') + parms[idx - 1] += ',' + parms[idx + 1] + del parms[idx:idx+2] + # the '-name' switch allows for two ways to specify the guest name, + # where the plain name overrides the name specified via 'guest=' + for arg in parms: + if '=' not in arg: + name = arg + break + if arg[:6] == 'guest=': + name = arg[6:] + except (ValueError, IOError, IndexError): + pass + + return name + def update_drilldown(self): """Sets or removes a filter that only allows fields without braces.""" if not self.stats.fields_filter: @@ -954,7 +1043,7 @@ class Tui(object): if pid is None: pid = self.stats.pid_filter self.screen.erase() - gname = get_gname_from_pid(pid) + gname = self.get_gname_from_pid(pid) if gname: gname = ('({})'.format(gname[:MAX_GUEST_NAME_LEN] + '...' if len(gname) > MAX_GUEST_NAME_LEN @@ -970,13 +1059,13 @@ class Tui(object): if len(regex) > MAX_REGEX_LEN: regex = regex[:MAX_REGEX_LEN] + '...' self.screen.addstr(1, 17, 'regex filter: {0}'.format(regex)) - self.screen.addstr(2, 1, 'Event') - self.screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH - - len('Total'), 'Total') - self.screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH + 7 - - len('%Total'), '%Total') - self.screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH + 7 + 8 - - len('Current'), 'Current') + if self._display_guests: + col_name = 'Guest Name' + else: + col_name = 'Event' + self.screen.addstr(2, 1, '%-40s %10s%7s %8s' % + (col_name, 'Total', '%Total', 'CurAvg/s'), + curses.A_STANDOUT) self.screen.addstr(4, 1, 'Collecting data...') self.screen.refresh() @@ -984,16 +1073,25 @@ class Tui(object): row = 3 self.screen.move(row, 0) self.screen.clrtobot() - stats = self.stats.get() + stats = self.stats.get(self._display_guests) - def sortkey(x): + def sortCurAvg(x): + # sort by current events if available if stats[x][1]: return (-stats[x][1], -stats[x][0]) else: return (0, -stats[x][0]) + + def sortTotal(x): + # sort by totals + return (0, -stats[x][0]) total = 0. for val in stats.values(): total += val[0] + if self._sorting == SORT_DEFAULT: + sortkey = sortCurAvg + else: + sortkey = sortTotal for key in sorted(stats.keys(), key=sortkey): if row >= self.screen.getmaxyx()[0]: @@ -1001,18 +1099,61 @@ class Tui(object): values = stats[key] if not values[0] and not values[1]: break - col = 1 - self.screen.addstr(row, col, key) - col += LABEL_WIDTH - self.screen.addstr(row, col, '%10d' % (values[0],)) - col += NUMBER_WIDTH - self.screen.addstr(row, col, '%7.1f' % (values[0] * 100 / total,)) - col += 7 - if values[1] is not None: - self.screen.addstr(row, col, '%8d' % (values[1] / sleeptime,)) + if values[0] is not None: + cur = int(round(values[1] / sleeptime)) if values[1] else '' + if self._display_guests: + key = self.get_gname_from_pid(key) + self.screen.addstr(row, 1, '%-40s %10d%7.1f %8s' % + (key, values[0], values[0] * 100 / total, + cur)) row += 1 + if row == 3: + self.screen.addstr(4, 1, 'No matching events reported yet') self.screen.refresh() + def show_msg(self, text): + """Display message centered text and exit on key press""" + hint = 'Press any key to continue' + curses.cbreak() + self.screen.erase() + (x, term_width) = self.screen.getmaxyx() + row = 2 + for line in text: + start = (term_width - len(line)) / 2 + self.screen.addstr(row, start, line) + row += 1 + self.screen.addstr(row + 1, (term_width - len(hint)) / 2, hint, + curses.A_STANDOUT) + self.screen.getkey() + + def show_help_interactive(self): + """Display help with list of interactive commands""" + msg = (' b toggle events by guests (debugfs only, honors' + ' filters)', + ' c clear filter', + ' f filter by regular expression', + ' g filter by guest name', + ' h display interactive commands reference', + ' o toggle sorting order (Total vs CurAvg/s)', + ' p filter by PID', + ' q quit', + ' r reset stats', + ' s set update interval', + ' x toggle reporting of stats for individual child trace' + ' events', + 'Any other key refreshes statistics immediately') + curses.cbreak() + self.screen.erase() + self.screen.addstr(0, 0, "Interactive commands reference", + curses.A_BOLD) + self.screen.addstr(2, 0, "Press any key to exit", curses.A_STANDOUT) + row = 4 + for line in msg: + self.screen.addstr(row, 0, line) + row += 1 + self.screen.getkey() + self.refresh_header() + def show_filter_selection(self): """Draws filter selection mask. @@ -1059,6 +1200,7 @@ class Tui(object): 'This might limit the shown data to the trace ' 'statistics.') self.screen.addstr(5, 0, msg) + self.print_all_gnames(7) curses.echo() self.screen.addstr(3, 0, "Pid [0 or pid]: ") @@ -1077,10 +1219,40 @@ class Tui(object): self.refresh_header(pid) self.update_pid(pid) break - except ValueError: msg = '"' + str(pid) + '": Not a valid pid' - continue + + def show_set_update_interval(self): + """Draws update interval selection mask.""" + msg = '' + while True: + self.screen.erase() + self.screen.addstr(0, 0, 'Set update interval (defaults to %fs).' % + DELAY_DEFAULT, curses.A_BOLD) + self.screen.addstr(4, 0, msg) + self.screen.addstr(2, 0, 'Change delay from %.1fs to ' % + self._delay_regular) + curses.echo() + val = self.screen.getstr() + curses.noecho() + + try: + if len(val) > 0: + delay = float(val) + if delay < 0.1: + msg = '"' + str(val) + '": Value must be >=0.1' + continue + if delay > 25.5: + msg = '"' + str(val) + '": Value must be <=25.5' + continue + else: + delay = DELAY_DEFAULT + self._delay_regular = delay + break + + except ValueError: + msg = '"' + str(val) + '": Invalid value' + self.refresh_header() def show_vm_selection_by_guest_name(self): """Draws guest selection mask. @@ -1098,6 +1270,7 @@ class Tui(object): 'This might limit the shown data to the trace ' 'statistics.') self.screen.addstr(5, 0, msg) + self.print_all_gnames(7) curses.echo() self.screen.addstr(3, 0, "Guest [ENTER or guest]: ") gname = self.screen.getstr() @@ -1110,7 +1283,7 @@ class Tui(object): else: pids = [] try: - pids = get_pid_from_gname(gname) + pids = self.get_pid_from_gname(gname) except: msg = '"' + gname + '": Internal error while searching, ' \ 'use pid filter instead' @@ -1128,38 +1301,60 @@ class Tui(object): def show_stats(self): """Refreshes the screen and processes user input.""" - sleeptime = DELAY_INITIAL + sleeptime = self._delay_initial self.refresh_header() + start = 0.0 # result based on init value never appears on screen while True: - self.refresh_body(sleeptime) + self.refresh_body(time.time() - start) curses.halfdelay(int(sleeptime * 10)) - sleeptime = DELAY_REGULAR + start = time.time() + sleeptime = self._delay_regular try: char = self.screen.getkey() - if char == 'x': + if char == 'b': + self._display_guests = not self._display_guests + if self.stats.toggle_display_guests(self._display_guests): + self.show_msg(['Command not available with tracepoints' + ' enabled', 'Restart with debugfs only ' + '(see option \'-d\') and try again!']) + self._display_guests = not self._display_guests self.refresh_header() - self.update_drilldown() - sleeptime = DELAY_INITIAL - if char == 'q': - break if char == 'c': self.stats.fields_filter = DEFAULT_REGEX self.refresh_header(0) self.update_pid(0) - sleeptime = DELAY_INITIAL if char == 'f': + curses.curs_set(1) self.show_filter_selection() - sleeptime = DELAY_INITIAL + curses.curs_set(0) + sleeptime = self._delay_initial if char == 'g': + curses.curs_set(1) self.show_vm_selection_by_guest_name() - sleeptime = DELAY_INITIAL + curses.curs_set(0) + sleeptime = self._delay_initial + if char == 'h': + self.show_help_interactive() + if char == 'o': + self._sorting = not self._sorting if char == 'p': + curses.curs_set(1) self.show_vm_selection_by_pid() - sleeptime = DELAY_INITIAL + curses.curs_set(0) + sleeptime = self._delay_initial + if char == 'q': + break if char == 'r': - self.refresh_header() self.stats.reset() - sleeptime = DELAY_INITIAL + if char == 's': + curses.curs_set(1) + self.show_set_update_interval() + curses.curs_set(0) + sleeptime = self._delay_initial + if char == 'x': + self.update_drilldown() + # prevents display of current values on next refresh + self.stats.get() except KeyboardInterrupt: break except curses.error: @@ -1227,13 +1422,17 @@ Requirements: the large number of files that are possibly opened. Interactive Commands: + b toggle events by guests (debugfs only, honors filters) c clear filter f filter by regular expression g filter by guest name + h display interactive commands reference + o toggle sorting order (Total vs CurAvg/s) p filter by PID q quit - x toggle reporting of stats for individual child trace events r reset stats + s set update interval + x toggle reporting of stats for individual child trace events Press any other key to refresh statistics immediately. """ @@ -1246,7 +1445,7 @@ Press any other key to refresh statistics immediately. def cb_guest_to_pid(option, opt, val, parser): try: - pids = get_pid_from_gname(val) + pids = Tui.get_pid_from_gname(val) except: raise optparse.OptionValueError('Error while searching for guest ' '"{}", use "-p" to specify a pid ' @@ -1268,6 +1467,13 @@ Press any other key to refresh statistics immediately. dest='once', help='run in batch mode for one second', ) + optparser.add_option('-i', '--debugfs-include-past', + action='store_true', + default=False, + dest='dbgfs_include_past', + help='include all available data on past events for ' + 'debugfs', + ) optparser.add_option('-l', '--log', action='store_true', default=False, @@ -1288,7 +1494,7 @@ Press any other key to refresh statistics immediately. ) optparser.add_option('-f', '--fields', action='store', - default=None, + default=DEFAULT_REGEX, dest='fields', help='fields to display (regex)', ) @@ -1311,20 +1517,6 @@ Press any other key to refresh statistics immediately. return options -def get_providers(options): - """Returns a list of data providers depending on the passed options.""" - providers = [] - - if options.tracepoints: - providers.append(TracepointProvider()) - if options.debugfs: - providers.append(DebugfsProvider()) - if len(providers) == 0: - providers.append(TracepointProvider()) - - return providers - - def check_access(options): """Exits if the current user can't access all needed directories.""" if not os.path.exists('/sys/kernel/debug'): @@ -1365,8 +1557,7 @@ def main(): sys.stderr.write('Did you use a (unsupported) tid instead of a pid?\n') sys.exit('Specified pid does not exist.') - providers = get_providers(options) - stats = Stats(providers, options.pid, fields=options.fields) + stats = Stats(options) if options.log: log(stats) diff --git a/tools/kvm/kvm_stat/kvm_stat.txt b/tools/kvm/kvm_stat/kvm_stat.txt index 109431bdc63c..e5cf836be8a1 100644 --- a/tools/kvm/kvm_stat/kvm_stat.txt +++ b/tools/kvm/kvm_stat/kvm_stat.txt @@ -29,18 +29,26 @@ meaning of events. INTERACTIVE COMMANDS -------------------- [horizontal] +*b*:: toggle events by guests (debugfs only, honors filters) + *c*:: clear filter *f*:: filter by regular expression *g*:: filter by guest name +*h*:: display interactive commands reference + +*o*:: toggle sorting order (Total vs CurAvg/s) + *p*:: filter by PID *q*:: quit *r*:: reset stats +*s*:: set update interval + *x*:: toggle reporting of stats for child trace events Press any other key to refresh statistics immediately. @@ -64,6 +72,10 @@ OPTIONS --debugfs:: retrieve statistics from debugfs +-i:: +--debugfs-include-past:: + include all available data on past events for debugfs + -p<pid>:: --pid=<pid>:: limit statistics to one virtual machine (pid) diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 6e178987af8e..7e0405e1651d 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -257,3 +257,71 @@ int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size, *duration = attr.test.duration; return ret; } + +int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id) +{ + union bpf_attr attr; + int err; + + bzero(&attr, sizeof(attr)); + attr.start_id = start_id; + + err = sys_bpf(BPF_PROG_GET_NEXT_ID, &attr, sizeof(attr)); + if (!err) + *next_id = attr.next_id; + + return err; +} + +int bpf_map_get_next_id(__u32 start_id, __u32 *next_id) +{ + union bpf_attr attr; + int err; + + bzero(&attr, sizeof(attr)); + attr.start_id = start_id; + + err = sys_bpf(BPF_MAP_GET_NEXT_ID, &attr, sizeof(attr)); + if (!err) + *next_id = attr.next_id; + + return err; +} + +int bpf_prog_get_fd_by_id(__u32 id) +{ + union bpf_attr attr; + + bzero(&attr, sizeof(attr)); + attr.prog_id = id; + + return sys_bpf(BPF_PROG_GET_FD_BY_ID, &attr, sizeof(attr)); +} + +int bpf_map_get_fd_by_id(__u32 id) +{ + union bpf_attr attr; + + bzero(&attr, sizeof(attr)); + attr.map_id = id; + + return sys_bpf(BPF_MAP_GET_FD_BY_ID, &attr, sizeof(attr)); +} + +int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len) +{ + union bpf_attr attr; + int err; + + bzero(&attr, sizeof(attr)); + bzero(info, *info_len); + attr.info.bpf_fd = prog_fd; + attr.info.info_len = *info_len; + attr.info.info = ptr_to_u64(info); + + err = sys_bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)); + if (!err) + *info_len = attr.info.info_len; + + return err; +} diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 972bd8333eb7..16de44a14b48 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -54,5 +54,10 @@ int bpf_prog_detach(int attachable_fd, enum bpf_attach_type type); int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size, void *data_out, __u32 *size_out, __u32 *retval, __u32 *duration); +int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id); +int bpf_map_get_next_id(__u32 start_id, __u32 *next_id); +int bpf_prog_get_fd_by_id(__u32 id); +int bpf_map_get_fd_by_id(__u32 id); +int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len); #endif diff --git a/tools/net/bpf_jit_disasm.c b/tools/net/bpf_jit_disasm.c index ad572e6cdbd0..422d9abd666a 100644 --- a/tools/net/bpf_jit_disasm.c +++ b/tools/net/bpf_jit_disasm.c @@ -159,8 +159,8 @@ static void put_log_buff(char *buff) free(buff); } -static unsigned int get_last_jit_image(char *haystack, size_t hlen, - uint8_t *image, size_t ilen) +static uint8_t *get_last_jit_image(char *haystack, size_t hlen, + unsigned int *ilen) { char *ptr, *pptr, *tmp; off_t off = 0; @@ -168,9 +168,10 @@ static unsigned int get_last_jit_image(char *haystack, size_t hlen, regmatch_t pmatch[1]; unsigned long base; regex_t regex; + uint8_t *image; if (hlen == 0) - return 0; + return NULL; ret = regcomp(®ex, "flen=[[:alnum:]]+ proglen=[[:digit:]]+ " "pass=[[:digit:]]+ image=[[:xdigit:]]+", REG_EXTENDED); @@ -194,11 +195,22 @@ static unsigned int get_last_jit_image(char *haystack, size_t hlen, &flen, &proglen, &pass, &base); if (ret != 4) { regfree(®ex); - return 0; + return NULL; + } + if (proglen > 1000000) { + printf("proglen of %d too big, stopping\n", proglen); + return NULL; } + image = malloc(proglen); + if (!image) { + printf("Out of memory\n"); + return NULL; + } + memset(image, 0, proglen); + tmp = ptr = haystack + off; - while ((ptr = strtok(tmp, "\n")) != NULL && ulen < ilen) { + while ((ptr = strtok(tmp, "\n")) != NULL && ulen < proglen) { tmp = NULL; if (!strstr(ptr, "JIT code")) continue; @@ -208,10 +220,12 @@ static unsigned int get_last_jit_image(char *haystack, size_t hlen, ptr = pptr; do { image[ulen++] = (uint8_t) strtoul(pptr, &pptr, 16); - if (ptr == pptr || ulen >= ilen) { + if (ptr == pptr) { ulen--; break; } + if (ulen >= proglen) + break; ptr = pptr; } while (1); } @@ -222,7 +236,8 @@ static unsigned int get_last_jit_image(char *haystack, size_t hlen, printf("%lx + <x>:\n", base); regfree(®ex); - return ulen; + *ilen = ulen; + return image; } static void usage(void) @@ -237,12 +252,12 @@ static void usage(void) int main(int argc, char **argv) { unsigned int len, klen, opt, opcodes = 0; - static uint8_t image[32768]; char *kbuff, *file = NULL; char *ofile = NULL; int ofd; ssize_t nr; uint8_t *pos; + uint8_t *image = NULL; while ((opt = getopt(argc, argv, "of:O:")) != -1) { switch (opt) { @@ -262,7 +277,6 @@ int main(int argc, char **argv) } bfd_init(); - memset(image, 0, sizeof(image)); kbuff = get_log_buff(file, &klen); if (!kbuff) { @@ -270,8 +284,8 @@ int main(int argc, char **argv) return -1; } - len = get_last_jit_image(kbuff, klen, image, sizeof(image)); - if (len <= 0) { + image = get_last_jit_image(kbuff, klen, &len); + if (!image) { fprintf(stderr, "No JIT image found!\n"); goto done; } @@ -301,5 +315,6 @@ int main(int argc, char **argv) done: put_log_buff(kbuff); + free(image); return 0; } diff --git a/tools/objtool/check.c b/tools/objtool/check.c index fea222192c57..2c6d74880403 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1371,6 +1371,12 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, func = insn->func; + if (func && insn->ignore) { + WARN_FUNC("BUG: why am I validating an ignored function?", + sec, insn->offset); + return -1; + } + if (insn->visited) { if (!!insn_state_match(insn, &state)) return 1; @@ -1426,16 +1432,19 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, case INSN_JUMP_CONDITIONAL: case INSN_JUMP_UNCONDITIONAL: - if (insn->jump_dest) { + if (insn->jump_dest && + (!func || !insn->jump_dest->func || + func == insn->jump_dest->func)) { ret = validate_branch(file, insn->jump_dest, state); if (ret) return 1; + } else if (func && has_modified_stack_frame(&state)) { WARN_FUNC("sibling call from callable instruction with modified stack frame", sec, insn->offset); return 1; - } /* else it's a sibling call */ + } if (insn->type == INSN_JUMP_UNCONDITIONAL) return 0; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 6f4882f8d61f..87b431886670 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -273,6 +273,7 @@ struct perf_evsel *perf_evsel__new_cycles(void) struct perf_event_attr attr = { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES, + .exclude_kernel = 1, }; struct perf_evsel *evsel; diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index 6d542a4e0648..8aef572d0889 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -50,7 +50,7 @@ int unwind__prepare_access(struct thread *thread, struct map *map, if (!ops) { pr_err("unwind: target platform=%s is not supported\n", arch); - return -1; + return 0; } out_register: unwind__register_ops(thread, ops); diff --git a/tools/power/acpi/os_specific/service_layers/osunixxf.c b/tools/power/acpi/os_specific/service_layers/osunixxf.c index c04e8fea2c60..025c1b07049d 100644 --- a/tools/power/acpi/os_specific/service_layers/osunixxf.c +++ b/tools/power/acpi/os_specific/service_layers/osunixxf.c @@ -750,9 +750,9 @@ acpi_os_wait_semaphore(acpi_handle handle, u32 units, u16 msec_timeout) { acpi_status status = AE_OK; sem_t *sem = (sem_t *) handle; + int ret_val; #ifndef ACPI_USE_ALTERNATE_TIMEOUT struct timespec time; - int ret_val; #endif if (!sem) { @@ -778,7 +778,10 @@ acpi_os_wait_semaphore(acpi_handle handle, u32 units, u16 msec_timeout) case ACPI_WAIT_FOREVER: - if (sem_wait(sem)) { + while (((ret_val = sem_wait(sem)) == -1) && (errno == EINTR)) { + continue; /* Restart if interrupted */ + } + if (ret_val != 0) { status = (AE_TIME); } break; @@ -831,7 +834,8 @@ acpi_os_wait_semaphore(acpi_handle handle, u32 units, u16 msec_timeout) while (((ret_val = sem_timedwait(sem, &time)) == -1) && (errno == EINTR)) { - continue; + continue; /* Restart if interrupted */ + } if (ret_val != 0) { diff --git a/tools/scripts/Makefile.include b/tools/scripts/Makefile.include index fc74db62fef4..ccad8ce925e4 100644 --- a/tools/scripts/Makefile.include +++ b/tools/scripts/Makefile.include @@ -58,15 +58,9 @@ else NO_SUBDIR = : endif -ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4 -ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),) +ifneq ($(findstring s,$(filter-out --%,$(MAKEFLAGS))),) silent=1 endif -else # make-3.8x -ifneq ($(filter s% -s%,$(MAKEFLAGS)),) - silent=1 -endif -endif # # Define a callable command for descending to a new directory diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index 28859da78edf..4c2fa98ef39d 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c @@ -1943,7 +1943,7 @@ static __init int nfit_test_init(void) nfit_test->setup = nfit_test0_setup; break; case 1: - nfit_test->num_pm = 1; + nfit_test->num_pm = 2; nfit_test->dcr_idx = NUM_DCR; nfit_test->num_dcr = 2; nfit_test->alloc = nfit_test1_alloc; diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index f389b02d43a0..2ca51a8a588c 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -14,7 +14,8 @@ LDLIBS += -lcap -lelf TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ test_align -TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o +TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \ + test_pkt_md_access.o TEST_PROGS := test_kmod.sh diff --git a/tools/testing/selftests/bpf/test_align.c b/tools/testing/selftests/bpf/test_align.c index 9644d4e069de..bccebd935907 100644 --- a/tools/testing/selftests/bpf/test_align.c +++ b/tools/testing/selftests/bpf/test_align.c @@ -9,6 +9,8 @@ #include <stddef.h> #include <stdbool.h> +#include <sys/resource.h> + #include <linux/unistd.h> #include <linux/filter.h> #include <linux/bpf_perf_event.h> @@ -426,12 +428,15 @@ static int do_test(unsigned int from, unsigned int to) } printf("Results: %d pass %d fail\n", all_pass, all_fail); - return 0; + return all_fail ? EXIT_FAILURE : EXIT_SUCCESS; } int main(int argc, char **argv) { unsigned int from = 0, to = ARRAY_SIZE(tests); + struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; + + setrlimit(RLIMIT_MEMLOCK, &rinf); if (argc == 3) { unsigned int l = atoi(argv[argc - 2]); diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index 93314524de0d..79601c81e169 100644 --- a/tools/testing/selftests/bpf/test_maps.c +++ b/tools/testing/selftests/bpf/test_maps.c @@ -239,6 +239,54 @@ static void test_hashmap_percpu(int task, void *data) close(fd); } +static void test_hashmap_walk(int task, void *data) +{ + int fd, i, max_entries = 100000; + long long key, value, next_key; + bool next_key_valid = true; + + fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), + max_entries, map_flags); + if (fd < 0) { + printf("Failed to create hashmap '%s'!\n", strerror(errno)); + exit(1); + } + + for (i = 0; i < max_entries; i++) { + key = i; value = key; + assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == 0); + } + + for (i = 0; bpf_map_get_next_key(fd, !i ? NULL : &key, + &next_key) == 0; i++) { + key = next_key; + assert(bpf_map_lookup_elem(fd, &key, &value) == 0); + } + + assert(i == max_entries); + + assert(bpf_map_get_next_key(fd, NULL, &key) == 0); + for (i = 0; next_key_valid; i++) { + next_key_valid = bpf_map_get_next_key(fd, &key, &next_key) == 0; + assert(bpf_map_lookup_elem(fd, &key, &value) == 0); + value++; + assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) == 0); + key = next_key; + } + + assert(i == max_entries); + + for (i = 0; bpf_map_get_next_key(fd, !i ? NULL : &key, + &next_key) == 0; i++) { + key = next_key; + assert(bpf_map_lookup_elem(fd, &key, &value) == 0); + assert(value - 1 == key); + } + + assert(i == max_entries); + close(fd); +} + static void test_arraymap(int task, void *data) { int key, next_key, fd; @@ -464,6 +512,7 @@ static void test_map_stress(void) run_parallel(100, test_hashmap, NULL); run_parallel(100, test_hashmap_percpu, NULL); run_parallel(100, test_hashmap_sizes, NULL); + run_parallel(100, test_hashmap_walk, NULL); run_parallel(100, test_arraymap, NULL); run_parallel(100, test_arraymap_percpu, NULL); @@ -549,6 +598,7 @@ static void run_all_tests(void) { test_hashmap(0, NULL); test_hashmap_percpu(0, NULL); + test_hashmap_walk(0, NULL); test_arraymap(0, NULL); test_arraymap_percpu(0, NULL); diff --git a/tools/testing/selftests/bpf/test_obj_id.c b/tools/testing/selftests/bpf/test_obj_id.c new file mode 100644 index 000000000000..880d2963b472 --- /dev/null +++ b/tools/testing/selftests/bpf/test_obj_id.c @@ -0,0 +1,35 @@ +/* Copyright (c) 2017 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#include <stddef.h> +#include <linux/bpf.h> +#include <linux/pkt_cls.h> +#include "bpf_helpers.h" + +/* It is a dumb bpf program such that it must have no + * issue to be loaded since testing the verifier is + * not the focus here. + */ + +int _version SEC("version") = 1; + +struct bpf_map_def SEC("maps") test_map_id = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(__u32), + .value_size = sizeof(__u64), + .max_entries = 1, +}; + +SEC("test_obj_id_dummy") +int test_obj_id(struct __sk_buff *skb) +{ + __u32 key = 0; + __u64 *value; + + value = bpf_map_lookup_elem(&test_map_id, &key); + + return TC_ACT_OK; +} diff --git a/tools/testing/selftests/bpf/test_pkt_md_access.c b/tools/testing/selftests/bpf/test_pkt_md_access.c new file mode 100644 index 000000000000..71729d47eb85 --- /dev/null +++ b/tools/testing/selftests/bpf/test_pkt_md_access.c @@ -0,0 +1,35 @@ +/* Copyright (c) 2017 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#include <stddef.h> +#include <string.h> +#include <linux/bpf.h> +#include <linux/pkt_cls.h> +#include "bpf_helpers.h" + +int _version SEC("version") = 1; + +#define TEST_FIELD(TYPE, FIELD, MASK) \ + { \ + TYPE tmp = *(volatile TYPE *)&skb->FIELD; \ + if (tmp != ((*(volatile __u32 *)&skb->FIELD) & MASK)) \ + return TC_ACT_SHOT; \ + } + +SEC("test1") +int process(struct __sk_buff *skb) +{ + TEST_FIELD(__u8, len, 0xFF); + TEST_FIELD(__u16, len, 0xFFFF); + TEST_FIELD(__u32, len, 0xFFFFFFFF); + TEST_FIELD(__u16, protocol, 0xFFFF); + TEST_FIELD(__u32, protocol, 0xFFFFFFFF); + TEST_FIELD(__u8, hash, 0xFF); + TEST_FIELD(__u16, hash, 0xFFFF); + TEST_FIELD(__u32, hash, 0xFFFFFFFF); + + return TC_ACT_OK; +} diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c index b59f5ed4ae40..5855cd3d3d45 100644 --- a/tools/testing/selftests/bpf/test_progs.c +++ b/tools/testing/selftests/bpf/test_progs.c @@ -22,6 +22,8 @@ typedef __u16 __sum16; #include <sys/wait.h> #include <sys/resource.h> +#include <sys/types.h> +#include <fcntl.h> #include <linux/bpf.h> #include <linux/err.h> @@ -70,6 +72,7 @@ static struct { pass_cnt++; \ printf("%s:PASS:%s %d nsec\n", __func__, tag, duration);\ } \ + __ret; \ }) static int bpf_prog_load(const char *file, enum bpf_prog_type type, @@ -283,6 +286,224 @@ static void test_tcp_estats(void) bpf_object__close(obj); } +static inline __u64 ptr_to_u64(const void *ptr) +{ + return (__u64) (unsigned long) ptr; +} + +static void test_bpf_obj_id(void) +{ + const __u64 array_magic_value = 0xfaceb00c; + const __u32 array_key = 0; + const int nr_iters = 2; + const char *file = "./test_obj_id.o"; + const char *jit_sysctl = "/proc/sys/net/core/bpf_jit_enable"; + + struct bpf_object *objs[nr_iters]; + int prog_fds[nr_iters], map_fds[nr_iters]; + /* +1 to test for the info_len returned by kernel */ + struct bpf_prog_info prog_infos[nr_iters + 1]; + struct bpf_map_info map_infos[nr_iters + 1]; + char jited_insns[128], xlated_insns[128]; + __u32 i, next_id, info_len, nr_id_found, duration = 0; + int sysctl_fd, jit_enabled = 0, err = 0; + __u64 array_value; + + sysctl_fd = open(jit_sysctl, 0, O_RDONLY); + if (sysctl_fd != -1) { + char tmpc; + + if (read(sysctl_fd, &tmpc, sizeof(tmpc)) == 1) + jit_enabled = (tmpc != '0'); + close(sysctl_fd); + } + + err = bpf_prog_get_fd_by_id(0); + CHECK(err >= 0 || errno != ENOENT, + "get-fd-by-notexist-prog-id", "err %d errno %d\n", err, errno); + + err = bpf_map_get_fd_by_id(0); + CHECK(err >= 0 || errno != ENOENT, + "get-fd-by-notexist-map-id", "err %d errno %d\n", err, errno); + + for (i = 0; i < nr_iters; i++) + objs[i] = NULL; + + /* Check bpf_obj_get_info_by_fd() */ + for (i = 0; i < nr_iters; i++) { + err = bpf_prog_load(file, BPF_PROG_TYPE_SOCKET_FILTER, + &objs[i], &prog_fds[i]); + /* test_obj_id.o is a dumb prog. It should never fail + * to load. + */ + assert(!err); + + /* Check getting prog info */ + info_len = sizeof(struct bpf_prog_info) * 2; + prog_infos[i].jited_prog_insns = ptr_to_u64(jited_insns); + prog_infos[i].jited_prog_len = sizeof(jited_insns); + prog_infos[i].xlated_prog_insns = ptr_to_u64(xlated_insns); + prog_infos[i].xlated_prog_len = sizeof(xlated_insns); + err = bpf_obj_get_info_by_fd(prog_fds[i], &prog_infos[i], + &info_len); + if (CHECK(err || + prog_infos[i].type != BPF_PROG_TYPE_SOCKET_FILTER || + info_len != sizeof(struct bpf_prog_info) || + (jit_enabled && !prog_infos[i].jited_prog_len) || + !prog_infos[i].xlated_prog_len, + "get-prog-info(fd)", + "err %d errno %d i %d type %d(%d) info_len %u(%lu) jit_enabled %d jited_prog_len %u xlated_prog_len %u\n", + err, errno, i, + prog_infos[i].type, BPF_PROG_TYPE_SOCKET_FILTER, + info_len, sizeof(struct bpf_prog_info), + jit_enabled, + prog_infos[i].jited_prog_len, + prog_infos[i].xlated_prog_len)) + goto done; + + map_fds[i] = bpf_find_map(__func__, objs[i], "test_map_id"); + assert(map_fds[i] >= 0); + err = bpf_map_update_elem(map_fds[i], &array_key, + &array_magic_value, 0); + assert(!err); + + /* Check getting map info */ + info_len = sizeof(struct bpf_map_info) * 2; + err = bpf_obj_get_info_by_fd(map_fds[i], &map_infos[i], + &info_len); + if (CHECK(err || + map_infos[i].type != BPF_MAP_TYPE_ARRAY || + map_infos[i].key_size != sizeof(__u32) || + map_infos[i].value_size != sizeof(__u64) || + map_infos[i].max_entries != 1 || + map_infos[i].map_flags != 0 || + info_len != sizeof(struct bpf_map_info), + "get-map-info(fd)", + "err %d errno %d type %d(%d) info_len %u(%lu) key_size %u value_size %u max_entries %u map_flags %X\n", + err, errno, + map_infos[i].type, BPF_MAP_TYPE_ARRAY, + info_len, sizeof(struct bpf_map_info), + map_infos[i].key_size, + map_infos[i].value_size, + map_infos[i].max_entries, + map_infos[i].map_flags)) + goto done; + } + + /* Check bpf_prog_get_next_id() */ + nr_id_found = 0; + next_id = 0; + while (!bpf_prog_get_next_id(next_id, &next_id)) { + struct bpf_prog_info prog_info; + int prog_fd; + + info_len = sizeof(prog_info); + + prog_fd = bpf_prog_get_fd_by_id(next_id); + if (prog_fd < 0 && errno == ENOENT) + /* The bpf_prog is in the dead row */ + continue; + if (CHECK(prog_fd < 0, "get-prog-fd(next_id)", + "prog_fd %d next_id %d errno %d\n", + prog_fd, next_id, errno)) + break; + + for (i = 0; i < nr_iters; i++) + if (prog_infos[i].id == next_id) + break; + + if (i == nr_iters) + continue; + + nr_id_found++; + + err = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &info_len); + CHECK(err || info_len != sizeof(struct bpf_prog_info) || + memcmp(&prog_info, &prog_infos[i], info_len), + "get-prog-info(next_id->fd)", + "err %d errno %d info_len %u(%lu) memcmp %d\n", + err, errno, info_len, sizeof(struct bpf_prog_info), + memcmp(&prog_info, &prog_infos[i], info_len)); + + close(prog_fd); + } + CHECK(nr_id_found != nr_iters, + "check total prog id found by get_next_id", + "nr_id_found %u(%u)\n", + nr_id_found, nr_iters); + + /* Check bpf_map_get_next_id() */ + nr_id_found = 0; + next_id = 0; + while (!bpf_map_get_next_id(next_id, &next_id)) { + struct bpf_map_info map_info; + int map_fd; + + info_len = sizeof(map_info); + + map_fd = bpf_map_get_fd_by_id(next_id); + if (map_fd < 0 && errno == ENOENT) + /* The bpf_map is in the dead row */ + continue; + if (CHECK(map_fd < 0, "get-map-fd(next_id)", + "map_fd %d next_id %u errno %d\n", + map_fd, next_id, errno)) + break; + + for (i = 0; i < nr_iters; i++) + if (map_infos[i].id == next_id) + break; + + if (i == nr_iters) + continue; + + nr_id_found++; + + err = bpf_map_lookup_elem(map_fd, &array_key, &array_value); + assert(!err); + + err = bpf_obj_get_info_by_fd(map_fd, &map_info, &info_len); + CHECK(err || info_len != sizeof(struct bpf_map_info) || + memcmp(&map_info, &map_infos[i], info_len) || + array_value != array_magic_value, + "check get-map-info(next_id->fd)", + "err %d errno %d info_len %u(%lu) memcmp %d array_value %llu(%llu)\n", + err, errno, info_len, sizeof(struct bpf_map_info), + memcmp(&map_info, &map_infos[i], info_len), + array_value, array_magic_value); + + close(map_fd); + } + CHECK(nr_id_found != nr_iters, + "check total map id found by get_next_id", + "nr_id_found %u(%u)\n", + nr_id_found, nr_iters); + +done: + for (i = 0; i < nr_iters; i++) + bpf_object__close(objs[i]); +} + +static void test_pkt_md_access(void) +{ + const char *file = "./test_pkt_md_access.o"; + struct bpf_object *obj; + __u32 duration, retval; + int err, prog_fd; + + err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); + if (err) + return; + + err = bpf_prog_test_run(prog_fd, 10, &pkt_v4, sizeof(pkt_v4), + NULL, NULL, &retval, &duration); + CHECK(err || retval, "", + "err %d errno %d retval %d duration %d\n", + err, errno, retval, duration); + + bpf_object__close(obj); +} + int main(void) { struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; @@ -293,7 +514,9 @@ int main(void) test_xdp(); test_l4lb(); test_tcp_estats(); + test_bpf_obj_id(); + test_pkt_md_access(); printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt); - return 0; + return error_cnt ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 0ff8c55c0464..404aec520812 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -1073,44 +1073,75 @@ static struct bpf_test tests[] = { .result = ACCEPT, }, { - "check cb access: byte, oob 1", + "__sk_buff->hash, offset 0, byte store not permitted", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, - offsetof(struct __sk_buff, cb[4]) + 4), + offsetof(struct __sk_buff, hash)), BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", .result = REJECT, }, { - "check cb access: byte, oob 2", + "__sk_buff->tc_index, offset 3, byte store not permitted", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, - offsetof(struct __sk_buff, cb[0]) - 1), + offsetof(struct __sk_buff, tc_index) + 3), BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", .result = REJECT, }, { - "check cb access: byte, oob 3", + "check skb->hash byte load permitted", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), +#ifdef __LITTLE_ENDIAN BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, cb[4]) + 4), + offsetof(struct __sk_buff, hash)), +#else + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, hash) + 3), +#endif + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + }, + { + "check skb->hash byte load not permitted 1", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, hash) + 1), BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", .result = REJECT, }, { - "check cb access: byte, oob 4", + "check skb->hash byte load not permitted 2", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, cb[0]) - 1), + offsetof(struct __sk_buff, hash) + 2), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + }, + { + "check skb->hash byte load not permitted 3", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), +#ifdef __LITTLE_ENDIAN + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, hash) + 3), +#else + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, hash)), +#endif BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", @@ -1188,44 +1219,53 @@ static struct bpf_test tests[] = { .result = REJECT, }, { - "check cb access: half, oob 1", + "check __sk_buff->hash, offset 0, half store not permitted", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_0, - offsetof(struct __sk_buff, cb[4]) + 4), + offsetof(struct __sk_buff, hash)), BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", .result = REJECT, }, { - "check cb access: half, oob 2", + "check __sk_buff->tc_index, offset 2, half store not permitted", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_0, - offsetof(struct __sk_buff, cb[0]) - 2), + offsetof(struct __sk_buff, tc_index) + 2), BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", .result = REJECT, }, { - "check cb access: half, oob 3", + "check skb->hash half load permitted", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), +#ifdef __LITTLE_ENDIAN BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, cb[4]) + 4), + offsetof(struct __sk_buff, hash)), +#else + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, hash) + 2), +#endif BPF_EXIT_INSN(), }, - .errstr = "invalid bpf_context access", - .result = REJECT, + .result = ACCEPT, }, { - "check cb access: half, oob 4", + "check skb->hash half load not permitted", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), +#ifdef __LITTLE_ENDIAN + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, hash) + 2), +#else BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, cb[0]) - 2), + offsetof(struct __sk_buff, hash)), +#endif BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", @@ -1368,28 +1408,6 @@ static struct bpf_test tests[] = { "check cb access: double, oob 2", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, - offsetof(struct __sk_buff, cb[4]) + 8), - BPF_EXIT_INSN(), - }, - .errstr = "invalid bpf_context access", - .result = REJECT, - }, - { - "check cb access: double, oob 3", - .insns = { - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, - offsetof(struct __sk_buff, cb[0]) - 8), - BPF_EXIT_INSN(), - }, - .errstr = "invalid bpf_context access", - .result = REJECT, - }, - { - "check cb access: double, oob 4", - .insns = { - BPF_MOV64_IMM(BPF_REG_0, 0), BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, offsetof(struct __sk_buff, cb[4])), BPF_EXIT_INSN(), @@ -1398,22 +1416,22 @@ static struct bpf_test tests[] = { .result = REJECT, }, { - "check cb access: double, oob 5", + "check __sk_buff->ifindex dw store not permitted", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, cb[4]) + 8), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, ifindex)), BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", .result = REJECT, }, { - "check cb access: double, oob 6", + "check __sk_buff->ifindex dw load not permitted", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, cb[0]) - 8), + offsetof(struct __sk_buff, ifindex)), BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", @@ -2640,6 +2658,171 @@ static struct bpf_test tests[] = { .flags = F_LOAD_WITH_STRICT_ALIGNMENT, }, { + "direct packet access: test18 (imm += pkt_ptr, 1)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_IMM(BPF_REG_0, 8), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_2), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), + BPF_STX_MEM(BPF_B, BPF_REG_2, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "direct packet access: test19 (imm += pkt_ptr, 2)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 3), + BPF_MOV64_IMM(BPF_REG_4, 4), + BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_2), + BPF_STX_MEM(BPF_B, BPF_REG_4, BPF_REG_4, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "direct packet access: test20 (x += pkt_ptr, 1)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_IMM(BPF_REG_0, 0xffffffff), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8), + BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffff), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_0), + BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_2), + BPF_MOV64_REG(BPF_REG_5, BPF_REG_4), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 0xffff - 1), + BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_5, BPF_REG_4, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .result = ACCEPT, + }, + { + "direct packet access: test21 (x += pkt_ptr, 2)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 9), + BPF_MOV64_IMM(BPF_REG_4, 0xffffffff), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_4, -8), + BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -8), + BPF_ALU64_IMM(BPF_AND, BPF_REG_4, 0xffff), + BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_2), + BPF_MOV64_REG(BPF_REG_5, BPF_REG_4), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 0xffff - 1), + BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_5, BPF_REG_4, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .result = ACCEPT, + }, + { + "direct packet access: test22 (x += pkt_ptr, 3)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_3, -16), + BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_10, -16), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 11), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -8), + BPF_MOV64_IMM(BPF_REG_4, 0xffffffff), + BPF_STX_XADD(BPF_DW, BPF_REG_10, BPF_REG_4, -8), + BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -8), + BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 48), + BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_2), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_4), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 2), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 2), + BPF_MOV64_IMM(BPF_REG_2, 1), + BPF_STX_MEM(BPF_H, BPF_REG_4, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .result = ACCEPT, + }, + { + "direct packet access: test23 (x += pkt_ptr, 4)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_IMM(BPF_REG_0, 0xffffffff), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8), + BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffff), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_0, 31), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_4), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_2), + BPF_MOV64_REG(BPF_REG_5, BPF_REG_0), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 0xffff - 1), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_5, BPF_REG_0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .result = REJECT, + .errstr = "cannot add integer value with 47 upper zero bits to ptr_to_packet", + }, + { + "direct packet access: test24 (x += pkt_ptr, 5)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_IMM(BPF_REG_0, 0xffffffff), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8), + BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xff), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_0, 64), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_4), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_2), + BPF_MOV64_REG(BPF_REG_5, BPF_REG_0), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 0xffff - 1), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_5, BPF_REG_0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .result = ACCEPT, + }, + { "helper access to packet: test1, valid packet_ptr range", .insns = { BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, @@ -5235,6 +5418,98 @@ static struct bpf_test tests[] = { }, .result = ACCEPT, }, + { + "check bpf_perf_event_data->sample_period byte load permitted", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), +#ifdef __LITTLE_ENDIAN + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct bpf_perf_event_data, sample_period)), +#else + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct bpf_perf_event_data, sample_period) + 7), +#endif + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_PERF_EVENT, + }, + { + "check bpf_perf_event_data->sample_period half load permitted", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), +#ifdef __LITTLE_ENDIAN + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct bpf_perf_event_data, sample_period)), +#else + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct bpf_perf_event_data, sample_period) + 6), +#endif + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_PERF_EVENT, + }, + { + "check bpf_perf_event_data->sample_period word load permitted", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), +#ifdef __LITTLE_ENDIAN + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct bpf_perf_event_data, sample_period)), +#else + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct bpf_perf_event_data, sample_period) + 4), +#endif + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_PERF_EVENT, + }, + { + "check bpf_perf_event_data->sample_period dword load permitted", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, + offsetof(struct bpf_perf_event_data, sample_period)), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_PERF_EVENT, + }, + { + "check skb->data half load not permitted", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), +#ifdef __LITTLE_ENDIAN + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, data)), +#else + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, data) + 2), +#endif + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid bpf_context access", + }, + { + "check skb->tc_classid half load not permitted for lwt prog", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), +#ifdef __LITTLE_ENDIAN + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, tc_classid)), +#else + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, tc_classid) + 2), +#endif + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid bpf_context access", + .prog_type = BPF_PROG_TYPE_LWT_IN, + }, }; static int probe_filter_length(const struct bpf_insn *fp) @@ -5484,7 +5759,7 @@ static int do_test(bool unpriv, unsigned int from, unsigned int to) } printf("Summary: %d PASSED, %d FAILED\n", passes, errors); - return errors ? -errors : 0; + return errors ? EXIT_FAILURE : EXIT_SUCCESS; } int main(int argc, char **argv) diff --git a/tools/testing/selftests/breakpoints/breakpoint_test.c b/tools/testing/selftests/breakpoints/breakpoint_test.c index 120895ab5505..f63356151ad4 100644 --- a/tools/testing/selftests/breakpoints/breakpoint_test.c +++ b/tools/testing/selftests/breakpoints/breakpoint_test.c @@ -16,6 +16,8 @@ #include <signal.h> #include <sys/types.h> #include <sys/wait.h> +#include <errno.h> +#include <string.h> #include "../kselftest.h" @@ -42,10 +44,9 @@ static void set_breakpoint_addr(void *addr, int n) ret = ptrace(PTRACE_POKEUSER, child_pid, offsetof(struct user, u_debugreg[n]), addr); - if (ret) { - perror("Can't set breakpoint addr\n"); - ksft_exit_fail(); - } + if (ret) + ksft_exit_fail_msg("Can't set breakpoint addr: %s\n", + strerror(errno)); } static void toggle_breakpoint(int n, int type, int len, @@ -106,8 +107,8 @@ static void toggle_breakpoint(int n, int type, int len, ret = ptrace(PTRACE_POKEUSER, child_pid, offsetof(struct user, u_debugreg[7]), dr7); if (ret) { - perror("Can't set dr7"); - ksft_exit_fail(); + ksft_print_msg("Can't set dr7: %s\n", strerror(errno)); + exit(-1); } } @@ -206,7 +207,7 @@ static void trigger_tests(void) ret = ptrace(PTRACE_TRACEME, 0, NULL, 0); if (ret) { - perror("Can't be traced?\n"); + ksft_print_msg("Can't be traced? %s\n", strerror(errno)); return; } @@ -261,29 +262,30 @@ static void trigger_tests(void) static void check_success(const char *msg) { - const char *msg2; int child_nr_tests; int status; + int ret; /* Wait for the child to SIGTRAP */ wait(&status); - msg2 = "Failed"; + ret = 0; if (WSTOPSIG(status) == SIGTRAP) { child_nr_tests = ptrace(PTRACE_PEEKDATA, child_pid, &nr_tests, 0); if (child_nr_tests == nr_tests) - msg2 = "Ok"; - if (ptrace(PTRACE_POKEDATA, child_pid, &trapped, 1)) { - perror("Can't poke\n"); - ksft_exit_fail(); - } + ret = 1; + if (ptrace(PTRACE_POKEDATA, child_pid, &trapped, 1)) + ksft_exit_fail_msg("Can't poke: %s\n", strerror(errno)); } nr_tests++; - printf("%s [%s]\n", msg, msg2); + if (ret) + ksft_test_result_pass(msg); + else + ksft_test_result_fail(msg); } static void launch_instruction_breakpoints(char *buf, int local, int global) @@ -294,7 +296,7 @@ static void launch_instruction_breakpoints(char *buf, int local, int global) set_breakpoint_addr(dummy_funcs[i], i); toggle_breakpoint(i, BP_X, 1, local, global, 1); ptrace(PTRACE_CONT, child_pid, NULL, 0); - sprintf(buf, "Test breakpoint %d with local: %d global: %d", + sprintf(buf, "Test breakpoint %d with local: %d global: %d\n", i, local, global); check_success(buf); toggle_breakpoint(i, BP_X, 1, local, global, 0); @@ -316,8 +318,9 @@ static void launch_watchpoints(char *buf, int mode, int len, set_breakpoint_addr(&dummy_var[i], i); toggle_breakpoint(i, mode, len, local, global, 1); ptrace(PTRACE_CONT, child_pid, NULL, 0); - sprintf(buf, "Test %s watchpoint %d with len: %d local: " - "%d global: %d", mode_str, i, len, local, global); + sprintf(buf, + "Test %s watchpoint %d with len: %d local: %d global: %d\n", + mode_str, i, len, local, global); check_success(buf); toggle_breakpoint(i, mode, len, local, global, 0); } @@ -378,10 +381,12 @@ int main(int argc, char **argv) pid_t pid; int ret; + ksft_print_header(); + pid = fork(); if (!pid) { trigger_tests(); - return 0; + exit(0); } child_pid = pid; @@ -392,5 +397,5 @@ int main(int argc, char **argv) wait(NULL); - return ksft_exit_pass(); + ksft_exit_pass(); } diff --git a/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c b/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c index 3897e996541e..960d02100c26 100644 --- a/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c +++ b/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c @@ -43,19 +43,25 @@ 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"); + ksft_print_msg( + "ptrace(PTRACE_TRACEME) failed: %s\n", + strerror(errno)); _exit(1); } if (raise(SIGSTOP) != 0) { - perror("raise(SIGSTOP) failed"); + ksft_print_msg( + "raise(SIGSTOP) failed: %s\n", strerror(errno)); _exit(1); } if ((uintptr_t) addr % size) { - perror("Wrong address write for the given size\n"); + ksft_print_msg( + "Wrong address write for the given size: %s\n", + strerror(errno)); _exit(1); } + switch (size) { case 1: *addr = 47; @@ -100,12 +106,14 @@ static bool set_watchpoint(pid_t pid, int size, int wp) 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"); + if (errno == EIO) + ksft_print_msg( + "ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) not supported on this hardware: %s\n", + strerror(errno)); + + ksft_print_msg( + "ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) failed: %s\n", + strerror(errno)); return false; } @@ -117,7 +125,8 @@ static bool run_test(int wr_size, int wp_size, int wr, int wp) pid_t wpid; if (pid < 0) { - perror("fork() failed"); + ksft_test_result_fail( + "fork() failed: %s\n", strerror(errno)); return false; } if (pid == 0) @@ -125,15 +134,17 @@ static bool run_test(int wr_size, int wp_size, int wr, int wp) wpid = waitpid(pid, &status, __WALL); if (wpid != pid) { - perror("waitpid() failed"); + ksft_print_msg( + "waitpid() failed: %s\n", strerror(errno)); return false; } if (!WIFSTOPPED(status)) { - printf("child did not stop\n"); + ksft_print_msg( + "child did not stop: %s\n", strerror(errno)); return false; } if (WSTOPSIG(status) != SIGSTOP) { - printf("child did not stop with SIGSTOP\n"); + ksft_print_msg("child did not stop with SIGSTOP\n"); return false; } @@ -141,42 +152,49 @@ static bool run_test(int wr_size, int wp_size, int wr, int wp) return false; if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) { - perror("ptrace(PTRACE_SINGLESTEP) failed"); + ksft_print_msg( + "ptrace(PTRACE_SINGLESTEP) failed: %s\n", + strerror(errno)); return false; } alarm(3); wpid = waitpid(pid, &status, __WALL); if (wpid != pid) { - perror("waitpid() failed"); + ksft_print_msg( + "waitpid() failed: %s\n", strerror(errno)); return false; } alarm(0); if (WIFEXITED(status)) { - printf("child did not single-step\t"); + ksft_print_msg("child did not single-step\n"); return false; } if (!WIFSTOPPED(status)) { - printf("child did not stop\n"); + ksft_print_msg("child did not stop\n"); return false; } if (WSTOPSIG(status) != SIGTRAP) { - printf("child did not stop with SIGTRAP\n"); + ksft_print_msg("child did not stop with SIGTRAP\n"); return false; } if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0) { - perror("ptrace(PTRACE_GETSIGINFO)"); + ksft_print_msg( + "ptrace(PTRACE_GETSIGINFO): %s\n", + strerror(errno)); return false; } if (siginfo.si_code != TRAP_HWBKPT) { - printf("Unexpected si_code %d\n", siginfo.si_code); + ksft_print_msg( + "Unexpected si_code %d\n", siginfo.si_code); return false; } kill(pid, SIGKILL); wpid = waitpid(pid, &status, 0); if (wpid != pid) { - perror("waitpid() failed"); + ksft_print_msg( + "waitpid() failed: %s\n", strerror(errno)); return false; } return true; @@ -194,6 +212,8 @@ int main(int argc, char **argv) int wr, wp, size; bool result; + ksft_print_header(); + act.sa_handler = sigalrm; sigemptyset(&act.sa_mask); act.sa_flags = 0; @@ -201,14 +221,16 @@ int main(int argc, char **argv) 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(); + if ((result && wr == wp) || + (!result && wr != wp)) + ksft_test_result_pass( + "Test size = %d write offset = %d watchpoint offset = %d\n", + size, wr, wp); + else { + ksft_test_result_fail( + "Test size = %d write offset = %d watchpoint offset = %d\n", + size, wr, wp); succeeded = false; } } @@ -216,19 +238,18 @@ int main(int argc, char **argv) } 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(); + if (run_test(size, 8, -size, -8)) + ksft_test_result_pass( + "Test size = %d write offset = %d watchpoint offset = -8\n", + size, -size); + else { + ksft_test_result_fail( + "Test size = %d write offset = %d watchpoint offset = -8\n", + size, -size); succeeded = false; } } - ksft_print_cnts(); if (succeeded) ksft_exit_pass(); else diff --git a/tools/testing/selftests/breakpoints/step_after_suspend_test.c b/tools/testing/selftests/breakpoints/step_after_suspend_test.c index 60b8a95dac26..3fece06e9f64 100644 --- a/tools/testing/selftests/breakpoints/step_after_suspend_test.c +++ b/tools/testing/selftests/breakpoints/step_after_suspend_test.c @@ -37,17 +37,19 @@ void child(int cpu) CPU_ZERO(&set); CPU_SET(cpu, &set); if (sched_setaffinity(0, sizeof(set), &set) != 0) { - perror("sched_setaffinity() failed"); + ksft_print_msg("sched_setaffinity() failed: %s\n", + strerror(errno)); _exit(1); } if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) { - perror("ptrace(PTRACE_TRACEME) failed"); + ksft_print_msg("ptrace(PTRACE_TRACEME) failed: %s\n", + strerror(errno)); _exit(1); } if (raise(SIGSTOP) != 0) { - perror("raise(SIGSTOP) failed"); + ksft_print_msg("raise(SIGSTOP) failed: %s\n", strerror(errno)); _exit(1); } @@ -61,7 +63,7 @@ bool run_test(int cpu) pid_t wpid; if (pid < 0) { - perror("fork() failed"); + ksft_print_msg("fork() failed: %s\n", strerror(errno)); return false; } if (pid == 0) @@ -69,57 +71,64 @@ bool run_test(int cpu) wpid = waitpid(pid, &status, __WALL); if (wpid != pid) { - perror("waitpid() failed"); + ksft_print_msg("waitpid() failed: %s\n", strerror(errno)); return false; } if (!WIFSTOPPED(status)) { - printf("child did not stop\n"); + ksft_print_msg("child did not stop: %s\n", strerror(errno)); return false; } if (WSTOPSIG(status) != SIGSTOP) { - printf("child did not stop with SIGSTOP\n"); + ksft_print_msg("child did not stop with SIGSTOP: %s\n", + strerror(errno)); return false; } if (ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL) < 0) { if (errno == EIO) { - printf("ptrace(PTRACE_SINGLESTEP) not supported on this architecture\n"); - ksft_exit_skip(); + ksft_exit_skip( + "ptrace(PTRACE_SINGLESTEP) not supported on this architecture: %s\n", + strerror(errno)); } - perror("ptrace(PTRACE_SINGLESTEP) failed"); + ksft_print_msg("ptrace(PTRACE_SINGLESTEP) failed: %s\n", + strerror(errno)); return false; } wpid = waitpid(pid, &status, __WALL); if (wpid != pid) { - perror("waitpid() failed"); + ksft_print_msg("waitpid() failed: $s\n", strerror(errno)); return false; } if (WIFEXITED(status)) { - printf("child did not single-step\n"); + ksft_print_msg("child did not single-step: %s\n", + strerror(errno)); return false; } if (!WIFSTOPPED(status)) { - printf("child did not stop\n"); + ksft_print_msg("child did not stop: %s\n", strerror(errno)); return false; } if (WSTOPSIG(status) != SIGTRAP) { - printf("child did not stop with SIGTRAP\n"); + ksft_print_msg("child did not stop with SIGTRAP: %s\n", + strerror(errno)); return false; } if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) { - perror("ptrace(PTRACE_CONT) failed"); + ksft_print_msg("ptrace(PTRACE_CONT) failed: %s\n", + strerror(errno)); return false; } wpid = waitpid(pid, &status, __WALL); if (wpid != pid) { - perror("waitpid() failed"); + ksft_print_msg("waitpid() failed: %s\n", strerror(errno)); return false; } if (!WIFEXITED(status)) { - printf("child did not exit after PTRACE_CONT\n"); + ksft_print_msg("child did not exit after PTRACE_CONT: %s\n", + strerror(errno)); return false; } @@ -135,28 +144,21 @@ void suspend(void) struct itimerspec spec = {}; power_state_fd = open("/sys/power/state", O_RDWR); - if (power_state_fd < 0) { - perror("open(\"/sys/power/state\") failed (is this test running as root?)"); - ksft_exit_fail(); - } + if (power_state_fd < 0) + ksft_exit_fail_msg( + "open(\"/sys/power/state\") failed (is this test running as root?)\n"); timerfd = timerfd_create(CLOCK_BOOTTIME_ALARM, 0); - if (timerfd < 0) { - perror("timerfd_create() failed"); - ksft_exit_fail(); - } + if (timerfd < 0) + ksft_exit_fail_msg("timerfd_create() failed\n"); spec.it_value.tv_sec = 5; err = timerfd_settime(timerfd, 0, &spec, NULL); - if (err < 0) { - perror("timerfd_settime() failed"); - ksft_exit_fail(); - } + if (err < 0) + ksft_exit_fail_msg("timerfd_settime() failed\n"); - if (write(power_state_fd, "mem", strlen("mem")) != strlen("mem")) { - perror("entering suspend failed"); - ksft_exit_fail(); - } + if (write(power_state_fd, "mem", strlen("mem")) != strlen("mem")) + ksft_exit_fail_msg("Failed to enter Suspend state\n"); close(timerfd); close(power_state_fd); @@ -171,6 +173,8 @@ int main(int argc, char **argv) int err; int cpu; + ksft_print_header(); + while ((opt = getopt(argc, argv, "n")) != -1) { switch (opt) { case 'n': @@ -187,10 +191,8 @@ int main(int argc, char **argv) suspend(); err = sched_getaffinity(0, sizeof(available_cpus), &available_cpus); - if (err < 0) { - perror("sched_getaffinity() failed"); - ksft_exit_fail(); - } + if (err < 0) + ksft_exit_fail_msg("sched_getaffinity() failed\n"); for (cpu = 0; cpu < CPU_SETSIZE; cpu++) { bool test_success; @@ -199,18 +201,14 @@ int main(int argc, char **argv) continue; test_success = run_test(cpu); - printf("CPU %d: ", cpu); if (test_success) { - printf("[OK]\n"); - ksft_inc_pass_cnt(); + ksft_test_result_pass("CPU %d\n", cpu); } else { - printf("[FAILED]\n"); - ksft_inc_fail_cnt(); + ksft_test_result_fail("CPU %d\n", cpu); succeeded = false; } } - ksft_print_cnts(); if (succeeded) ksft_exit_pass(); else diff --git a/tools/testing/selftests/capabilities/test_execve.c b/tools/testing/selftests/capabilities/test_execve.c index 10a21a958aaf..763f37fecfb8 100644 --- a/tools/testing/selftests/capabilities/test_execve.c +++ b/tools/testing/selftests/capabilities/test_execve.c @@ -138,9 +138,6 @@ static void chdir_to_tmpfs(void) if (chdir(cwd) != 0) err(1, "chdir to private tmpfs"); - - if (umount2(".", MNT_DETACH) != 0) - err(1, "detach private tmpfs"); } static void copy_fromat_to(int fromfd, const char *fromname, const char *toname) @@ -248,7 +245,7 @@ static int do_tests(int uid, const char *our_path) err(1, "chown"); if (chmod("validate_cap_sgidnonroot", S_ISGID | 0710) != 0) err(1, "chmod"); -} + } capng_get_caps_process(); @@ -384,7 +381,7 @@ static int do_tests(int uid, const char *our_path) } else { printf("[RUN]\tNon-root +ia, sgidnonroot => i\n"); exec_other_validate_cap("./validate_cap_sgidnonroot", - false, false, true, false); + false, false, true, false); if (fork_wait()) { printf("[RUN]\tNon-root +ia, sgidroot => i\n"); diff --git a/tools/testing/selftests/ftrace/ftracetest b/tools/testing/selftests/ftrace/ftracetest index 717581145cfc..14a03ea1e21d 100755 --- a/tools/testing/selftests/ftrace/ftracetest +++ b/tools/testing/selftests/ftrace/ftracetest @@ -250,7 +250,7 @@ run_test() { # testfile local testlog=`mktemp $LOG_DIR/${testname}-log.XXXXXX` export TMPDIR=`mktemp -d /tmp/ftracetest-dir.XXXXXX` testcase $1 - echo "execute: "$1 > $testlog + echo "execute$INSTANCE: "$1 > $testlog SIG_RESULT=0 if [ $VERBOSE -ge 2 ]; then __run_test $1 2>> $testlog | tee -a $testlog diff --git a/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc b/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc index 0bb5df3c00d4..15e2d3fe1731 100644 --- a/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc +++ b/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc @@ -28,7 +28,9 @@ echo '*:*' > set_event yield -count=`cat trace | grep -v ^# | wc -l` +echo 0 > tracing_on + +count=`head -n 128 trace | grep -v ^# | wc -l` if [ $count -eq 0 ]; then fail "none of events are recorded" fi @@ -36,10 +38,12 @@ fi do_reset echo 1 > events/enable +echo 1 > tracing_on yield -count=`cat trace | grep -v ^# | wc -l` +echo 0 > tracing_on +count=`head -n 128 trace | grep -v ^# | wc -l` if [ $count -eq 0 ]; then fail "none of events are recorded" fi 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 index 9dcd0ca1f49c..8095e122daa9 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-glob.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-glob.tc @@ -11,17 +11,6 @@ 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 @@ -39,11 +28,28 @@ ftrace_filter_check '*schedule*' '^.*schedule.*$' # filter by *, end match ftrace_filter_check 'schedule*' '^schedule.*$' +# Advanced full-glob matching feature is recently supported. +# Skip the tests if we are sure the kernel does not support it. +if grep -q 'accepts: .* glob-matching-pattern' README ; then + # 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]_.*$' +# 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 + +fi + echo > set_ftrace_filter enable_tracing diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc index aa31368851c9..77dfb6b48186 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc @@ -72,6 +72,15 @@ run_enable_disable() { test_event_enabled $check_disable echo "schedule:${enable}_event:$EVENT" > set_ftrace_filter + if [ -d ../../instances ]; then # Check instances + cur=`cat set_ftrace_filter` + top=`cat ../../set_ftrace_filter` + if [ "$cur" = "$top" ]; then + echo "This kernel is too old to support per instance filter" + reset_ftrace_filter + exit_unsupported + fi + fi echo " make sure it works 5 times" diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func_traceonoff_triggers.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func_traceonoff_triggers.tc index c8e02ec01eaf..7a9ab4ff83b6 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func_traceonoff_triggers.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func_traceonoff_triggers.tc @@ -63,6 +63,10 @@ fi # powerpc uses .schedule func="schedule" +available_file=available_filter_functions +if [ -d ../../instances -a -f ../../available_filter_functions ]; then + available_file=../../available_filter_functions +fi x=`grep '^\.schedule$' available_filter_functions | wc -l` if [ "$x" -eq 1 ]; then func=".schedule" @@ -71,6 +75,15 @@ fi echo '** SET TRACEOFF' echo "$func:traceoff" > set_ftrace_filter +if [ -d ../../instances ]; then # Check instances + cur=`cat set_ftrace_filter` + top=`cat ../../set_ftrace_filter` + if [ "$cur" = "$top" ]; then + echo "This kernel is too old to support per instance filter" + reset_ftrace_filter + exit_unsupported + fi +fi cnt=`grep schedule set_ftrace_filter | wc -l` if [ $cnt -ne 1 ]; then @@ -90,11 +103,11 @@ if [ $on != "0" ]; then fail "Tracing is not off" fi -line1=`cat trace | tail -1` +csum1=`md5sum trace` sleep $SLEEP_TIME -line2=`cat trace | tail -1` +csum2=`md5sum trace` -if [ "$line1" != "$line2" ]; then +if [ "$csum1" != "$csum2" ]; then fail "Tracing file is still changing" fi diff --git a/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc b/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc index c73db7863adb..8a353314dc9b 100644 --- a/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc +++ b/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc @@ -82,7 +82,10 @@ rmdir foo if [ -d foo ]; then fail "foo still exists" fi - +if grep -q "schedule:enable_event:sched:sched_switch" ../set_ftrace_filter; then + echo "Older kernel detected. Cleanup filter" + echo '!schedule:enable_event:sched:sched_switch' > ../set_ftrace_filter +fi instance_slam() { while :; do diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_maxactive.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_maxactive.tc index 57abdf1caabf..7ec6f2639ad6 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_maxactive.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_maxactive.tc @@ -2,6 +2,7 @@ # description: Kretprobe dynamic event with maxactive [ -f kprobe_events ] || exit_unsupported # this is configurable +grep -q 'r\[maxactive\]' README || exit_unsupported # this is older version echo > kprobe_events diff --git a/tools/testing/selftests/futex/functional/futex_requeue_pi.c b/tools/testing/selftests/futex/functional/futex_requeue_pi.c index 3da06ad23996..d24ab7421e73 100644 --- a/tools/testing/selftests/futex/functional/futex_requeue_pi.c +++ b/tools/testing/selftests/futex/functional/futex_requeue_pi.c @@ -32,6 +32,7 @@ #include "futextest.h" #include "logging.h" +#define TEST_NAME "futex-requeue-pi" #define MAX_WAKE_ITERS 1000 #define THREAD_MAX 10 #define SIGNAL_PERIOD_US 100 @@ -404,6 +405,6 @@ int main(int argc, char *argv[]) */ ret = unit_test(broadcast, locked, owner, timeout_ns); - print_result(ret); + print_result(TEST_NAME, ret); return ret; } diff --git a/tools/testing/selftests/futex/functional/futex_requeue_pi_mismatched_ops.c b/tools/testing/selftests/futex/functional/futex_requeue_pi_mismatched_ops.c index d5e4f2c4da2a..e0a798ad0d21 100644 --- a/tools/testing/selftests/futex/functional/futex_requeue_pi_mismatched_ops.c +++ b/tools/testing/selftests/futex/functional/futex_requeue_pi_mismatched_ops.c @@ -30,6 +30,8 @@ #include "futextest.h" #include "logging.h" +#define TEST_NAME "futex-requeue-pi-mismatched-ops" + futex_t f1 = FUTEX_INITIALIZER; futex_t f2 = FUTEX_INITIALIZER; int child_ret = 0; @@ -130,6 +132,6 @@ int main(int argc, char *argv[]) out: /* If the kernel crashes, we shouldn't return at all. */ - print_result(ret); + print_result(TEST_NAME, ret); return ret; } diff --git a/tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c b/tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c index 3d7dc6afc3f8..982f83577501 100644 --- a/tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c +++ b/tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c @@ -32,6 +32,7 @@ #include "futextest.h" #include "logging.h" +#define TEST_NAME "futex-requeue-pi-signal-restart" #define DELAY_US 100 futex_t f1 = FUTEX_INITIALIZER; @@ -218,6 +219,6 @@ int main(int argc, char *argv[]) if (ret == RET_PASS && waiter_ret) ret = waiter_ret; - print_result(ret); + print_result(TEST_NAME, ret); return ret; } diff --git a/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c b/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c index 5f687f247454..bdc48dc047e5 100644 --- a/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c +++ b/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c @@ -34,6 +34,7 @@ #include "logging.h" #include "futextest.h" +#define TEST_NAME "futex-wait-private-mapped-file" #define PAGE_SZ 4096 char pad[PAGE_SZ] = {1}; @@ -60,7 +61,7 @@ void *thr_futex_wait(void *arg) ret = futex_wait(&val, 1, &wait_timeout, 0); if (ret && errno != EWOULDBLOCK && errno != ETIMEDOUT) { error("futex error.\n", errno); - print_result(RET_ERROR); + print_result(TEST_NAME, RET_ERROR); exit(RET_ERROR); } @@ -120,6 +121,6 @@ int main(int argc, char **argv) pthread_join(thr, NULL); out: - print_result(ret); + print_result(TEST_NAME, ret); return ret; } diff --git a/tools/testing/selftests/futex/functional/futex_wait_timeout.c b/tools/testing/selftests/futex/functional/futex_wait_timeout.c index ab428ca894de..6aadd560366e 100644 --- a/tools/testing/selftests/futex/functional/futex_wait_timeout.c +++ b/tools/testing/selftests/futex/functional/futex_wait_timeout.c @@ -27,6 +27,8 @@ #include "futextest.h" #include "logging.h" +#define TEST_NAME "futex-wait-timeout" + static long timeout_ns = 100000; /* 100us default timeout */ void usage(char *prog) @@ -81,6 +83,6 @@ int main(int argc, char *argv[]) ret = RET_FAIL; } - print_result(ret); + print_result(TEST_NAME, ret); return ret; } diff --git a/tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c b/tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c index fe7aee96844b..d237a8b702f0 100644 --- a/tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c +++ b/tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c @@ -36,6 +36,7 @@ #include "logging.h" #include "futextest.h" +#define TEST_NAME "futex-wait-uninitialized-heap" #define WAIT_US 5000000 static int child_blocked = 1; @@ -119,6 +120,6 @@ int main(int argc, char **argv) } out: - print_result(ret); + print_result(TEST_NAME, ret); return ret; } diff --git a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c index b6b027448825..9a2c56fa7305 100644 --- a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c +++ b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c @@ -28,6 +28,7 @@ #include "futextest.h" #include "logging.h" +#define TEST_NAME "futex-wait-wouldblock" #define timeout_ns 100000 void usage(char *prog) @@ -74,6 +75,6 @@ int main(int argc, char *argv[]) ret = RET_FAIL; } - print_result(ret); + print_result(TEST_NAME, ret); return ret; } diff --git a/tools/testing/selftests/futex/include/logging.h b/tools/testing/selftests/futex/include/logging.h index e14469103f07..4e7944984fbb 100644 --- a/tools/testing/selftests/futex/include/logging.h +++ b/tools/testing/selftests/futex/include/logging.h @@ -107,7 +107,7 @@ void log_verbosity(int level) * * print_result() is primarily intended for functional tests. */ -void print_result(int ret) +void print_result(const char *test_name, int ret) { const char *result = "Unknown return code"; @@ -124,7 +124,7 @@ void print_result(int ret) result = FAIL; break; } - printf("Result: %s\n", result); + printf("selftests: %s [%s]\n", test_name, result); } /* log level macros */ diff --git a/tools/testing/selftests/intel_pstate/.gitignore b/tools/testing/selftests/intel_pstate/.gitignore new file mode 100644 index 000000000000..3bfcbae5fa13 --- /dev/null +++ b/tools/testing/selftests/intel_pstate/.gitignore @@ -0,0 +1,2 @@ +aperf +msr diff --git a/tools/testing/selftests/intel_pstate/Makefile b/tools/testing/selftests/intel_pstate/Makefile index 19678e90efb2..849a90ffe8dd 100644 --- a/tools/testing/selftests/intel_pstate/Makefile +++ b/tools/testing/selftests/intel_pstate/Makefile @@ -1,5 +1,5 @@ CFLAGS := $(CFLAGS) -Wall -D_GNU_SOURCE -LDFLAGS := $(LDFLAGS) -lm +LDLIBS := $(LDLIBS) -lm TEST_GEN_FILES := msr aperf diff --git a/tools/testing/selftests/kselftest.h b/tools/testing/selftests/kselftest.h index ef1c80d67ac7..08e90c2cc5cb 100644 --- a/tools/testing/selftests/kselftest.h +++ b/tools/testing/selftests/kselftest.h @@ -12,6 +12,7 @@ #include <stdlib.h> #include <unistd.h> +#include <stdarg.h> /* define kselftest exit codes */ #define KSFT_PASS 0 @@ -31,38 +32,125 @@ struct ksft_count { static struct ksft_count ksft_cnt; +static inline int ksft_test_num(void) +{ + return ksft_cnt.ksft_pass + ksft_cnt.ksft_fail + + ksft_cnt.ksft_xfail + ksft_cnt.ksft_xpass + + ksft_cnt.ksft_xskip; +} + static inline void ksft_inc_pass_cnt(void) { ksft_cnt.ksft_pass++; } static inline void ksft_inc_fail_cnt(void) { ksft_cnt.ksft_fail++; } static inline void ksft_inc_xfail_cnt(void) { ksft_cnt.ksft_xfail++; } static inline void ksft_inc_xpass_cnt(void) { ksft_cnt.ksft_xpass++; } static inline void ksft_inc_xskip_cnt(void) { ksft_cnt.ksft_xskip++; } +static inline void ksft_print_header(void) +{ + printf("TAP version 13\n"); +} + static inline void ksft_print_cnts(void) { - printf("Pass: %d Fail: %d Xfail: %d Xpass: %d, Xskip: %d\n", - ksft_cnt.ksft_pass, ksft_cnt.ksft_fail, - ksft_cnt.ksft_xfail, ksft_cnt.ksft_xpass, - ksft_cnt.ksft_xskip); + printf("1..%d\n", ksft_test_num()); +} + +static inline void ksft_print_msg(const char *msg, ...) +{ + va_list args; + + va_start(args, msg); + printf("# "); + vprintf(msg, args); + va_end(args); +} + +static inline void ksft_test_result_pass(const char *msg, ...) +{ + va_list args; + + ksft_cnt.ksft_pass++; + + va_start(args, msg); + printf("ok %d ", ksft_test_num()); + vprintf(msg, args); + va_end(args); +} + +static inline void ksft_test_result_fail(const char *msg, ...) +{ + va_list args; + + ksft_cnt.ksft_fail++; + + va_start(args, msg); + printf("not ok %d ", ksft_test_num()); + vprintf(msg, args); + va_end(args); +} + +static inline void ksft_test_result_skip(const char *msg, ...) +{ + va_list args; + + ksft_cnt.ksft_xskip++; + + va_start(args, msg); + printf("ok %d # skip ", ksft_test_num()); + vprintf(msg, args); + va_end(args); } static inline int ksft_exit_pass(void) { + ksft_print_cnts(); exit(KSFT_PASS); } + static inline int ksft_exit_fail(void) { + printf("Bail out!\n"); + ksft_print_cnts(); + exit(KSFT_FAIL); +} + +static inline int ksft_exit_fail_msg(const char *msg, ...) +{ + va_list args; + + va_start(args, msg); + printf("Bail out! "); + vprintf(msg, args); + va_end(args); + + ksft_print_cnts(); exit(KSFT_FAIL); } + static inline int ksft_exit_xfail(void) { + ksft_print_cnts(); exit(KSFT_XFAIL); } + static inline int ksft_exit_xpass(void) { + ksft_print_cnts(); exit(KSFT_XPASS); } -static inline int ksft_exit_skip(void) + +static inline int ksft_exit_skip(const char *msg, ...) { + if (msg) { + va_list args; + + va_start(args, msg); + printf("1..%d # Skipped: ", ksft_test_num()); + vprintf(msg, args); + va_end(args); + } else { + ksft_print_cnts(); + } exit(KSFT_SKIP); } diff --git a/tools/testing/selftests/seccomp/test_harness.h b/tools/testing/selftests/kselftest_harness.h index a786c69c7584..c56f72e07cd7 100644 --- a/tools/testing/selftests/seccomp/test_harness.h +++ b/tools/testing/selftests/kselftest_harness.h @@ -2,44 +2,53 @@ * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by the GPLv2 license. * - * test_harness.h: simple C unit test helper. - * - * Usage: - * #include "test_harness.h" - * TEST(standalone_test) { - * do_some_stuff; - * EXPECT_GT(10, stuff) { - * stuff_state_t state; - * enumerate_stuff_state(&state); - * TH_LOG("expectation failed with state: %s", state.msg); - * } - * more_stuff; - * ASSERT_NE(some_stuff, NULL) TH_LOG("how did it happen?!"); - * last_stuff; - * EXPECT_EQ(0, last_stuff); - * } - * - * FIXTURE(my_fixture) { - * mytype_t *data; - * int awesomeness_level; - * }; - * FIXTURE_SETUP(my_fixture) { - * self->data = mytype_new(); - * ASSERT_NE(NULL, self->data); - * } - * FIXTURE_TEARDOWN(my_fixture) { - * mytype_free(self->data); - * } - * TEST_F(my_fixture, data_is_good) { - * EXPECT_EQ(1, is_my_data_good(self->data)); - * } - * - * TEST_HARNESS_MAIN + * kselftest_harness.h: simple C unit test helper. + * + * See documentation in Documentation/dev-tools/kselftest.rst * * API inspired by code.google.com/p/googletest */ -#ifndef TEST_HARNESS_H_ -#define TEST_HARNESS_H_ + +/** + * DOC: example + * + * .. code-block:: c + * + * #include "../kselftest_harness.h" + * + * TEST(standalone_test) { + * do_some_stuff; + * EXPECT_GT(10, stuff) { + * stuff_state_t state; + * enumerate_stuff_state(&state); + * TH_LOG("expectation failed with state: %s", state.msg); + * } + * more_stuff; + * ASSERT_NE(some_stuff, NULL) TH_LOG("how did it happen?!"); + * last_stuff; + * EXPECT_EQ(0, last_stuff); + * } + * + * FIXTURE(my_fixture) { + * mytype_t *data; + * int awesomeness_level; + * }; + * FIXTURE_SETUP(my_fixture) { + * self->data = mytype_new(); + * ASSERT_NE(NULL, self->data); + * } + * FIXTURE_TEARDOWN(my_fixture) { + * mytype_free(self->data); + * } + * TEST_F(my_fixture, data_is_good) { + * EXPECT_EQ(1, is_my_data_good(self->data)); + * } + * + * TEST_HARNESS_MAIN + */ + +#ifndef __KSELFTEST_HARNESS_H +#define __KSELFTEST_HARNESS_H #define _GNU_SOURCE #include <stdint.h> @@ -50,158 +59,33 @@ #include <sys/wait.h> #include <unistd.h> -/* All exported functionality should be declared through this macro. */ -#define TEST_API(x) _##x -/* - * Exported APIs - */ +/* Utilities exposed to the test definitions */ +#ifndef TH_LOG_STREAM +# define TH_LOG_STREAM stderr +#endif -/* TEST(name) { implementation } - * Defines a test by name. - * Names must be unique and tests must not be run in parallel. The - * implementation containing block is a function and scoping should be treated - * as such. Returning early may be performed with a bare "return;" statement. - * - * EXPECT_* and ASSERT_* are valid in a TEST() { } context. - */ -#define TEST TEST_API(TEST) +#ifndef TH_LOG_ENABLED +# define TH_LOG_ENABLED 1 +#endif -/* TEST_SIGNAL(name, signal) { implementation } - * Defines a test by name and the expected term signal. - * Names must be unique and tests must not be run in parallel. The - * implementation containing block is a function and scoping should be treated - * as such. Returning early may be performed with a bare "return;" statement. +/** + * TH_LOG(fmt, ...) * - * EXPECT_* and ASSERT_* are valid in a TEST() { } context. - */ -#define TEST_SIGNAL TEST_API(TEST_SIGNAL) - -/* FIXTURE(datatype name) { - * type property1; - * ... - * }; - * Defines the data provided to TEST_F()-defined tests as |self|. It should be - * populated and cleaned up using FIXTURE_SETUP and FIXTURE_TEARDOWN. - */ -#define FIXTURE TEST_API(FIXTURE) - -/* FIXTURE_DATA(datatype name) - * This call may be used when the type of the fixture data - * is needed. In general, this should not be needed unless - * the |self| is being passed to a helper directly. - */ -#define FIXTURE_DATA TEST_API(FIXTURE_DATA) - -/* FIXTURE_SETUP(fixture name) { implementation } - * Populates the required "setup" function for a fixture. An instance of the - * datatype defined with _FIXTURE_DATA will be exposed as |self| for the - * implementation. + * @fmt: format string + * @...: optional arguments * - * ASSERT_* are valid for use in this context and will prempt the execution - * of any dependent fixture tests. + * .. code-block:: c * - * A bare "return;" statement may be used to return early. - */ -#define FIXTURE_SETUP TEST_API(FIXTURE_SETUP) - -/* FIXTURE_TEARDOWN(fixture name) { implementation } - * Populates the required "teardown" function for a fixture. An instance of the - * datatype defined with _FIXTURE_DATA will be exposed as |self| for the - * implementation to clean up. + * TH_LOG(format, ...) * - * A bare "return;" statement may be used to return early. - */ -#define FIXTURE_TEARDOWN TEST_API(FIXTURE_TEARDOWN) - -/* TEST_F(fixture, name) { implementation } - * Defines a test that depends on a fixture (e.g., is part of a test case). - * Very similar to TEST() except that |self| is the setup instance of fixture's - * datatype exposed for use by the implementation. - */ -#define TEST_F TEST_API(TEST_F) - -#define TEST_F_SIGNAL TEST_API(TEST_F_SIGNAL) - -/* Use once to append a main() to the test file. E.g., - * TEST_HARNESS_MAIN - */ -#define TEST_HARNESS_MAIN TEST_API(TEST_HARNESS_MAIN) - -/* - * Operators for use in TEST and TEST_F. - * ASSERT_* calls will stop test execution immediately. - * EXPECT_* calls will emit a failure warning, note it, and continue. - */ - -/* ASSERT_EQ(expected, measured): expected == measured */ -#define ASSERT_EQ TEST_API(ASSERT_EQ) -/* ASSERT_NE(expected, measured): expected != measured */ -#define ASSERT_NE TEST_API(ASSERT_NE) -/* ASSERT_LT(expected, measured): expected < measured */ -#define ASSERT_LT TEST_API(ASSERT_LT) -/* ASSERT_LE(expected, measured): expected <= measured */ -#define ASSERT_LE TEST_API(ASSERT_LE) -/* ASSERT_GT(expected, measured): expected > measured */ -#define ASSERT_GT TEST_API(ASSERT_GT) -/* ASSERT_GE(expected, measured): expected >= measured */ -#define ASSERT_GE TEST_API(ASSERT_GE) -/* ASSERT_NULL(measured): NULL == measured */ -#define ASSERT_NULL TEST_API(ASSERT_NULL) -/* ASSERT_TRUE(measured): measured != 0 */ -#define ASSERT_TRUE TEST_API(ASSERT_TRUE) -/* ASSERT_FALSE(measured): measured == 0 */ -#define ASSERT_FALSE TEST_API(ASSERT_FALSE) -/* ASSERT_STREQ(expected, measured): !strcmp(expected, measured) */ -#define ASSERT_STREQ TEST_API(ASSERT_STREQ) -/* ASSERT_STRNE(expected, measured): strcmp(expected, measured) */ -#define ASSERT_STRNE TEST_API(ASSERT_STRNE) -/* EXPECT_EQ(expected, measured): expected == measured */ -#define EXPECT_EQ TEST_API(EXPECT_EQ) -/* EXPECT_NE(expected, measured): expected != measured */ -#define EXPECT_NE TEST_API(EXPECT_NE) -/* EXPECT_LT(expected, measured): expected < measured */ -#define EXPECT_LT TEST_API(EXPECT_LT) -/* EXPECT_LE(expected, measured): expected <= measured */ -#define EXPECT_LE TEST_API(EXPECT_LE) -/* EXPECT_GT(expected, measured): expected > measured */ -#define EXPECT_GT TEST_API(EXPECT_GT) -/* EXPECT_GE(expected, measured): expected >= measured */ -#define EXPECT_GE TEST_API(EXPECT_GE) -/* EXPECT_NULL(measured): NULL == measured */ -#define EXPECT_NULL TEST_API(EXPECT_NULL) -/* EXPECT_TRUE(measured): 0 != measured */ -#define EXPECT_TRUE TEST_API(EXPECT_TRUE) -/* EXPECT_FALSE(measured): 0 == measured */ -#define EXPECT_FALSE TEST_API(EXPECT_FALSE) -/* EXPECT_STREQ(expected, measured): !strcmp(expected, measured) */ -#define EXPECT_STREQ TEST_API(EXPECT_STREQ) -/* EXPECT_STRNE(expected, measured): strcmp(expected, measured) */ -#define EXPECT_STRNE TEST_API(EXPECT_STRNE) - -/* TH_LOG(format, ...) * Optional debug logging function available for use in tests. * Logging may be enabled or disabled by defining TH_LOG_ENABLED. * E.g., #define TH_LOG_ENABLED 1 - * If no definition is provided, logging is enabled by default. - */ -#define TH_LOG TEST_API(TH_LOG) - -/* - * Internal implementation. * + * If no definition is provided, logging is enabled by default. */ - -/* Utilities exposed to the test definitions */ -#ifndef TH_LOG_STREAM -# define TH_LOG_STREAM stderr -#endif - -#ifndef TH_LOG_ENABLED -# define TH_LOG_ENABLED 1 -#endif - -#define _TH_LOG(fmt, ...) do { \ +#define TH_LOG(fmt, ...) do { \ if (TH_LOG_ENABLED) \ __TH_LOG(fmt, ##__VA_ARGS__); \ } while (0) @@ -211,10 +95,43 @@ fprintf(TH_LOG_STREAM, "%s:%d:%s:" fmt "\n", \ __FILE__, __LINE__, _metadata->name, ##__VA_ARGS__) -/* Defines the test function and creates the registration stub. */ -#define _TEST(test_name) __TEST_IMPL(test_name, -1) +/** + * TEST(test_name) - Defines the test function and creates the registration + * stub + * + * @test_name: test name + * + * .. code-block:: c + * + * TEST(name) { implementation } + * + * Defines a test by name. + * Names must be unique and tests must not be run in parallel. The + * implementation containing block is a function and scoping should be treated + * as such. Returning early may be performed with a bare "return;" statement. + * + * EXPECT_* and ASSERT_* are valid in a TEST() { } context. + */ +#define TEST(test_name) __TEST_IMPL(test_name, -1) -#define _TEST_SIGNAL(test_name, signal) __TEST_IMPL(test_name, signal) +/** + * TEST_SIGNAL(test_name, signal) + * + * @test_name: test name + * @signal: signal number + * + * .. code-block:: c + * + * TEST_SIGNAL(name, signal) { implementation } + * + * Defines a test by name and the expected term signal. + * Names must be unique and tests must not be run in parallel. The + * implementation containing block is a function and scoping should be treated + * as such. Returning early may be performed with a bare "return;" statement. + * + * EXPECT_* and ASSERT_* are valid in a TEST() { } context. + */ +#define TEST_SIGNAL(test_name, signal) __TEST_IMPL(test_name, signal) #define __TEST_IMPL(test_name, _signal) \ static void test_name(struct __test_metadata *_metadata); \ @@ -228,50 +145,121 @@ static void test_name( \ struct __test_metadata __attribute__((unused)) *_metadata) -/* Wraps the struct name so we have one less argument to pass around. */ -#define _FIXTURE_DATA(fixture_name) struct _test_data_##fixture_name +/** + * FIXTURE_DATA(datatype_name) - Wraps the struct name so we have one less + * argument to pass around + * + * @datatype_name: datatype name + * + * .. code-block:: c + * + * FIXTURE_DATA(datatype name) + * + * This call may be used when the type of the fixture data + * is needed. In general, this should not be needed unless + * the *self* is being passed to a helper directly. + */ +#define FIXTURE_DATA(datatype_name) struct _test_data_##datatype_name -/* Called once per fixture to setup the data and register. */ -#define _FIXTURE(fixture_name) \ +/** + * FIXTURE(fixture_name) - Called once per fixture to setup the data and + * register + * + * @fixture_name: fixture name + * + * .. code-block:: c + * + * FIXTURE(datatype name) { + * type property1; + * ... + * }; + * + * Defines the data provided to TEST_F()-defined tests as *self*. It should be + * populated and cleaned up using FIXTURE_SETUP() and FIXTURE_TEARDOWN(). + */ +#define FIXTURE(fixture_name) \ static void __attribute__((constructor)) \ _register_##fixture_name##_data(void) \ { \ __fixture_count++; \ } \ - _FIXTURE_DATA(fixture_name) + FIXTURE_DATA(fixture_name) -/* Prepares the setup function for the fixture. |_metadata| is included - * so that ASSERT_* work as a convenience. +/** + * FIXTURE_SETUP(fixture_name) - Prepares the setup function for the fixture. + * *_metadata* is included so that ASSERT_* work as a convenience + * + * @fixture_name: fixture name + * + * .. code-block:: c + * + * FIXTURE_SETUP(fixture name) { implementation } + * + * Populates the required "setup" function for a fixture. An instance of the + * datatype defined with FIXTURE_DATA() will be exposed as *self* for the + * implementation. + * + * ASSERT_* are valid for use in this context and will prempt the execution + * of any dependent fixture tests. + * + * A bare "return;" statement may be used to return early. */ -#define _FIXTURE_SETUP(fixture_name) \ +#define FIXTURE_SETUP(fixture_name) \ void fixture_name##_setup( \ struct __test_metadata __attribute__((unused)) *_metadata, \ - _FIXTURE_DATA(fixture_name) __attribute__((unused)) *self) -#define _FIXTURE_TEARDOWN(fixture_name) \ + FIXTURE_DATA(fixture_name) __attribute__((unused)) *self) +/** + * FIXTURE_TEARDOWN(fixture_name) + * + * @fixture_name: fixture name + * + * .. code-block:: c + * + * FIXTURE_TEARDOWN(fixture name) { implementation } + * + * Populates the required "teardown" function for a fixture. An instance of the + * datatype defined with FIXTURE_DATA() will be exposed as *self* for the + * implementation to clean up. + * + * A bare "return;" statement may be used to return early. + */ +#define FIXTURE_TEARDOWN(fixture_name) \ void fixture_name##_teardown( \ struct __test_metadata __attribute__((unused)) *_metadata, \ - _FIXTURE_DATA(fixture_name) __attribute__((unused)) *self) + FIXTURE_DATA(fixture_name) __attribute__((unused)) *self) -/* Emits test registration and helpers for fixture-based test - * cases. - * TODO(wad) register fixtures on dedicated test lists. +/** + * TEST_F(fixture_name, test_name) - Emits test registration and helpers for + * fixture-based test cases + * + * @fixture_name: fixture name + * @test_name: test name + * + * .. code-block:: c + * + * TEST_F(fixture, name) { implementation } + * + * Defines a test that depends on a fixture (e.g., is part of a test case). + * Very similar to TEST() except that *self* is the setup instance of fixture's + * datatype exposed for use by the implementation. */ -#define _TEST_F(fixture_name, test_name) \ +/* TODO(wad) register fixtures on dedicated test lists. */ +#define TEST_F(fixture_name, test_name) \ __TEST_F_IMPL(fixture_name, test_name, -1) -#define _TEST_F_SIGNAL(fixture_name, test_name, signal) \ +#define TEST_F_SIGNAL(fixture_name, test_name, signal) \ __TEST_F_IMPL(fixture_name, test_name, signal) #define __TEST_F_IMPL(fixture_name, test_name, signal) \ static void fixture_name##_##test_name( \ struct __test_metadata *_metadata, \ - _FIXTURE_DATA(fixture_name) *self); \ + FIXTURE_DATA(fixture_name) *self); \ static inline void wrapper_##fixture_name##_##test_name( \ struct __test_metadata *_metadata) \ { \ /* fixture data is alloced, setup, and torn down per call. */ \ - _FIXTURE_DATA(fixture_name) self; \ - memset(&self, 0, sizeof(_FIXTURE_DATA(fixture_name))); \ + FIXTURE_DATA(fixture_name) self; \ + memset(&self, 0, sizeof(FIXTURE_DATA(fixture_name))); \ fixture_name##_setup(_metadata, &self); \ /* Let setup failure terminate early. */ \ if (!_metadata->passed) \ @@ -292,10 +280,18 @@ } \ static void fixture_name##_##test_name( \ struct __test_metadata __attribute__((unused)) *_metadata, \ - _FIXTURE_DATA(fixture_name) __attribute__((unused)) *self) + FIXTURE_DATA(fixture_name) __attribute__((unused)) *self) -/* Exports a simple wrapper to run the test harness. */ -#define _TEST_HARNESS_MAIN \ +/** + * TEST_HARNESS_MAIN - Simple wrapper to run the test harness + * + * .. code-block:: c + * + * TEST_HARNESS_MAIN + * + * Use once to append a main() to the test file. + */ +#define TEST_HARNESS_MAIN \ static void __attribute__((constructor)) \ __constructor_order_last(void) \ { \ @@ -306,54 +302,249 @@ return test_harness_run(argc, argv); \ } -#define _ASSERT_EQ(_expected, _seen) \ - __EXPECT(_expected, _seen, ==, 1) -#define _ASSERT_NE(_expected, _seen) \ - __EXPECT(_expected, _seen, !=, 1) -#define _ASSERT_LT(_expected, _seen) \ - __EXPECT(_expected, _seen, <, 1) -#define _ASSERT_LE(_expected, _seen) \ - __EXPECT(_expected, _seen, <=, 1) -#define _ASSERT_GT(_expected, _seen) \ - __EXPECT(_expected, _seen, >, 1) -#define _ASSERT_GE(_expected, _seen) \ - __EXPECT(_expected, _seen, >=, 1) -#define _ASSERT_NULL(_seen) \ - __EXPECT(NULL, _seen, ==, 1) - -#define _ASSERT_TRUE(_seen) \ - _ASSERT_NE(0, _seen) -#define _ASSERT_FALSE(_seen) \ - _ASSERT_EQ(0, _seen) -#define _ASSERT_STREQ(_expected, _seen) \ - __EXPECT_STR(_expected, _seen, ==, 1) -#define _ASSERT_STRNE(_expected, _seen) \ - __EXPECT_STR(_expected, _seen, !=, 1) - -#define _EXPECT_EQ(_expected, _seen) \ - __EXPECT(_expected, _seen, ==, 0) -#define _EXPECT_NE(_expected, _seen) \ - __EXPECT(_expected, _seen, !=, 0) -#define _EXPECT_LT(_expected, _seen) \ - __EXPECT(_expected, _seen, <, 0) -#define _EXPECT_LE(_expected, _seen) \ - __EXPECT(_expected, _seen, <=, 0) -#define _EXPECT_GT(_expected, _seen) \ - __EXPECT(_expected, _seen, >, 0) -#define _EXPECT_GE(_expected, _seen) \ - __EXPECT(_expected, _seen, >=, 0) - -#define _EXPECT_NULL(_seen) \ - __EXPECT(NULL, _seen, ==, 0) -#define _EXPECT_TRUE(_seen) \ - _EXPECT_NE(0, _seen) -#define _EXPECT_FALSE(_seen) \ - _EXPECT_EQ(0, _seen) - -#define _EXPECT_STREQ(_expected, _seen) \ - __EXPECT_STR(_expected, _seen, ==, 0) -#define _EXPECT_STRNE(_expected, _seen) \ - __EXPECT_STR(_expected, _seen, !=, 0) +/** + * DOC: operators + * + * Operators for use in TEST() and TEST_F(). + * ASSERT_* calls will stop test execution immediately. + * EXPECT_* calls will emit a failure warning, note it, and continue. + */ + +/** + * ASSERT_EQ(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * ASSERT_EQ(expected, measured): expected == measured + */ +#define ASSERT_EQ(expected, seen) \ + __EXPECT(expected, seen, ==, 1) + +/** + * ASSERT_NE(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * ASSERT_NE(expected, measured): expected != measured + */ +#define ASSERT_NE(expected, seen) \ + __EXPECT(expected, seen, !=, 1) + +/** + * ASSERT_LT(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * ASSERT_LT(expected, measured): expected < measured + */ +#define ASSERT_LT(expected, seen) \ + __EXPECT(expected, seen, <, 1) + +/** + * ASSERT_LE(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * ASSERT_LE(expected, measured): expected <= measured + */ +#define ASSERT_LE(expected, seen) \ + __EXPECT(expected, seen, <=, 1) + +/** + * ASSERT_GT(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * ASSERT_GT(expected, measured): expected > measured + */ +#define ASSERT_GT(expected, seen) \ + __EXPECT(expected, seen, >, 1) + +/** + * ASSERT_GE(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * ASSERT_GE(expected, measured): expected >= measured + */ +#define ASSERT_GE(expected, seen) \ + __EXPECT(expected, seen, >=, 1) + +/** + * ASSERT_NULL(seen) + * + * @seen: measured value + * + * ASSERT_NULL(measured): NULL == measured + */ +#define ASSERT_NULL(seen) \ + __EXPECT(NULL, seen, ==, 1) + +/** + * ASSERT_TRUE(seen) + * + * @seen: measured value + * + * ASSERT_TRUE(measured): measured != 0 + */ +#define ASSERT_TRUE(seen) \ + ASSERT_NE(0, seen) + +/** + * ASSERT_FALSE(seen) + * + * @seen: measured value + * + * ASSERT_FALSE(measured): measured == 0 + */ +#define ASSERT_FALSE(seen) \ + ASSERT_EQ(0, seen) + +/** + * ASSERT_STREQ(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * ASSERT_STREQ(expected, measured): !strcmp(expected, measured) + */ +#define ASSERT_STREQ(expected, seen) \ + __EXPECT_STR(expected, seen, ==, 1) + +/** + * ASSERT_STRNE(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * ASSERT_STRNE(expected, measured): strcmp(expected, measured) + */ +#define ASSERT_STRNE(expected, seen) \ + __EXPECT_STR(expected, seen, !=, 1) + +/** + * EXPECT_EQ(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * EXPECT_EQ(expected, measured): expected == measured + */ +#define EXPECT_EQ(expected, seen) \ + __EXPECT(expected, seen, ==, 0) + +/** + * EXPECT_NE(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * EXPECT_NE(expected, measured): expected != measured + */ +#define EXPECT_NE(expected, seen) \ + __EXPECT(expected, seen, !=, 0) + +/** + * EXPECT_LT(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * EXPECT_LT(expected, measured): expected < measured + */ +#define EXPECT_LT(expected, seen) \ + __EXPECT(expected, seen, <, 0) + +/** + * EXPECT_LE(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * EXPECT_LE(expected, measured): expected <= measured + */ +#define EXPECT_LE(expected, seen) \ + __EXPECT(expected, seen, <=, 0) + +/** + * EXPECT_GT(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * EXPECT_GT(expected, measured): expected > measured + */ +#define EXPECT_GT(expected, seen) \ + __EXPECT(expected, seen, >, 0) + +/** + * EXPECT_GE(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * EXPECT_GE(expected, measured): expected >= measured + */ +#define EXPECT_GE(expected, seen) \ + __EXPECT(expected, seen, >=, 0) + +/** + * EXPECT_NULL(seen) + * + * @seen: measured value + * + * EXPECT_NULL(measured): NULL == measured + */ +#define EXPECT_NULL(seen) \ + __EXPECT(NULL, seen, ==, 0) + +/** + * EXPECT_TRUE(seen) + * + * @seen: measured value + * + * EXPECT_TRUE(measured): 0 != measured + */ +#define EXPECT_TRUE(seen) \ + EXPECT_NE(0, seen) + +/** + * EXPECT_FALSE(seen) + * + * @seen: measured value + * + * EXPECT_FALSE(measured): 0 == measured + */ +#define EXPECT_FALSE(seen) \ + EXPECT_EQ(0, seen) + +/** + * EXPECT_STREQ(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * EXPECT_STREQ(expected, measured): !strcmp(expected, measured) + */ +#define EXPECT_STREQ(expected, seen) \ + __EXPECT_STR(expected, seen, ==, 0) + +/** + * EXPECT_STRNE(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * EXPECT_STRNE(expected, measured): strcmp(expected, measured) + */ +#define EXPECT_STRNE(expected, seen) \ + __EXPECT_STR(expected, seen, !=, 0) #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) @@ -532,4 +723,4 @@ static void __attribute__((constructor)) __constructor_order_first(void) __constructor_order = _CONSTRUCTOR_ORDER_FORWARD; } -#endif /* TEST_HARNESS_H_ */ +#endif /* __KSELFTEST_HARNESS_H */ diff --git a/tools/testing/selftests/lib/bitmap.sh b/tools/testing/selftests/lib/bitmap.sh index 2da187b6ddad..b073c22a3435 100755 --- a/tools/testing/selftests/lib/bitmap.sh +++ b/tools/testing/selftests/lib/bitmap.sh @@ -1,5 +1,9 @@ #!/bin/sh # Runs bitmap infrastructure tests using test_bitmap kernel module +if ! /sbin/modprobe -q -n test_bitmap; then + echo "bitmap: [SKIP]" + exit 77 +fi if /sbin/modprobe -q test_bitmap; then /sbin/modprobe -q -r test_bitmap diff --git a/tools/testing/selftests/lib/printf.sh b/tools/testing/selftests/lib/printf.sh index 4fdc70fe6980..cbf3b124bd94 100755 --- a/tools/testing/selftests/lib/printf.sh +++ b/tools/testing/selftests/lib/printf.sh @@ -1,5 +1,9 @@ #!/bin/sh # Runs printf infrastructure using test_printf kernel module +if ! /sbin/modprobe -q -n test_printf; then + echo "printf: [SKIP]" + exit 77 +fi if /sbin/modprobe -q test_printf; then /sbin/modprobe -q -r test_printf diff --git a/tools/testing/selftests/membarrier/membarrier_test.c b/tools/testing/selftests/membarrier/membarrier_test.c index 535f0fef4d0b..21399fcf1a59 100644 --- a/tools/testing/selftests/membarrier/membarrier_test.c +++ b/tools/testing/selftests/membarrier/membarrier_test.c @@ -7,56 +7,63 @@ #include "../kselftest.h" -enum test_membarrier_status { - TEST_MEMBARRIER_PASS = 0, - TEST_MEMBARRIER_FAIL, - TEST_MEMBARRIER_SKIP, -}; - static int sys_membarrier(int cmd, int flags) { return syscall(__NR_membarrier, cmd, flags); } -static enum test_membarrier_status test_membarrier_cmd_fail(void) +static int test_membarrier_cmd_fail(void) { int cmd = -1, flags = 0; if (sys_membarrier(cmd, flags) != -1) { - printf("membarrier: Wrong command should fail but passed.\n"); - return TEST_MEMBARRIER_FAIL; + ksft_exit_fail_msg( + "sys membarrier invalid command test: command = %d, flags = %d. Should fail, but passed\n", + cmd, flags); } - return TEST_MEMBARRIER_PASS; + + ksft_test_result_pass( + "sys membarrier invalid command test: command = %d, flags = %d. Failed as expected\n", + cmd, flags); + return 0; } -static enum test_membarrier_status test_membarrier_flags_fail(void) +static int test_membarrier_flags_fail(void) { int cmd = MEMBARRIER_CMD_QUERY, flags = 1; if (sys_membarrier(cmd, flags) != -1) { - printf("membarrier: Wrong flags should fail but passed.\n"); - return TEST_MEMBARRIER_FAIL; + ksft_exit_fail_msg( + "sys membarrier MEMBARRIER_CMD_QUERY invalid flags test: flags = %d. Should fail, but passed\n", + flags); } - return TEST_MEMBARRIER_PASS; + + ksft_test_result_pass( + "sys membarrier MEMBARRIER_CMD_QUERY invalid flags test: flags = %d. Failed as expected\n", + flags); + return 0; } -static enum test_membarrier_status test_membarrier_success(void) +static int test_membarrier_success(void) { int cmd = MEMBARRIER_CMD_SHARED, flags = 0; + const char *test_name = "sys membarrier MEMBARRIER_CMD_SHARED\n"; if (sys_membarrier(cmd, flags) != 0) { - printf("membarrier: Executing MEMBARRIER_CMD_SHARED failed. %s.\n", - strerror(errno)); - return TEST_MEMBARRIER_FAIL; + ksft_exit_fail_msg( + "sys membarrier MEMBARRIER_CMD_SHARED test: flags = %d\n", + flags); } - printf("membarrier: MEMBARRIER_CMD_SHARED success.\n"); - return TEST_MEMBARRIER_PASS; + ksft_test_result_pass( + "sys membarrier MEMBARRIER_CMD_SHARED test: flags = %d\n", + flags); + return 0; } -static enum test_membarrier_status test_membarrier(void) +static int test_membarrier(void) { - enum test_membarrier_status status; + int status; status = test_membarrier_cmd_fail(); if (status) @@ -67,52 +74,38 @@ static enum test_membarrier_status test_membarrier(void) status = test_membarrier_success(); if (status) return status; - return TEST_MEMBARRIER_PASS; + return 0; } -static enum test_membarrier_status test_membarrier_query(void) +static int test_membarrier_query(void) { int flags = 0, ret; - printf("membarrier MEMBARRIER_CMD_QUERY "); ret = sys_membarrier(MEMBARRIER_CMD_QUERY, flags); if (ret < 0) { - printf("failed. %s.\n", strerror(errno)); - switch (errno) { - case ENOSYS: + if (errno == ENOSYS) { /* * It is valid to build a kernel with * CONFIG_MEMBARRIER=n. However, this skips the tests. */ - return TEST_MEMBARRIER_SKIP; - case EINVAL: - default: - return TEST_MEMBARRIER_FAIL; + ksft_exit_skip( + "sys membarrier (CONFIG_MEMBARRIER) is disabled.\n"); } + ksft_exit_fail_msg("sys_membarrier() failed\n"); } - if (!(ret & MEMBARRIER_CMD_SHARED)) { - printf("command MEMBARRIER_CMD_SHARED is not supported.\n"); - return TEST_MEMBARRIER_FAIL; - } - printf("syscall available.\n"); - return TEST_MEMBARRIER_PASS; + if (!(ret & MEMBARRIER_CMD_SHARED)) + ksft_exit_fail_msg("sys_membarrier is not supported.\n"); + + ksft_test_result_pass("sys_membarrier available\n"); + return 0; } int main(int argc, char **argv) { - switch (test_membarrier_query()) { - case TEST_MEMBARRIER_FAIL: - return ksft_exit_fail(); - case TEST_MEMBARRIER_SKIP: - return ksft_exit_skip(); - } - switch (test_membarrier()) { - case TEST_MEMBARRIER_FAIL: - return ksft_exit_fail(); - case TEST_MEMBARRIER_SKIP: - return ksft_exit_skip(); - } + ksft_print_header(); + + test_membarrier_query(); + test_membarrier(); - printf("membarrier: tests done!\n"); - return ksft_exit_pass(); + ksft_exit_pass(); } diff --git a/tools/testing/selftests/memfd/Makefile b/tools/testing/selftests/memfd/Makefile index 79891d033de1..ad8a0897e47f 100644 --- a/tools/testing/selftests/memfd/Makefile +++ b/tools/testing/selftests/memfd/Makefile @@ -7,7 +7,7 @@ TEST_PROGS := run_fuse_test.sh TEST_GEN_FILES := memfd_test fuse_mnt fuse_test fuse_mnt.o: CFLAGS += $(shell pkg-config fuse --cflags) -fuse_mnt: LDFLAGS += $(shell pkg-config fuse --libs) include ../lib.mk +$(OUTPUT)/fuse_mnt: LDLIBS += $(shell pkg-config fuse --libs) diff --git a/tools/testing/selftests/memory-hotplug/mem-on-off-test.sh b/tools/testing/selftests/memory-hotplug/mem-on-off-test.sh index 6cddde0b96f8..35025ce9ca66 100755 --- a/tools/testing/selftests/memory-hotplug/mem-on-off-test.sh +++ b/tools/testing/selftests/memory-hotplug/mem-on-off-test.sh @@ -22,6 +22,11 @@ prerequisite() echo $msg memory hotplug is not supported >&2 exit 0 fi + + if ! grep -q 1 $SYSFS/devices/system/memory/memory*/removable; then + echo $msg no hot-pluggable memory >&2 + exit 0 + fi } # @@ -39,7 +44,7 @@ hotpluggable_memory() done } -hotplaggable_offline_memory() +hotpluggable_offline_memory() { hotpluggable_memory offline } @@ -75,9 +80,12 @@ online_memory_expect_success() if ! online_memory $memory; then echo $FUNCNAME $memory: unexpected fail >&2 + return 1 elif ! memory_is_online $memory; then echo $FUNCNAME $memory: unexpected offline >&2 + return 1 fi + return 0 } online_memory_expect_fail() @@ -86,9 +94,12 @@ online_memory_expect_fail() if online_memory $memory 2> /dev/null; then echo $FUNCNAME $memory: unexpected success >&2 + return 1 elif ! memory_is_offline $memory; then echo $FUNCNAME $memory: unexpected online >&2 + return 1 fi + return 0 } offline_memory_expect_success() @@ -97,9 +108,12 @@ offline_memory_expect_success() if ! offline_memory $memory; then echo $FUNCNAME $memory: unexpected fail >&2 + return 1 elif ! memory_is_offline $memory; then echo $FUNCNAME $memory: unexpected offline >&2 + return 1 fi + return 0 } offline_memory_expect_fail() @@ -108,14 +122,18 @@ offline_memory_expect_fail() if offline_memory $memory 2> /dev/null; then echo $FUNCNAME $memory: unexpected success >&2 + return 1 elif ! memory_is_online $memory; then echo $FUNCNAME $memory: unexpected offline >&2 + return 1 fi + return 0 } error=-12 priority=0 ratio=10 +retval=0 while getopts e:hp:r: opt; do case $opt in @@ -131,6 +149,10 @@ while getopts e:hp:r: opt; do ;; r) ratio=$OPTARG + if [ "$ratio" -gt 100 ] || [ "$ratio" -lt 0 ]; then + echo "The percentage should be an integer within 0~100 range" + exit 1 + fi ;; esac done @@ -143,35 +165,58 @@ fi prerequisite echo "Test scope: $ratio% hotplug memory" -echo -e "\t online all hotplug memory in offline state" -echo -e "\t offline $ratio% hotplug memory in online state" -echo -e "\t online all hotplug memory in offline state" # # Online all hot-pluggable memory # -for memory in `hotplaggable_offline_memory`; do - echo offline-online $memory - online_memory_expect_success $memory -done +hotpluggable_num=`hotpluggable_offline_memory | wc -l` +echo -e "\t online all hot-pluggable memory in offline state:" +if [ "$hotpluggable_num" -gt 0 ]; then + for memory in `hotpluggable_offline_memory`; do + echo "offline->online memory$memory" + if ! online_memory_expect_success $memory; then + retval=1 + fi + done +else + echo -e "\t\t SKIPPED - no hot-pluggable memory in offline state" +fi # # Offline $ratio percent of hot-pluggable memory # +hotpluggable_num=`hotpluggable_online_memory | wc -l` +target=`echo "a=$hotpluggable_num*$ratio; if ( a%100 ) a/100+1 else a/100" | bc` +echo -e "\t offline $ratio% hot-pluggable memory in online state" +echo -e "\t trying to offline $target out of $hotpluggable_num memory block(s):" for memory in `hotpluggable_online_memory`; do - if [ $((RANDOM % 100)) -lt $ratio ]; then - echo online-offline $memory - offline_memory_expect_success $memory + if [ "$target" -gt 0 ]; then + echo "online->offline memory$memory" + if offline_memory_expect_success $memory; then + target=$(($target - 1)) + fi fi done +if [ "$target" -gt 0 ]; then + retval=1 + echo -e "\t\t FAILED - unable to offline some memory blocks, device busy?" +fi # # Online all hot-pluggable memory again # -for memory in `hotplaggable_offline_memory`; do - echo offline-online $memory - online_memory_expect_success $memory -done +hotpluggable_num=`hotpluggable_offline_memory | wc -l` +echo -e "\t online all hot-pluggable memory in offline state:" +if [ "$hotpluggable_num" -gt 0 ]; then + for memory in `hotpluggable_offline_memory`; do + echo "offline->online memory$memory" + if ! online_memory_expect_success $memory; then + retval=1 + fi + done +else + echo -e "\t\t SKIPPED - no hot-pluggable memory in offline state" +fi # # Test with memory notifier error injection @@ -189,15 +234,16 @@ prerequisite_extra() if [ ! -d "$DEBUGFS" ]; then echo $msg debugfs is not mounted >&2 - exit 0 + exit $retval fi if [ ! -d $NOTIFIER_ERR_INJECT_DIR ]; then echo $msg memory-notifier-error-inject module is not available >&2 - exit 0 + exit $retval fi } +echo -e "\t Test with memory notifier error injection" prerequisite_extra # @@ -214,7 +260,7 @@ done # Test memory hot-add error handling (offline => online) # echo $error > $NOTIFIER_ERR_INJECT_DIR/actions/MEM_GOING_ONLINE/error -for memory in `hotplaggable_offline_memory`; do +for memory in `hotpluggable_offline_memory`; do online_memory_expect_fail $memory done @@ -222,7 +268,7 @@ done # Online all hot-pluggable memory # echo 0 > $NOTIFIER_ERR_INJECT_DIR/actions/MEM_GOING_ONLINE/error -for memory in `hotplaggable_offline_memory`; do +for memory in `hotpluggable_offline_memory`; do online_memory_expect_success $memory done @@ -236,3 +282,5 @@ done echo 0 > $NOTIFIER_ERR_INJECT_DIR/actions/MEM_GOING_OFFLINE/error /sbin/modprobe -q -r memory-notifier-error-inject + +exit $retval diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index 35cbb4cba410..f6c9dbf478f8 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -3,8 +3,6 @@ CFLAGS = -Wall -Wl,--no-as-needed -O2 -g CFLAGS += -I../../../../usr/include/ -reuseport_bpf_numa: LDFLAGS += -lnuma - TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh TEST_GEN_FILES = socket TEST_GEN_FILES += psock_fanout psock_tpacket @@ -13,3 +11,4 @@ TEST_GEN_FILES += reuseport_dualstack include ../lib.mk +$(OUTPUT)/reuseport_bpf_numa: LDFLAGS += -lnuma diff --git a/tools/testing/selftests/powerpc/benchmarks/context_switch.c b/tools/testing/selftests/powerpc/benchmarks/context_switch.c index 778f5fbfd784..f4241339edd2 100644 --- a/tools/testing/selftests/powerpc/benchmarks/context_switch.c +++ b/tools/testing/selftests/powerpc/benchmarks/context_switch.c @@ -258,9 +258,14 @@ static unsigned long xchg(unsigned long *p, unsigned long val) return __atomic_exchange_n(p, val, __ATOMIC_SEQ_CST); } +static int processes; + static int mutex_lock(unsigned long *m) { int c; + int flags = FUTEX_WAIT; + if (!processes) + flags |= FUTEX_PRIVATE_FLAG; c = cmpxchg(m, 0, 1); if (!c) @@ -270,7 +275,7 @@ static int mutex_lock(unsigned long *m) c = xchg(m, 2); while (c) { - sys_futex(m, FUTEX_WAIT, 2, NULL, NULL, 0); + sys_futex(m, flags, 2, NULL, NULL, 0); c = xchg(m, 2); } @@ -279,12 +284,16 @@ static int mutex_lock(unsigned long *m) static int mutex_unlock(unsigned long *m) { + int flags = FUTEX_WAKE; + if (!processes) + flags |= FUTEX_PRIVATE_FLAG; + if (*m == 2) *m = 0; else if (xchg(m, 0) == 1) return 0; - sys_futex(m, FUTEX_WAKE, 1, NULL, NULL, 0); + sys_futex(m, flags, 1, NULL, NULL, 0); return 0; } @@ -293,26 +302,32 @@ static unsigned long *m1, *m2; static void futex_setup(int cpu1, int cpu2) { - int shmid; - void *shmaddr; + if (!processes) { + static unsigned long _m1, _m2; + m1 = &_m1; + m2 = &_m2; + } else { + int shmid; + void *shmaddr; - shmid = shmget(IPC_PRIVATE, getpagesize(), SHM_R | SHM_W); - if (shmid < 0) { - perror("shmget"); - exit(1); - } + shmid = shmget(IPC_PRIVATE, getpagesize(), SHM_R | SHM_W); + if (shmid < 0) { + perror("shmget"); + exit(1); + } - shmaddr = shmat(shmid, NULL, 0); - if (shmaddr == (char *)-1) { - perror("shmat"); - shmctl(shmid, IPC_RMID, NULL); - exit(1); - } + shmaddr = shmat(shmid, NULL, 0); + if (shmaddr == (char *)-1) { + perror("shmat"); + shmctl(shmid, IPC_RMID, NULL); + exit(1); + } - shmctl(shmid, IPC_RMID, NULL); + shmctl(shmid, IPC_RMID, NULL); - m1 = shmaddr; - m2 = shmaddr + sizeof(*m1); + m1 = shmaddr; + m2 = shmaddr + sizeof(*m1); + } *m1 = 0; *m2 = 0; @@ -352,8 +367,6 @@ static struct actions futex_actions = { .thread2 = futex_thread2, }; -static int processes; - static struct option options[] = { { "test", required_argument, 0, 't' }, { "process", no_argument, &processes, 1 }, diff --git a/tools/testing/selftests/seccomp/Makefile b/tools/testing/selftests/seccomp/Makefile index 5fa6fd2246b1..aeb0c805f3ca 100644 --- a/tools/testing/selftests/seccomp/Makefile +++ b/tools/testing/selftests/seccomp/Makefile @@ -4,3 +4,5 @@ LDFLAGS += -lpthread include ../lib.mk +$(TEST_GEN_PROGS): seccomp_bpf.c ../kselftest_harness.h + $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index 03f1fa495d74..73f5ea6778ce 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -37,7 +37,7 @@ #include <unistd.h> #include <sys/syscall.h> -#include "test_harness.h" +#include "../kselftest_harness.h" #ifndef PR_SET_PTRACER # define PR_SET_PTRACER 0x59616d61 @@ -1310,7 +1310,7 @@ void change_syscall(struct __test_metadata *_metadata, iov.iov_len = sizeof(regs); ret = ptrace(PTRACE_GETREGSET, tracee, NT_PRSTATUS, &iov); #endif - EXPECT_EQ(0, ret); + EXPECT_EQ(0, ret) {} #if defined(__x86_64__) || defined(__i386__) || defined(__powerpc__) || \ defined(__s390__) || defined(__hppa__) @@ -1822,6 +1822,23 @@ struct tsync_sibling { struct __test_metadata *metadata; }; +/* + * To avoid joining joined threads (which is not allowed by Bionic), + * make sure we both successfully join and clear the tid to skip a + * later join attempt during fixture teardown. Any remaining threads + * will be directly killed during teardown. + */ +#define PTHREAD_JOIN(tid, status) \ + do { \ + int _rc = pthread_join(tid, status); \ + if (_rc) { \ + TH_LOG("pthread_join of tid %u failed: %d\n", \ + (unsigned int)tid, _rc); \ + } else { \ + tid = 0; \ + } \ + } while (0) + FIXTURE_DATA(TSYNC) { struct sock_fprog root_prog, apply_prog; struct tsync_sibling sibling[TSYNC_SIBLINGS]; @@ -1890,14 +1907,14 @@ FIXTURE_TEARDOWN(TSYNC) for ( ; sib < self->sibling_count; ++sib) { struct tsync_sibling *s = &self->sibling[sib]; - void *status; if (!s->tid) continue; - if (pthread_kill(s->tid, 0)) { - pthread_cancel(s->tid); - pthread_join(s->tid, &status); - } + /* + * If a thread is still running, it may be stuck, so hit + * it over the head really hard. + */ + pthread_kill(s->tid, 9); } pthread_mutex_destroy(&self->mutex); pthread_cond_destroy(&self->cond); @@ -1987,9 +2004,9 @@ TEST_F(TSYNC, siblings_fail_prctl) pthread_mutex_unlock(&self->mutex); /* Ensure diverging sibling failed to call prctl. */ - pthread_join(self->sibling[0].tid, &status); + PTHREAD_JOIN(self->sibling[0].tid, &status); EXPECT_EQ(SIBLING_EXIT_FAILURE, (long)status); - pthread_join(self->sibling[1].tid, &status); + PTHREAD_JOIN(self->sibling[1].tid, &status); EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status); } @@ -2029,9 +2046,9 @@ TEST_F(TSYNC, two_siblings_with_ancestor) } pthread_mutex_unlock(&self->mutex); /* Ensure they are both killed and don't exit cleanly. */ - pthread_join(self->sibling[0].tid, &status); + PTHREAD_JOIN(self->sibling[0].tid, &status); EXPECT_EQ(0x0, (long)status); - pthread_join(self->sibling[1].tid, &status); + PTHREAD_JOIN(self->sibling[1].tid, &status); EXPECT_EQ(0x0, (long)status); } @@ -2055,9 +2072,9 @@ TEST_F(TSYNC, two_sibling_want_nnp) pthread_mutex_unlock(&self->mutex); /* Ensure they are both upset about lacking nnp. */ - pthread_join(self->sibling[0].tid, &status); + PTHREAD_JOIN(self->sibling[0].tid, &status); EXPECT_EQ(SIBLING_EXIT_NEWPRIVS, (long)status); - pthread_join(self->sibling[1].tid, &status); + PTHREAD_JOIN(self->sibling[1].tid, &status); EXPECT_EQ(SIBLING_EXIT_NEWPRIVS, (long)status); } @@ -2095,9 +2112,9 @@ TEST_F(TSYNC, two_siblings_with_no_filter) pthread_mutex_unlock(&self->mutex); /* Ensure they are both killed and don't exit cleanly. */ - pthread_join(self->sibling[0].tid, &status); + PTHREAD_JOIN(self->sibling[0].tid, &status); EXPECT_EQ(0x0, (long)status); - pthread_join(self->sibling[1].tid, &status); + PTHREAD_JOIN(self->sibling[1].tid, &status); EXPECT_EQ(0x0, (long)status); } @@ -2140,9 +2157,9 @@ TEST_F(TSYNC, two_siblings_with_one_divergence) pthread_mutex_unlock(&self->mutex); /* Ensure they are both unkilled. */ - pthread_join(self->sibling[0].tid, &status); + PTHREAD_JOIN(self->sibling[0].tid, &status); EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status); - pthread_join(self->sibling[1].tid, &status); + PTHREAD_JOIN(self->sibling[1].tid, &status); EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status); } @@ -2199,7 +2216,7 @@ TEST_F(TSYNC, two_siblings_not_under_filter) TH_LOG("cond broadcast non-zero"); } pthread_mutex_unlock(&self->mutex); - pthread_join(self->sibling[sib].tid, &status); + PTHREAD_JOIN(self->sibling[sib].tid, &status); EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status); /* Poll for actual task death. pthread_join doesn't guarantee it. */ while (!kill(self->sibling[sib].system_tid, 0)) @@ -2224,7 +2241,7 @@ TEST_F(TSYNC, two_siblings_not_under_filter) TH_LOG("cond broadcast non-zero"); } pthread_mutex_unlock(&self->mutex); - pthread_join(self->sibling[sib].tid, &status); + PTHREAD_JOIN(self->sibling[sib].tid, &status); EXPECT_EQ(0, (long)status); /* Poll for actual task death. pthread_join doesn't guarantee it. */ while (!kill(self->sibling[sib].system_tid, 0)) diff --git a/tools/testing/selftests/size/get_size.c b/tools/testing/selftests/size/get_size.c index 2d1af7cca463..d4b59ab979a0 100644 --- a/tools/testing/selftests/size/get_size.c +++ b/tools/testing/selftests/size/get_size.c @@ -75,26 +75,31 @@ void _start(void) int ccode; struct sysinfo info; unsigned long used; + static const char *test_name = " get runtime memory use\n"; - print("Testing system size.\n"); - print("1..1\n"); + print("TAP version 13\n"); + print("# Testing system size.\n"); ccode = sysinfo(&info); if (ccode < 0) { - print("not ok 1 get runtime memory use\n"); - print("# could not get sysinfo\n"); + print("not ok 1"); + print(test_name); + print(" ---\n reason: \"could not get sysinfo\"\n ...\n"); _exit(ccode); } + print("ok 1"); + print(test_name); + /* ignore cache complexities for now */ used = info.totalram - info.freeram - info.bufferram; - print_k_value("ok 1 get runtime memory use # size = ", used, - info.mem_unit); - print("# System runtime memory report (units in Kilobytes):\n"); - print_k_value("# Total: ", info.totalram, info.mem_unit); - print_k_value("# Free: ", info.freeram, info.mem_unit); - print_k_value("# Buffer: ", info.bufferram, info.mem_unit); - print_k_value("# In use: ", used, info.mem_unit); + print(" ---\n"); + print_k_value(" Total: ", info.totalram, info.mem_unit); + print_k_value(" Free: ", info.freeram, info.mem_unit); + print_k_value(" Buffer: ", info.bufferram, info.mem_unit); + print_k_value(" In use: ", used, info.mem_unit); + print(" ...\n"); + print("1..1\n"); _exit(0); } diff --git a/tools/testing/selftests/sync/sync_test.c b/tools/testing/selftests/sync/sync_test.c index 9ea08d9f0b13..62fa666e501a 100644 --- a/tools/testing/selftests/sync/sync_test.c +++ b/tools/testing/selftests/sync/sync_test.c @@ -29,6 +29,7 @@ #include <unistd.h> #include <stdlib.h> #include <sys/types.h> +#include <sys/stat.h> #include <sys/wait.h> #include "synctest.h" @@ -52,10 +53,22 @@ static int run_test(int (*test)(void), char *name) exit(test()); } +static int sync_api_supported(void) +{ + struct stat sbuf; + + return 0 == stat("/sys/kernel/debug/sync/sw_sync", &sbuf); +} + int main(void) { int err = 0; + if (!sync_api_supported()) { + printf("SKIP: Sync framework not supported by kernel\n"); + return 0; + } + printf("[RUN]\tTesting sync framework\n"); err += RUN_TEST(test_alloc_timeline); diff --git a/tools/testing/selftests/sysctl/common_tests b/tools/testing/selftests/sysctl/common_tests index 17d534b1b7b4..b6862322962f 100644 --- a/tools/testing/selftests/sysctl/common_tests +++ b/tools/testing/selftests/sysctl/common_tests @@ -24,6 +24,14 @@ verify() return 0 } +exit_test() +{ + if [ ! -z ${old_strict} ]; then + echo ${old_strict} > ${WRITES_STRICT} + fi + exit $rc +} + trap 'set_orig; rm -f "${TEST_FILE}"' EXIT rc=0 @@ -63,6 +71,20 @@ else echo "ok" fi +echo -n "Checking write strict setting ... " +WRITES_STRICT="${SYSCTL}/kernel/sysctl_writes_strict" +if [ ! -e ${WRITES_STRICT} ]; then + echo "FAIL, but skip in case of old kernel" >&2 +else + old_strict=$(cat ${WRITES_STRICT}) + if [ "$old_strict" = "1" ]; then + echo "ok" + else + echo "FAIL, strict value is 0 but force to 1 to continue" >&2 + echo "1" > ${WRITES_STRICT} + fi +fi + # Now that we've validated the sanity of "set_test" and "set_orig", # we can use those functions to set starting states before running # specific behavioral tests. diff --git a/tools/testing/selftests/sysctl/run_numerictests b/tools/testing/selftests/sysctl/run_numerictests index 8510f93f2d14..e6e76c93d948 100755 --- a/tools/testing/selftests/sysctl/run_numerictests +++ b/tools/testing/selftests/sysctl/run_numerictests @@ -7,4 +7,4 @@ TEST_STR=$(( $ORIG + 1 )) . ./common_tests -exit $rc +exit_test diff --git a/tools/testing/selftests/sysctl/run_stringtests b/tools/testing/selftests/sysctl/run_stringtests index 90a9293d520c..857ec667fb02 100755 --- a/tools/testing/selftests/sysctl/run_stringtests +++ b/tools/testing/selftests/sysctl/run_stringtests @@ -74,4 +74,4 @@ else echo "ok" fi -exit $rc +exit_test diff --git a/tools/testing/selftests/tc-testing/.gitignore b/tools/testing/selftests/tc-testing/.gitignore new file mode 100644 index 000000000000..c18dd8d83cee --- /dev/null +++ b/tools/testing/selftests/tc-testing/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/tools/testing/selftests/tc-testing/README b/tools/testing/selftests/tc-testing/README new file mode 100644 index 000000000000..970ff294fec8 --- /dev/null +++ b/tools/testing/selftests/tc-testing/README @@ -0,0 +1,102 @@ +tdc - Linux Traffic Control (tc) unit testing suite + +Author: Lucas Bates - lucasb@mojatatu.com + +tdc is a Python script to load tc unit tests from a separate JSON file and +execute them inside a network namespace dedicated to the task. + + +REQUIREMENTS +------------ + +* Minimum Python version of 3.4. Earlier 3.X versions may work but are not + guaranteed. + +* The kernel must have network namespace support + +* The kernel must have veth support available, as a veth pair is created + prior to running the tests. + +* All tc-related features must be built in or available as modules. + To check what is required in current setup run: + ./tdc.py -c + + Note: + In the current release, tdc run will abort due to a failure in setup or + teardown commands - which includes not being able to run a test simply + because the kernel did not support a specific feature. (This will be + handled in a future version - the current workaround is to run the tests + on specific test categories that your kernel supports) + + +BEFORE YOU RUN +-------------- + +The path to the tc executable that will be most commonly tested can be defined +in the tdc_config.py file. Find the 'TC' entry in the NAMES dictionary and +define the path. + +If you need to test a different tc executable on the fly, you can do so by +using the -p option when running tdc: + ./tdc.py -p /path/to/tc + + +RUNNING TDC +----------- + +To use tdc, root privileges are required. tdc will not run otherwise. + +All tests are executed inside a network namespace to prevent conflicts +within the host. + +Running tdc without any arguments will run all tests. Refer to the section +on command line arguments for more information, or run: + ./tdc.py -h + +tdc will list the test names as they are being run, and print a summary in +TAP (Test Anything Protocol) format when they are done. If tests fail, +output captured from the failing test will be printed immediately following +the failed test in the TAP output. + + +USER-DEFINED CONSTANTS +---------------------- + +The tdc_config.py file contains multiple values that can be altered to suit +your needs. Any value in the NAMES dictionary can be altered without affecting +the tests to be run. These values are used in the tc commands that will be +executed as part of the test. More will be added as test cases require. + +Example: + $TC qdisc add dev $DEV1 ingress + + +COMMAND LINE ARGUMENTS +---------------------- + +Run tdc.py -h to see the full list of available arguments. + +-p PATH Specify the tc executable located at PATH to be used on this + test run +-c Show the available test case categories in this test file +-c CATEGORY Run only tests that belong to CATEGORY +-f FILE Read test cases from the JSON file named FILE +-l [CATEGORY] List all test cases in the JSON file. If CATEGORY is + specified, list test cases matching that category. +-s ID Show the test case matching ID +-e ID Execute the test case identified by ID +-i Generate unique ID numbers for test cases with no existing + ID number + + +ACKNOWLEDGEMENTS +---------------- + +Thanks to: + +Jamal Hadi Salim, for providing valuable test cases +Keara Leibovitz, who wrote the CLI test driver that I used as a base for the + first version of the tc testing suite. This work was presented at + Netdev 1.2 Tokyo in October 2016. +Samir Hussain, for providing help while I dove into Python for the first time + and being a second eye for this code. diff --git a/tools/testing/selftests/tc-testing/TODO.txt b/tools/testing/selftests/tc-testing/TODO.txt new file mode 100644 index 000000000000..6a266d811a78 --- /dev/null +++ b/tools/testing/selftests/tc-testing/TODO.txt @@ -0,0 +1,10 @@ +tc Testing Suite To-Do list: + +- Determine what tc features are supported in the kernel. If features are not + present, prevent the related categories from running. + +- Add support for multiple versions of tc to run successively + +- Improve error messages when tdc aborts its run + +- Allow tdc to write its results to file diff --git a/tools/testing/selftests/tc-testing/creating-testcases/AddingTestCases.txt b/tools/testing/selftests/tc-testing/creating-testcases/AddingTestCases.txt new file mode 100644 index 000000000000..4e09257bc443 --- /dev/null +++ b/tools/testing/selftests/tc-testing/creating-testcases/AddingTestCases.txt @@ -0,0 +1,69 @@ +tdc - Adding test cases for tdc + +Author: Lucas Bates - lucasb@mojatatu.com + +ADDING TEST CASES +----------------- + +User-defined tests should be added by defining a separate JSON file. This +will help prevent conflicts when updating the repository. Refer to +template.json for the required JSON format for test cases. + +Include the 'id' field, but do not assign a value. Running tdc with the -i +option will generate a unique ID for that test case. + +tdc will recursively search the 'tc' subdirectory for .json files. Any +test case files you create in these directories will automatically be included. +If you wish to store your custom test cases elsewhere, be sure to run tdc +with the -f argument and the path to your file. + +Be aware of required escape characters in the JSON data - particularly when +defining the match pattern. Refer to the tctests.json file for examples when +in doubt. + + +TEST CASE STRUCTURE +------------------- + +Each test case has required data: + +id: A unique alphanumeric value to identify a particular test case +name: Descriptive name that explains the command under test +category: A list of single-word descriptions covering what the command + under test is testing. Example: filter, actions, u32, gact, etc. +setup: The list of commands required to ensure the command under test + succeeds. For example: if testing a filter, the command to create + the qdisc would appear here. +cmdUnderTest: The tc command being tested itself. +expExitCode: The code returned by the command under test upon its termination. + tdc will compare this value against the actual returned value. +verifyCmd: The tc command to be run to verify successful execution. + For example: if the command under test creates a gact action, + verifyCmd should be "$TC actions show action gact" +matchPattern: A regular expression to be applied against the output of the + verifyCmd to prove the command under test succeeded. This pattern + should be as specific as possible so that a false positive is not + matched. +matchCount: How many times the regex in matchPattern should match. A value + of 0 is acceptable. +teardown: The list of commands to clean up after the test is completed. + The environment should be returned to the same state as when + this test was started: qdiscs deleted, actions flushed, etc. + + +SETUP/TEARDOWN ERRORS +--------------------- + +If an error is detected during the setup/teardown process, execution of the +tests will immediately stop with an error message and the namespace in which +the tests are run will be destroyed. This is to prevent inaccurate results +in the test cases. + +Repeated failures of the setup/teardown may indicate a problem with the test +case, or possibly even a bug in one of the commands that are not being tested. + +It's possible to include acceptable exit codes with the setup/teardown command +so that it doesn't halt the script for an error that doesn't matter. Turn the +individual command into a list, with the command being first, followed by all +acceptable exit codes for the command. + diff --git a/tools/testing/selftests/tc-testing/creating-testcases/template.json b/tools/testing/selftests/tc-testing/creating-testcases/template.json new file mode 100644 index 000000000000..87971744bdd4 --- /dev/null +++ b/tools/testing/selftests/tc-testing/creating-testcases/template.json @@ -0,0 +1,40 @@ +[ + { + "id": "", + "name": "", + "category": [ + "", + "" + ], + "setup": [ + "" + ], + "cmdUnderTest": "", + "expExitCode": "", + "verifyCmd": "", + "matchPattern": "", + "matchCount": "", + "teardown": [ + "" + ] + }, + { + "id": "", + "name": "", + "category": [ + "", + "" + ], + "setup": [ + "" + ], + "cmdUnderTest": "", + "expExitCode": "", + "verifyCmd": "", + "matchPattern": "", + "matchCount": "", + "teardown": [ + "" + ] + } +] diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/tests.json b/tools/testing/selftests/tc-testing/tc-tests/actions/tests.json new file mode 100644 index 000000000000..af519bc97a8e --- /dev/null +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/tests.json @@ -0,0 +1,1115 @@ +[ + { + "id": "e89a", + "name": "Add valid pass action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action pass index 8", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action pass.*index 8 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "a02c", + "name": "Add valid pipe action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action pipe index 6", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action pipe.*index 6 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "feef", + "name": "Add valid reclassify action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action reclassify index 5", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action reclassify.*index 5 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "8a7a", + "name": "Add valid drop action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action drop index 30", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action drop.*index 30 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "9a52", + "name": "Add valid continue action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action continue index 432", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action continue.*index 432 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "d700", + "name": "Add invalid action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action pump index 386", + "expExitCode": "255", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action.*index 386 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "9215", + "name": "Add action with duplicate index", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action pipe index 15" + ], + "cmdUnderTest": "$TC actions add action drop index 15", + "expExitCode": "255", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action drop.*index 15 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "798e", + "name": "Add action with index exceeding 32-bit maximum", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action drop index 4294967296", + "expExitCode": "255", + "verifyCmd": "actions list action gact", + "matchPattern": "action order [0-9]*: gact action drop.*index 4294967296 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "22be", + "name": "Add action with index at 32-bit maximum", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action drop index 4294967295", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action drop.*index 4294967295 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "ac2a", + "name": "List actions", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action reclassify index 101", + "$TC actions add action reclassify index 102", + "$TC actions add action reclassify index 103", + "$TC actions add action reclassify index 104", + "$TC actions add action reclassify index 105" + ], + "cmdUnderTest": "$TC actions list action gact", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action reclassify", + "matchCount": "5", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "63ec", + "name": "Delete pass action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action pass index 1" + ], + "cmdUnderTest": "$TC actions del action gact index 1", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action pass.*index 1 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "46be", + "name": "Delete pipe action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action pipe index 9" + ], + "cmdUnderTest": "$TC actions del action gact index 9", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action pipe.*index 9 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "2e08", + "name": "Delete reclassify action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action reclassify index 65536" + ], + "cmdUnderTest": "$TC actions del action gact index 65536", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action reclassify.*index 65536 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "99c4", + "name": "Delete drop action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action drop index 16" + ], + "cmdUnderTest": "$TC actions del action gact index 16", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action drop.*index 16 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "fb6b", + "name": "Delete continue action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action continue index 32" + ], + "cmdUnderTest": "$TC actions del action gact index 32", + "expExitCode": "0", + "verifyCmd": "actions list action gact", + "matchPattern": "action order [0-9]*: gact action continue.*index 32 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "0eb3", + "name": "Delete non-existent action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions del action gact index 2", + "expExitCode": "255", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action", + "matchCount": "0", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "5124", + "name": "Add mirred mirror to egress action", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action mirred egress mirror index 1 dev lo", + "expExitCode": "0", + "verifyCmd": "$TC actions list action mirred", + "matchPattern": "action order [0-9]*: mirred \\(Egress Mirror to device lo\\).*index 1 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "6fb4", + "name": "Add mirred redirect to egress action", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action mirred egress redirect index 2 dev lo action pipe", + "expExitCode": "0", + "verifyCmd": "$TC actions list action mirred", + "matchPattern": "action order [0-9]*: mirred \\(Egress Redirect to device lo\\).*index 2 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "ba38", + "name": "Get mirred actions", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ], + "$TC actions add action mirred egress mirror index 1 dev lo", + "$TC actions add action mirred egress redirect index 2 dev lo" + ], + "cmdUnderTest": "$TC actions show action mirred", + "expExitCode": "0", + "verifyCmd": "$TC actions list action mirred", + "matchPattern": "[Mirror|Redirect] to device lo", + "matchCount": "2", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "d7c0", + "name": "Add invalid mirred direction", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action mirred inbound mirror index 20 dev lo", + "expExitCode": "255", + "verifyCmd": "$TC actions list action mirred", + "matchPattern": "action order [0-9]*: mirred \\(.*to device lo\\).*index 20 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "e213", + "name": "Add invalid mirred action", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action mirred egress remirror index 20 dev lo", + "expExitCode": "255", + "verifyCmd": "$TC actions list action mirred", + "matchPattern": "action order [0-9]*: mirred \\(Egress.*to device lo\\).*index 20 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "2d89", + "name": "Add mirred action with invalid device", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action mirred egress mirror index 20 dev eltoh", + "expExitCode": "255", + "verifyCmd": "$TC actions list action mirred", + "matchPattern": "action order [0-9]*: mirred \\(.*to device eltoh\\).*index 20 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "300b", + "name": "Add mirred action with duplicate index", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ], + "$TC actions add action mirred egress redirect index 15 dev lo" + ], + "cmdUnderTest": "$TC actions add action mirred egress mirror index 15 dev lo", + "expExitCode": "255", + "verifyCmd": "$TC actions list action mirred", + "matchPattern": "action order [0-9]*: mirred \\(.*to device lo\\).*index 15 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "a70e", + "name": "Delete mirred mirror action", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ], + "$TC actions add action mirred egress mirror index 5 dev lo" + ], + "cmdUnderTest": "$TC actions del action mirred index 5", + "expExitCode": "0", + "verifyCmd": "$TC actions list action mirred", + "matchPattern": "action order [0-9]*: mirred \\(Egress Mirror to device lo\\).*index 5 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "3fb3", + "name": "Delete mirred redirect action", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ], + "$TC actions add action mirred egress redirect index 5 dev lo" + ], + "cmdUnderTest": "$TC actions del action mirred index 5", + "expExitCode": "0", + "verifyCmd": "$TC actions list action mirred", + "matchPattern": "action order [0-9]*: mirred \\(Egress Redirect to device lo\\).*index 5 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "b078", + "name": "Add simple action", + "category": [ + "actions", + "simple" + ], + "setup": [ + [ + "$TC actions flush action simple", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action simple sdata \"A triumph\" index 60", + "expExitCode": "0", + "verifyCmd": "$TC actions list action simple", + "matchPattern": "action order [0-9]*: Simple <A triumph>.*index 60 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action simple" + ] + }, + { + "id": "6d4c", + "name": "Add simple action with duplicate index", + "category": [ + "actions", + "simple" + ], + "setup": [ + [ + "$TC actions flush action simple", + 0, + 1, + 255 + ], + "$TC actions add action simple sdata \"Aruba\" index 4" + ], + "cmdUnderTest": "$TC actions add action simple sdata \"Jamaica\" index 4", + "expExitCode": "255", + "verifyCmd": "$TC actions list action simple", + "matchPattern": "action order [0-9]*: Simple <Jamaica>.*ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action simple" + ] + }, + { + "id": "2542", + "name": "List simple actions", + "category": [ + "actions", + "simple" + ], + "setup": [ + [ + "$TC actions flush action simple", + 0, + 1, + 255 + ], + "$TC actions add action simple sdata \"Rock\"", + "$TC actions add action simple sdata \"Paper\"", + "$TC actions add action simple sdata \"Scissors\" index 98" + ], + "cmdUnderTest": "$TC actions list action simple", + "expExitCode": "0", + "verifyCmd": "$TC actions list action simple", + "matchPattern": "action order [0-9]*: Simple <[A-Z][a-z]*>", + "matchCount": "3", + "teardown": [ + "$TC actions flush action simple" + ] + }, + { + "id": "ea67", + "name": "Delete simple action", + "category": [ + "actions", + "simple" + ], + "setup": [ + [ + "$TC actions flush action simple", + 0, + 1, + 255 + ], + "$TC actions add action simple sdata \"Blinkenlights\" index 1" + ], + "cmdUnderTest": "$TC actions delete action simple index 1", + "expExitCode": "0", + "verifyCmd": "$TC actions list action simple", + "matchPattern": "action order [0-9]*: Simple <Blinkenlights>.*index 1 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action simple" + ] + }, + { + "id": "8ff1", + "name": "Flush simple actions", + "category": [ + "actions", + "simple" + ], + "setup": [ + [ + "$TC actions flush action simple", + 0, + 1, + 255 + ], + "$TC actions add action simple sdata \"Kirk\"", + "$TC actions add action simple sdata \"Spock\" index 50", + "$TC actions add action simple sdata \"McCoy\" index 9" + ], + "cmdUnderTest": "$TC actions flush action simple", + "expExitCode": "0", + "verifyCmd": "$TC actions list action simple", + "matchPattern": "action order [0-9]*: Simple <[A-Z][a-z]*>", + "matchCount": "0", + "teardown": [ + "" + ] + }, + { + "id": "6236", + "name": "Add skbedit action with valid mark", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit mark 1", + "expExitCode": "0", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit mark 1", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "407b", + "name": "Add skbedit action with invalid mark", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit mark 666777888999", + "expExitCode": "255", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit mark", + "matchCount": "0", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "081d", + "name": "Add skbedit action with priority", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit prio 99", + "expExitCode": "0", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit priority :99", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "cc37", + "name": "Add skbedit action with invalid priority", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit prio foo", + "expExitCode": "255", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit priority", + "matchCount": "0", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "3c95", + "name": "Add skbedit action with queue_mapping", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit queue_mapping 909", + "expExitCode": "0", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit queue_mapping 909", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "985c", + "name": "Add skbedit action with invalid queue_mapping", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit queue_mapping 67000", + "expExitCode": "255", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit queue_mapping", + "matchCount": "0", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "224f", + "name": "Add skbedit action with ptype host", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit ptype host", + "expExitCode": "0", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit ptype host", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "d1a3", + "name": "Add skbedit action with ptype otherhost", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit ptype otherhost", + "expExitCode": "0", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit ptype otherhost", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "b9c6", + "name": "Add skbedit action with invalid ptype", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit ptype openair", + "expExitCode": "255", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit ptype openair", + "matchCount": "0", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "5172", + "name": "List skbedit actions", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ], + "$TC actions add action skbedit ptype otherhost", + "$TC actions add action skbedit ptype broadcast", + "$TC actions add action skbedit mark 59", + "$TC actions add action skbedit mark 409" + ], + "cmdUnderTest": "$TC actions list action skbedit", + "expExitCode": "0", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit", + "matchCount": "4", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "a6d6", + "name": "Add skbedit action with index", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit mark 808 index 4040404040", + "expExitCode": "0", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "index 4040404040", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "38f3", + "name": "Delete skbedit action", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ], + "$TC actions add action skbedit mark 42 index 9009" + ], + "cmdUnderTest": "$TC actions del action skbedit index 9009", + "expExitCode": "0", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit mark 42", + "matchCount": "0", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "ce97", + "name": "Flush skbedit actions", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + "$TC actions add action skbedit mark 500", + "$TC actions add action skbedit mark 501", + "$TC actions add action skbedit mark 502", + "$TC actions add action skbedit mark 503", + "$TC actions add action skbedit mark 504", + "$TC actions add action skbedit mark 505", + "$TC actions add action skbedit mark 506" + ], + "cmdUnderTest": "$TC actions flush action skbedit", + "expExitCode": "0", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit", + "matchCount": "0", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "f02c", + "name": "Replace gact action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action drop index 10", + "$TC actions add action drop index 12" + ], + "cmdUnderTest": "$TC actions replace action ok index 12", + "expExitCode": "0", + "verifyCmd": "$TC actions ls action gact", + "matchPattern": "action order [0-9]*: gact action pass", + "matchCount": "1", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "525f", + "name": "Get gact action by index", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action drop index 3900800700" + ], + "cmdUnderTest": "$TC actions get action gact index 3900800700", + "expExitCode": "0", + "verifyCmd": "$TC actions get action gact index 3900800700", + "matchPattern": "index 3900800700", + "matchCount": "1", + "teardown": [ + "$TC actions flush action gact" + ] + } +]
\ No newline at end of file diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json b/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json new file mode 100644 index 000000000000..c727b96a59b0 --- /dev/null +++ b/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json @@ -0,0 +1,21 @@ +[ + { + "id": "e9a3", + "name": "Add u32 with source match", + "category": [ + "filter", + "u32" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: protocol ip prio 1 u32 match ip src 127.0.0.1/32 flowid 1:1 action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter show dev $DEV1 parent ffff:", + "matchPattern": "match 7f000002/ffffffff at 12", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + } +]
\ No newline at end of file diff --git a/tools/testing/selftests/tc-testing/tdc.py b/tools/testing/selftests/tc-testing/tdc.py new file mode 100755 index 000000000000..cd61b7844c0d --- /dev/null +++ b/tools/testing/selftests/tc-testing/tdc.py @@ -0,0 +1,413 @@ +#!/usr/bin/env python3 + +""" +tdc.py - Linux tc (Traffic Control) unit test driver + +Copyright (C) 2017 Lucas Bates <lucasb@mojatatu.com> +""" + +import re +import os +import sys +import argparse +import json +import subprocess +from collections import OrderedDict +from string import Template + +from tdc_config import * +from tdc_helper import * + + +USE_NS = True + + +def replace_keywords(cmd): + """ + For a given executable command, substitute any known + variables contained within NAMES with the correct values + """ + tcmd = Template(cmd) + subcmd = tcmd.safe_substitute(NAMES) + return subcmd + + +def exec_cmd(command, nsonly=True): + """ + Perform any required modifications on an executable command, then run + it in a subprocess and return the results. + """ + if (USE_NS and nsonly): + command = 'ip netns exec $NS ' + command + + if '$' in command: + command = replace_keywords(command) + + proc = subprocess.Popen(command, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + (rawout, serr) = proc.communicate() + + if proc.returncode != 0: + foutput = serr.decode("utf-8") + else: + foutput = rawout.decode("utf-8") + + proc.stdout.close() + proc.stderr.close() + return proc, foutput + + +def prepare_env(cmdlist): + """ + Execute the setup/teardown commands for a test case. Optionally + terminate test execution if the command fails. + """ + for cmdinfo in cmdlist: + if (type(cmdinfo) == list): + exit_codes = cmdinfo[1:] + cmd = cmdinfo[0] + else: + exit_codes = [0] + cmd = cmdinfo + + if (len(cmd) == 0): + continue + + (proc, foutput) = exec_cmd(cmd) + + if proc.returncode not in exit_codes: + print + print("Could not execute:") + print(cmd) + print("\nError message:") + print(foutput) + print("\nAborting test run.") + ns_destroy() + exit(1) + + +def test_runner(filtered_tests): + """ + Driver function for the unit tests. + + Prints information about the tests being run, executes the setup and + teardown commands and the command under test itself. Also determines + success/failure based on the information in the test case and generates + TAP output accordingly. + """ + testlist = filtered_tests + tcount = len(testlist) + index = 1 + tap = str(index) + ".." + str(tcount) + "\n" + + for tidx in testlist: + result = True + tresult = "" + print("Test " + tidx["id"] + ": " + tidx["name"]) + prepare_env(tidx["setup"]) + (p, procout) = exec_cmd(tidx["cmdUnderTest"]) + exit_code = p.returncode + + if (exit_code != int(tidx["expExitCode"])): + result = False + print("exit:", exit_code, int(tidx["expExitCode"])) + print(procout) + else: + match_pattern = re.compile(str(tidx["matchPattern"]), re.DOTALL) + (p, procout) = exec_cmd(tidx["verifyCmd"]) + match_index = re.findall(match_pattern, procout) + if len(match_index) != int(tidx["matchCount"]): + result = False + + if result == True: + tresult += "ok " + else: + tresult += "not ok " + tap += tresult + str(index) + " " + tidx["id"] + " " + tidx["name"] + "\n" + + if result == False: + tap += procout + + prepare_env(tidx["teardown"]) + index += 1 + + return tap + + +def ns_create(): + """ + Create the network namespace in which the tests will be run and set up + the required network devices for it. + """ + if (USE_NS): + cmd = 'ip netns add $NS' + exec_cmd(cmd, False) + cmd = 'ip link add $DEV0 type veth peer name $DEV1' + exec_cmd(cmd, False) + cmd = 'ip link set $DEV1 netns $NS' + exec_cmd(cmd, False) + cmd = 'ip link set $DEV0 up' + exec_cmd(cmd, False) + cmd = 'ip -s $NS link set $DEV1 up' + exec_cmd(cmd, False) + + +def ns_destroy(): + """ + Destroy the network namespace for testing (and any associated network + devices as well) + """ + if (USE_NS): + cmd = 'ip netns delete $NS' + exec_cmd(cmd, False) + + +def has_blank_ids(idlist): + """ + Search the list for empty ID fields and return true/false accordingly. + """ + return not(all(k for k in idlist)) + + +def load_from_file(filename): + """ + Open the JSON file containing the test cases and return them as an + ordered dictionary object. + """ + with open(filename) as test_data: + testlist = json.load(test_data, object_pairs_hook=OrderedDict) + idlist = get_id_list(testlist) + if (has_blank_ids(idlist)): + for k in testlist: + k['filename'] = filename + return testlist + + +def args_parse(): + """ + Create the argument parser. + """ + parser = argparse.ArgumentParser(description='Linux TC unit tests') + return parser + + +def set_args(parser): + """ + Set the command line arguments for tdc. + """ + parser.add_argument('-p', '--path', type=str, + help='The full path to the tc executable to use') + parser.add_argument('-c', '--category', type=str, nargs='?', const='+c', + help='Run tests only from the specified category, or if no category is specified, list known categories.') + parser.add_argument('-f', '--file', type=str, + help='Run tests from the specified file') + parser.add_argument('-l', '--list', type=str, nargs='?', const="", metavar='CATEGORY', + help='List all test cases, or those only within the specified category') + parser.add_argument('-s', '--show', type=str, nargs=1, metavar='ID', dest='showID', + help='Display the test case with specified id') + parser.add_argument('-e', '--execute', type=str, nargs=1, metavar='ID', + help='Execute the single test case with specified ID') + parser.add_argument('-i', '--id', action='store_true', dest='gen_id', + help='Generate ID numbers for new test cases') + return parser + return parser + + +def check_default_settings(args): + """ + Process any arguments overriding the default settings, and ensure the + settings are correct. + """ + # Allow for overriding specific settings + global NAMES + + if args.path != None: + NAMES['TC'] = args.path + if not os.path.isfile(NAMES['TC']): + print("The specified tc path " + NAMES['TC'] + " does not exist.") + exit(1) + + +def get_id_list(alltests): + """ + Generate a list of all IDs in the test cases. + """ + return [x["id"] for x in alltests] + + +def check_case_id(alltests): + """ + Check for duplicate test case IDs. + """ + idl = get_id_list(alltests) + return [x for x in idl if idl.count(x) > 1] + + +def does_id_exist(alltests, newid): + """ + Check if a given ID already exists in the list of test cases. + """ + idl = get_id_list(alltests) + return (any(newid == x for x in idl)) + + +def generate_case_ids(alltests): + """ + If a test case has a blank ID field, generate a random hex ID for it + and then write the test cases back to disk. + """ + import random + for c in alltests: + if (c["id"] == ""): + while True: + newid = str('%04x' % random.randrange(16**4)) + if (does_id_exist(alltests, newid)): + continue + else: + c['id'] = newid + break + + ufilename = [] + for c in alltests: + if ('filename' in c): + ufilename.append(c['filename']) + ufilename = get_unique_item(ufilename) + for f in ufilename: + testlist = [] + for t in alltests: + if 'filename' in t: + if t['filename'] == f: + del t['filename'] + testlist.append(t) + outfile = open(f, "w") + json.dump(testlist, outfile, indent=4) + outfile.close() + + +def get_test_cases(args): + """ + If a test case file is specified, retrieve tests from that file. + Otherwise, glob for all json files in subdirectories and load from + each one. + """ + import fnmatch + if args.file != None: + if not os.path.isfile(args.file): + print("The specified test case file " + args.file + " does not exist.") + exit(1) + flist = [args.file] + else: + flist = [] + for root, dirnames, filenames in os.walk('tc-tests'): + for filename in fnmatch.filter(filenames, '*.json'): + flist.append(os.path.join(root, filename)) + alltests = list() + for casefile in flist: + alltests = alltests + (load_from_file(casefile)) + return alltests + + +def set_operation_mode(args): + """ + Load the test case data and process remaining arguments to determine + what the script should do for this run, and call the appropriate + function. + """ + alltests = get_test_cases(args) + + if args.gen_id: + idlist = get_id_list(alltests) + if (has_blank_ids(idlist)): + alltests = generate_case_ids(alltests) + else: + print("No empty ID fields found in test files.") + exit(0) + + duplicate_ids = check_case_id(alltests) + if (len(duplicate_ids) > 0): + print("The following test case IDs are not unique:") + print(str(set(duplicate_ids))) + print("Please correct them before continuing.") + exit(1) + + ucat = get_test_categories(alltests) + + if args.showID: + show_test_case_by_id(alltests, args.showID[0]) + exit(0) + + if args.execute: + target_id = args.execute[0] + else: + target_id = "" + + if args.category: + if (args.category == '+c'): + print("Available categories:") + print_sll(ucat) + exit(0) + else: + target_category = args.category + else: + target_category = "" + + + testcases = get_categorized_testlist(alltests, ucat) + + if args.list: + if (len(args.list) == 0): + list_test_cases(alltests) + exit(0) + elif(len(args.list > 0)): + if (args.list not in ucat): + print("Unknown category " + args.list) + print("Available categories:") + print_sll(ucat) + exit(1) + list_test_cases(testcases[args.list]) + exit(0) + + if (os.geteuid() != 0): + print("This script must be run with root privileges.\n") + exit(1) + + ns_create() + + if (len(target_category) == 0): + if (len(target_id) > 0): + alltests = list(filter(lambda x: target_id in x['id'], alltests)) + if (len(alltests) == 0): + print("Cannot find a test case with ID matching " + target_id) + exit(1) + catresults = test_runner(alltests) + print("All test results: " + "\n\n" + catresults) + elif (len(target_category) > 0): + if (target_category not in ucat): + print("Specified category is not present in this file.") + exit(1) + else: + catresults = test_runner(testcases[target_category]) + print("Category " + target_category + "\n\n" + catresults) + + ns_destroy() + + +def main(): + """ + Start of execution; set up argument parser and get the arguments, + and start operations. + """ + parser = args_parse() + parser = set_args(parser) + (args, remaining) = parser.parse_known_args() + check_default_settings(args) + + set_operation_mode(args) + + exit(0) + + +if __name__ == "__main__": + main() diff --git a/tools/testing/selftests/tc-testing/tdc_config.py b/tools/testing/selftests/tc-testing/tdc_config.py new file mode 100644 index 000000000000..01087375a7c3 --- /dev/null +++ b/tools/testing/selftests/tc-testing/tdc_config.py @@ -0,0 +1,17 @@ +""" +tdc_config.py - tdc user-specified values + +Copyright (C) 2017 Lucas Bates <lucasb@mojatatu.com> +""" + +# Dictionary containing all values that can be substituted in executable +# commands. +NAMES = { + # Substitute your own tc path here + 'TC': '/sbin/tc', + # Name of veth devices to be created for the namespace + 'DEV0': 'v0p0', + 'DEV1': 'v0p1', + # Name of the namespace to use + 'NS': 'tcut' + } diff --git a/tools/testing/selftests/tc-testing/tdc_helper.py b/tools/testing/selftests/tc-testing/tdc_helper.py new file mode 100644 index 000000000000..c3254f861fb2 --- /dev/null +++ b/tools/testing/selftests/tc-testing/tdc_helper.py @@ -0,0 +1,75 @@ +""" +tdc_helper.py - tdc helper functions + +Copyright (C) 2017 Lucas Bates <lucasb@mojatatu.com> +""" + +def get_categorized_testlist(alltests, ucat): + """ Sort the master test list into categories. """ + testcases = dict() + + for category in ucat: + testcases[category] = list(filter(lambda x: category in x['category'], alltests)) + + return(testcases) + + +def get_unique_item(lst): + """ For a list, return a set of the unique items in the list. """ + return list(set(lst)) + + +def get_test_categories(alltests): + """ Discover all unique test categories present in the test case file. """ + ucat = [] + for t in alltests: + ucat.extend(get_unique_item(t['category'])) + ucat = get_unique_item(ucat) + return ucat + +def list_test_cases(testlist): + """ Print IDs and names of all test cases. """ + for curcase in testlist: + print(curcase['id'] + ': (' + ', '.join(curcase['category']) + ") " + curcase['name']) + + +def list_categories(testlist): + """ Show all categories that are present in a test case file. """ + categories = set(map(lambda x: x['category'], testlist)) + print("Available categories:") + print(", ".join(str(s) for s in categories)) + print("") + + +def print_list(cmdlist): + """ Print a list of strings prepended with a tab. """ + for l in cmdlist: + if (type(l) == list): + print("\t" + str(l[0])) + else: + print("\t" + str(l)) + + +def print_sll(items): + print("\n".join(str(s) for s in items)) + + +def print_test_case(tcase): + """ Pretty-printing of a given test case. """ + for k in tcase.keys(): + if (type(tcase[k]) == list): + print(k + ":") + print_list(tcase[k]) + else: + print(k + ": " + tcase[k]) + + +def show_test_case_by_id(testlist, caseID): + """ Find the specified test case to pretty-print. """ + if not any(d.get('id', None) == caseID for d in testlist): + print("That ID does not exist.") + exit(1) + else: + print_test_case(next((d for d in testlist if d['id'] == caseID))) + + diff --git a/tools/testing/selftests/vm/virtual_address_range.c b/tools/testing/selftests/vm/virtual_address_range.c index 3b02aa6eb9da..1830d66a6f0e 100644 --- a/tools/testing/selftests/vm/virtual_address_range.c +++ b/tools/testing/selftests/vm/virtual_address_range.c @@ -10,7 +10,6 @@ #include <string.h> #include <unistd.h> #include <errno.h> -#include <numaif.h> #include <sys/mman.h> #include <sys/time.h> @@ -32,15 +31,33 @@ * different areas one below 128TB and one above 128TB * till it reaches 512TB. One with size 128TB and the * other being 384TB. + * + * On Arm64 the address space is 256TB and no high mappings + * are supported so far. */ + #define NR_CHUNKS_128TB 8192UL /* Number of 16GB chunks for 128TB */ -#define NR_CHUNKS_384TB 24576UL /* Number of 16GB chunks for 384TB */ +#define NR_CHUNKS_256TB (NR_CHUNKS_128TB * 2UL) +#define NR_CHUNKS_384TB (NR_CHUNKS_128TB * 3UL) #define ADDR_MARK_128TB (1UL << 47) /* First address beyond 128TB */ +#define ADDR_MARK_256TB (1UL << 48) /* First address beyond 256TB */ + +#ifdef __aarch64__ +#define HIGH_ADDR_MARK ADDR_MARK_256TB +#define HIGH_ADDR_SHIFT 49 +#define NR_CHUNKS_LOW NR_CHUNKS_256TB +#define NR_CHUNKS_HIGH 0 +#else +#define HIGH_ADDR_MARK ADDR_MARK_128TB +#define HIGH_ADDR_SHIFT 48 +#define NR_CHUNKS_LOW NR_CHUNKS_128TB +#define NR_CHUNKS_HIGH NR_CHUNKS_384TB +#endif static char *hind_addr(void) { - int bits = 48 + rand() % 15; + int bits = HIGH_ADDR_SHIFT + rand() % (63 - HIGH_ADDR_SHIFT); return (char *) (1UL << bits); } @@ -50,14 +67,14 @@ static int validate_addr(char *ptr, int high_addr) unsigned long addr = (unsigned long) ptr; if (high_addr) { - if (addr < ADDR_MARK_128TB) { + if (addr < HIGH_ADDR_MARK) { printf("Bad address %lx\n", addr); return 1; } return 0; } - if (addr > ADDR_MARK_128TB) { + if (addr > HIGH_ADDR_MARK) { printf("Bad address %lx\n", addr); return 1; } @@ -79,12 +96,12 @@ static int validate_lower_address_hint(void) int main(int argc, char *argv[]) { - char *ptr[NR_CHUNKS_128TB]; - char *hptr[NR_CHUNKS_384TB]; + char *ptr[NR_CHUNKS_LOW]; + char *hptr[NR_CHUNKS_HIGH]; char *hint; unsigned long i, lchunks, hchunks; - for (i = 0; i < NR_CHUNKS_128TB; i++) { + for (i = 0; i < NR_CHUNKS_LOW; i++) { ptr[i] = mmap(NULL, MAP_CHUNK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); @@ -99,7 +116,7 @@ int main(int argc, char *argv[]) } lchunks = i; - for (i = 0; i < NR_CHUNKS_384TB; i++) { + for (i = 0; i < NR_CHUNKS_HIGH; i++) { hint = hind_addr(); hptr[i] = mmap(hint, MAP_CHUNK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |