1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://opensource.org/licenses/CDDL-1.0.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22#include <stdlib.h>
23#include <stdio.h>
24#include <string.h>
25#include <sys/mman.h>
26#include <sys/stat.h>
27#include <sys/time.h>
28#include <fcntl.h>
29#include <unistd.h>
30#include <time.h>
31
32static void
33cleanup(char *file)
34{
35	(void) remove(file);
36}
37
38int
39main(int argc, char *argv[])
40{
41	char *testdir = getenv("TESTDIR");
42	if (!testdir) {
43		fprintf(stderr, "environment variable TESTDIR not set\n");
44		return (1);
45	}
46
47	struct stat st;
48	umask(0);
49	if (stat(testdir, &st) != 0 &&
50	    mkdir(testdir, 0777) != 0) {
51		perror("mkdir");
52		return (1);
53	}
54
55	if (argc > 3) {
56		fprintf(stderr, "usage: %s "
57		    "[run time in mins] "
58		    "[max msync time in ms]\n", argv[0]);
59		return (1);
60	}
61
62	int run_time_mins = 1;
63	if (argc >= 2) {
64		run_time_mins = atoi(argv[1]);
65	}
66
67	int max_msync_time_ms = 1000;
68	if (argc >= 3) {
69		max_msync_time_ms = atoi(argv[2]);
70	}
71
72	char filepath[512];
73	filepath[0] = '\0';
74	char *file = &filepath[0];
75
76	(void) snprintf(file, 512, "%s/msync_file", testdir);
77
78	const int LEN = 8;
79	cleanup(file);
80
81	int fd = open(file, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR |
82	    S_IRGRP | S_IROTH);
83
84	if (fd == -1) {
85		(void) fprintf(stderr, "%s: %s: ", argv[0], file);
86		perror("open");
87		return (1);
88	}
89
90	if (ftruncate(fd, LEN) != 0) {
91		perror("ftruncate");
92		cleanup(file);
93		return (1);
94	}
95
96	void *ptr = mmap(NULL, LEN, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
97
98	if (ptr == MAP_FAILED) {
99		perror("mmap");
100		cleanup(file);
101		return (1);
102	}
103
104	struct timeval tstart;
105	gettimeofday(&tstart, NULL);
106
107	long long x = 0LL;
108
109	for (;;) {
110		*((long long *)ptr) = x;
111		x++;
112
113		struct timeval t1, t2;
114		gettimeofday(&t1, NULL);
115		if (msync(ptr, LEN, MS_SYNC|MS_INVALIDATE) != 0) {
116			perror("msync");
117			cleanup(file);
118			return (1);
119		}
120
121		gettimeofday(&t2, NULL);
122
123		double elapsed = (t2.tv_sec - t1.tv_sec) * 1000.0;
124		elapsed += ((t2.tv_usec - t1.tv_usec) / 1000.0);
125		if (elapsed > max_msync_time_ms) {
126			fprintf(stderr, "slow msync: %f ms\n", elapsed);
127			if (munmap(ptr, LEN) != 0)
128				perror("munmap");
129			cleanup(file);
130			return (1);
131		}
132
133		double elapsed_start = (t2.tv_sec - tstart.tv_sec) * 1000.0;
134		elapsed_start += ((t2.tv_usec - tstart.tv_usec) / 1000.0);
135		if (elapsed_start > run_time_mins * 60 * 1000) {
136			break;
137		}
138	}
139
140	if (munmap(ptr, LEN) != 0) {
141		perror("munmap");
142		cleanup(file);
143		return (1);
144	}
145
146	if (close(fd) != 0) {
147		perror("close");
148	}
149
150	cleanup(file);
151	return (0);
152}
153