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