162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2018 Dmitry Safonov, Arista Networks
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * MAP_POPULATE | MAP_PRIVATE should COW VMA pages.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define _GNU_SOURCE
962306a36Sopenharmony_ci#include <errno.h>
1062306a36Sopenharmony_ci#include <fcntl.h>
1162306a36Sopenharmony_ci#include <sys/mman.h>
1262306a36Sopenharmony_ci#include <sys/socket.h>
1362306a36Sopenharmony_ci#include <sys/types.h>
1462306a36Sopenharmony_ci#include <sys/wait.h>
1562306a36Sopenharmony_ci#include <stdio.h>
1662306a36Sopenharmony_ci#include <stdlib.h>
1762306a36Sopenharmony_ci#include <string.h>
1862306a36Sopenharmony_ci#include <unistd.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define MMAP_SZ		4096
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define BUG_ON(condition, description)					\
2362306a36Sopenharmony_ci	do {								\
2462306a36Sopenharmony_ci		if (condition) {					\
2562306a36Sopenharmony_ci			fprintf(stderr, "[FAIL]\t%s:%d\t%s:%s\n", __func__, \
2662306a36Sopenharmony_ci				__LINE__, (description), strerror(errno)); \
2762306a36Sopenharmony_ci			exit(1);					\
2862306a36Sopenharmony_ci		}							\
2962306a36Sopenharmony_ci	} while (0)
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int parent_f(int sock, unsigned long *smap, int child)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	int status, ret;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	ret = read(sock, &status, sizeof(int));
3662306a36Sopenharmony_ci	BUG_ON(ret <= 0, "read(sock)");
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	*smap = 0x22222BAD;
3962306a36Sopenharmony_ci	ret = msync(smap, MMAP_SZ, MS_SYNC);
4062306a36Sopenharmony_ci	BUG_ON(ret, "msync()");
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	ret = write(sock, &status, sizeof(int));
4362306a36Sopenharmony_ci	BUG_ON(ret <= 0, "write(sock)");
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	waitpid(child, &status, 0);
4662306a36Sopenharmony_ci	BUG_ON(!WIFEXITED(status), "child in unexpected state");
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	return WEXITSTATUS(status);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int child_f(int sock, unsigned long *smap, int fd)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	int ret, buf = 0;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	smap = mmap(0, MMAP_SZ, PROT_READ | PROT_WRITE,
5662306a36Sopenharmony_ci			MAP_PRIVATE | MAP_POPULATE, fd, 0);
5762306a36Sopenharmony_ci	BUG_ON(smap == MAP_FAILED, "mmap()");
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	BUG_ON(*smap != 0xdeadbabe, "MAP_PRIVATE | MAP_POPULATE changed file");
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	ret = write(sock, &buf, sizeof(int));
6262306a36Sopenharmony_ci	BUG_ON(ret <= 0, "write(sock)");
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	ret = read(sock, &buf, sizeof(int));
6562306a36Sopenharmony_ci	BUG_ON(ret <= 0, "read(sock)");
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	BUG_ON(*smap == 0x22222BAD, "MAP_POPULATE didn't COW private page");
6862306a36Sopenharmony_ci	BUG_ON(*smap != 0xdeadbabe, "mapping was corrupted");
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return 0;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ciint main(int argc, char **argv)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	int sock[2], child, ret;
7662306a36Sopenharmony_ci	FILE *ftmp;
7762306a36Sopenharmony_ci	unsigned long *smap;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	ftmp = tmpfile();
8062306a36Sopenharmony_ci	BUG_ON(!ftmp, "tmpfile()");
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	ret = ftruncate(fileno(ftmp), MMAP_SZ);
8362306a36Sopenharmony_ci	BUG_ON(ret, "ftruncate()");
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	smap = mmap(0, MMAP_SZ, PROT_READ | PROT_WRITE,
8662306a36Sopenharmony_ci			MAP_SHARED, fileno(ftmp), 0);
8762306a36Sopenharmony_ci	BUG_ON(smap == MAP_FAILED, "mmap()");
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	*smap = 0xdeadbabe;
9062306a36Sopenharmony_ci	/* Probably unnecessary, but let it be. */
9162306a36Sopenharmony_ci	ret = msync(smap, MMAP_SZ, MS_SYNC);
9262306a36Sopenharmony_ci	BUG_ON(ret, "msync()");
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	ret = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sock);
9562306a36Sopenharmony_ci	BUG_ON(ret, "socketpair()");
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	child = fork();
9862306a36Sopenharmony_ci	BUG_ON(child == -1, "fork()");
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (child) {
10162306a36Sopenharmony_ci		ret = close(sock[0]);
10262306a36Sopenharmony_ci		BUG_ON(ret, "close()");
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		return parent_f(sock[1], smap, child);
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	ret = close(sock[1]);
10862306a36Sopenharmony_ci	BUG_ON(ret, "close()");
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return child_f(sock[0], smap, fileno(ftmp));
11162306a36Sopenharmony_ci}
112