Add the following 2 scenarios to the allocinfo ioctl kselftest: 1. Validate size based filtering 2. Validate lineno based filtering The first test uses "do_init_module" as the candidate function for the test. This is because the associated site will only allocate memory when a kernel module is loaded. The return value of get_content_id() changes every time modules are loaded or unloaded. Hence, as long as get_content_id() values at the start and the end of the test are the same, the memory allocated by the do_init_module call site should also remain the same. Consequently, the test can assume consistency between the value returned by the ioctl and the procfs resulting in less flakiness. Signed-off-by: Abhishek Bapat --- .../alloc_tag/allocinfo_ioctl_test.c | 194 +++++++++++++++++- 1 file changed, 193 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c b/tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c index 543023ca3d27..573d84a3619a 100644 --- a/tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c +++ b/tools/testing/selftests/alloc_tag/allocinfo_ioctl_test.c @@ -294,11 +294,191 @@ static int test_function_filter(void) return run_filter_test(&filter); } +static int test_size_filter(void) +{ + int fd; + struct allocinfo_tag_data_vec *tags = malloc(sizeof(*tags)); + struct allocinfo_tag_data_vec *procfs_entries = malloc(sizeof(*procfs_entries)); + struct allocinfo_filter filter; + int ret = KSFT_PASS; + __u64 target_size, i; + bool found = false; + const char *target_function = "do_init_module"; + + if (!tags || !procfs_entries) { + ksft_print_msg("Memory allocation failed.\n"); + ret = KSFT_FAIL; + goto freemem; + } + + fd = open(ALLOCINFO_PROC, O_RDONLY); + if (fd < 0) { + ksft_exit_skip("Failed to open " ALLOCINFO_PROC ": %s\n", strerror(errno)); + ret = KSFT_FAIL; + goto freemem; + } + + memset(&filter, 0, sizeof(filter)); + filter.mask |= ALLOCINFO_FILTER_MASK_FUNCTION; + strncpy(filter.fields.function, target_function, ALLOCINFO_STR_SIZE); + + if (get_filtered_procfs_entries(procfs_entries, &filter, fd)) { + ksft_print_msg("Error retrieving entries from " ALLOCINFO_PROC "\n"); + ret = KSFT_FAIL; + goto exit; + } + + if (procfs_entries->count == 0) { + ksft_print_msg("Function %s not found in procfs\n", target_function); + ret = KSFT_SKIP; + goto exit; + } + + /* + * We depend on the result of procfs entries to create the ioctl_filter. Hence we + * cannot recycle the run_filter_test function here. + */ + target_size = procfs_entries->tag[0].counter.bytes; + + memset(&filter, 0, sizeof(filter)); + filter.mask |= ALLOCINFO_FILTER_MASK_MIN_SIZE | ALLOCINFO_FILTER_MASK_MAX_SIZE; + filter.fields.min_size = target_size; + filter.fields.max_size = target_size; + + __u64 pos = 0; + enum ioctl_ret ioctl_status; + + /* + * This loop is required because the first 32 entries fetched by the IOCTL based on + * the size parameter might not contain the exact entry that was used from procfs. + * If that happens, we must update pos and fetch again until we find the exact entry. + */ + while (1) { + ioctl_status = get_filtered_ioctl_entries(tags, &filter, fd, pos); + if (ioctl_status == IOCTL_INVALID_DATA) { + ksft_print_msg("Trouble retrieving valid IOCTL entries, skipping.\n"); + ret = KSFT_SKIP; + goto exit; + } + if (ioctl_status == IOCTL_FAILURE) { + ksft_print_msg("Error retrieving IOCTL entries.\n"); + ret = KSFT_FAIL; + goto exit; + } + + for (i = 0; i < tags->count; i++) { + if (strcmp(tags->tag[i].tag.function, target_function) == 0) { + found = true; + break; + } + } + + if (found) + break; + + if (tags->count < VEC_MAX_ENTRIES) + break; + + pos += tags->count; + } + + if (!found) { + ksft_print_msg("Entry with function %s not found in IOCTL results\n", + target_function); + ret = KSFT_FAIL; + } + +exit: + close(fd); +freemem: + free(tags); + free(procfs_entries); + return ret; +} + +static int test_lineno_filter(void) +{ + int fd; + struct allocinfo_tag_data_vec *tags = malloc(sizeof(*tags)); + struct allocinfo_tag_data_vec *procfs_entries = malloc(sizeof(*procfs_entries)); + struct allocinfo_filter filter; + enum ioctl_ret ioctl_status; + int ret = KSFT_PASS; + __u64 target_lineno, i; + + if (!tags || !procfs_entries) { + ksft_print_msg("Memory allocation failed.\n"); + ret = KSFT_FAIL; + goto freemem; + } + + fd = open(ALLOCINFO_PROC, O_RDONLY); + if (fd < 0) { + ksft_exit_skip("Failed to open " ALLOCINFO_PROC ": %s\n", strerror(errno)); + ret = KSFT_FAIL; + goto freemem; + } + + memset(&filter, 0, sizeof(filter)); + + if (get_filtered_procfs_entries(procfs_entries, &filter, fd)) { + ksft_print_msg("Error retrieving entries from " ALLOCINFO_PROC "\n"); + ret = KSFT_FAIL; + goto exit; + } + if (procfs_entries->count == 0) { + ksft_print_msg("Could not retrieve procfs entries\n"); + ret = KSFT_SKIP; + goto exit; + } + /* + * We depend on the result of procfs entries to create the ioctl_filter. Hence we + * cannot recycle the run_filter_test function here. + */ + target_lineno = procfs_entries->tag[0].tag.lineno; + + filter.mask |= ALLOCINFO_FILTER_MASK_LINENO; + filter.fields.lineno = target_lineno; + + ioctl_status = get_filtered_ioctl_entries(tags, &filter, fd, 0); + if (ioctl_status == IOCTL_INVALID_DATA) { + ksft_print_msg("Trouble retrieving valid IOCTL entries, skipping.\n"); + ret = KSFT_SKIP; + goto exit; + } + if (ioctl_status == IOCTL_FAILURE) { + ksft_print_msg("Error retrieving IOCTL entries.\n"); + ret = KSFT_FAIL; + goto exit; + } + + if (tags->count == 0) { + ksft_print_msg("IOCTL returned 0 matches for target lineno %llu.\n", target_lineno); + ret = KSFT_FAIL; + goto exit; + } + for (i = 0; i < tags->count; i++) { + if (tags->tag[i].tag.lineno != target_lineno) { + ksft_print_msg("IOCTL entry %llu has incorrect lineno %llu.\n", + i, tags->tag[i].tag.lineno); + ret = KSFT_FAIL; + goto exit; + } + } + +exit: + close(fd); +freemem: + free(tags); + free(procfs_entries); + return ret; +} + int main(int argc, char *argv[]) { int ret; - ksft_set_plan(2); + ksft_set_plan(4); ret = test_filename_filter(); if (ret == KSFT_SKIP) @@ -312,5 +492,17 @@ int main(int argc, char *argv[]) else ksft_test_result(ret == KSFT_PASS, "test_function_filter\n"); + ret = test_size_filter(); + if (ret == KSFT_SKIP) + ksft_test_result_skip("Skipping test_size_filter\n"); + else + ksft_test_result(ret == KSFT_PASS, "test_size_filter\n"); + + ret = test_lineno_filter(); + if (ret == KSFT_SKIP) + ksft_test_result_skip("Skipping test_lineno_filter\n"); + else + ksft_test_result(ret == KSFT_PASS, "test_lineno_filter\n"); + ksft_finished(); } -- 2.54.0.545.g6539524ca2-goog