From 9793a67915f2191eb00d04d23d496d278c43ca02 Mon Sep 17 00:00:00 2001 From: Richard Cheng Date: Fri, 21 Aug 2026 08:39:08 +0800 Subject: [PATCH 01/14] selftests/resctrl: Skip L3_CAT when no exclusive cache portion exists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L3_CAT requires an exclusive cache portion to test cache allocation. This means that the cache portion used by the test can't overlap with cache portions into which other agents may allocate. Some platforms legitimately report every cache portion as shareable, leaving no exclusive cache portion for the test and causing L3_CAT to fail. Skip the L3_CAT when the platform has no exclusive cache portion that the test can use. Link: https://lore.kernel.org/r/20260821003908.12977-1-icheng@nvidia.com Tested-by: Chen Yu Reviewed-by: Ilpo Järvinen Signed-off-by: Richard Cheng Reviewed-by: Reinette Chatre Signed-off-by: Shuah Khan --- tools/testing/selftests/resctrl/cat_test.c | 23 +++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/resctrl/cat_test.c b/tools/testing/selftests/resctrl/cat_test.c index 371a2f26dc47..0a417267dd74 100644 --- a/tools/testing/selftests/resctrl/cat_test.c +++ b/tools/testing/selftests/resctrl/cat_test.c @@ -357,11 +357,32 @@ static bool noncont_cat_feature_check(const struct resctrl_test *test) return resource_info_file_exists(test->resource, "sparse_masks"); } +static bool cat_feature_check(const struct resctrl_test *test) +{ + unsigned long mask; + + if (!test_resource_feature_check(test)) + return false; + + /* + * Test requires an exclusive cache portion. Some platforms may + * legitimately report all bits as shareable, skip the test if that + * is the case. + */ + if (get_mask_no_shareable(test->resource, &mask)) { + ksft_print_msg("All %s bits are shareable, test requires an exclusive cache portion\n", + test->resource); + return false; + } + + return true; +} + struct resctrl_test l3_cat_test = { .name = "L3_CAT", .group = "CAT", .resource = "L3", - .feature_check = test_resource_feature_check, + .feature_check = cat_feature_check, .run_test = cat_run_test, .cleanup = cat_test_cleanup, }; From fd38cbee26944f8f2bcbea23d71a23b4b1813e9e Mon Sep 17 00:00:00 2001 From: Yifan Wu Date: Sat, 1 Aug 2026 17:24:27 +0800 Subject: [PATCH 02/14] selftests/resctrl: Introduce linked list management for IMC counters The static array approach to managing IMC counters has fixed size constraints and limited compatibility and scalability. Introduce a linked list-based dynamic management infrastructure to address these limitations. Add the core data structure definitions and memory allocation and cleanup functions for dynamic counter configurations. Link: https://lore.kernel.org/r/20260801092429.533956-2-wuyifan50@huawei.com Reviewed-by: Reinette Chatre Signed-off-by: Yifan Wu Signed-off-by: Shuah Khan --- tools/testing/selftests/resctrl/mba_test.c | 1 + tools/testing/selftests/resctrl/mbm_test.c | 1 + tools/testing/selftests/resctrl/resctrl.h | 2 ++ tools/testing/selftests/resctrl/resctrl_val.c | 27 +++++++++++++++++-- 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/resctrl/mba_test.c b/tools/testing/selftests/resctrl/mba_test.c index 39cee9898359..4bb1a82eb195 100644 --- a/tools/testing/selftests/resctrl/mba_test.c +++ b/tools/testing/selftests/resctrl/mba_test.c @@ -166,6 +166,7 @@ static int check_results(void) static void mba_test_cleanup(void) { + cleanup_read_mem_bw_imc(); remove(RESULT_FILE_NAME); } diff --git a/tools/testing/selftests/resctrl/mbm_test.c b/tools/testing/selftests/resctrl/mbm_test.c index 6dbbc3b76003..68c89f50a34a 100644 --- a/tools/testing/selftests/resctrl/mbm_test.c +++ b/tools/testing/selftests/resctrl/mbm_test.c @@ -125,6 +125,7 @@ static int mbm_measure(const struct user_params *uparams, static void mbm_test_cleanup(void) { + cleanup_read_mem_bw_imc(); remove(RESULT_FILE_NAME); } diff --git a/tools/testing/selftests/resctrl/resctrl.h b/tools/testing/selftests/resctrl/resctrl.h index 175101022bf3..a7556cdae0de 100644 --- a/tools/testing/selftests/resctrl/resctrl.h +++ b/tools/testing/selftests/resctrl/resctrl.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "kselftest.h" #define MB (1024 * 1024) @@ -183,6 +184,7 @@ void mem_flush(unsigned char *buf, size_t buf_size); void fill_cache_read(unsigned char *buf, size_t buf_size, bool once); ssize_t get_fill_buf_size(int cpu_no, const char *cache_type); int initialize_read_mem_bw_imc(void); +void cleanup_read_mem_bw_imc(void); int measure_read_mem_bw(const struct user_params *uparams, struct resctrl_val_param *param, pid_t bm_pid); void initialize_mem_bw_resctrl(const struct resctrl_val_param *param, diff --git a/tools/testing/selftests/resctrl/resctrl_val.c b/tools/testing/selftests/resctrl/resctrl_val.c index f20d2194c35f..a72dc4ae61fe 100644 --- a/tools/testing/selftests/resctrl/resctrl_val.c +++ b/tools/testing/selftests/resctrl/resctrl_val.c @@ -28,6 +28,7 @@ struct membw_read_format { }; struct imc_counter_config { + struct list_head entry; __u32 type; __u64 event; __u64 umask; @@ -38,6 +39,7 @@ struct imc_counter_config { static char mbm_total_path[1024]; static int imcs; static struct imc_counter_config imc_counters_config[MAX_IMCS]; +LIST_HEAD(imc_counters_list); static const struct resctrl_test *current_test; static void read_mem_bw_initialize_perf_event_attr(int i) @@ -113,6 +115,7 @@ static int parse_imc_read_bw_events(char *imc_dir, unsigned int type, unsigned int *count) { char imc_events_dir[PATH_MAX], imc_counter_cfg[PATH_MAX]; + struct imc_counter_config *imc_counter; unsigned int orig_count = *count; char cas_count_cfg[1024]; struct dirent *ep; @@ -126,13 +129,13 @@ static int parse_imc_read_bw_events(char *imc_dir, unsigned int type, imc_dir); if (path_len >= sizeof(imc_events_dir)) { ksft_print_msg("Unable to create path to %sevents\n", imc_dir); - return -1; + goto out; } dp = opendir(imc_events_dir); if (!dp) { ksft_perror("Unable to open PMU events directory"); - return -1; + goto out; } while ((ep = readdir(dp))) { @@ -167,11 +170,17 @@ static int parse_imc_read_bw_events(char *imc_dir, unsigned int type, ksft_print_msg("Maximum iMC count exceeded\n"); goto out_close; } + imc_counter = calloc(1, sizeof(*imc_counter)); + if (!imc_counter) { + ksft_perror("Unable to allocate memory for iMC counters"); + goto out_close; + } imc_counters_config[*count].type = type; get_read_event_and_umask(cas_count_cfg, *count); /* Do not fail after incrementing *count. */ *count += 1; + list_add(&imc_counter->entry, &imc_counters_list); } if (*count == orig_count) { ksft_print_msg("Unable to find events in %s\n", imc_events_dir); @@ -180,6 +189,10 @@ static int parse_imc_read_bw_events(char *imc_dir, unsigned int type, ret = 0; out_close: closedir(dp); +out: + if (ret) + cleanup_read_mem_bw_imc(); + return ret; } @@ -303,6 +316,16 @@ int initialize_read_mem_bw_imc(void) return 0; } +void cleanup_read_mem_bw_imc(void) +{ + struct imc_counter_config *imc_counter, *tmp; + + list_for_each_entry_safe(imc_counter, tmp, &imc_counters_list, entry) { + list_del(&imc_counter->entry); + free(imc_counter); + } +} + static void perf_close_imc_read_mem_bw(void) { int mc; From a7f2fe32a8066f28c2f5ad90441cc6efc3aa8272 Mon Sep 17 00:00:00 2001 From: Yifan Wu Date: Sat, 1 Aug 2026 17:24:28 +0800 Subject: [PATCH 03/14] selftests/resctrl: Replace counter index references with pointers Replace direct counter number references with pointers to remove the dependency on fixed array indexing and enable the use of different data structures for counter management. Link: https://lore.kernel.org/r/20260801092429.533956-3-wuyifan50@huawei.com Reviewed-by: Reinette Chatre Signed-off-by: Yifan Wu Signed-off-by: Shuah Khan --- tools/testing/selftests/resctrl/resctrl_val.c | 62 +++++++++---------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/tools/testing/selftests/resctrl/resctrl_val.c b/tools/testing/selftests/resctrl/resctrl_val.c index a72dc4ae61fe..f4111da26fc2 100644 --- a/tools/testing/selftests/resctrl/resctrl_val.c +++ b/tools/testing/selftests/resctrl/resctrl_val.c @@ -42,40 +42,37 @@ static struct imc_counter_config imc_counters_config[MAX_IMCS]; LIST_HEAD(imc_counters_list); static const struct resctrl_test *current_test; -static void read_mem_bw_initialize_perf_event_attr(int i) +static void read_mem_bw_initialize_perf_event_attr(struct imc_counter_config *imc_counter) { - memset(&imc_counters_config[i].pe, 0, - sizeof(struct perf_event_attr)); - imc_counters_config[i].pe.type = imc_counters_config[i].type; - imc_counters_config[i].pe.size = sizeof(struct perf_event_attr); - imc_counters_config[i].pe.disabled = 1; - imc_counters_config[i].pe.inherit = 1; - imc_counters_config[i].pe.exclude_guest = 0; - imc_counters_config[i].pe.config = - imc_counters_config[i].umask << 8 | - imc_counters_config[i].event; - imc_counters_config[i].pe.sample_type = PERF_SAMPLE_IDENTIFIER; - imc_counters_config[i].pe.read_format = + memset(&imc_counter->pe, 0, sizeof(struct perf_event_attr)); + imc_counter->pe.type = imc_counter->type; + imc_counter->pe.size = sizeof(struct perf_event_attr); + imc_counter->pe.disabled = 1; + imc_counter->pe.inherit = 1; + imc_counter->pe.exclude_guest = 0; + imc_counter->pe.config = imc_counter->umask << 8 | imc_counter->event; + imc_counter->pe.sample_type = PERF_SAMPLE_IDENTIFIER; + imc_counter->pe.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING; } -static void read_mem_bw_ioctl_perf_event_ioc_reset_enable(int i) +static void read_mem_bw_ioctl_perf_event_ioc_reset_enable(struct imc_counter_config *imc_counter) { - ioctl(imc_counters_config[i].fd, PERF_EVENT_IOC_RESET, 0); - ioctl(imc_counters_config[i].fd, PERF_EVENT_IOC_ENABLE, 0); + ioctl(imc_counter->fd, PERF_EVENT_IOC_RESET, 0); + ioctl(imc_counter->fd, PERF_EVENT_IOC_ENABLE, 0); } -static void read_mem_bw_ioctl_perf_event_ioc_disable(int i) +static void read_mem_bw_ioctl_perf_event_ioc_disable(struct imc_counter_config *imc_counter) { - ioctl(imc_counters_config[i].fd, PERF_EVENT_IOC_DISABLE, 0); + ioctl(imc_counter->fd, PERF_EVENT_IOC_DISABLE, 0); } /* * get_read_event_and_umask: Parse config into event and umask * @cas_count_cfg: Config - * @count: iMC number + * @imc_counter: iMC counter config */ -static void get_read_event_and_umask(char *cas_count_cfg, unsigned int count) +static void get_read_event_and_umask(char *cas_count_cfg, struct imc_counter_config *imc_counter) { char *token[MAX_TOKENS]; int i = 0; @@ -89,21 +86,20 @@ static void get_read_event_and_umask(char *cas_count_cfg, unsigned int count) if (!token[i]) break; if (strcmp(token[i], "event") == 0) - imc_counters_config[count].event = strtol(token[i + 1], NULL, 16); + imc_counter->event = strtol(token[i + 1], NULL, 16); if (strcmp(token[i], "umask") == 0) - imc_counters_config[count].umask = strtol(token[i + 1], NULL, 16); + imc_counter->umask = strtol(token[i + 1], NULL, 16); } } -static int open_perf_read_event(int i, int cpu_no) +static int open_perf_read_event(int cpu_no, struct imc_counter_config *imc_counter) { - imc_counters_config[i].fd = - perf_event_open(&imc_counters_config[i].pe, -1, cpu_no, -1, - PERF_FLAG_FD_CLOEXEC); + imc_counter->fd = perf_event_open(&imc_counter->pe, -1, cpu_no, -1, + PERF_FLAG_FD_CLOEXEC); - if (imc_counters_config[i].fd == -1) { + if (imc_counter->fd == -1) { fprintf(stderr, "Error opening leader %llx\n", - imc_counters_config[i].pe.config); + imc_counter->pe.config); return -1; } @@ -177,7 +173,7 @@ static int parse_imc_read_bw_events(char *imc_dir, unsigned int type, } imc_counters_config[*count].type = type; - get_read_event_and_umask(cas_count_cfg, *count); + get_read_event_and_umask(cas_count_cfg, &imc_counters_config[*count]); /* Do not fail after incrementing *count. */ *count += 1; list_add(&imc_counter->entry, &imc_counters_list); @@ -311,7 +307,7 @@ int initialize_read_mem_bw_imc(void) /* Initialize perf_event_attr structures for all iMC's */ for (imc = 0; imc < imcs; imc++) - read_mem_bw_initialize_perf_event_attr(imc); + read_mem_bw_initialize_perf_event_attr(&imc_counters_config[imc]); return 0; } @@ -350,7 +346,7 @@ static int perf_open_imc_read_mem_bw(int cpu_no) imc_counters_config[imc].fd = -1; for (imc = 0; imc < imcs; imc++) { - ret = open_perf_read_event(imc, cpu_no); + ret = open_perf_read_event(cpu_no, &imc_counters_config[imc]); if (ret) goto close_fds; } @@ -373,13 +369,13 @@ static void do_imc_read_mem_bw_test(void) int imc; for (imc = 0; imc < imcs; imc++) - read_mem_bw_ioctl_perf_event_ioc_reset_enable(imc); + read_mem_bw_ioctl_perf_event_ioc_reset_enable(&imc_counters_config[imc]); sleep(1); /* Stop counters after a second to get results. */ for (imc = 0; imc < imcs; imc++) - read_mem_bw_ioctl_perf_event_ioc_disable(imc); + read_mem_bw_ioctl_perf_event_ioc_disable(&imc_counters_config[imc]); } /* From 8459ea00c0abad92cbe4a9b836c4c979823dbb59 Mon Sep 17 00:00:00 2001 From: Yifan Wu Date: Sat, 1 Aug 2026 17:24:29 +0800 Subject: [PATCH 04/14] selftests/resctrl: Enable dynamic management of IMC counters via linked list Remove the static array and the count and upper limit checks during initialization, allowing the system to dynamically use all available IMC counters detected by hardware. Link: https://lore.kernel.org/r/20260801092429.533956-4-wuyifan50@huawei.com Reviewed-by: Reinette Chatre Signed-off-by: Yifan Wu Signed-off-by: Shuah Khan --- tools/testing/selftests/resctrl/resctrl_val.c | 94 ++++++++----------- 1 file changed, 41 insertions(+), 53 deletions(-) diff --git a/tools/testing/selftests/resctrl/resctrl_val.c b/tools/testing/selftests/resctrl/resctrl_val.c index f4111da26fc2..7e7d0f9cc249 100644 --- a/tools/testing/selftests/resctrl/resctrl_val.c +++ b/tools/testing/selftests/resctrl/resctrl_val.c @@ -14,7 +14,6 @@ #define READ_FILE_NAME "cas_count_read" #define DYN_PMU_PATH "/sys/bus/event_source/devices" #define SCALE 0.00006103515625 -#define MAX_IMCS 40 #define MAX_TOKENS 5 #define CON_MBM_LOCAL_BYTES_PATH \ @@ -37,8 +36,6 @@ struct imc_counter_config { }; static char mbm_total_path[1024]; -static int imcs; -static struct imc_counter_config imc_counters_config[MAX_IMCS]; LIST_HEAD(imc_counters_list); static const struct resctrl_test *current_test; @@ -107,12 +104,11 @@ static int open_perf_read_event(int cpu_no, struct imc_counter_config *imc_count return 0; } -static int parse_imc_read_bw_events(char *imc_dir, unsigned int type, - unsigned int *count) +static int parse_imc_read_bw_events(char *imc_dir, unsigned int type) { char imc_events_dir[PATH_MAX], imc_counter_cfg[PATH_MAX]; struct imc_counter_config *imc_counter; - unsigned int orig_count = *count; + bool found_event = false; char cas_count_cfg[1024]; struct dirent *ep; int path_len; @@ -162,23 +158,18 @@ static int parse_imc_read_bw_events(char *imc_dir, unsigned int type, ksft_perror("Could not get iMC cas count read"); goto out_close; } - if (*count >= MAX_IMCS) { - ksft_print_msg("Maximum iMC count exceeded\n"); - goto out_close; - } imc_counter = calloc(1, sizeof(*imc_counter)); if (!imc_counter) { ksft_perror("Unable to allocate memory for iMC counters"); goto out_close; } - imc_counters_config[*count].type = type; - get_read_event_and_umask(cas_count_cfg, &imc_counters_config[*count]); - /* Do not fail after incrementing *count. */ - *count += 1; + imc_counter->type = type; + get_read_event_and_umask(cas_count_cfg, imc_counter); list_add(&imc_counter->entry, &imc_counters_list); + found_event = true; } - if (*count == orig_count) { + if (!found_event) { ksft_print_msg("Unable to find events in %s\n", imc_events_dir); goto out_close; } @@ -193,7 +184,7 @@ static int parse_imc_read_bw_events(char *imc_dir, unsigned int type, } /* Get type and config of an iMC counter's read event. */ -static int read_from_imc_dir(char *imc_dir, unsigned int *count) +static int read_from_imc_dir(char *imc_dir) { char imc_counter_type[PATH_MAX]; unsigned int type; @@ -221,7 +212,7 @@ static int read_from_imc_dir(char *imc_dir, unsigned int *count) ksft_perror("Could not get iMC type"); return -1; } - ret = parse_imc_read_bw_events(imc_dir, type, count); + ret = parse_imc_read_bw_events(imc_dir, type); if (ret) { ksft_print_msg("Unable to parse bandwidth event and umask\n"); return ret; @@ -231,21 +222,18 @@ static int read_from_imc_dir(char *imc_dir, unsigned int *count) } /* - * A system can have 'n' number of iMC (Integrated Memory Controller) - * counters, get that 'n'. Discover the properties of the available - * counters in support of needed performance measurement via perf. - * For each iMC counter get it's type and config. Also obtain each - * counter's event and umask for the memory read events that will be - * measured. + * Discover the properties of the available iMC (Integrated Memory Controller) + * counters in support of needed performance measurement via perf. For each iMC + * counter get it's type and config. Also obtain each counter's event and umask + * for the memory read events that will be measured. * - * Enumerate all these details into an array of structures. + * Enumerate all these details into a linked list of structures. * * Return: >= 0 on success. < 0 on failure. */ -static int num_of_imcs(void) +static int enumerate_imcs(void) { char imc_dir[512], *temp; - unsigned int count = 0; struct dirent *ep; int ret; DIR *dp; @@ -274,7 +262,7 @@ static int num_of_imcs(void) if (temp[0] >= '0' && temp[0] <= '9') { sprintf(imc_dir, "%s/%s/", DYN_PMU_PATH, ep->d_name); - ret = read_from_imc_dir(imc_dir, &count); + ret = read_from_imc_dir(imc_dir); if (ret) { closedir(dp); @@ -283,7 +271,7 @@ static int num_of_imcs(void) } } closedir(dp); - if (count == 0) { + if (list_empty(&imc_counters_list)) { ksft_print_msg("Unable to find iMC counters\n"); return -1; @@ -294,20 +282,21 @@ static int num_of_imcs(void) return -1; } - return count; + return 0; } int initialize_read_mem_bw_imc(void) { - int imc; + struct imc_counter_config *imc_counter; + int ret; - imcs = num_of_imcs(); - if (imcs <= 0) - return imcs; + ret = enumerate_imcs(); + if (ret < 0) + return ret; /* Initialize perf_event_attr structures for all iMC's */ - for (imc = 0; imc < imcs; imc++) - read_mem_bw_initialize_perf_event_attr(&imc_counters_config[imc]); + list_for_each_entry(imc_counter, &imc_counters_list, entry) + read_mem_bw_initialize_perf_event_attr(imc_counter); return 0; } @@ -324,11 +313,11 @@ void cleanup_read_mem_bw_imc(void) static void perf_close_imc_read_mem_bw(void) { - int mc; + struct imc_counter_config *imc_counter; - for (mc = 0; mc < imcs; mc++) { - if (imc_counters_config[mc].fd != -1) - close(imc_counters_config[mc].fd); + list_for_each_entry(imc_counter, &imc_counters_list, entry) { + if (imc_counter->fd != -1) + close(imc_counter->fd); } } @@ -340,13 +329,14 @@ static void perf_close_imc_read_mem_bw(void) */ static int perf_open_imc_read_mem_bw(int cpu_no) { - int imc, ret; + struct imc_counter_config *imc_counter; + int ret; - for (imc = 0; imc < imcs; imc++) - imc_counters_config[imc].fd = -1; + list_for_each_entry(imc_counter, &imc_counters_list, entry) + imc_counter->fd = -1; - for (imc = 0; imc < imcs; imc++) { - ret = open_perf_read_event(cpu_no, &imc_counters_config[imc]); + list_for_each_entry(imc_counter, &imc_counters_list, entry) { + ret = open_perf_read_event(cpu_no, imc_counter); if (ret) goto close_fds; } @@ -366,16 +356,16 @@ static int perf_open_imc_read_mem_bw(int cpu_no) */ static void do_imc_read_mem_bw_test(void) { - int imc; + struct imc_counter_config *imc_counter; - for (imc = 0; imc < imcs; imc++) - read_mem_bw_ioctl_perf_event_ioc_reset_enable(&imc_counters_config[imc]); + list_for_each_entry(imc_counter, &imc_counters_list, entry) + read_mem_bw_ioctl_perf_event_ioc_reset_enable(imc_counter); sleep(1); /* Stop counters after a second to get results. */ - for (imc = 0; imc < imcs; imc++) - read_mem_bw_ioctl_perf_event_ioc_disable(&imc_counters_config[imc]); + list_for_each_entry(imc_counter, &imc_counters_list, entry) + read_mem_bw_ioctl_perf_event_ioc_disable(imc_counter); } /* @@ -390,17 +380,15 @@ static void do_imc_read_mem_bw_test(void) static int get_read_mem_bw_imc(float *bw_imc) { float reads = 0, of_mul_read = 1; - int imc; + struct imc_counter_config *r; /* * Log read event values from all iMC counters into * struct imc_counter_config. * Take overflow into consideration before calculating total bandwidth. */ - for (imc = 0; imc < imcs; imc++) { + list_for_each_entry(r, &imc_counters_list, entry) { struct membw_read_format measurement; - struct imc_counter_config *r = - &imc_counters_config[imc]; if (read(r->fd, &measurement, sizeof(measurement)) == -1) { ksft_perror("Couldn't get read bandwidth through iMC"); From 30af56a227e27b892d34bc44c23298b5274913e3 Mon Sep 17 00:00:00 2001 From: Eva Kurchatova Date: Fri, 25 Sep 2026 16:35:41 +0300 Subject: [PATCH 05/14] selftests/ftrace: skip gcov symbols when picking a function to probe kprobe_eventname.tc takes the last kallsyms entry whose name ends in vfs_read and puts a probe on it. On a kernel built with CONFIG_GCOV_PROFILE_ALL=y the counter symbol gcc emits for the function sorts after it: ffffffff819bd1e0 T vfs_read ffffffff845013e0 t __gcov_.vfs_read so the test picks __gcov_.vfs_read, which is not a function entry, and the write fails: kprobe_eventname.tc: line 10: echo: write error: Invalid argument Leave those symbols out of the choice. The test then picks vfs_read and passes; without a coverage build nothing changes, as no such symbol exists there. This is the same class of problem as commit 23a4b108accc ("tracing/selftests: Fix kprobe event name test for .isra. functions"), where a compiler-generated symbol was picked instead of a real function. Link: https://lore.kernel.org/r/20260925133558.3636030-1-eva.kurchatova@virtuozzo.com Signed-off-by: Eva Kurchatova Acked-by: Masami Hiramatsu (Google) Signed-off-by: Shuah Khan --- .../testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc index ba19b81cef39..0688a5c47d1e 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc @@ -5,7 +5,7 @@ :;: "Add an event on function without name" ;: -FUNC=`grep " [tT] .*vfs_read$" /proc/kallsyms | tail -n 1 | cut -f 3 -d " "` +FUNC=`grep " [tT] .*vfs_read$" /proc/kallsyms | grep -v __gcov | tail -n 1 | cut -f 3 -d " "` [ "x" != "x$FUNC" ] || exit_unresolved echo "p $FUNC" > kprobe_events PROBE_NAME=`echo $FUNC | tr ".:" "_"` From 4c5a4cd8b95f5cacba31608b43e15bc64a2543f0 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Mon, 5 Oct 2026 19:30:12 +0200 Subject: [PATCH 06/14] selftests/lib.mk: run_tests: print summary at the end When there are many tests and/or many log lines, it is difficult to quickly find out how the different tests went when running: make -C tools/testing/selftests run_tests This run_tests target executes 'run_many' from runner.sh, which relies on ktap_helpers.sh. At the end of the execution, the KTAP_CNT_* counters have been populated, and the total can then be simply printed with the ktap_print_totals helper. Now a summary is printed at the end, e.g. here with the two last lines: $ make -sC tools/testing/selftests TARGETS=(...) run_tests # timeout set to (...) # selftests: (...) (...) ok 1 selftests: (...) # timeout set to (...) # selftests: (...) (...) ok 2 selftests: (...) # SKIP # timeout set to (...) # selftests: (...) (...) not ok 3 selftests: (...) # 1 skipped test(s) detected. Consider enabling relevant config options to improve coverage. # Totals: pass:1 fail:1 xfail:0 xpass:0 skip:1 error:0 This summary is printed, using '#' as prefix to be treated as a comment in the TAP format. The summary is also printed when summary=1 is used to redirect log lines in different files because it still looks useful when there are many tests, not to have to check each line. Note that this 'make' command will continue to exit without errors (returned code is 0) when the tests can be executed. I guess it is better not to change this behaviour to be able to continue detecting kselftests failures. Link: https://lore.kernel.org/r/20261005-sft-run-tests-print-totals-v2-1-29c45c0e2fe6@kernel.org Signed-off-by: Matthieu Baerts (NGI0) Signed-off-by: Shuah Khan --- tools/testing/selftests/lib.mk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/lib.mk b/tools/testing/selftests/lib.mk index f02cc8a2e4ae..de15e63bc6b4 100644 --- a/tools/testing/selftests/lib.mk +++ b/tools/testing/selftests/lib.mk @@ -106,7 +106,8 @@ define RUN_TESTS if [ "X$(summary)" != "X" ]; then \ per_test_logging=1; \ fi; \ - run_many $(1) + run_many $(1); \ + ktap_print_totals endef define INSTALL_INCLUDES From 2bd817bd2e2310f59aad0508f4859d97f612c0d7 Mon Sep 17 00:00:00 2001 From: Woradorn Laodhanadhaworn Date: Fri, 18 Sep 2026 13:19:13 +0700 Subject: [PATCH 07/14] tracing/selftests: Fix incorrect invocation of exit_fail Replace all `exit fail` and `exit_fail;` with exit_fail to keep the usage of exit_fail consistent across the ftrace selftests. This also reduces warning messsege such as 'exit: Illegal number: fail' in test logs. Link: https://lore.kernel.org/linux-trace-kernel/20260918061913.28069-1-woradorn.laon@gmail.com Signed-off-by: Woradorn Laodhanadhaworn Reviewed-by: Masami Hiramatsu (Google) Signed-off-by: Shuah Khan --- .../ftrace/test.d/00basic/ringbuffer_subbuf_size.tc | 6 +++--- .../testing/selftests/ftrace/test.d/00basic/trace_marker.tc | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/ftrace/test.d/00basic/ringbuffer_subbuf_size.tc b/tools/testing/selftests/ftrace/test.d/00basic/ringbuffer_subbuf_size.tc index d44d09a33a74..8cfe59d32b1f 100644 --- a/tools/testing/selftests/ftrace/test.d/00basic/ringbuffer_subbuf_size.tc +++ b/tools/testing/selftests/ftrace/test.d/00basic/ringbuffer_subbuf_size.tc @@ -60,7 +60,7 @@ test_buffer() { # the size must be greater than or equal to page_size - data_offset page_size=$((page_size-data_offset)) if [ $size -lt $page_size ]; then - exit fail + exit_fail fi # Now add a little more the meta data overhead will overflow @@ -71,14 +71,14 @@ test_buffer() { new_str=`awk ' /tracing_mark_write:/ { sub(/^.*tracing_mark_write: /,"");printf "%s", $0; exit}' trace` if [ "$new_str" = "$str" ]; then - exit fail; + exit_fail fi # Make sure the entire line can be found new_str=`awk ' /tracing_mark_write:/ { sub(/^.*tracing_mark_write: /,"");printf "%s", $0; }' trace` if [ "$new_str" != "$str" ]; then - exit fail; + exit_fail fi } diff --git a/tools/testing/selftests/ftrace/test.d/00basic/trace_marker.tc b/tools/testing/selftests/ftrace/test.d/00basic/trace_marker.tc index 9aa0db2b84fc..1986c1bf1606 100644 --- a/tools/testing/selftests/ftrace/test.d/00basic/trace_marker.tc +++ b/tools/testing/selftests/ftrace/test.d/00basic/trace_marker.tc @@ -68,14 +68,14 @@ test_buffer() { new_str=`awk ' /tracing_mark_write:/ { sub(/^.*tracing_mark_write: /,"");printf "%s", $0; exit}' trace` if [ "$new_str" = "$str" ]; then - exit fail; + exit_fail fi # Make sure the entire line can be found new_str=`awk ' /tracing_mark_write:/ { sub(/^.*tracing_mark_write: /,"");printf "%s", $0; }' trace` if [ "$new_str" != "$str" ]; then - exit fail; + exit_fail fi } From 0abfa9ad5c6917cb9a509276f32009d944b3c269 Mon Sep 17 00:00:00 2001 From: Jaewon Jung Date: Wed, 14 Oct 2026 20:48:25 +0900 Subject: [PATCH 08/14] selftests/ftrace: Avoid unlink error on first run On the first run, ftracetest tries to unlink a symlink that does not exist. Use rm -f to avoid the "No such file or directory" error. Link: https://lore.kernel.org/r/20261014114839.6258-1-jaewon8868@gmail.com Fixes: 7dc1e125f07a ("ftrace/selftests: Add softlink to latest log directory") Signed-off-by: Jaewon Jung Acked-by: Masami Hiramatsu (Google) Signed-off-by: Shuah Khan --- tools/testing/selftests/ftrace/ftracetest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/ftrace/ftracetest b/tools/testing/selftests/ftrace/ftracetest index 246d7e1d015c..95804610e079 100755 --- a/tools/testing/selftests/ftrace/ftracetest +++ b/tools/testing/selftests/ftrace/ftracetest @@ -243,7 +243,7 @@ else mkdir -p $LOG_DIR || errexit "Failed to make a log directory: $LOG_DIR" date > $LOG_FILE if [ "x-$LINK_PTR" != "x-" ]; then - unlink $LINK_PTR + rm -f $LINK_PTR ln -fs $LOG_DATE $LINK_PTR fi fi From 62d432038329456b71643f77ae586165fb1c2c35 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 14 Oct 2026 14:03:33 +0100 Subject: [PATCH 09/14] selftests: lib.mk: Don't fail install if no modules were built The install rule for TEST_GEN_MODS_DIR copies *.ko from the directory with rsync which fails if there are no modules, for example because the directory's Makefile skipped building them. Only run rsync if there is something to install. This currently affects the livepatch suite but it seems sensible to fix here for added robstness. Link: https://lore.kernel.org/r/20261014-kselftest-lib-no-modules-v1-1-e4f7208dcb56@kernel.org Signed-off-by: Mark Brown Signed-off-by: Shuah Khan --- tools/testing/selftests/lib.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/lib.mk b/tools/testing/selftests/lib.mk index de15e63bc6b4..881f13ee2c8a 100644 --- a/tools/testing/selftests/lib.mk +++ b/tools/testing/selftests/lib.mk @@ -156,7 +156,7 @@ endef define INSTALL_MODS_RULE $(if $(INSTALL_LIST),@mkdir -p $(INSTALL_PATH)/$(INSTALL_LIST)) - $(if $(INSTALL_LIST),rsync -a --copy-unsafe-links $(INSTALL_LIST)/*.ko $(INSTALL_PATH)/$(INSTALL_LIST)) + $(if $(wildcard $(INSTALL_LIST)/*.ko),rsync -a --copy-unsafe-links $(INSTALL_LIST)/*.ko $(INSTALL_PATH)/$(INSTALL_LIST)) endef define INSTALL_RULE From cc4a09cc4391098cd7241e010567e49289c211d0 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 13 Oct 2026 20:40:00 +0100 Subject: [PATCH 10/14] selftests: kselftest_deps.sh: Support LLVM The kselftest_deps.sh script currently takes a compiler as the first argument and uses it to check that libraries needed by the selftests are installed. This works well for GCC where a separate binary is provided for each target architecture but is less of a good fit for LLVM where a single compiler binary can target all of the supported architetures. Extend the script to accept "LLVM" as the compiler with an additional optional ARCH= argument so that it can be used when building with LLVM in a similar fashion to how the actual build works. The duplication with the main Makefile isn't ideal here. Link: https://lore.kernel.org/r/20261013-kselftest-deps-llvm-v1-1-f2ee70ae1b79@kernel.org Signed-off-by: Mark Brown Acked-by: Nick Desaulniers Signed-off-by: Shuah Khan --- tools/testing/selftests/kselftest_deps.sh | 83 ++++++++++++++++++++++- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/kselftest_deps.sh b/tools/testing/selftests/kselftest_deps.sh index 487e49fdf2a6..70b834361cb7 100755 --- a/tools/testing/selftests/kselftest_deps.sh +++ b/tools/testing/selftests/kselftest_deps.sh @@ -10,13 +10,20 @@ usage() { -echo -e "Usage: $0 -[p] [test_name]\n" +echo -e "Usage: $0 -[p] [ARCH=] [test_name]\n" echo -e "\tkselftest_deps.sh [-p] gcc" echo -e "\tkselftest_deps.sh [-p] gcc mm" echo -e "\tkselftest_deps.sh [-p] aarch64-linux-gnu-gcc" -echo -e "\tkselftest_deps.sh [-p] aarch64-linux-gnu-gcc mm\n" +echo -e "\tkselftest_deps.sh [-p] aarch64-linux-gnu-gcc mm" +echo -e "\tkselftest_deps.sh [-p] LLVM" +echo -e "\tkselftest_deps.sh [-p] LLVM ARCH=arm64 mm\n" echo "- Should be run in selftests directory in the kernel repo." echo "- Checks if Kselftests can be built/cross-built on a system." +echo "- Compiler can be given as LLVM to use clang, as with LLVM=1 in" +echo " the kselftest build. LLVM=/ and LLVM=- select" +echo " the clang binary as in the kselftest build. ARCH= selects" +echo " the clang target architecture, defaulting to the host; it is" +echo " ignored for other compilers." echo "- Parses all test/sub-test Makefile to find library dependencies." echo "- Runs compile test on a trivial C file with LDLIBS specified" echo " in the test Makefiles to identify missing library dependencies." @@ -53,13 +60,32 @@ while getopts "p" arg; do esac done +# Pull an optional ARCH= out of the remaining arguments. The +# environment is used if it is not given on the command line, as with +# make. +args=() +for arg in "$@"; do + case "$arg" in + ARCH=*) + ARCH=${arg#ARCH=};; + *) + args+=("$arg");; + esac +done +set -- "${args[@]}" + if [ $# -eq 0 ] then usage fi # Compiler -CC=$1 +case "$1" in + LLVM|LLVM=*) + setup_llvm "${1#LLVM}";; + *) + CC=$1;; +esac tmp_file=$(mktemp).c trap "rm -f $tmp_file.o $tmp_file $tmp_file.bin" EXIT @@ -178,6 +204,56 @@ exit $? } # end main() +# Mirror the LLVM handling in lib.mk: LLVM=/ or LLVM=- +# select the clang binary, and ARCH selects the target triple with the +# host as the default. +setup_llvm() +{ + local llvm=${1#=} + local llvm_prefix="" + local llvm_suffix="" + local clang + local target + + declare -A clang_target + clang_target[arm]=arm-linux-gnueabi + clang_target[arm64]=aarch64-linux-gnu + clang_target[hexagon]=hexagon-linux-musl + clang_target[i386]=i386-linux-gnu + clang_target[m68k]=m68k-linux-gnu + clang_target[mips]=mipsel-linux-gnu + clang_target[powerpc]=powerpc64le-linux-gnu + clang_target[riscv]=riscv64-linux-gnu + clang_target[s390]=s390x-linux-gnu + clang_target[x86]=x86_64-linux-gnu + clang_target[x86_64]=x86_64-linux-gnu + + case "$llvm" in + */) + llvm_prefix=$llvm;; + -*) + llvm_suffix=$llvm;; + esac + clang=${llvm_prefix}clang${llvm_suffix} + + if [ -z "$ARCH" ]; then + target=$($clang -print-target-triple 2>/dev/null) + if [ -z "$target" ]; then + echo -e "\tUnable to run $clang ..." + exit 1 + fi + else + target=${clang_target[$ARCH]} + if [ -z "$target" ]; then + echo -e "\tUnknown ARCH $ARCH for LLVM, supported:" + echo -e "\t$(echo "${!clang_target[*]}" | xargs -n1 | sort | xargs)" + exit 1 + fi + fi + + CC="$clang --target=$target -fintegrated-as" +} + all_tests() { for test in $l1_tests; do @@ -285,6 +361,7 @@ print_results() { echo -e "========================================================"; echo -e "Kselftest Dependency Check for [$0 $1 $2] results..." + echo -e "Compiler: $CC" if [ $print_targets -ne 0 ] then From cec2b4f55cf66e77019308442b4fbb555f551c30 Mon Sep 17 00:00:00 2001 From: Jaewon Jung Date: Thu, 15 Oct 2026 11:23:39 +0900 Subject: [PATCH 11/14] selftests/ftrace: Add nice dependency The tests use nice but do not list it as a requirement. Add nice to the requirements so the tests are unresolved, not failed, when it is not available. Link: https://lore.kernel.org/r/20261015022416.43534-1-jaewon8868@gmail.com` Signed-off-by: Jaewon Jung Acked-by: Masami Hiramatsu (Google) Signed-off-by: Shuah Khan --- .../test.d/trigger/inter-event/trigger-onchange-action-hist.tc | 2 +- .../test.d/trigger/inter-event/trigger-snapshot-action-hist.tc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-onchange-action-hist.tc b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-onchange-action-hist.tc index 55ab0270e5f7..04e6db2a734d 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-onchange-action-hist.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-onchange-action-hist.tc @@ -1,7 +1,7 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 # description: event trigger - test inter-event histogram trigger onchange action -# requires: set_event "onchange(var)":README ping:program +# requires: set_event "onchange(var)":README ping:program nice:program # flags: instance fail() { #msg diff --git a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-snapshot-action-hist.tc b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-snapshot-action-hist.tc index 9eb37c2fa417..64bb0dca7e3b 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-snapshot-action-hist.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-snapshot-action-hist.tc @@ -1,7 +1,7 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 # description: event trigger - test inter-event histogram trigger snapshot action -# requires: set_event snapshot events/sched/sched_process_fork/hist "onchange(var)":README "snapshot()":README ping:program +# requires: set_event snapshot events/sched/sched_process_fork/hist "onchange(var)":README "snapshot()":README ping:program nice:program # flags: instance fail() { #msg From 907d138279ee3747efedc9f7403d8b0edb52aca2 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 14 Oct 2026 16:01:09 -0400 Subject: [PATCH 12/14] selftests: keep the build status from wrapping to success The top-level all and install rules return failure only when every selected target fails, which commit cad6f32665cb ("selftests: Deescalate error reporting") deliberately kept as the default. They implement this by multiplying the exit statuses of the targets: ret=$$((ret * $$?)) A failing recursive make exits with status 2, so eight failing targets produce 256, and "exit 256" reports success. Building eight or more targets that all fail therefore succeeds, and install goes on to package an empty installation. Record a successful target instead of multiplying statuses. The rules still fail only when no target succeeds, and FORCE_TARGETS still stops at the first failure. Link: https://lore.kernel.org/r/20261014200115.1363363-2-sashal@kernel.org Fixes: 5f70bde26a48 ("selftests: fix build behaviour on targets' failures") Signed-off-by: Sasha Levin Reviewed-by: Mark Brown Signed-off-by: Shuah Khan --- tools/testing/selftests/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 2d960626750e..62a24e742be6 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -227,7 +227,7 @@ all: $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET \ O=$(abs_objtree) \ $(if $(FORCE_TARGETS),|| exit); \ - ret=$$((ret * $$?)); \ + [ $$? -eq 0 ] && ret=0; \ done; exit $$ret; run_tests: all @@ -294,7 +294,7 @@ ifdef INSTALL_PATH OBJ_PATH=$(INSTALL_PATH) \ O=$(abs_objtree) \ $(if $(FORCE_TARGETS),|| exit); \ - ret=$$((ret * $$?)); \ + [ $$? -eq 0 ] && ret=0; \ done; exit $$ret; From 39261b797f40e5ac29d1bb85a676f74cbfd55e9e Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 14 Oct 2026 16:01:10 -0400 Subject: [PATCH 13/14] selftests: optionally record per-suite build and install results By default the selftests build and installation succeed as long as one target succeeds, so that a single broken suite does not cost CI systems the coverage of all others; commit cad6f32665cb ("selftests: Deescalate error reporting") restored that policy after it had been tightened. The exit status of make therefore cannot tell which suites failed, and inspecting the installation is not reliable either: a suite whose compilation failed can still install its scripts. Add an opt-in report. When KSFT_BUILD_REPORT names an absolute file, the all and install rules append one tab-separated record per target with the phase, the target name and the exit status of its recursive make. Generating the runlist is recorded as part of the install phase. The records are written without changing which targets are built or installed or the exit status of make, also with FORCE_TARGETS. Nothing is recorded by make -n, -t or -q, including when the selftests are built through the kernel's kselftest targets, which clear MAKEFLAGS. The recording helper lives in a small fragment so that suites built from subdirectories can add records for them, and the variable is exported to sub-makes. A relative path is rejected, since every recursive make runs in a different directory. Keep the exit status of the runlist generation loop as before, and create an empty runlist for installations in which no target emits tests. Document the format, version 1, and how to read it. Link: https://lore.kernel.org/r/20261014200115.1363363-3-sashal@kernel.org Link: https://lore.kernel.org/r/20260416-selftests-deescalate-error-reporting-v1-1-38e7c0536227@kernel.org Signed-off-by: Sasha Levin Signed-off-by: Shuah Khan --- Documentation/dev-tools/kselftest.rst | 38 +++++++++++++++++++++++++ tools/testing/selftests/Makefile | 31 ++++++++++++++------ tools/testing/selftests/build_report.mk | 26 +++++++++++++++++ 3 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 tools/testing/selftests/build_report.mk diff --git a/Documentation/dev-tools/kselftest.rst b/Documentation/dev-tools/kselftest.rst index 64c0ec7428a2..26a536ef4cd6 100644 --- a/Documentation/dev-tools/kselftest.rst +++ b/Documentation/dev-tools/kselftest.rst @@ -133,6 +133,44 @@ build successfully; make will abort as soon as any target fails:: This applies to both the ``all`` and ``install`` targets and is useful in CI environments where a silent partial build would be misleading. +Recording suite build results +============================= + +Since the build succeeds as long as one target builds, the exit status of +make does not show which targets failed. Set ``KSFT_BUILD_REPORT`` to the +absolute path of a file to have the ``all`` and ``install`` targets append +a tab-separated record for each target they process:: + + $ : > /tmp/ksft-build.tsv + $ make -C tools/testing/selftests O=/tmp/kernel-build \ + KSFT_BUILD_REPORT=/tmp/ksft-build.tsv install + +Each record holds the phase (``all`` or ``install``), a name and the exit +status of that step. Recording does not change which targets are built or +installed, or the exit status of make, also with ``FORCE_TARGETS=1``. The +caller creates or truncates the report before a build; the rules only +append to it. Nothing is recorded by ``make -n``, ``-t`` or ``-q``. + +The same name can appear several times: ``install`` builds the targets +again, and generating ``kselftest-list.txt`` is recorded as part of the +``install`` phase. Targets that are added automatically as installation +dependencies of other targets, such as ``net/lib``, are recorded too. +Suites that are built from several subdirectories may also record each +subdirectory under its path, for example ``arm64/tags``; a failure there +means that the suite was built or installed only partially. A record +belongs to the target with the longest name that is equal to the record +name or followed by ``/`` in it. + +A target was built and installed successfully only if all of its records +have a zero status. Missing records do not show that a target was built. +A successful target that installs no tests has no entries in +``kselftest-list.txt``. Install the kernel headers with ``make headers`` +before building: a suite that fails because the headers are missing is +recorded as failed even if a later step installs them. + +``KSFT_BUILD_REPORT_VERSION`` in tools/testing/selftests/Makefile gives the +version of this format, currently ``1``. + Running the full range hotplug selftests ======================================== diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 62a24e742be6..d4c71ad20bbb 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -171,6 +171,10 @@ endif # of the targets gets built. FORCE_TARGETS ?= +# Remember dry runs for the build report before MAKEFLAGS may be cleared. +KSFT_BUILD_REPORT_DRY_RUN := $(strip $(foreach f,n t q,$(findstring $(f),$(firstword -$(MAKEFLAGS))))) +export KSFT_BUILD_REPORT_DRY_RUN + # Clear LDFLAGS and MAKEFLAGS when implicit rules are missing. This provides # implicit rules to sub-test Makefiles which avoids build failures in test # Makefile that don't have explicit build rules. @@ -215,6 +219,10 @@ ARCH ?= $(SUBARCH) export BUILD export KHDR_INCLUDES +# Version of the optional KSFT_BUILD_REPORT format written by these rules. +KSFT_BUILD_REPORT_VERSION := 1 +include $(top_srcdir)/tools/testing/selftests/build_report.mk + # set default goal to all, so make without a target runs all, even when # all isn't the first target in the file. .DEFAULT_GOAL := all @@ -223,11 +231,13 @@ all: @ret=1; \ for TARGET in $(TARGETS) $(INSTALL_DEP_TARGETS); do \ BUILD_TARGET=$$BUILD/$$TARGET; \ - mkdir $$BUILD_TARGET -p; \ + mkdir -p $$BUILD_TARGET && \ $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET \ - O=$(abs_objtree) \ - $(if $(FORCE_TARGETS),|| exit); \ - [ $$? -eq 0 ] && ret=0; \ + O=$(abs_objtree); \ + status=$$?; \ + $(call ksft_record_build_status,all,$$TARGET); \ + $(if $(FORCE_TARGETS),[ $$status -eq 0 ] || exit $$status;) \ + [ $$status -eq 0 ] && ret=0; \ done; exit $$ret; run_tests: all @@ -284,7 +294,7 @@ ifdef INSTALL_PATH install -m 744 kselftest/ktap_helpers.sh $(INSTALL_PATH)/kselftest/ install -m 744 kselftest/ksft.py $(INSTALL_PATH)/kselftest/ install -m 744 run_kselftest.sh $(INSTALL_PATH)/ - rm -f $(TEST_LIST) + : > $(TEST_LIST) @ret=1; \ for TARGET in $(TARGETS) $(INSTALL_DEP_TARGETS); do \ BUILD_TARGET=$$BUILD/$$TARGET; \ @@ -292,9 +302,11 @@ ifdef INSTALL_PATH INSTALL_PATH=$(INSTALL_PATH)/$$TARGET \ SRC_PATH=$(shell readlink -e $$(pwd)) \ OBJ_PATH=$(INSTALL_PATH) \ - O=$(abs_objtree) \ - $(if $(FORCE_TARGETS),|| exit); \ - [ $$? -eq 0 ] && ret=0; \ + O=$(abs_objtree); \ + status=$$?; \ + $(call ksft_record_build_status,install,$$TARGET); \ + $(if $(FORCE_TARGETS),[ $$status -eq 0 ] || exit $$status;) \ + [ $$status -eq 0 ] && ret=0; \ done; exit $$ret; @@ -308,6 +320,9 @@ ifdef INSTALL_PATH printf "Emit Tests for $$TARGET\n"; \ $(MAKE) -s --no-print-directory OUTPUT=$$BUILD_TARGET COLLECTION=$$TARGET \ -C $$TARGET emit_tests >> $(TEST_LIST); \ + status=$$?; \ + $(call ksft_record_build_status,install,$$TARGET); \ + (exit $$status); \ done; @VERSION=$$(git describe HEAD 2>/dev/null); \ if [ -n "$$VERSION" ]; then \ diff --git a/tools/testing/selftests/build_report.mk b/tools/testing/selftests/build_report.mk new file mode 100644 index 000000000000..047c239925e1 --- /dev/null +++ b/tools/testing/selftests/build_report.mk @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Optional per-suite build report, see "Recording suite build results" in +# Documentation/dev-tools/kselftest.rst. Callers set KSFT_BUILD_REPORT to an +# absolute filename and use ksft_record_build_status from a shell loop that +# has stored the exit status of the recorded step in $status. + +ifneq (x$(KSFT_BUILD_REPORT)x,xx) +ifeq ($(filter x/%,$(firstword x$(KSFT_BUILD_REPORT))),) +$(error KSFT_BUILD_REPORT must be an absolute path: '$(KSFT_BUILD_REPORT)') +endif +endif +export KSFT_BUILD_REPORT + +ksft_build_report_dry_run := $(KSFT_BUILD_REPORT_DRY_RUN) \ + $(foreach f,n t q,$(findstring $(f),$(firstword -$(MAKEFLAGS)))) + +ifneq ($(strip $(ksft_build_report_dry_run)),) +ksft_record_build_status = : +else +define ksft_record_build_status +if [ -n "$$KSFT_BUILD_REPORT" ]; then \ + printf '%s\t%s\t%d\n' '$(1)' "$(2)" "$$status" >> "$$KSFT_BUILD_REPORT" || exit 1; \ +fi +endef +endif From 368e5d4d12a802ee3708800319cd13904721286f Mon Sep 17 00:00:00 2001 From: Anirudh Srinivasan Date: Fri, 2 Oct 2026 16:54:46 -0500 Subject: [PATCH 14/14] selftests: breakpoints: Skip step_after_suspend_test on systems without a wakeup capable RTC This test puts a system into suspend state and uses CLOCK_BOOTTIME_ALARM to wake the system up. This alarm can only wake up systems that have a wakeup capable RTC source. On systems without such an RTC (for example, most RISC-V boards), this test puts the system into suspend and just hangs, requiring power cycling. clock_getres(CLOCK_BOOTTIME_ALARM) fails with EINVAL on such systems, so add a check based on that to skip this test on systems without a wakeup capable RTC source. Link: https://lore.kernel.org/r/20261002-breakpoints_rtc-v1-1-a3b6542a0fdf@oss.tenstorrent.com Signed-off-by: Anirudh Srinivasan Signed-off-by: Shuah Khan --- .../selftests/breakpoints/step_after_suspend_test.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tools/testing/selftests/breakpoints/step_after_suspend_test.c b/tools/testing/selftests/breakpoints/step_after_suspend_test.c index ca2aaab9e4ca..727790259429 100644 --- a/tools/testing/selftests/breakpoints/step_after_suspend_test.c +++ b/tools/testing/selftests/breakpoints/step_after_suspend_test.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "kselftest.h" @@ -158,11 +159,21 @@ void suspend(void) int err; int count_before; int count_after; + struct timespec res; struct itimerspec spec = {}; if (getuid() != 0) ksft_exit_skip("Please run the test as root - Exiting.\n"); + /* + * CLOCK_BOOTTIME_ALARM cannot wake up systems that don't have a + * wakeup capable RTC from suspend. Check if we have a wakeup-capable + * RTC to prevent this test from causing the system to hang after + * it suspends. + */ + if (clock_getres(CLOCK_BOOTTIME_ALARM, &res) != 0) + ksft_exit_skip("No wakeup-capable RTC for CLOCK_BOOTTIME_ALARM\n"); + timerfd = timerfd_create(CLOCK_BOOTTIME_ALARM, 0); if (timerfd < 0) ksft_exit_fail_msg("timerfd_create() failed\n");