linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2] selftest: Add test for Soft-Dirty PTE bit
@ 2021-06-03 15:15 Gabriel Krisman Bertazi
  2021-08-13 15:00 ` Will Deacon
  0 siblings, 1 reply; 3+ messages in thread
From: Gabriel Krisman Bertazi @ 2021-06-03 15:15 UTC (permalink / raw)
  To: shuah
  Cc: will, linux-kernel, peterz, luto, linux-kselftest,
	Gabriel Krisman Bertazi, kernel

This introduces three tests:

1) Sanity check soft dirty basic semantics: allocate area, clean, dirty,
check if the SD bit flipped.

2) Check VMA reuse: validate the VM_SOFTDIRTY usage

3) Check soft-dirty on huge pages

This was motivated by Will Deacon's fix commit 912efa17e512 ("mm: proc:
Invalidate TLB after clearing soft-dirty page state"). I was tracking the
same issue that he fixed, and this test would have caught it.

Cc: Will Deacon <will@kernel.org>
Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>

--
Changes since V1:
  - Fix last minute build break with page_size
---
 tools/testing/selftests/Makefile              |   1 +
 tools/testing/selftests/soft-dirty/.gitignore |   1 +
 tools/testing/selftests/soft-dirty/Makefile   |   9 +
 .../testing/selftests/soft-dirty/soft-dirty.c | 254 ++++++++++++++++++
 4 files changed, 265 insertions(+)
 create mode 100644 tools/testing/selftests/soft-dirty/.gitignore
 create mode 100644 tools/testing/selftests/soft-dirty/Makefile
 create mode 100644 tools/testing/selftests/soft-dirty/soft-dirty.c

diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index bc3299a20338..c8dcd7defd33 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -55,6 +55,7 @@ TARGETS += seccomp
 TARGETS += sgx
 TARGETS += sigaltstack
 TARGETS += size
+TARGETS += soft-dirty
 TARGETS += sparc64
 TARGETS += splice
 TARGETS += static_keys
diff --git a/tools/testing/selftests/soft-dirty/.gitignore b/tools/testing/selftests/soft-dirty/.gitignore
new file mode 100644
index 000000000000..cfb0cfda9bdf
--- /dev/null
+++ b/tools/testing/selftests/soft-dirty/.gitignore
@@ -0,0 +1 @@
+soft-dirty
diff --git a/tools/testing/selftests/soft-dirty/Makefile b/tools/testing/selftests/soft-dirty/Makefile
new file mode 100644
index 000000000000..d76ad8e0f10d
--- /dev/null
+++ b/tools/testing/selftests/soft-dirty/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+top_srcdir = ../../../..
+INSTALL_HDR_PATH = $(top_srcdir)/usr
+LINUX_HDR_PATH = $(INSTALL_HDR_PATH)/include/
+
+CFLAGS += -Wall -I$(LINUX_HDR_PATH) -O0 -g3
+
+TEST_GEN_PROGS := soft-dirty
+include ../lib.mk
diff --git a/tools/testing/selftests/soft-dirty/soft-dirty.c b/tools/testing/selftests/soft-dirty/soft-dirty.c
new file mode 100644
index 000000000000..5a3d1d0e9043
--- /dev/null
+++ b/tools/testing/selftests/soft-dirty/soft-dirty.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <syscall.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <err.h>
+#include <string.h>
+#include <stdbool.h>
+#include <malloc.h>
+
+#define PAGEMAP_PATH "/proc/self/pagemap"
+#define CLEAR_REFS_PATH "/proc/self/clear_refs"
+#define SMAP_PATH "/proc/self/smaps"
+#define MAX_LINE_LENGTH 512
+
+#define TEST_ITERATIONS 10000
+
+#define PMD_SIZE_PATH "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size"
+
+int clear_refs;
+int pagemap;
+
+int pagesize;
+int mmap_size;	/* Size of test region */
+
+static void clear_all_refs(void)
+{
+	if (write(clear_refs, "4\n", 2) != 2)
+		printf("%s: failed to clear references\n", __func__);
+}
+
+static void touch_page(char *map, int n)
+{
+	map[(pagesize * n) + 1]++;
+}
+
+static int check_page(char *map, uint64_t n, int clear)
+{
+	uint64_t off;
+	uint64_t buf = 0;
+
+	off = (n + ((uint64_t)map >> 12)) << 3;
+
+	if (lseek(pagemap, off, SEEK_SET) == (off_t) -1)
+		errx(EXIT_FAILURE, "pagemap llseek failed");
+
+	if (read(pagemap, &buf, 8) != 8)
+		errx(EXIT_FAILURE, "pagemap read failed");
+
+	if (clear)
+		clear_all_refs();
+
+	return ((buf >> 55) & 1);
+}
+
+static void test_simple(void)
+{
+	int i;
+	char *map;
+
+	printf("- Test %s:\n", __func__);
+
+	map = aligned_alloc(pagesize, mmap_size);
+	if (!map)
+		errx(EXIT_FAILURE, "mmap");
+
+	clear_all_refs();
+
+	for (i = 0 ; i < TEST_ITERATIONS; i++) {
+		if (check_page(map, 2, 1) == 1) {
+			errx(EXIT_FAILURE, "dirty bit was 1, but should be 0 (i=%d)", i);
+			break;
+		}
+
+		touch_page(map, 2);
+
+		if (check_page(map, 2, 1) == 0) {
+			errx(EXIT_FAILURE, "dirty bit was 0, but should be 1 (i=%d)", i);
+			break;
+		}
+
+	}
+	free(map);
+
+	printf("success\n");
+}
+
+static void test_vma_reuse(void)
+{
+	char *map, *map2;
+
+	printf("- Test %s:\n", __func__);
+
+	map = (char *) 0x900000000000;
+	map = mmap(map, mmap_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
+	if (map == MAP_FAILED)
+		errx(EXIT_FAILURE, "mmap");
+
+	clear_all_refs();
+	touch_page(map, 2);
+
+	munmap(map, mmap_size);
+	map2 = mmap(map, mmap_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
+	if (map2 == MAP_FAILED)
+		errx(EXIT_FAILURE, "mmap2");
+
+	if (map != map2)
+		errx(EXIT_FAILURE, "map != map2");
+
+	if (check_page(map, 2, 1) == 0)
+		errx(-1, "map/unmap lost dirty");
+
+	munmap(map2, mmap_size);
+
+	printf("success\n");
+}
+
+/*
+ * read_pmd_pagesize(), check_for_pattern() and check_huge() adapted
+ * from 'tools/testing/selftest/vm/split_huge_page_test.c'
+ */
+static uint64_t read_pmd_pagesize(void)
+{
+	int fd;
+	char buf[20];
+	ssize_t num_read;
+
+	fd = open(PMD_SIZE_PATH, O_RDONLY);
+	if (fd == -1)
+		errx(EXIT_FAILURE, "Open hpage_pmd_size failed");
+
+	num_read = read(fd, buf, 19);
+	if (num_read < 1) {
+		close(fd);
+		errx(EXIT_FAILURE, "Read hpage_pmd_size failed");
+	}
+	buf[num_read] = '\0';
+	close(fd);
+
+	return strtoul(buf, NULL, 10);
+}
+
+static bool check_for_pattern(FILE *fp, const char *pattern, char *buf)
+{
+	while (fgets(buf, MAX_LINE_LENGTH, fp) != NULL) {
+		if (!strncmp(buf, pattern, strlen(pattern)))
+			return true;
+	}
+	return false;
+}
+
+static uint64_t check_huge(void *addr)
+{
+	uint64_t thp = 0;
+	int ret;
+	FILE *fp;
+	char buffer[MAX_LINE_LENGTH];
+	char addr_pattern[MAX_LINE_LENGTH];
+
+	ret = snprintf(addr_pattern, MAX_LINE_LENGTH, "%08lx-",
+		       (unsigned long) addr);
+	if (ret >= MAX_LINE_LENGTH)
+		errx(EXIT_FAILURE, "%s: Pattern is too long\n", __func__);
+
+	fp = fopen(SMAP_PATH, "r");
+	if (!fp)
+		errx(EXIT_FAILURE, "%s: Failed to open file %s\n", __func__, SMAP_PATH);
+
+	if (!check_for_pattern(fp, addr_pattern, buffer))
+		goto err_out;
+
+	/*
+	 * Fetch the AnonHugePages: in the same block and check the number of
+	 * hugepages.
+	 */
+	if (!check_for_pattern(fp, "AnonHugePages:", buffer))
+		goto err_out;
+
+	if (sscanf(buffer, "AnonHugePages:%10ld kB", &thp) != 1)
+		errx(EXIT_FAILURE, "Reading smap error\n");
+
+err_out:
+	fclose(fp);
+
+	return thp;
+}
+
+static void test_hugepage(void)
+{
+	char *map;
+	int i, ret;
+	size_t hpage_len = read_pmd_pagesize();
+
+	printf("- Test %s:\n", __func__);
+
+	map = memalign(hpage_len, hpage_len);
+	if (!map)
+		errx(EXIT_FAILURE, "memalign");
+
+	ret = madvise(map, hpage_len, MADV_HUGEPAGE);
+	if (ret)
+		errx(EXIT_FAILURE, "madvise %d", ret);
+
+	for (i = 0; i < hpage_len; i++)
+		map[i] = (char)i;
+
+	if (!check_huge(map))
+		errx(EXIT_FAILURE, "failed to allocate THP");
+
+	clear_all_refs();
+	for (i = 0 ; i < TEST_ITERATIONS ; i++) {
+		if (check_page(map, 2, 1) == 1) {
+			errx(EXIT_FAILURE, "dirty bit was 1, but should be 0 (i=%d)", i);
+			break;
+		}
+
+		touch_page(map, 2);
+
+		if (check_page(map, 2, 1) == 0) {
+			errx(EXIT_FAILURE, "dirty bit was 0, but should be 1 (i=%d)", i);
+			break;
+		}
+	}
+	munmap(map, mmap_size);
+
+	printf("success\n");
+}
+
+int main(int argc, char **argv)
+{
+	pagemap = open(PAGEMAP_PATH, O_RDONLY, 0);
+	if (pagemap < 0)
+		errx(EXIT_FAILURE, "Failed to open %s", PAGEMAP_PATH);
+
+	clear_refs = open(CLEAR_REFS_PATH, O_WRONLY, 0);
+	if (clear_refs < 0)
+		errx(EXIT_FAILURE, "Failed to open %s", CLEAR_REFS_PATH);
+
+	pagesize = getpagesize();
+	mmap_size = 10 * pagesize;
+
+	test_simple();
+	test_vma_reuse();
+	test_hugepage();
+
+	return 0;
+}
-- 
2.31.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH v2] selftest: Add test for Soft-Dirty PTE bit
  2021-06-03 15:15 [PATCH v2] selftest: Add test for Soft-Dirty PTE bit Gabriel Krisman Bertazi
@ 2021-08-13 15:00 ` Will Deacon
  2021-08-13 19:47   ` Gabriel Krisman Bertazi
  0 siblings, 1 reply; 3+ messages in thread
From: Will Deacon @ 2021-08-13 15:00 UTC (permalink / raw)
  To: Gabriel Krisman Bertazi
  Cc: shuah, linux-kernel, peterz, luto, linux-kselftest, kernel

On Thu, Jun 03, 2021 at 11:15:18AM -0400, Gabriel Krisman Bertazi wrote:
> This introduces three tests:
> 
> 1) Sanity check soft dirty basic semantics: allocate area, clean, dirty,
> check if the SD bit flipped.
> 
> 2) Check VMA reuse: validate the VM_SOFTDIRTY usage
> 
> 3) Check soft-dirty on huge pages
> 
> This was motivated by Will Deacon's fix commit 912efa17e512 ("mm: proc:
> Invalidate TLB after clearing soft-dirty page state"). I was tracking the
> same issue that he fixed, and this test would have caught it.
> 
> Cc: Will Deacon <will@kernel.org>
> Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
> 
> --
> Changes since V1:
>   - Fix last minute build break with page_size
> ---
>  tools/testing/selftests/Makefile              |   1 +
>  tools/testing/selftests/soft-dirty/.gitignore |   1 +
>  tools/testing/selftests/soft-dirty/Makefile   |   9 +
>  .../testing/selftests/soft-dirty/soft-dirty.c | 254 ++++++++++++++++++
>  4 files changed, 265 insertions(+)
>  create mode 100644 tools/testing/selftests/soft-dirty/.gitignore
>  create mode 100644 tools/testing/selftests/soft-dirty/Makefile
>  create mode 100644 tools/testing/selftests/soft-dirty/soft-dirty.c

Although I think adding a test for this is great (and I certainly wouldn't
want to get in the way of that; quite the opposite), I notice that we
already have test_softdirty() in selftests/vm/madv_populate.c. Would we be
better off extending that test instead of introducing another one?

Will

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH v2] selftest: Add test for Soft-Dirty PTE bit
  2021-08-13 15:00 ` Will Deacon
@ 2021-08-13 19:47   ` Gabriel Krisman Bertazi
  0 siblings, 0 replies; 3+ messages in thread
From: Gabriel Krisman Bertazi @ 2021-08-13 19:47 UTC (permalink / raw)
  To: Will Deacon; +Cc: shuah, linux-kernel, peterz, luto, linux-kselftest, kernel

Will Deacon <will@kernel.org> writes:

> On Thu, Jun 03, 2021 at 11:15:18AM -0400, Gabriel Krisman Bertazi wrote:
>> This introduces three tests:
>> 
>> 1) Sanity check soft dirty basic semantics: allocate area, clean, dirty,
>> check if the SD bit flipped.
>> 
>> 2) Check VMA reuse: validate the VM_SOFTDIRTY usage
>> 
>> 3) Check soft-dirty on huge pages
>> 
>> This was motivated by Will Deacon's fix commit 912efa17e512 ("mm: proc:
>> Invalidate TLB after clearing soft-dirty page state"). I was tracking the
>> same issue that he fixed, and this test would have caught it.
>> 
>> Cc: Will Deacon <will@kernel.org>
>> Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
>> 
>> --
>> Changes since V1:
>>   - Fix last minute build break with page_size
>> ---
>>  tools/testing/selftests/Makefile              |   1 +
>>  tools/testing/selftests/soft-dirty/.gitignore |   1 +
>>  tools/testing/selftests/soft-dirty/Makefile   |   9 +
>>  .../testing/selftests/soft-dirty/soft-dirty.c | 254 ++++++++++++++++++
>>  4 files changed, 265 insertions(+)
>>  create mode 100644 tools/testing/selftests/soft-dirty/.gitignore
>>  create mode 100644 tools/testing/selftests/soft-dirty/Makefile
>>  create mode 100644 tools/testing/selftests/soft-dirty/soft-dirty.c
>
> Although I think adding a test for this is great (and I certainly wouldn't
> want to get in the way of that; quite the opposite), I notice that we
> already have test_softdirty() in selftests/vm/madv_populate.c. Would we be
> better off extending that test instead of introducing another one?

Hi, Will. Thanks for the review.

Looks like selftests/vm/madv_populate.c was merged after I sent this,
which explains why I missed it.  From a quick look, it also doesn't
verify the corner case of I'm checking here.  I will look into merging
the two tests, or at least moving this to vm/ and remove the overlap.

Thanks,

-- 
Gabriel Krisman Bertazi

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2021-08-13 19:47 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-03 15:15 [PATCH v2] selftest: Add test for Soft-Dirty PTE bit Gabriel Krisman Bertazi
2021-08-13 15:00 ` Will Deacon
2021-08-13 19:47   ` Gabriel Krisman Bertazi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).