Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tools/testing/selftests/mm: add MADV_COLLAPSE test case

To ensure the retract_page_tables() logic functions correctly with the
introduction of VM_MAYBE_GUARD, add a test to assert that madvise collapse
fails when guard regions are established in the collapsed range in all
cases.

Unfortunately we cannot differentiate between e.g.
CONFIG_READ_ONLY_THP_FOR_FS not being set vs. a file-backed VMA having
collapse correctly disallowed, so in each instance we will get an assert
pass here.

We add an additional check to see whether guard regions are preserved
across collapse in case of a bug causing the collapse to succeed, which
will give us more data to debug with should this occur in future.

Link: https://lkml.kernel.org/r/0748beeb864525b8ddfa51adad7128dd32eb3ac4.1763460113.git.lorenzo.stoakes@oracle.com
Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: Andrei Vagin <avagin@gmail.com>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Barry Song <baohua@kernel.org>
Cc: David Hildenbrand (Red Hat) <david@kernel.org>
Cc: Dev Jain <dev.jain@arm.com>
Cc: Jann Horn <jannh@google.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Lance Yang <lance.yang@linux.dev>
Cc: Liam Howlett <liam.howlett@oracle.com>
Cc: "Masami Hiramatsu (Google)" <mhiramat@kernel.org>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Nico Pache <npache@redhat.com>
Cc: Pedro Falcato <pfalcato@suse.de>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Zi Yan <ziy@nvidia.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Lorenzo Stoakes and committed by
Andrew Morton
89330ec8 29bef05e

+65
+65
tools/testing/selftests/mm/guard-regions.c
··· 2138 2138 ASSERT_EQ(munmap(ptr, 10 * page_size), 0); 2139 2139 } 2140 2140 2141 + TEST_F(guard_regions, collapse) 2142 + { 2143 + const unsigned long page_size = self->page_size; 2144 + const unsigned long size = 2 * HPAGE_SIZE; 2145 + const unsigned long num_pages = size / page_size; 2146 + char *ptr; 2147 + int i; 2148 + 2149 + /* Need file to be correct size for tests for non-anon. */ 2150 + if (variant->backing != ANON_BACKED) 2151 + ASSERT_EQ(ftruncate(self->fd, size), 0); 2152 + 2153 + /* 2154 + * We must close and re-open local-file backed as read-only for 2155 + * CONFIG_READ_ONLY_THP_FOR_FS to work. 2156 + */ 2157 + if (variant->backing == LOCAL_FILE_BACKED) { 2158 + ASSERT_EQ(close(self->fd), 0); 2159 + 2160 + self->fd = open(self->path, O_RDONLY); 2161 + ASSERT_GE(self->fd, 0); 2162 + } 2163 + 2164 + ptr = mmap_(self, variant, NULL, size, PROT_READ, 0, 0); 2165 + ASSERT_NE(ptr, MAP_FAILED); 2166 + 2167 + /* Prevent being faulted-in as huge. */ 2168 + ASSERT_EQ(madvise(ptr, size, MADV_NOHUGEPAGE), 0); 2169 + /* Fault in. */ 2170 + ASSERT_EQ(madvise(ptr, size, MADV_POPULATE_READ), 0); 2171 + 2172 + /* Install guard regions in ever other page. */ 2173 + for (i = 0; i < num_pages; i += 2) { 2174 + char *ptr_page = &ptr[i * page_size]; 2175 + 2176 + ASSERT_EQ(madvise(ptr_page, page_size, MADV_GUARD_INSTALL), 0); 2177 + /* Accesses should now fail. */ 2178 + ASSERT_FALSE(try_read_buf(ptr_page)); 2179 + } 2180 + 2181 + /* Allow huge page throughout region. */ 2182 + ASSERT_EQ(madvise(ptr, size, MADV_HUGEPAGE), 0); 2183 + 2184 + /* 2185 + * Now collapse the entire region. This should fail in all cases. 2186 + * 2187 + * The madvise() call will also fail if CONFIG_READ_ONLY_THP_FOR_FS is 2188 + * not set for the local file case, but we can't differentiate whether 2189 + * this occurred or if the collapse was rightly rejected. 2190 + */ 2191 + EXPECT_NE(madvise(ptr, size, MADV_COLLAPSE), 0); 2192 + 2193 + /* 2194 + * If we introduce a bug that causes the collapse to succeed, gather 2195 + * data on whether guard regions are at least preserved. The test will 2196 + * fail at this point in any case. 2197 + */ 2198 + for (i = 0; i < num_pages; i += 2) { 2199 + char *ptr_page = &ptr[i * page_size]; 2200 + 2201 + /* Accesses should still fail. */ 2202 + ASSERT_FALSE(try_read_buf(ptr_page)); 2203 + } 2204 + } 2205 + 2141 2206 TEST_HARNESS_MAIN