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 #define MMAP_SZ 4096 22 23 #define BUG_ON(condition, description) \ 24 do { \ 25 if (condition) \ 26 ksft_exit_fail_msg("[FAIL]\t%s:%d\t%s:%s\n", \ 27 __func__, __LINE__, (description), \ 28 strerror(errno)); \ 29 } while (0) 30 31 #define TESTS_IN_CHILD 2 32 33 static void parent_f(int sock, unsigned long *smap, int child) 34 { 35 int status, ret; 36 37 ret = read(sock, &status, sizeof(int)); 38 BUG_ON(ret <= 0, "read(sock)"); 39 40 *smap = 0x22222BAD; 41 ret = msync(smap, MMAP_SZ, MS_SYNC); 42 BUG_ON(ret, "msync()"); 43 44 ret = write(sock, &status, sizeof(int)); 45 BUG_ON(ret <= 0, "write(sock)"); 46 47 waitpid(child, &status, 0); 48 49 /* The ksft macros don't keep counters between processes */ 50 ksft_cnt.ksft_pass = WEXITSTATUS(status); 51 ksft_cnt.ksft_fail = TESTS_IN_CHILD - WEXITSTATUS(status); 52 } 53 54 static int child_f(int sock, unsigned long *smap, int fd) 55 { 56 int ret, buf = 0; 57 58 smap = mmap(0, MMAP_SZ, PROT_READ | PROT_WRITE, 59 MAP_PRIVATE | MAP_POPULATE, fd, 0); 60 BUG_ON(smap == MAP_FAILED, "mmap()"); 61 62 BUG_ON(*smap != 0xdeadbabe, "MAP_PRIVATE | MAP_POPULATE changed file"); 63 64 ret = write(sock, &buf, sizeof(int)); 65 BUG_ON(ret <= 0, "write(sock)"); 66 67 ret = read(sock, &buf, sizeof(int)); 68 BUG_ON(ret <= 0, "read(sock)"); 69 70 ksft_test_result(*smap != 0x22222BAD, "MAP_POPULATE COW private page\n"); 71 ksft_test_result(*smap == 0xdeadbabe, "The mapping state\n"); 72 73 /* The ksft macros don't keep counters between processes */ 74 return ksft_cnt.ksft_pass; 75 } 76 77 int main(int argc, char **argv) 78 { 79 int sock[2], child, ret; 80 FILE *ftmp; 81 unsigned long *smap; 82 83 ksft_print_header(); 84 ksft_set_plan(TESTS_IN_CHILD); 85 86 ftmp = tmpfile(); 87 BUG_ON(!ftmp, "tmpfile()"); 88 89 ret = ftruncate(fileno(ftmp), MMAP_SZ); 90 BUG_ON(ret, "ftruncate()"); 91 92 smap = mmap(0, MMAP_SZ, PROT_READ | PROT_WRITE, 93 MAP_SHARED, fileno(ftmp), 0); 94 BUG_ON(smap == MAP_FAILED, "mmap()"); 95 96 *smap = 0xdeadbabe; 97 /* Probably unnecessary, but let it be. */ 98 ret = msync(smap, MMAP_SZ, MS_SYNC); 99 BUG_ON(ret, "msync()"); 100 101 ret = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sock); 102 BUG_ON(ret, "socketpair()"); 103 104 child = fork(); 105 BUG_ON(child == -1, "fork()"); 106 107 if (child) { 108 ret = close(sock[0]); 109 BUG_ON(ret, "close()"); 110 111 parent_f(sock[1], smap, child); 112 113 ksft_finished(); 114 } 115 116 ret = close(sock[1]); 117 BUG_ON(ret, "close()"); 118 119 return child_f(sock[0], smap, fileno(ftmp)); 120 } 121