From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BBAF2372ED0; Fri, 11 Sep 2026 19:12:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789153973; cv=none; b=WQqeNUtSHmRvNp8L3VRoo3sOWyC9KzqiXP5D5iH+b3hR5Z75pDw7cO/L+49M4zUg0OFZVoqdtUpF5wudS5VQJgaRpyjdQK4Q2sX3ootQOf2XLbudA3g6v14Ruu6UGlMewHemCCXwvqGhyqwWfM8YQCLuGHdJYT1iDVHH8/7sURA= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789153973; c=relaxed/simple; bh=c4yd1kthwTDA0wbnB35YfKitPCxfeYtclDVdCTwjdvU=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=QqGe78EyyiLQalPXHR3COtwSLdWPqkYmeRyvs55EFjet7qUv/CZsMHSHLk/IFZJCfTVPAHXm+rPlsMcoC5RtvHBv4txX5uy7nOGBaKFVAEbxeXYIsSLVZmCIeQizT8d34QCPTkv121hdEzq2YzVdOMeneLsJIGpLZ1nfw39LpL4= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=MgJh0y02; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="MgJh0y02" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0ACB11F000FF; Fri, 11 Sep 2026 19:12:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789153960; bh=68KFiehfIHvDcGVNGsPrWYIf012K7fCsBnqAtZBD1Hw=; h=Date:From:To:Cc:Subject:References:In-Reply-To; b=MgJh0y02DAWbVmlXfPlNkkDtdNLPZcojTJp087N/3g+LYogw6GJmoGQfBHPM6D4xE KaElPdcZ3qfCRK8n7UMrbSLAhuvH28wdV4Q1l8T+/mpEgNHXzlyrRzQ7pXWTxeVOOF NFjYSWEWvmEbHJ3zAlEkZp0vQqSNDQ8iUIRw+Zuz1PWaPqbPaBRXG2e8ilH9yJQdz6 5yEojp89jNzi8wbVLK7xPTJUXc7ce8dE0b3349ZufNED7xdd6gIauyQf1gwROuXcbG v0CYdcxH1PbCCpb8WkLyAMJ6TQJlwO7w/UiLqCueljb1w+Oz13s2LyTqEZJCyRFmAl +ITqpGBwQRJ2g== Date: Fri, 11 Sep 2026 20:12:34 +0100 From: "Lorenzo Stoakes (ARM)" To: Suren Baghdasaryan Cc: akpm@linux-foundation.org, liam@infradead.org, vbabka@kernel.org, david@redhat.com, willy@infradead.org, jannh@google.com, paulmck@kernel.org, pfalcato@suse.de, xueyuan.chen21@gmail.com, linux-mm@kvack.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Subject: Re: [PATCH v3 7/7] selftests/proc: add /proc/pid/smaps_rollup tearing tests Message-ID: References: <20260910234737.1340642-1-surenb@google.com> <20260910234737.1340642-8-surenb@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20260910234737.1340642-8-surenb@google.com> On Thu, Sep 10, 2026 at 04:47:37PM -0700, Suren Baghdasaryan wrote: > During tearing tests, smaps_rollup Pss* metrics should stay constant. > Extend /proc/pid/smaps tearing tests to also check for smaps_rollup > consistency. > > Signed-off-by: Suren Baghdasaryan All looks reasonable an dall passing locally so: Acked-by: Lorenzo Stoakes (ARM) > --- > tools/testing/selftests/proc/proc-maps-race.c | 187 +++++++++++++++++- > 1 file changed, 182 insertions(+), 5 deletions(-) > > diff --git a/tools/testing/selftests/proc/proc-maps-race.c b/tools/testing/selftests/proc/proc-maps-race.c > index 415eccb70468..8d00d7db1c65 100644 > --- a/tools/testing/selftests/proc/proc-maps-race.c > +++ b/tools/testing/selftests/proc/proc-maps-race.c > @@ -80,6 +80,61 @@ enum maps_file { > > struct vma_modifier_info; > > +enum smaps_rollup_stat { > + Rss, > + Pss, > + Pss_Dirty, > + Pss_Anon, > + Pss_File, > + Pss_Shmem, > + Shared_Clean, > + Shared_Dirty, > + Private_Clean, > + Private_Dirty, > + Referenced, > + Anonymous, > + KSM, > + LazyFree, > + AnonHugePages, > + ShmemPmdMapped, > + FilePmdMapped, > + Shared_Hugetlb, > + Private_Hugetlb, > + Swap, > + SwapPss, > + Locked, > + RollupFieldCount > +}; > + > +static const char *smaps_rollup_stat_names[RollupFieldCount] = { > + "Rss", > + "Pss", > + "Pss_Dirty", > + "Pss_Anon", > + "Pss_File", > + "Pss_Shmem", > + "Shared_Clean", > + "Shared_Dirty", > + "Private_Clean", > + "Private_Dirty", > + "Referenced", > + "Anonymous", > + "KSM", > + "LazyFree", > + "AnonHugePages", > + "ShmemPmdMapped", > + "FilePmdMapped", > + "Shared_Hugetlb", > + "Private_Hugetlb", > + "Swap", > + "SwapPss", > + "Locked", > +}; > + > +struct smaps_rollup_stats { > + unsigned long values[RollupFieldCount]; > +}; > + > FIXTURE(proc_maps_race) > { > struct vma_modifier_info *mod_info; > @@ -91,6 +146,7 @@ FIXTURE(proc_maps_race) > enum maps_file maps_file; > int shared_mem_size; > int skip_pages; > + int rollup_fd; > int page_size; > int vma_count; > bool verbose; > @@ -132,12 +188,12 @@ struct vma_modifier_info { > void *child_mapped_addr[]; > }; > > -static bool read_page(FIXTURE_DATA(proc_maps_race) *self, > +static bool read_page(FIXTURE_DATA(proc_maps_race) *self, int fd, > struct page_content *page) > { > ssize_t bytes_read; > > - bytes_read = read(self->maps_fd, page->data, self->page_size); > + bytes_read = read(fd, page->data, self->page_size); > if (bytes_read <= 0) > return false; > > @@ -175,7 +231,7 @@ static int locate_containing_page(FIXTURE_DATA(proc_maps_race) *self, > char *curr_pos; > char *end_pos; > > - if (!read_page(self, &self->page1)) > + if (!read_page(self, self->maps_fd, &self->page1)) > return -1; > > curr_pos = self->page1.data; > @@ -205,10 +261,11 @@ static bool read_two_pages(FIXTURE_DATA(proc_maps_race) *self) > return false; > > for (int i = 0; i < self->skip_pages; i++) > - if (!read_page(self, &self->page1)) > + if (!read_page(self, self->maps_fd, &self->page1)) > return false; > > - return read_page(self, &self->page1) && read_page(self, &self->page2); > + return read_page(self, self->maps_fd, &self->page1) && > + read_page(self, self->maps_fd, &self->page2); > } > > static void copy_line(const char *line_start, const char *line_end, > @@ -317,6 +374,61 @@ static bool read_boundary_lines(FIXTURE_DATA(proc_maps_race) *self, > &first_line->end_addr) == 2; > } > > +static bool parse_smaps_rollup(FIXTURE_DATA(proc_maps_race) *self, > + struct smaps_rollup_stats *stats) > +{ > + unsigned int dev_maj, dev_min, inode; > + unsigned long start, end, offs; > + unsigned long value; > + char name[32], perm[5]; > + char *curr_pos; > + char *end_pos; > + char *line_end; > + > + if (lseek(self->rollup_fd, 0, SEEK_SET) < 0) > + return false; > + > + if (!read_page(self, self->rollup_fd, &self->page1)) > + return false; > + > + curr_pos = self->page1.data; > + end_pos = self->page1.data + self->page1.size; > + > + line_end = strchr(curr_pos, '\n'); > + if (!line_end) > + return false; > + > + if (sscanf(curr_pos, "%lx-%lx %4s %lx %u:%u %u %31s", > + &start, &end, perm, &offs, &dev_maj, &dev_min, &inode, name) != 8) > + return false; > + > + if (strcmp(name, "[rollup]")) > + return false; > + > + for (int stat = 0; stat < ARRAY_SIZE(smaps_rollup_stat_names); stat++) { > + int len; > + > + curr_pos = line_end + 1; > + if (curr_pos >= end_pos) > + return false; > + > + line_end = strchr(curr_pos, '\n'); > + if (!line_end) > + return false; > + > + if (sscanf(curr_pos, "%31s %lu kB", name, &value) != 2) > + return false; > + > + len = strlen(name); > + if (name[len - 1] != ':' || strncmp(name, smaps_rollup_stat_names[stat], len - 1)) > + return false; > + > + stats->values[stat] = value; > + } > + > + return true; > +} > + > /* Thread synchronization routines */ > static void wait_for_state(struct vma_modifier_info *mod_info, enum test_state state) > { > @@ -397,6 +509,41 @@ static bool print_boundaries_on(bool condition, const char *title, > return condition; > } > > +static void print_smaps_rollup_stats(const char *title, FIXTURE_DATA(proc_maps_race) *self, > + struct smaps_rollup_stats *stats) > +{ > + printf("%s", title); > + for (int stat = 0; stat < ARRAY_SIZE(smaps_rollup_stat_names); stat++) > + printf("%64s %lu kB\n", smaps_rollup_stat_names[stat], stats->values[stat]); > +} > + > +static bool cmp_smaps_rollup_stat(struct smaps_rollup_stats *s1, > + struct smaps_rollup_stats *s2, > + enum smaps_rollup_stat stat) > +{ > + return s1->values[stat] == s2->values[stat]; > +} > + > +static bool compare_smaps_rollup(FIXTURE_DATA(proc_maps_race) *self, > + struct smaps_rollup_stats *expected, > + struct smaps_rollup_stats *actual) > +{ > + /* > + * Clean/dirty metrics might change but Pss-related ones > + * should stay constant. > + */ > + if (cmp_smaps_rollup_stat(expected, actual, Pss) && > + cmp_smaps_rollup_stat(expected, actual, Pss_Anon) && > + cmp_smaps_rollup_stat(expected, actual, Pss_File) && > + cmp_smaps_rollup_stat(expected, actual, Pss_Shmem)) > + return true; > + > + print_smaps_rollup_stats("Expected stats:", self, expected); > + print_smaps_rollup_stats("Actual stats:", self, actual); > + > + return false; > +} > + > static void report_test_start(const char *name, bool verbose) > { > if (verbose) > @@ -572,6 +719,7 @@ FIXTURE_SETUP(proc_maps_race) > unsigned long first_map_addr; > unsigned long last_map_addr; > unsigned long duration_sec; > + char rollup_fname[32]; > char fname[32]; > > self->page_size = (unsigned long)sysconf(_SC_PAGESIZE); > @@ -649,6 +797,9 @@ FIXTURE_SETUP(proc_maps_race) > break; > case SMAPS: > sprintf(fname, "/proc/%d/smaps", self->pid); > + sprintf(rollup_fname, "/proc/%d/smaps_rollup", self->pid); > + self->rollup_fd = open(rollup_fname, O_RDONLY); > + ASSERT_NE(self->rollup_fd, -1); > break; > default: > ksft_exit_fail(); > @@ -711,6 +862,8 @@ FIXTURE_TEARDOWN(proc_maps_race) > for (int i = 0; i < self->vma_count; i++) > munmap(self->mod_info->child_mapped_addr[i], self->page_size); > close(self->maps_fd); > + if (self->maps_file == SMAPS) > + close(self->rollup_fd); > waitpid(self->pid, &status, 0); > munmap(self->mod_info, self->shared_mem_size); > } > @@ -723,6 +876,7 @@ TEST_F(proc_maps_race, test_maps_tearing_from_split) > struct line_content split_first_line; > struct line_content restored_last_line; > struct line_content restored_first_line; > + struct smaps_rollup_stats orig_stats; > > wait_for_state(mod_info, SETUP_READY); > > @@ -736,6 +890,8 @@ TEST_F(proc_maps_race, test_maps_tearing_from_split) > report_test_start("Tearing from split", self->verbose); > ASSERT_TRUE(capture_mod_pattern(self, &split_last_line, &split_first_line, > &restored_last_line, &restored_first_line)); > + if (self->maps_file == SMAPS) > + ASSERT_TRUE(parse_smaps_rollup(self, &orig_stats)); > > /* Now start concurrent modifications for self->duration_sec */ > signal_state(mod_info, TEST_READY); > @@ -799,6 +955,11 @@ TEST_F(proc_maps_race, test_maps_tearing_from_split) > vma_end == self->last_line.end_addr) || > (vma_start == split_first_line.start_addr && > vma_end == split_first_line.end_addr)); > + } else { > + struct smaps_rollup_stats stats; > + > + ASSERT_TRUE(parse_smaps_rollup(self, &stats)); > + ASSERT_TRUE(compare_smaps_rollup(self, &orig_stats, &stats)); > } > clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); > end_test_iteration(&end_ts, self->verbose); > @@ -817,6 +978,7 @@ TEST_F(proc_maps_race, test_maps_tearing_from_resize) > struct line_content shrunk_first_line; > struct line_content restored_last_line; > struct line_content restored_first_line; > + struct smaps_rollup_stats orig_stats; > > wait_for_state(mod_info, SETUP_READY); > > @@ -830,6 +992,8 @@ TEST_F(proc_maps_race, test_maps_tearing_from_resize) > report_test_start("Tearing from resize", self->verbose); > ASSERT_TRUE(capture_mod_pattern(self, &shrunk_last_line, &shrunk_first_line, > &restored_last_line, &restored_first_line)); > + if (self->maps_file == SMAPS) > + ASSERT_TRUE(parse_smaps_rollup(self, &orig_stats)); > > /* Now start concurrent modifications for self->duration_sec */ > signal_state(mod_info, TEST_READY); > @@ -880,6 +1044,11 @@ TEST_F(proc_maps_race, test_maps_tearing_from_resize) > ASSERT_TRUE(vma_start == self->last_line.start_addr && > (vma_end - vma_start == self->page_size * 3 || > vma_end - vma_start == self->page_size)); > + } else { > + struct smaps_rollup_stats stats; > + > + ASSERT_TRUE(parse_smaps_rollup(self, &stats)); > + ASSERT_TRUE(compare_smaps_rollup(self, &orig_stats, &stats)); > } > clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); > end_test_iteration(&end_ts, self->verbose); > @@ -898,6 +1067,7 @@ TEST_F(proc_maps_race, test_maps_tearing_from_remap) > struct line_content remapped_first_line; > struct line_content restored_last_line; > struct line_content restored_first_line; > + struct smaps_rollup_stats orig_stats; > > wait_for_state(mod_info, SETUP_READY); > > @@ -911,6 +1081,8 @@ TEST_F(proc_maps_race, test_maps_tearing_from_remap) > report_test_start("Tearing from remap", self->verbose); > ASSERT_TRUE(capture_mod_pattern(self, &remapped_last_line, &remapped_first_line, > &restored_last_line, &restored_first_line)); > + if (self->maps_file == SMAPS) > + ASSERT_TRUE(parse_smaps_rollup(self, &orig_stats)); > > /* Now start concurrent modifications for self->duration_sec */ > signal_state(mod_info, TEST_READY); > @@ -963,6 +1135,11 @@ TEST_F(proc_maps_race, test_maps_tearing_from_remap) > vma_end - vma_start == self->page_size * 3) || > (vma_start == self->last_line.start_addr + self->page_size && > vma_end - vma_start == self->page_size)); > + } else { > + struct smaps_rollup_stats stats; > + > + ASSERT_TRUE(parse_smaps_rollup(self, &stats)); > + ASSERT_TRUE(compare_smaps_rollup(self, &orig_stats, &stats)); > } > clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts); > end_test_iteration(&end_ts, self->verbose); > -- > 2.55.0.1007.g17ff1f9808-goog > -- Cheers, Lorenzo