1*96c8483aSYuri Pankov /*
2*96c8483aSYuri Pankov  * This file and its contents are supplied under the terms of the
3*96c8483aSYuri Pankov  * Common Development and Distribution License ("CDDL"), version 1.0.
4*96c8483aSYuri Pankov  * You may only use this file in accordance with the terms of version
5*96c8483aSYuri Pankov  * 1.0 of the CDDL.
6*96c8483aSYuri Pankov  *
7*96c8483aSYuri Pankov  * A full copy of the text of the CDDL should have accompanied this
8*96c8483aSYuri Pankov  * source.  A copy of the CDDL is also available via the Internet at
9*96c8483aSYuri Pankov  * http://www.illumos.org/license/CDDL.
10*96c8483aSYuri Pankov  */
11*96c8483aSYuri Pankov 
12*96c8483aSYuri Pankov /*
13*96c8483aSYuri Pankov  * Copyright 2012 Jilin Xpd <jilinxpd@gmail.com>
14*96c8483aSYuri Pankov  * Copyright 2018 Nexenta Systems, Inc.
15*96c8483aSYuri Pankov  */
16*96c8483aSYuri Pankov 
17*96c8483aSYuri Pankov /*
18*96c8483aSYuri Pankov  * Test if file read/write is coherent with mmap, perform 2 tests:
19*96c8483aSYuri Pankov  * modify file through mmap, and check the result through file read.
20*96c8483aSYuri Pankov  * modify file through file write, and check the result through mmap.
21*96c8483aSYuri Pankov  */
22*96c8483aSYuri Pankov 
23*96c8483aSYuri Pankov #include <sys/mman.h>
24*96c8483aSYuri Pankov #include <sys/types.h>
25*96c8483aSYuri Pankov #include <sys/stat.h>
26*96c8483aSYuri Pankov #include <fcntl.h>
27*96c8483aSYuri Pankov #include <stdio.h>
28*96c8483aSYuri Pankov #include <stdlib.h>
29*96c8483aSYuri Pankov #include <unistd.h>
30*96c8483aSYuri Pankov #include <string.h>
31*96c8483aSYuri Pankov #include <errno.h>
32*96c8483aSYuri Pankov 
33*96c8483aSYuri Pankov void
usage(void)34*96c8483aSYuri Pankov usage(void)
35*96c8483aSYuri Pankov {
36*96c8483aSYuri Pankov 	fprintf(stderr,
37*96c8483aSYuri Pankov 	    "usage: rw_mmap -n <size>[b|k|m|g] -f <filename>\n");
38*96c8483aSYuri Pankov 	exit(1);
39*96c8483aSYuri Pankov }
40*96c8483aSYuri Pankov 
41*96c8483aSYuri Pankov int
main(int argc,char ** argv)42*96c8483aSYuri Pankov main(int argc, char **argv)
43*96c8483aSYuri Pankov {
44*96c8483aSYuri Pankov 	char *suffix;
45*96c8483aSYuri Pankov 	char *filename = NULL;
46*96c8483aSYuri Pankov 	char *file_addr;
47*96c8483aSYuri Pankov 	char *p;
48*96c8483aSYuri Pankov 	size_t filesize;
49*96c8483aSYuri Pankov 	ssize_t blksize;
50*96c8483aSYuri Pankov 	size_t cnt = 1;
51*96c8483aSYuri Pankov 	size_t mul = 1;
52*96c8483aSYuri Pankov 	int c, fid;
53*96c8483aSYuri Pankov 	char *buf;
54*96c8483aSYuri Pankov 
55*96c8483aSYuri Pankov 	/*
56*96c8483aSYuri Pankov 	 * parse arguments
57*96c8483aSYuri Pankov 	 */
58*96c8483aSYuri Pankov 	while ((c = getopt(argc, argv, "n:f:")) != -1) {
59*96c8483aSYuri Pankov 		switch (c) {
60*96c8483aSYuri Pankov 		case 'n':
61*96c8483aSYuri Pankov 			cnt = (size_t)strtoul(optarg, &suffix, 0);
62*96c8483aSYuri Pankov 			if (cnt == 0)
63*96c8483aSYuri Pankov 				goto bad_n_arg;
64*96c8483aSYuri Pankov 			switch (*suffix) {
65*96c8483aSYuri Pankov 			case '\0':
66*96c8483aSYuri Pankov 			case 'b':
67*96c8483aSYuri Pankov 				mul = 1;
68*96c8483aSYuri Pankov 				break;
69*96c8483aSYuri Pankov 			case 'k':
70*96c8483aSYuri Pankov 				mul = 1024;
71*96c8483aSYuri Pankov 				break;
72*96c8483aSYuri Pankov 			case 'm':
73*96c8483aSYuri Pankov 				mul = (1024 * 1024);
74*96c8483aSYuri Pankov 				break;
75*96c8483aSYuri Pankov 			case 'g':
76*96c8483aSYuri Pankov 				mul = (1024 * 1024 * 1024);
77*96c8483aSYuri Pankov 				break;
78*96c8483aSYuri Pankov 			default:
79*96c8483aSYuri Pankov 			bad_n_arg:
80*96c8483aSYuri Pankov 				fprintf(stderr, "-n %s: invalid size\n",
81*96c8483aSYuri Pankov 				    optarg);
82*96c8483aSYuri Pankov 				return (1);
83*96c8483aSYuri Pankov 			}
84*96c8483aSYuri Pankov 			cnt = cnt * mul;
85*96c8483aSYuri Pankov 			break;
86*96c8483aSYuri Pankov 
87*96c8483aSYuri Pankov 		case 'f': /* target file */
88*96c8483aSYuri Pankov 			filename = optarg;
89*96c8483aSYuri Pankov 			break;
90*96c8483aSYuri Pankov 
91*96c8483aSYuri Pankov 		case ':':   /* missing optarg */
92*96c8483aSYuri Pankov 			fprintf(stderr,
93*96c8483aSYuri Pankov 			    "Option -%c requires an arg\n", optopt);
94*96c8483aSYuri Pankov 			usage();
95*96c8483aSYuri Pankov 			break;
96*96c8483aSYuri Pankov 		case '?':
97*96c8483aSYuri Pankov 			fprintf(stderr,
98*96c8483aSYuri Pankov 			    "Unrecognized option: -%c\n", optopt);
99*96c8483aSYuri Pankov 			usage();
100*96c8483aSYuri Pankov 			break;
101*96c8483aSYuri Pankov 		}
102*96c8483aSYuri Pankov 	}
103*96c8483aSYuri Pankov 
104*96c8483aSYuri Pankov 	/* open test file */
105*96c8483aSYuri Pankov 	fid = open(filename, O_RDWR | O_CREAT | O_TRUNC,
106*96c8483aSYuri Pankov 	    S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH);
107*96c8483aSYuri Pankov 	if (fid == -1) {
108*96c8483aSYuri Pankov 		fprintf(stderr, "open %s error=%d\n", filename, errno);
109*96c8483aSYuri Pankov 		return (1);
110*96c8483aSYuri Pankov 	}
111*96c8483aSYuri Pankov 
112*96c8483aSYuri Pankov 	/* extend file */
113*96c8483aSYuri Pankov 	filesize = cnt;
114*96c8483aSYuri Pankov 	if (ftruncate(fid, filesize) == -1) {
115*96c8483aSYuri Pankov 		fprintf(stderr, "ftrunc %s error=%d\n", filename, errno);
116*96c8483aSYuri Pankov 		return (1);
117*96c8483aSYuri Pankov 	}
118*96c8483aSYuri Pankov 
119*96c8483aSYuri Pankov 	/* map file */
120*96c8483aSYuri Pankov 	file_addr = mmap(NULL, filesize,
121*96c8483aSYuri Pankov 	    PROT_READ | PROT_WRITE, MAP_SHARED, fid, 0);
122*96c8483aSYuri Pankov 	if (file_addr == MAP_FAILED) {
123*96c8483aSYuri Pankov 		fprintf(stderr, "mmap %s error=%d\n", filename, errno);
124*96c8483aSYuri Pankov 		return (1);
125*96c8483aSYuri Pankov 	}
126*96c8483aSYuri Pankov 
127*96c8483aSYuri Pankov 	blksize = 4096;
128*96c8483aSYuri Pankov 	buf = malloc(blksize);
129*96c8483aSYuri Pankov 	if (buf == NULL) {
130*96c8483aSYuri Pankov 		fprintf(stderr, "malloc failed\n");
131*96c8483aSYuri Pankov 		return (1);
132*96c8483aSYuri Pankov 	}
133*96c8483aSYuri Pankov 
134*96c8483aSYuri Pankov 	/* verify fread and mmap see the same data */
135*96c8483aSYuri Pankov 	p = file_addr + 2013; /* not aligned to 4KB, on purpose */
136*96c8483aSYuri Pankov 	lseek(fid, 2013, SEEK_SET);
137*96c8483aSYuri Pankov 	while (p < file_addr + filesize) {
138*96c8483aSYuri Pankov 		blksize = read(fid, buf, blksize);
139*96c8483aSYuri Pankov 		if (blksize < 0) {
140*96c8483aSYuri Pankov 			perror(filename);
141*96c8483aSYuri Pankov 			return (1);
142*96c8483aSYuri Pankov 		}
143*96c8483aSYuri Pankov 		if (blksize == 0)
144*96c8483aSYuri Pankov 			break;
145*96c8483aSYuri Pankov 		if (memcmp(buf, p, blksize) != 0) {
146*96c8483aSYuri Pankov 			fprintf(stderr, "memcmp failed 1\n");
147*96c8483aSYuri Pankov 			return (1);
148*96c8483aSYuri Pankov 		}
149*96c8483aSYuri Pankov 		p += blksize;
150*96c8483aSYuri Pankov 	}
151*96c8483aSYuri Pankov 
152*96c8483aSYuri Pankov 	/* modify file through mmap, verify fread can see the change */
153*96c8483aSYuri Pankov 	blksize = 4096;
154*96c8483aSYuri Pankov 	p = file_addr + 2013; /* not aligned to 4KB */
155*96c8483aSYuri Pankov 	lseek(fid, 2013, SEEK_SET);
156*96c8483aSYuri Pankov 	c = 'a';
157*96c8483aSYuri Pankov 	while (p < file_addr + filesize) {
158*96c8483aSYuri Pankov 		if (p + blksize > file_addr + filesize)
159*96c8483aSYuri Pankov 			blksize = file_addr + filesize - p;
160*96c8483aSYuri Pankov 		memset(p, c++, blksize);
161*96c8483aSYuri Pankov 		blksize = read(fid, buf, blksize);
162*96c8483aSYuri Pankov 		if (blksize < 0) {
163*96c8483aSYuri Pankov 			perror(filename);
164*96c8483aSYuri Pankov 			return (1);
165*96c8483aSYuri Pankov 		}
166*96c8483aSYuri Pankov 		if (blksize == 0)
167*96c8483aSYuri Pankov 			break;
168*96c8483aSYuri Pankov 		if (memcmp(buf, p, blksize) != 0) {
169*96c8483aSYuri Pankov 			fprintf(stderr, "memcmp failed 2\n");
170*96c8483aSYuri Pankov 			return (1);
171*96c8483aSYuri Pankov 		}
172*96c8483aSYuri Pankov 		p += blksize;
173*96c8483aSYuri Pankov 	}
174*96c8483aSYuri Pankov 
175*96c8483aSYuri Pankov 	/* modify file through fwrite, verify mmap can see the change */
176*96c8483aSYuri Pankov 	blksize = 4096;
177*96c8483aSYuri Pankov 	p = file_addr + 2013; /* not aligned to 4KB */
178*96c8483aSYuri Pankov 	lseek(fid, 2013, SEEK_SET);
179*96c8483aSYuri Pankov 	c = 'Z';
180*96c8483aSYuri Pankov 	while (p < file_addr + filesize) {
181*96c8483aSYuri Pankov 		if (p + blksize > file_addr + filesize)
182*96c8483aSYuri Pankov 			blksize = file_addr + filesize - p;
183*96c8483aSYuri Pankov 		memset(buf, c--, blksize);
184*96c8483aSYuri Pankov 		blksize = write(fid, buf, blksize);
185*96c8483aSYuri Pankov 		if (blksize < 0) {
186*96c8483aSYuri Pankov 			perror(filename);
187*96c8483aSYuri Pankov 			return (1);
188*96c8483aSYuri Pankov 		}
189*96c8483aSYuri Pankov 		if (blksize == 0)
190*96c8483aSYuri Pankov 			break;
191*96c8483aSYuri Pankov 		if (memcmp(buf, p, blksize) != 0) {
192*96c8483aSYuri Pankov 			fprintf(stderr, "memcmp failed 3\n");
193*96c8483aSYuri Pankov 			return (1);
194*96c8483aSYuri Pankov 		}
195*96c8483aSYuri Pankov 		p += blksize;
196*96c8483aSYuri Pankov 	}
197*96c8483aSYuri Pankov 
198*96c8483aSYuri Pankov 	/* sync pages to file */
199*96c8483aSYuri Pankov 	if (msync(file_addr, filesize, MS_SYNC) == -1) {
200*96c8483aSYuri Pankov 		fprintf(stderr, "msync %s error=%d\n", filename, errno);
201*96c8483aSYuri Pankov 		return (1);
202*96c8483aSYuri Pankov 	}
203*96c8483aSYuri Pankov 
204*96c8483aSYuri Pankov 	/* unmap file */
205*96c8483aSYuri Pankov 	if (munmap(file_addr, filesize) == -1) {
206*96c8483aSYuri Pankov 		fprintf(stderr, "munmap %s error=%d\n", filename, errno);
207*96c8483aSYuri Pankov 		return (1);
208*96c8483aSYuri Pankov 	}
209*96c8483aSYuri Pankov 
210*96c8483aSYuri Pankov 	/* close file */
211*96c8483aSYuri Pankov 	if (close(fid) == -1) {
212*96c8483aSYuri Pankov 		fprintf(stderr, "close %s error=%d\n", filename, errno);
213*96c8483aSYuri Pankov 		return (1);
214*96c8483aSYuri Pankov 	}
215*96c8483aSYuri Pankov 
216*96c8483aSYuri Pankov 	return (0);
217*96c8483aSYuri Pankov }
218