X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=lustre%2Ftests%2Fmmap_sanity.c;h=90a2edb3f3cae8b316db5f80b025a766eca82eb9;hb=1155c2fee4c197bfc295f761d52f9b748c37ad79;hp=f4d05957f77f9fd88cd08c1805c589084b23bd5d;hpb=113303973ec9f8484eb2355a1a6ef3c4c7fd6a56;p=fs%2Flustre-release.git diff --git a/lustre/tests/mmap_sanity.c b/lustre/tests/mmap_sanity.c index f4d0595..90a2edb 100644 --- a/lustre/tests/mmap_sanity.c +++ b/lustre/tests/mmap_sanity.c @@ -1,30 +1,62 @@ -/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- - * vim:expandtab:shiftwidth=8:tabstop=8: +/* + * GPL HEADER START + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 for more details (a copy is included + * in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; If not, see + * http://www.gnu.org/licenses/gpl-2.0.html + * + * GPL HEADER END */ +/* + * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. + * Use is subject to license terms. + * + * Copyright (c) 2012, Intel Corporation. + */ +/* + * This file is part of Lustre, http://www.lustre.org/ + * Lustre is a trademark of Sun Microsystems, Inc. + */ + +#include +#include +#include +#include +#include +#include #include #include #include -#include -#include -#include -#include #include -#include -#include -#include +#include #include -#include +#include + +#include char *dir = NULL, *dir2 = NULL; long page_size; char mmap_sanity[256]; - static void usage(void) { - printf("Usage: mmap_sanity -d dir [-m dir2]\n"); - printf(" dir lustre mount point\n"); - printf(" dir2 another mount point\n"); + printf("Usage: mmap_sanity -d dir [-m dir2] [-e ]\n"); + printf(" -d dir lustre mount point\n"); + printf(" -m dir2 another mount point\n"); + printf(" -e testcases skipped test cases, -e 1 -e 2 to exclude" + " test cases 1 and 2.\n"); exit(127); } @@ -36,14 +68,14 @@ static int mmap_run(int tc) child = fork(); if (child < 0) - return errno; + return -errno; else if (child) return 0; if (dir2 != NULL) { rc = remote_tst(tc, dir2); } else { - rc = EINVAL; + rc = -EINVAL; fprintf(stderr, "invalid argument!\n"); } _exit(rc); @@ -53,24 +85,24 @@ static int mmap_initialize(char *myself) { char buf[1024], *file; int fdr, fdw, count, rc = 0; - + page_size = sysconf(_SC_PAGESIZE); if (page_size == -1) { perror("sysconf(_SC_PAGESIZE)"); - return errno; + return -errno; } /* copy myself to lustre for another client */ fdr = open(myself, O_RDONLY); if (fdr < 0) { perror(myself); - return EINVAL; + return -EINVAL; } file = strrchr(myself, '/'); if (file == NULL) { fprintf(stderr, "can't get test filename\n"); close(fdr); - return EINVAL; + return -EINVAL; } file++; sprintf(mmap_sanity, "%s/%s", dir, file); @@ -79,20 +111,20 @@ static int mmap_initialize(char *myself) if (fdw < 0) { perror(mmap_sanity); close(fdr); - return EINVAL; + return -EINVAL; } while ((count = read(fdr, buf, sizeof(buf))) != 0) { int writes; if (count < 0) { perror("read()"); - rc = errno; + rc = -errno; break; } writes = write(fdw, buf, count); if (writes != count) { perror("write()"); - rc = errno; + rc = -errno; break; } } @@ -114,23 +146,27 @@ static int mmap_tst1(char *mnt) region = page_size * 10; sprintf(mmap_file, "%s/%s", mnt, "mmap_file1"); - + if (unlink(mmap_file) && errno != ENOENT) { perror("unlink()"); - return errno; + return -errno; } fd = open(mmap_file, O_CREAT|O_RDWR, 0600); if (fd < 0) { perror(mmap_file); - return errno; + return -errno; + } + if (ftruncate(fd, region) < 0) { + perror("ftruncate()"); + rc = -errno; + goto out_close; } - ftruncate(fd, region); ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) { perror("mmap()"); - rc = errno; + rc = -errno; goto out_close; } memset(ptr, 'a', region); @@ -152,20 +188,24 @@ static int mmap_tst2(char *mnt) if (unlink(mmap_file) && errno != ENOENT) { perror("unlink()"); - return errno; + return -errno; } fd = open(mmap_file, O_CREAT|O_RDWR, 0600); if (fd < 0) { perror(mmap_file); - return errno; + return -errno; + } + if (ftruncate(fd, page_size) < 0) { + perror("ftruncate()"); + rc = -errno; + goto out_close; } - ftruncate(fd, page_size); ptr = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); if (ptr == MAP_FAILED) { perror("mmap()"); - rc = errno; + rc = -errno; goto out_close; } memcpy(ptr, "blah", strlen("blah")); @@ -174,24 +214,24 @@ static int mmap_tst2(char *mnt) out_close: close(fd); if (rc) - return rc; + return -rc; fd = open(mmap_file, O_RDONLY); if (fd < 0) { perror(mmap_file); - return errno; + return -errno; } rc = read(fd, buf, sizeof(buf)); if (rc < 0) { perror("read()"); - rc = errno; + rc = -errno; goto out_close; } rc = 0; - + if (strncmp("blah", buf, strlen("blah")) == 0) { fprintf(stderr, "mmap write back with MAP_PRIVATE!\n"); - rc = EFAULT; + rc = -EFAULT; } close(fd); unlink(mmap_file); @@ -206,30 +246,34 @@ static int mmap_tst3(char *mnt) region = page_size * 100; sprintf(mmap_file, "%s/%s", mnt, "mmap_file3"); - + if (unlink(mmap_file) && errno != ENOENT) { perror("unlink()"); - return errno; + return -errno; } fd = open(mmap_file, O_CREAT|O_RDWR, 0600); if (fd < 0) { perror(mmap_file); - return errno; + return -errno; + } + if (ftruncate(fd, region) < 0) { + perror("ftruncate()"); + rc = -errno; + goto out_close; } - ftruncate(fd, region); ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) { perror("mmap()"); - rc = errno; + rc = -errno; goto out_close; } rc = mmap_run(3); if (rc) goto out_unmap; - + memset(ptr, 'a', region); sleep(2); /* wait for remote test finish */ out_unmap: @@ -238,7 +282,7 @@ out_close: close(fd); unlink(mmap_file); return rc; -} +} static int remote_tst3(char *mnt) { @@ -251,18 +295,18 @@ static int remote_tst3(char *mnt) fd = open(mmap_file, O_RDWR, 0600); if (fd < 0) { perror(mmap_file); - return errno; + return -errno; } ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) { perror("mmap()"); - rc = errno; + rc = -errno; goto out_close; } memset(ptr, 'b', region); memset(ptr, 'c', region); - + munmap(ptr, region); out_close: close(fd); @@ -282,45 +326,53 @@ static int mmap_tst4(char *mnt) if (unlink(filea) && errno != ENOENT) { perror("unlink()"); - return errno; + return -errno; } if (unlink(fileb) && errno != ENOENT) { perror("unlink()"); - return errno; + return -errno; } fdr = fdw = -1; fdr = open(fileb, O_CREAT|O_RDWR, 0600); if (fdr < 0) { perror(fileb); - return errno; + return -errno; + } + if (ftruncate(fdr, region) < 0) { + perror("ftruncate()"); + rc = -errno; + goto out_close; } - ftruncate(fdr, region); fdw = open(filea, O_CREAT|O_RDWR, 0600); if (fdw < 0) { perror(filea); - rc = errno; + rc = -errno; + goto out_close; + } + if (ftruncate(fdw, region) < 0) { + perror("ftruncate()"); + rc = -errno; goto out_close; } - ftruncate(fdw, region); - + ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fdr, 0); if (ptr == MAP_FAILED) { perror("mmap()"); - rc = errno; + rc = -errno; goto out_close; } rc = mmap_run(4); if (rc) goto out_unmap; - + memset(ptr, '1', region); - + rc = write(fdw, ptr, region); if (rc <= 0) { perror("write()"); - rc = errno; + rc = -errno; } else rc = 0; @@ -350,19 +402,19 @@ static int remote_tst4(char *mnt) fdr = open(filea, O_RDWR, 0600); if (fdr < 0) { perror(filea); - return errno; + return -errno; } fdw = open(fileb, O_RDWR, 0600); if (fdw < 0) { perror(fileb); - rc = errno; + rc = -errno; goto out_close; } ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fdr, 0); if (ptr == MAP_FAILED) { perror("mmap()"); - rc = errno; + rc = -errno; goto out_close; } @@ -371,10 +423,10 @@ static int remote_tst4(char *mnt) rc = write(fdw, ptr, region); if (rc <= 0) { perror("write()"); - rc = errno; + rc = -errno; } else rc = 0; - + munmap(ptr, region); out_close: if (fdr >= 0) @@ -384,60 +436,56 @@ out_close: return rc; } -static int cancel_lru_locks(char *prefix) +static int cancel_lru_locks(char *filter) { - char cmd[256], line[1024]; - FILE *file; - pid_t child; - int len = 1024, rc = 0; - - child = fork(); - if (child < 0) - return errno; - else if (child) { - int status; - rc = waitpid(child, &status, WNOHANG); - if (rc == child) - rc = 0; - return rc; - } - - if (prefix) - sprintf(cmd, "ls /proc/fs/lustre/ldlm/namespaces/*/lru_size | grep -i %s", prefix); - else - sprintf(cmd, "ls /proc/fs/lustre/ldlm/namespaces/*/lru_size"); - - file = popen(cmd, "r"); - if (file == NULL) { - perror("popen()"); - return errno; - } - - while (fgets(line, len, file)) { - FILE *f; - - if (!strlen(line)) - continue; - /* trim newline character */ - *(line + strlen(line) - 1) = '\0'; - f = fopen(line, "w"); - if (f == NULL) { - perror("fopen()"); - rc = errno; - break; - } - rc = fwrite("clear", strlen("clear") + 1, 1, f); - if (rc < 1) { - perror("fwrite()"); - rc = errno; - fclose(f); - break; - } - fclose(f); - } - - pclose(file); - _exit(rc); + glob_t paths; + pid_t child; + int rc, i; + + child = fork(); + if (child < 0) + return -errno; + else if (child) { + int status; + + rc = waitpid(child, &status, WNOHANG); + if (rc == child) + rc = 0; + return rc; + } + + if (filter != NULL) + rc = cfs_get_param_paths(&paths, + "ldlm/namespaces/*-%s-*/lru_size", + filter); + else + rc = cfs_get_param_paths(&paths, + "ldlm/namespaces/*/lru_size"); + if (rc != 0) + return -EINVAL; + + for (i = 0; i < paths.gl_pathc; i++) { + FILE *f = fopen(paths.gl_pathv[i], "r"); + if (f == NULL) { + rc = -errno; + fprintf(stderr, "cannot open '%s': %s\n", + paths.gl_pathv[i], strerror(errno)); + break; + } + + rc = fwrite("clear", strlen("clear") + 1, 1, f); + if (rc < 1) { + rc = -errno; + fprintf(stderr, "fwrite failed for '%s': %s\n", + paths.gl_pathv[i], strerror(errno)); + fclose(f); + break; + } + fclose(f); + } + + cfs_free_param_data(&paths); + _exit(rc); } /* don't dead lock while read/write file to/from the buffer which @@ -453,20 +501,24 @@ static int mmap_tst5(char *mnt) if (unlink(mmap_file) && errno != ENOENT) { perror("unlink()"); - return errno; + return -errno; } fd = open(mmap_file, O_CREAT|O_RDWR, 0600); if (fd < 0) { perror(mmap_file); - return errno; + return -errno; + } + if (ftruncate(fd, region) < 0) { + perror("ftruncate()"); + rc = -errno; + goto out_close; } - ftruncate(fd, region); ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) { perror("mmap()"); - rc = errno; + rc = -errno; goto out_close; } memset(ptr, 'a', region); @@ -480,13 +532,13 @@ static int mmap_tst5(char *mnt) rc = read(fd, ptr + off, off * 2); if (rc != off * 2) { perror("read()"); - rc = errno; + rc = -errno; goto out_unmap; } rc = write(fd, ptr + off, off * 2); if (rc != off * 2) { perror("write()"); - rc = errno; + rc = -errno; } rc = 0; out_unmap: @@ -508,33 +560,38 @@ static int mmap_tst6(char *mnt) sprintf(mmap_file2, "%s/%s", dir2, "mmap_file6"); if (unlink(mmap_file) && errno != ENOENT) { perror("unlink()"); - return errno; + return -errno; } fd = open(mmap_file, O_CREAT|O_RDWR, 0600); if (fd < 0) { perror(mmap_file); - return errno; + return -errno; + } + if (ftruncate(fd, page_size) < 0) { + perror("ftruncate()"); + rc = -errno; + goto out; } - ftruncate(fd, page_size); fd2 = open(mmap_file2, O_RDWR, 0600); if (fd2 < 0) { perror(mmap_file2); + rc = -errno; goto out; } ptr = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) { perror("mmap()"); - rc = errno; + rc = -errno; goto out; } - + ptr2 = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0); if (ptr2 == MAP_FAILED) { perror("mmap()"); - rc = errno; + rc = -errno; goto out; } @@ -545,13 +602,13 @@ static int mmap_tst6(char *mnt) memcpy(ptr, "blah", strlen("blah")); if (strncmp(ptr, ptr2, strlen("blah"))) { fprintf(stderr, "client2 mmap mismatch!\n"); - rc = EFAULT; + rc = -EFAULT; goto out; } memcpy(ptr2, "foo", strlen("foo")); if (strncmp(ptr, ptr2, strlen("foo"))) { fprintf(stderr, "client1 mmap mismatch!\n"); - rc = EFAULT; + rc = -EFAULT; } out: if (ptr2) @@ -566,10 +623,185 @@ out: return rc; } +static int mmap_tst7_func(char *mnt, int rw) +{ + char fname[256]; + char *buf = MAP_FAILED; + ssize_t bytes; + int fd = -1; + int rc = 0; + + if (snprintf(fname, 256, "%s/mmap_tst7.%s", mnt, + (rw == 0) ? "read" : "write") >= 256) { + fprintf(stderr, "dir name too long\n"); + rc = -ENAMETOOLONG; + goto out; + } + fd = open(fname, O_RDWR | O_DIRECT | O_CREAT, 0644); + if (fd == -1) { + perror("open"); + rc = -errno; + goto out; + } + if (ftruncate(fd, 2 * page_size) == -1) { + perror("truncate"); + rc = -errno; + goto out; + } + buf = mmap(NULL, page_size * 2, + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (buf == MAP_FAILED) { + perror("mmap"); + rc = -errno; + goto out; + } + /* ensure the second page isn't mapped */ + munmap(buf + page_size, page_size); + bytes = (rw == 0) ? read(fd, buf, 2 * page_size) : + write(fd, buf, 2 * page_size); + /* Expected behavior */ + if (bytes == page_size) + goto out; + + fprintf(stderr, "%s returned %zd, errno = %d\n", + (rw == 0) ? "read" : "write", bytes, errno); + rc = -EIO; +out: + if (buf != MAP_FAILED) + munmap(buf, page_size); + if (fd != -1) + close(fd); + return rc; +} + +static int mmap_tst7(char *mnt) +{ + int rc; + + rc = mmap_tst7_func(mnt, 0); + if (rc != 0) + return rc; + rc = mmap_tst7_func(mnt, 1); + return rc; +} + +static int mmap_tst8(char *mnt) +{ + char fname[256]; + char *buf = MAP_FAILED; + int fd = -1; + int rc = 0; + pid_t pid; + char xyz[page_size * 2]; + + if (snprintf(fname, 256, "%s/mmap_tst8", mnt) >= 256) { + fprintf(stderr, "dir name too long\n"); + rc = -ENAMETOOLONG; + goto out; + } + fd = open(fname, O_RDWR | O_CREAT, 0644); + if (fd == -1) { + perror("open"); + rc = -errno; + goto out; + } + if (ftruncate(fd, page_size) == -1) { + perror("truncate"); + rc = -errno; + goto out; + } + buf = mmap(NULL, page_size * 2, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (buf == MAP_FAILED) { + perror("mmap"); + rc = -errno; + goto out; + } + + pid = fork(); + if (pid == 0) { /* child */ + memcpy(xyz, buf, page_size * 2); + /* shouldn't reach here. */ + exit(0); + } else if (pid > 0) { /* parent */ + int status = 0; + pid = waitpid(pid, &status, 0); + if (pid < 0) { + perror("wait"); + rc = -errno; + goto out; + } + + rc = -EFAULT; + if (WIFSIGNALED(status) && SIGBUS == WTERMSIG(status)) + rc = 0; + } else { + perror("fork"); + rc = -errno; + } + +out: + if (buf != MAP_FAILED) + munmap(buf, page_size); + if (fd != -1) + close(fd); + return rc; +} + +static int mmap_tst9(char *mnt) +{ + char fname[256]; + char *buf = MAP_FAILED; + int fd = -1; + int rc = 0; + + if (snprintf(fname, 256, "%s/mmap_tst9", mnt) >= 256) { + fprintf(stderr, "dir name too long\n"); + rc = -ENAMETOOLONG; + goto out; + } + if (unlink(fname) == -1 && errno != ENOENT) { + perror("unlink"); + rc = -errno; + goto out; + } + fd = open(fname, O_RDWR | O_CREAT, 0644); + if (fd == -1) { + perror("open"); + rc = -errno; + goto out; + } + buf = mmap(NULL, page_size * 2, + PROT_READ , MAP_PRIVATE, fd, (loff_t)(-10 * page_size)); + if (buf == MAP_FAILED) { + perror("mmap"); + rc = -errno; + goto out; + } + rc = write(STDOUT_FILENO, buf, 2 * page_size); + if (rc != -1) { + fprintf(stderr, "write succeded with %d instead of failing\n", rc); + rc = -EINVAL; + goto out; + } else if (errno != EFAULT) { + fprintf(stderr, "write failed with %d instead of EFAULT(%d)\n", + errno, EFAULT); + rc = -errno; + goto out; + } + rc = 0; +out: + if (buf != MAP_FAILED) + munmap(buf, page_size * 2); + if (fd != -1) + close(fd); + return rc; +} + static int remote_tst(int tc, char *mnt) { int rc = 0; - switch(tc) { + switch (tc) { case 3: rc = remote_tst3(mnt); break; @@ -578,54 +810,111 @@ static int remote_tst(int tc, char *mnt) break; default: fprintf(stderr, "wrong test case number %d\n", tc); - rc = EINVAL; + rc = -EINVAL; break; } return rc; } - + struct test_case { int tc; /* test case number */ char *desc; /* test description */ - int (* test_fn)(char *mnt); /* test function */ + int (*test_fn)(char *mnt); /* test function */ int node_cnt; /* node count */ + int skipped; /* skipped by caller */ }; struct test_case tests[] = { - { 1, "mmap test1: basic mmap operation", mmap_tst1, 1 }, - { 2, "mmap test2: MAP_PRIVATE not write back", mmap_tst2, 1 }, - { 3, "mmap test3: concurrent mmap ops on two nodes", mmap_tst3, 2 }, - { 4, "mmap test4: c1 write to f1 from mmapped f2, " - "c2 write to f1 from mmapped f1", mmap_tst4, 2 }, - { 5, "mmap test5: read/write file to/from the buffer " - "which mmapped to just this file", mmap_tst5, 1 }, - { 6, "mmap test6: check mmap write/read content on two nodes", - mmap_tst6, 2 }, - { 0, NULL, 0, 0 } + { + .tc = 1, + .desc = "mmap test1: basic mmap operation", + .test_fn = mmap_tst1, + .node_cnt = 1 + }, + { + .tc = 2, + .desc = "mmap test2: MAP_PRIVATE not write back", + .test_fn = mmap_tst2, + .node_cnt = 1 + }, + { + .tc = 3, + .desc = "mmap test3: concurrent mmap ops on " + "two nodes", + .test_fn = mmap_tst3, + .node_cnt = 2 + }, + { + .tc = 4, + .desc = "mmap test4: c1 write to f1 from mmapped f2, " + "c2 write to f1 from mmapped f1", + .test_fn = mmap_tst4, + .node_cnt = 2 + }, + { + .tc = 5, + .desc = "mmap test5: read/write file to/from the " + "buffer which mmapped to just this file", + .test_fn = mmap_tst5, + .node_cnt = 1 + }, + { + .tc = 6, + .desc = "mmap test6: check mmap write/read content " + "on two nodes", + .test_fn = mmap_tst6, + .node_cnt = 2 + }, + { + .tc = 7, + .desc = "mmap test7: file i/o with an unmapped " + "buffer", + .test_fn = mmap_tst7, + .node_cnt = 1 + }, + { + .tc = 8, + .desc = "mmap test8: SIGBUS for beyond file size", + .test_fn = mmap_tst8, + .node_cnt = 1 + }, + { + .tc = 9, + .desc = "mmap test9: SIGBUS for negative file offset", + .test_fn = mmap_tst9, + .node_cnt = 1 + }, + { + .tc = 0 + } }; int main(int argc, char **argv) { - extern char *optarg; struct test_case *test; + int nr_cases = sizeof(tests)/sizeof(*test); int c, rc = 0; - for(;;) { - c = getopt(argc, argv, "d:m:"); - if ( c == -1 ) + while ((c = getopt(argc, argv, "d:m:e:")) != -1) { + switch (c) { + case 'd': + dir = optarg; break; - - switch(c) { - case 'd': - dir = optarg; - break; - case 'm': - dir2 = optarg; - break; - default: - case '?': + case 'm': + dir2 = optarg; + break; + case 'e': { + char *endptr = NULL; + rc = strtol(optarg, &endptr, 10); + if (endptr != NULL && *endptr != '\0') usage(); - break; + if (rc > 0 && rc < nr_cases) + tests[rc - 1].skipped = 1; + break; + } + default: + usage(); + break; } } @@ -634,21 +923,31 @@ int main(int argc, char **argv) if (mmap_initialize(argv[0]) != 0) { fprintf(stderr, "mmap_initialize failed!\n"); - return EINVAL; + return -EINVAL; } + rc = 0; for (test = tests; test->tc; test++) { - char *rs = "skip"; - rc = 0; - if (test->node_cnt == 1 || dir2 != NULL) { + double duration = 0.0; + char *rs = "SKIPPED"; + + if (!test->skipped && (test->node_cnt == 1 || dir2 != NULL)) { + struct timeval start, end; + + gettimeofday(&start, NULL); rc = test->test_fn(dir); - rs = rc ? "fail" : "pass"; + gettimeofday(&end, NULL); + + duration = (double)(end.tv_sec - start.tv_sec) + + (double)(end.tv_usec - start.tv_usec) / 1000000; + rs = rc ? "FAIL" : "PASS"; } - fprintf(stderr, "%s (%s)\n", test->desc, rs); + + fprintf(stderr, "%s (%s, %.5gs)\n", test->desc, rs, duration); if (rc) break; } mmap_finalize(); - return rc; + return -rc; }