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
17 static int test_fd;
18 static char *mmaped_area;
19 static long pagesize;
20
21 #define STRING_TO_WRITE "AAAAAAAAAA"
22
get_dirty_bit(void * data)23 uint64_t get_dirty_bit(void *data)
24 {
25 int pagemap_fd, pageflags_fd;
26 unsigned long addr;
27 uint64_t pagemap_entry, pageflag_entry, pfn, index;
28
29 addr = (unsigned long)data;
30 index = (addr / pagesize) * sizeof(uint64_t);
31 pagemap_fd = SAFE_OPEN("/proc/self/pagemap", O_RDONLY);
32 SAFE_LSEEK(pagemap_fd, index, SEEK_SET);
33 SAFE_READ(1, pagemap_fd, &pagemap_entry, sizeof(pagemap_entry));
34 SAFE_CLOSE(pagemap_fd);
35 pfn = pagemap_entry & ((1ULL << 55) - 1);
36 if (!pfn)
37 return 0;
38 pageflags_fd = SAFE_OPEN("/proc/kpageflags", O_RDONLY);
39 index = pfn * sizeof(uint64_t);
40 SAFE_LSEEK(pageflags_fd, index, SEEK_SET);
41 SAFE_READ(1, pageflags_fd, &pageflag_entry, sizeof(pageflag_entry));
42 SAFE_CLOSE(pageflags_fd);
43 return pageflag_entry & (1ULL << 4);
44 }
45
test_msync(void)46 static void test_msync(void)
47 {
48 uint64_t dirty;
49
50 test_fd = SAFE_OPEN("msync04/testfile", O_CREAT | O_TRUNC | O_RDWR);
51 SAFE_WRITE(0, test_fd, STRING_TO_WRITE, sizeof(STRING_TO_WRITE) - 1);
52 mmaped_area = SAFE_MMAP(NULL, pagesize, PROT_READ | PROT_WRITE,
53 MAP_SHARED, test_fd, 0);
54 SAFE_CLOSE(test_fd);
55 mmaped_area[8] = 'B';
56 dirty = get_dirty_bit(mmaped_area);
57 if (!dirty) {
58 tst_res(TFAIL, "Expected dirty bit to be set after writing to"
59 " mmap()-ed area");
60 goto clean;
61 }
62 if (msync(mmaped_area, pagesize, MS_SYNC) < 0) {
63 tst_res(TFAIL | TERRNO, "msync() failed");
64 goto clean;
65 }
66 dirty = get_dirty_bit(mmaped_area);
67 if (dirty)
68 tst_res(TFAIL, "msync() failed to write dirty page despite"
69 " succeeding");
70 else
71 tst_res(TPASS, "msync() working correctly");
72
73 clean:
74 SAFE_MUNMAP(mmaped_area, pagesize);
75 mmaped_area = NULL;
76 }
77
setup(void)78 static void setup(void)
79 {
80 pagesize = (off_t)SAFE_SYSCONF(_SC_PAGESIZE);
81 }
82
cleanup(void)83 static void cleanup(void)
84 {
85 if (mmaped_area)
86 SAFE_MUNMAP(mmaped_area, pagesize);
87
88 if (test_fd > 0)
89 SAFE_CLOSE(test_fd);
90 }
91
92 static struct tst_test test = {
93 .test_all = test_msync,
94 .setup = setup,
95 .cleanup = cleanup,
96 .needs_root = 1,
97 .mntpoint = "msync04",
98 .mount_device = 1,
99 .all_filesystems = 1,
100 .min_kver = "2.6.25",
101 };
102