1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2018 Dmitry Safonov, Arista Networks 4 * 5 * MAP_POPULATE | MAP_PRIVATE should COW VMA pages. 6 */ 7 8 #define _GNU_SOURCE 9 #include <errno.h> 10 #include <fcntl.h> 11 #include <sys/mman.h> 12 #include <sys/socket.h> 13 #include <sys/types.h> 14 #include <sys/wait.h> 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <unistd.h> 19 #include "../kselftest.h" 20 21 #include "vm_util.h" 22 23 #define MMAP_SZ 4096 24 25 #define BUG_ON(condition, description) \ 26 do { \ 27 if (condition) \ 28 ksft_exit_fail_msg("[FAIL]\t%s:%d\t%s:%s\n", \ 29 __func__, __LINE__, (description), \ 30 strerror(errno)); \ 31 } while (0) 32 33 #define TESTS_IN_CHILD 2 34 35 static void parent_f(int sock, unsigned long *smap, int child) 36 { 37 int status, ret; 38 39 ret = read(sock, &status, sizeof(int)); 40 BUG_ON(ret <= 0, "read(sock)"); 41 42 *smap = 0x22222BAD; 43 ret = msync(smap, MMAP_SZ, MS_SYNC); 44 BUG_ON(ret, "msync()"); 45 46 ret = write(sock, &status, sizeof(int)); 47 BUG_ON(ret <= 0, "write(sock)"); 48 49 waitpid(child, &status, 0); 50 51 /* The ksft macros don't keep counters between processes */ 52 ksft_cnt.ksft_pass = WEXITSTATUS(status); 53 ksft_cnt.ksft_fail = TESTS_IN_CHILD - WEXITSTATUS(status); 54 } 55 56 static int child_f(int sock, unsigned long *smap, int fd) 57 { 58 int ret, buf = 0; 59 60 smap = mmap(0, MMAP_SZ, PROT_READ | PROT_WRITE, 61 MAP_PRIVATE | MAP_POPULATE, fd, 0); 62 BUG_ON(smap == MAP_FAILED, "mmap()"); 63 64 BUG_ON(*smap != 0xdeadbabe, "MAP_PRIVATE | MAP_POPULATE changed file"); 65 66 ret = write(sock, &buf, sizeof(int)); 67 BUG_ON(ret <= 0, "write(sock)"); 68 69 ret = read(sock, &buf, sizeof(int)); 70 BUG_ON(ret <= 0, "read(sock)"); 71 72 ksft_test_result(*smap != 0x22222BAD, "MAP_POPULATE COW private page\n"); 73 ksft_test_result(*smap == 0xdeadbabe, "The mapping state\n"); 74 75 /* The ksft macros don't keep counters between processes */ 76 return ksft_cnt.ksft_pass; 77 } 78 79 int main(int argc, char **argv) 80 { 81 int sock[2], child, ret; 82 FILE *ftmp; 83 unsigned long *smap; 84 85 ksft_print_header(); 86 ksft_set_plan(TESTS_IN_CHILD); 87 88 ftmp = tmpfile(); 89 BUG_ON(!ftmp, "tmpfile()"); 90 91 ret = ftruncate(fileno(ftmp), MMAP_SZ); 92 if (ret < 0 && errno == ENOENT) { 93 skip_test_dodgy_fs("ftruncate()"); 94 } 95 BUG_ON(ret, "ftruncate()"); 96 97 smap = mmap(0, MMAP_SZ, PROT_READ | PROT_WRITE, 98 MAP_SHARED, fileno(ftmp), 0); 99 BUG_ON(smap == MAP_FAILED, "mmap()"); 100 101 *smap = 0xdeadbabe; 102 /* Probably unnecessary, but let it be. */ 103 ret = msync(smap, MMAP_SZ, MS_SYNC); 104 BUG_ON(ret, "msync()"); 105 106 ret = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sock); 107 BUG_ON(ret, "socketpair()"); 108 109 child = fork(); 110 BUG_ON(child == -1, "fork()"); 111 112 if (child) { 113 ret = close(sock[0]); 114 BUG_ON(ret, "close()"); 115 116 parent_f(sock[1], smap, child); 117 118 ksft_finished(); 119 } 120 121 ret = close(sock[1]); 122 BUG_ON(ret, "close()"); 123 124 return child_f(sock[0], smap, fileno(ftmp)); 125 } 126