xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/msync/msync04.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2017 Red Hat, Inc.
4  *
5  */
6 
7 /*
8  * Test description: Verify msync() after writing into mmap()-ed file works.
9  *
10  * Write to mapped region and sync the memory back with file. Check the page
11  * is no longer dirty after msync() call.
12  */
13 
14 #include <errno.h>
15 #include "tst_test.h"
16 #include "pgsize_helpers.h"
17 
18 static int test_fd;
19 static char *mmaped_area;
20 static long pagesize;
21 
22 #define STRING_TO_WRITE	"AAAAAAAAAA"
23 
get_dirty_bit(void * data)24 uint64_t get_dirty_bit(void *data)
25 {
26 	int pagemap_fd, pageflags_fd;
27 	unsigned long addr;
28 	uint64_t pagemap_entry, pageflag_entry, pfn, index;
29 
30 	addr = (unsigned long)data;
31 	index = (addr / kernel_page_size()) * sizeof(uint64_t);
32 	pagemap_fd = SAFE_OPEN("/proc/self/pagemap", O_RDONLY);
33 	SAFE_LSEEK(pagemap_fd, index, SEEK_SET);
34 	SAFE_READ(1, pagemap_fd, &pagemap_entry, sizeof(pagemap_entry));
35 	SAFE_CLOSE(pagemap_fd);
36 	pfn = pagemap_entry & ((1ULL << 55) - 1);
37 	if (!pfn)
38 		return 0;
39 	pageflags_fd = SAFE_OPEN("/proc/kpageflags", O_RDONLY);
40 	index = pfn * sizeof(uint64_t);
41 	SAFE_LSEEK(pageflags_fd, index, SEEK_SET);
42 	SAFE_READ(1, pageflags_fd, &pageflag_entry, sizeof(pageflag_entry));
43 	SAFE_CLOSE(pageflags_fd);
44 	return pageflag_entry & (1ULL << 4);
45 }
46 
test_msync(void)47 static void test_msync(void)
48 {
49 	uint64_t dirty;
50 
51 	test_fd = SAFE_OPEN("msync04/testfile", O_CREAT | O_TRUNC | O_RDWR,
52 		0644);
53 	SAFE_WRITE(SAFE_WRITE_ANY, test_fd, STRING_TO_WRITE, sizeof(STRING_TO_WRITE) - 1);
54 	mmaped_area = SAFE_MMAP(NULL, pagesize, PROT_READ | PROT_WRITE,
55 			MAP_SHARED, test_fd, 0);
56 	SAFE_CLOSE(test_fd);
57 	mmaped_area[8] = 'B';
58 	dirty = get_dirty_bit(mmaped_area);
59 	if (!dirty) {
60 		tst_res(TFAIL, "Expected dirty bit to be set after writing to"
61 				" mmap()-ed area");
62 		goto clean;
63 	}
64 	if (msync(mmaped_area, pagesize, MS_SYNC) < 0) {
65 		tst_res(TFAIL | TERRNO, "msync() failed");
66 		goto clean;
67 	}
68 	dirty = get_dirty_bit(mmaped_area);
69 	if (dirty)
70 		tst_res(TFAIL, "msync() failed to write dirty page despite"
71 				" succeeding");
72 	else
73 		tst_res(TPASS, "msync() working correctly");
74 
75 clean:
76 	SAFE_MUNMAP(mmaped_area, pagesize);
77 	mmaped_area = NULL;
78 }
79 
setup(void)80 static void setup(void)
81 {
82 	pagesize = (off_t)SAFE_SYSCONF(_SC_PAGESIZE);
83 }
84 
cleanup(void)85 static void cleanup(void)
86 {
87 	if (mmaped_area)
88 		SAFE_MUNMAP(mmaped_area, pagesize);
89 
90 	if (test_fd > 0)
91 		SAFE_CLOSE(test_fd);
92 }
93 
94 static struct tst_test test = {
95 	.test_all = test_msync,
96 	.setup = setup,
97 	.cleanup = cleanup,
98 	.needs_root = 1,
99 	.mntpoint = "msync04",
100 	.mount_device = 1,
101 	.all_filesystems = 1,
102 	.skip_filesystems = (const char *[]) {
103 		"tmpfs",
104 		NULL
105 	},
106 };
107