• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 Red Hat, Inc.
3  *
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the Free
7  * Software Foundation; either version 2 of the License, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /*
20  * Test description: Verify msync() after writing into mmap()-ed file works.
21  *
22  * Write to mapped region and sync the memory back with file. Check the page
23  * is no longer dirty after msync() call.
24  */
25 
26 #include <errno.h>
27 #include "tst_test.h"
28 
29 static int test_fd;
30 static char *mmaped_area;
31 static long pagesize;
32 
33 #define STRING_TO_WRITE	"AAAAAAAAAA"
34 
get_dirty_bit(void * data)35 uint64_t get_dirty_bit(void *data)
36 {
37 	int pagemap_fd, pageflags_fd;
38 	unsigned long addr;
39 	uint64_t pagemap_entry, pageflag_entry, pfn, index;
40 
41 	addr = (unsigned long)data;
42 	index = (addr / pagesize) * sizeof(uint64_t);
43 	pagemap_fd = SAFE_OPEN("/proc/self/pagemap", O_RDONLY);
44 	SAFE_LSEEK(pagemap_fd, index, SEEK_SET);
45 	SAFE_READ(1, pagemap_fd, &pagemap_entry, sizeof(pagemap_entry));
46 	SAFE_CLOSE(pagemap_fd);
47 	pfn = pagemap_entry & ((1ULL << 55) - 1);
48 	if (!pfn)
49 		return 0;
50 	pageflags_fd = SAFE_OPEN("/proc/kpageflags", O_RDONLY);
51 	index = pfn * sizeof(uint64_t);
52 	SAFE_LSEEK(pageflags_fd, index, SEEK_SET);
53 	SAFE_READ(1, pageflags_fd, &pageflag_entry, sizeof(pageflag_entry));
54 	SAFE_CLOSE(pageflags_fd);
55 	return pageflag_entry & (1ULL << 4);
56 }
57 
test_msync(void)58 static void test_msync(void)
59 {
60 	uint64_t dirty;
61 
62 	test_fd = SAFE_OPEN("msync04/testfile", O_CREAT | O_TRUNC | O_RDWR);
63 	SAFE_WRITE(0, test_fd, STRING_TO_WRITE, sizeof(STRING_TO_WRITE) - 1);
64 	mmaped_area = SAFE_MMAP(NULL, pagesize, PROT_READ | PROT_WRITE,
65 			MAP_SHARED, test_fd, 0);
66 	SAFE_CLOSE(test_fd);
67 	mmaped_area[8] = 'B';
68 	dirty = get_dirty_bit(mmaped_area);
69 	if (!dirty) {
70 		tst_res(TFAIL, "Expected dirty bit to be set after writing to"
71 				" mmap()-ed area");
72 		goto clean;
73 	}
74 	if (msync(mmaped_area, pagesize, MS_SYNC) < 0) {
75 		tst_res(TFAIL | TERRNO, "msync() failed");
76 		goto clean;
77 	}
78 	dirty = get_dirty_bit(mmaped_area);
79 	if (dirty)
80 		tst_res(TFAIL, "msync() failed to write dirty page despite"
81 				" succeeding");
82 	else
83 		tst_res(TPASS, "msync() working correctly");
84 
85 clean:
86 	SAFE_MUNMAP(mmaped_area, pagesize);
87 	mmaped_area = NULL;
88 }
89 
setup(void)90 static void setup(void)
91 {
92 	pagesize = (off_t)SAFE_SYSCONF(_SC_PAGESIZE);
93 }
94 
cleanup(void)95 static void cleanup(void)
96 {
97 	if (mmaped_area)
98 		SAFE_MUNMAP(mmaped_area, pagesize);
99 
100 	if (test_fd > 0)
101 		SAFE_CLOSE(test_fd);
102 }
103 
104 static struct tst_test test = {
105 	.test_all = test_msync,
106 	.setup = setup,
107 	.cleanup = cleanup,
108 	.needs_tmpdir = 1,
109 	.needs_root = 1,
110 	.mntpoint = "msync04",
111 	.mount_device = 1,
112 	.all_filesystems = 1,
113 	.min_kver = "2.6.25",
114 };
115