blob: 38364de8c5c3f695199027cfdc8e85d19e389e91 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-or-newer
/*
* Copyright (c) 2019 Oracle.
* All Rights Reserved.
*
* Create writable mappings to multiple files and write them all to test
* concurrent mmap writes to the same shared blocks.
*/
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
struct file_info {
char *mapping;
off_t file_offset;
off_t file_length;
int fd;
};
int
main(
int argc,
char *argv[])
{
struct file_info *fi;
size_t length;
char *endptr;
unsigned int nr_files;
unsigned int i;
char *buf;
int ret;
if (argc < 4) {
printf("Usage: %s len offset file [offset file]...\n", argv[0]);
return 1;
}
/* Parse mwrite length. */
errno = 0;
length = strtoul(argv[1], &endptr, 0);
if (errno) {
perror(argv[1]);
return 1;
}
if (*endptr != '\0') {
fprintf(stderr, "%s: not a proper file length?\n", argv[2]);
return 1;
}
/* Allocate file info */
nr_files = (argc - 2) / 2;
fi = calloc(nr_files, sizeof(struct file_info));
if (!fi) {
perror("calloc file info");
return 1;
}
buf = malloc(length);
if (!buf) {
perror("malloc buf");
return 1;
}
for (i = 0; i < nr_files; i++) {
struct stat statbuf;
char *offset = argv[((i + 1) * 2)];
char *fname = argv[((i + 1) * 2) + 1];
/* Open file, create mapping for the range we want. */
fi[i].fd = open(fname, O_RDWR);
if (fi[i].fd < 0) {
perror(fname);
return 1;
}
/* Parse mwrite offset */
errno = 0;
fi[i].file_offset = strtoul(offset, &endptr, 0);
if (errno) {
perror(argv[1]);
return 1;
}
/* Remember file size */
ret = fstat(fi[i].fd, &statbuf);
if (ret) {
perror(fname);
return 1;
}
fi[i].file_length = statbuf.st_size;
if (fi[i].file_offset + length > fi[i].file_length) {
fprintf(stderr, "%s: file must be %llu bytes\n",
fname,
(unsigned long long)fi[i].file_offset + length);
return 1;
}
/* Create the mapping */
fi[i].mapping = mmap(NULL, fi[i].file_length,
PROT_READ | PROT_WRITE, MAP_SHARED,
fi[i].fd, 0);
if (fi[i].mapping == MAP_FAILED) {
perror(fname);
return 1;
}
/*
* Make sure the mapping for region we're going to write is
* already populated in the page cache.
*/
memcpy(buf, fi[i].mapping + fi[i].file_offset, length);
}
/* Dirty the same region in each file to test COW. */
for (i = 0; i < nr_files; i++) {
memset(buf, 0x62 + i, length);
memcpy(fi[i].mapping + fi[i].file_offset, buf, length);
}
for (i = 0; i < nr_files; i++) {
ret = msync(fi[i].mapping, fi[i].file_offset + length, MS_SYNC);
if (ret) {
perror("msync");
return 1;
}
}
/* Close everything. */
for (i = 0; i < nr_files; i++) {
ret = munmap(fi[i].mapping, fi[i].file_length);
if (ret) {
perror("munmap");
return 1;
}
ret = close(fi[i].fd);
if (ret) {
perror("close");
return 1;
}
}
/* Free everything. */
free(buf);
free(fi);
return 0;
}