1#!/bin/sh
2
3#
4# Copyright (c) 2009 Peter Holm <pho@FreeBSD.org>
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in the
14#    documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26# SUCH DAMAGE.
27#
28# $FreeBSD$
29#
30
31# Threaded variation of datamove.sh
32
33# Based on a test scenario by ups and suggestions by kib
34
35[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1
36
37here=`pwd`
38cd /tmp
39sed '1,/^EOF/d' < $here/$0 > dl.c
40cc -o dl -Wall dl.c -lpthread
41rm -f dl.c
42
43n=5
44old=`sysctl vm.old_msync | awk '{print $NF}'`
45sysctl vm.old_msync=1
46for i in `jot $n`; do
47	mkdir -p /tmp/dl.dir.$i
48	cd /tmp/dl.dir.$i
49	/tmp/dl &
50done
51cd /tmp
52for i in `jot $n`; do
53	wait
54done
55for i in `jot $n`; do
56	rm -rf /tmp/dl.dir.$i
57done
58sysctl vm.old_msync=$old
59
60rm -rf /tmp/dl
61exit 0
62EOF
63#include <err.h>
64#include <fcntl.h>
65#include <pthread.h>
66#include <stdio.h>
67#include <stdio.h>
68#include <stdlib.h>
69#include <string.h>
70#include <sys/mman.h>
71#include <sys/stat.h>
72#include <sys/types.h>
73#include <unistd.h>
74
75struct args {
76	char *bp;
77	int fd1;
78	int fd2;
79} a[2];
80
81int prepareFile(char *, int *);
82void * mapBuffer(void *);
83int startIO(int, char *);
84
85int pagesize;
86
87#define FILESIZE (32*1024)
88char wbuffer   [FILESIZE];
89
90/* Create a FILESIZE sized file - then remove file data from the cache */
91int
92prepareFile(char *filename, int *fdp)
93{
94	int fd;
95	int len;
96	int status;
97	void *addr;
98
99	fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);
100	if (fd == -1) {
101		perror("Creating file");
102		return fd;
103	}
104	len = write(fd, wbuffer, FILESIZE);
105	if (len < 0) {
106		perror("Write failed");
107		return 1;
108	}
109	status = fsync(fd);
110	if (status != 0) {
111		perror("fsync failed");
112		return 1;
113	}
114	addr = mmap(NULL, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
115	if (addr == MAP_FAILED) {
116		perror("Mmap failed");
117		return 1;
118	}
119	status = msync(addr, FILESIZE, MS_INVALIDATE | MS_SYNC);
120	if (status != 0) {
121		perror("Msync failed");
122		return 1;
123	}
124	munmap(addr, FILESIZE);
125
126	*fdp = fd;
127	return 0;
128}
129
130
131/* mmap a 2 page buffer - first page is from fd1, second page from fd2 */
132void *
133mapBuffer(void *ar)
134{
135	void *addr;
136	char *buffer;
137	int i;
138
139	i = (int )ar;
140	addr = mmap(NULL, pagesize * 2, PROT_READ | PROT_WRITE, MAP_SHARED, a[i].fd1, 0);
141	if (addr == MAP_FAILED) {
142		err(1, "Mmap failed");
143	}
144	buffer = addr;
145	addr = mmap(buffer + pagesize, pagesize, PROT_READ | PROT_WRITE, MAP_FIXED |
146		    MAP_SHARED, a[i].fd2, 0);
147
148	if (addr == MAP_FAILED) {
149		err(1, "Mmap2 failed");
150	}
151	a[i].bp = buffer;
152	sleep(1);
153	return (NULL);
154}
155
156
157int
158startIO(int fd, char *buffer)
159{
160	ssize_t len;
161
162	len = write(fd, buffer, 2 * pagesize);
163	if (len == -1) {
164		warn("startIO(%d, %p): write failed", fd, buffer);
165		return 1;
166	}
167	return 0;
168}
169
170
171
172int
173main(int argc, char *argv[], char *envp[])
174{
175
176	int fdA, fdB, fdDelayA, fdDelayB;
177	int r, status;
178	char *bufferA, *bufferB;
179	pid_t pid;
180	pthread_t threads[2];
181
182	pagesize = getpagesize();
183
184	if ((prepareFile("A", &fdA))
185	    || (prepareFile("B", &fdB))
186	    || (prepareFile("DelayA", &fdDelayA))
187	    || (prepareFile("DelayB", &fdDelayB)))
188		exit(1);
189
190	a[0].fd1 = fdDelayA;
191	a[0].fd2 = fdB;
192
193	a[1].fd1 = fdDelayB;
194	a[1].fd2 = fdA;
195
196	if ((r = pthread_create(&threads[0], NULL, mapBuffer, (void *)0)) != 0)
197		err(1, "pthread_create(): %s\n", strerror(r));
198	if ((r = pthread_create(&threads[1], NULL, mapBuffer, (void *)1)) != 0)
199		err(1, "pthread_create(): %s\n", strerror(r));
200
201	while (a[0].bp == NULL || a[1].bp == NULL)
202		pthread_yield();
203
204	bufferA = a[0].bp;
205	bufferB = a[1].bp;
206
207	pid = fork();
208
209	if (pid == 0) {
210		status = startIO(fdA, bufferA);
211		exit(status);
212	}
213	if (pid == -1) {
214		exit(1);
215	}
216	status = startIO(fdB, bufferB);
217	exit(status);
218
219}
220