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