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 17static int test_fd; 18static char *mmaped_area; 19static long pagesize; 20 21#define STRING_TO_WRITE "AAAAAAAAAA" 22 23uint64_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 46static void test_msync(void) 47{ 48 uint64_t dirty; 49 50 test_fd = SAFE_OPEN("msync04/testfile", O_CREAT | O_TRUNC | O_RDWR, 51 0644); 52 SAFE_WRITE(SAFE_WRITE_ANY, test_fd, STRING_TO_WRITE, sizeof(STRING_TO_WRITE) - 1); 53 mmaped_area = SAFE_MMAP(NULL, pagesize, PROT_READ | PROT_WRITE, 54 MAP_SHARED, test_fd, 0); 55 SAFE_CLOSE(test_fd); 56 mmaped_area[8] = 'B'; 57 dirty = get_dirty_bit(mmaped_area); 58 if (!dirty) { 59 tst_res(TFAIL, "Expected dirty bit to be set after writing to" 60 " mmap()-ed area"); 61 goto clean; 62 } 63 if (msync(mmaped_area, pagesize, MS_SYNC) < 0) { 64 tst_res(TFAIL | TERRNO, "msync() failed"); 65 goto clean; 66 } 67 dirty = get_dirty_bit(mmaped_area); 68 if (dirty) 69 tst_res(TFAIL, "msync() failed to write dirty page despite" 70 " succeeding"); 71 else 72 tst_res(TPASS, "msync() working correctly"); 73 74clean: 75 SAFE_MUNMAP(mmaped_area, pagesize); 76 mmaped_area = NULL; 77} 78 79static void setup(void) 80{ 81 pagesize = (off_t)SAFE_SYSCONF(_SC_PAGESIZE); 82} 83 84static void cleanup(void) 85{ 86 if (mmaped_area) 87 SAFE_MUNMAP(mmaped_area, pagesize); 88 89 if (test_fd > 0) 90 SAFE_CLOSE(test_fd); 91} 92 93static struct tst_test test = { 94 .test_all = test_msync, 95 .setup = setup, 96 .cleanup = cleanup, 97 .needs_root = 1, 98 .mntpoint = "msync04", 99 .mount_device = 1, 100 .all_filesystems = 1, 101 .skip_filesystems = (const char *[]) { 102 "tmpfs", 103 NULL 104 }, 105}; 106