mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-27 03:19:35 +03:00
89cd2197b9
When a page is faulted in for memory mapped I/O the page lock may be dropped before it has been read and marked up to date. If a buffered read encounters such a page in mappedread() it must wait until the page has been updated. Failure to do so will result in a panic on debug builds and incorrect data on production builds. The critical part of this change is in mappedread() where pages which are not up to date are now handled. Additionally, it includes the following simplifications. - zfs_getpage() and zfs_fillpage() could be passed an array of pages. This could be more efficient if it was used but in practice only a single page was ever provided. These interfaces were simplified to acknowledge that. - update_pages() was modified to correctly set the PG_error bit on a page when it cannot be read by dmu_read(). - Setting PG_error and PG_uptodate was moved to zfs_fillpage() from zpl_readpage_common(). This is consistent with the handling in update_pages() and mappedread(). - Minor additional refactoring to comments and variable declarations to improve readability. - Add a test case to exercise concurrent buffered, direct, and mmap IO to the same file. - Reduce the mmap_sync test case default run time. Reviewed-by: Richard Yao <richard.yao@alumni.stonybrook.edu> Reviewed-by: Brian Atkinson <batkinson@lanl.gov> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #13608 Closes #14498
153 lines
3.1 KiB
C
153 lines
3.1 KiB
C
/*
|
|
* CDDL HEADER START
|
|
*
|
|
* The contents of this file are subject to the terms of the
|
|
* Common Development and Distribution License (the "License").
|
|
* You may not use this file except in compliance with the License.
|
|
*
|
|
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
|
* or http://opensource.org/licenses/CDDL-1.0.
|
|
* See the License for the specific language governing permissions
|
|
* and limitations under the License.
|
|
*
|
|
* When distributing Covered Code, include this CDDL HEADER in each
|
|
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
|
* If applicable, add the following below this CDDL HEADER, with the
|
|
* fields enclosed by brackets "[]" replaced with your own identifying
|
|
* information: Portions Copyright [yyyy] [name of copyright owner]
|
|
*
|
|
* CDDL HEADER END
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
|
|
static void
|
|
cleanup(char *file)
|
|
{
|
|
(void) remove(file);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
char *testdir = getenv("TESTDIR");
|
|
if (!testdir) {
|
|
fprintf(stderr, "environment variable TESTDIR not set\n");
|
|
return (1);
|
|
}
|
|
|
|
struct stat st;
|
|
umask(0);
|
|
if (stat(testdir, &st) != 0 &&
|
|
mkdir(testdir, 0777) != 0) {
|
|
perror("mkdir");
|
|
return (1);
|
|
}
|
|
|
|
if (argc > 3) {
|
|
fprintf(stderr, "usage: %s "
|
|
"[run time in mins] "
|
|
"[max msync time in ms]\n", argv[0]);
|
|
return (1);
|
|
}
|
|
|
|
int run_time_mins = 1;
|
|
if (argc >= 2) {
|
|
run_time_mins = atoi(argv[1]);
|
|
}
|
|
|
|
int max_msync_time_ms = 1000;
|
|
if (argc >= 3) {
|
|
max_msync_time_ms = atoi(argv[2]);
|
|
}
|
|
|
|
char filepath[512];
|
|
filepath[0] = '\0';
|
|
char *file = &filepath[0];
|
|
|
|
(void) snprintf(file, 512, "%s/msync_file", testdir);
|
|
|
|
const int LEN = 8;
|
|
cleanup(file);
|
|
|
|
int fd = open(file, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR |
|
|
S_IRGRP | S_IROTH);
|
|
|
|
if (fd == -1) {
|
|
(void) fprintf(stderr, "%s: %s: ", argv[0], file);
|
|
perror("open");
|
|
return (1);
|
|
}
|
|
|
|
if (ftruncate(fd, LEN) != 0) {
|
|
perror("ftruncate");
|
|
cleanup(file);
|
|
return (1);
|
|
}
|
|
|
|
void *ptr = mmap(NULL, LEN, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
|
|
if (ptr == MAP_FAILED) {
|
|
perror("mmap");
|
|
cleanup(file);
|
|
return (1);
|
|
}
|
|
|
|
struct timeval tstart;
|
|
gettimeofday(&tstart, NULL);
|
|
|
|
long long x = 0LL;
|
|
|
|
for (;;) {
|
|
*((long long *)ptr) = x;
|
|
x++;
|
|
|
|
struct timeval t1, t2;
|
|
gettimeofday(&t1, NULL);
|
|
if (msync(ptr, LEN, MS_SYNC|MS_INVALIDATE) != 0) {
|
|
perror("msync");
|
|
cleanup(file);
|
|
return (1);
|
|
}
|
|
|
|
gettimeofday(&t2, NULL);
|
|
|
|
double elapsed = (t2.tv_sec - t1.tv_sec) * 1000.0;
|
|
elapsed += ((t2.tv_usec - t1.tv_usec) / 1000.0);
|
|
if (elapsed > max_msync_time_ms) {
|
|
fprintf(stderr, "slow msync: %f ms\n", elapsed);
|
|
if (munmap(ptr, LEN) != 0)
|
|
perror("munmap");
|
|
cleanup(file);
|
|
return (1);
|
|
}
|
|
|
|
double elapsed_start = (t2.tv_sec - tstart.tv_sec) * 1000.0;
|
|
elapsed_start += ((t2.tv_usec - tstart.tv_usec) / 1000.0);
|
|
if (elapsed_start > run_time_mins * 60 * 1000) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (munmap(ptr, LEN) != 0) {
|
|
perror("munmap");
|
|
cleanup(file);
|
|
return (1);
|
|
}
|
|
|
|
if (close(fd) != 0) {
|
|
perror("close");
|
|
}
|
|
|
|
cleanup(file);
|
|
return (0);
|
|
}
|