]> Zhao Yanbai Git Server - minix.git/commitdiff
many new tests 11/511/6
authorBen Gras <ben@minix3.org>
Fri, 12 Apr 2013 20:54:42 +0000 (20:54 +0000)
committerBen Gras <ben@minix3.org>
Fri, 19 Apr 2013 16:21:48 +0000 (16:21 +0000)
. test70: regression test for m_out vfs race condition

The following tests use testcache.c to generate test i/o
patterns, generate random write data and verify the reads.

. test71: blackbox full-stack test of FS operation, testing
  using the regular VFS interface crazy i/o patterns
  with various working set sizes, triggering only
  primary cache, also secondary cache, and finally
  disk i/o and verifying contents all the time
. test72: unit test of libminixfs, implementing
  functions it needs from -lsys and -lblockdriver
  and the client in order to simulate a working
  cache client and backend environment.
. test73: blackbox test of secondary vm cache in isolation

Change-Id: I1287e9753182b8719e634917ad158e3c1e079ceb

17 files changed:
distrib/sets/lists/minix/mi
include/minix/libminixfs.h
lib/libminixfs/cache.c
servers/vm/region.c
test/Makefile
test/common.c
test/common.h
test/run
test/test70.c [new file with mode: 0644]
test/test71.c [new file with mode: 0644]
test/test72.c [new file with mode: 0644]
test/test73.c [new file with mode: 0644]
test/testcache.c [new file with mode: 0644]
test/testcache.h [new file with mode: 0644]
test/testvm.c [new file with mode: 0644]
test/testvm.conf [new file with mode: 0644]
test/testvm.h [new file with mode: 0644]

index 1a702bff347c162b632dbda715259f81c2b7d02f..0a77844c2d909b752c043c61e38f0d09d2430eac 100644 (file)
 ./usr/tests/minix-posix/test67         minix-sys
 ./usr/tests/minix-posix/test68         minix-sys
 ./usr/tests/minix-posix/test69         minix-sys
+./usr/tests/minix-posix/test70         minix-sys
+./usr/tests/minix-posix/test71         minix-sys
+./usr/tests/minix-posix/test72         minix-sys
+./usr/tests/minix-posix/test73         minix-sys
 ./usr/tests/minix-posix/test7          minix-sys
 ./usr/tests/minix-posix/test8          minix-sys
 ./usr/tests/minix-posix/test9          minix-sys
 ./usr/tests/minix-posix/testinterp     minix-sys
 ./usr/tests/minix-posix/testsh1                minix-sys
 ./usr/tests/minix-posix/testsh2                minix-sys
+./usr/tests/minix-posix/testvm         minix-sys
+./usr/tests/minix-posix/testvm.conf    minix-sys
 ./usr/tmp                              minix-sys
 ./usr/var/db                           minix-sys
 ./usr/var/db/pkg                       minix-sys
index d68ef5e7956057691b1aaf640e1e82dcffa75de4..bbeda887777fde60a82a5befcdbde8b306a251dd 100644 (file)
@@ -45,6 +45,7 @@ struct buf *lmfs_get_block(dev_t dev, block_t block,int only_search);
 void lmfs_invalidate(dev_t device);
 void lmfs_put_block(struct buf *bp, int block_type);
 void lmfs_rw_scattered(dev_t, struct buf **, int, int);
+void lmfs_setquiet(int q);
 
 /* calls that libminixfs does into fs */
 void fs_blockstats(u32_t *blocks, u32_t *free, u32_t *used);
index 2772db091b68bb944904b5e582df119849add286..aa7999260b28a658617930f14260a6c2d4ed8729 100644 (file)
@@ -43,6 +43,11 @@ static int fs_block_size = 1024;     /* raw i/o block size */
 
 static int rdwt_err;
 
+static int quiet = 0;
+
+void
+lmfs_setquiet(int q) { quiet = q; }
+
 u32_t fs_bufs_heuristic(int minbufs, u32_t btotal, u32_t bfree, 
          int blocksize, dev_t majordev)
 {
@@ -66,7 +71,7 @@ u32_t fs_bufs_heuristic(int minbufs, u32_t btotal, u32_t bfree,
    */
   if((vm_info_stats(&vsi) != OK)) {
        bufs = 1024;
-       printf("fslib: heuristic info fail: default to %d bufs\n", bufs);
+       if(!quiet) printf("fslib: heuristic info fail: default to %d bufs\n", bufs);
        return bufs;
   }
 
index 5f46a0fa451e00802c47043acdfee7c47ce214bc..1733eafa74f17c470046227e4b2b9d3b966fcb9b 100644 (file)
@@ -1876,7 +1876,7 @@ int do_yieldblockgetblock(message *m)
        endpoint_t caller = m->m_source;
        struct vmproc *vmp;
        yielded_t *yb = NULL;
-       int r = ESRCH;
+       int r = OK;
        int pages;
 
        if(vm_isokendpt(caller, &n) != OK)
@@ -1901,11 +1901,11 @@ int do_yieldblockgetblock(message *m)
 
        if(cmp64(yieldid, VM_BLOCKID_NONE) != 0) {
                /* A block was given to yield. */
-               yieldblock(vmp, yieldid, (vir_bytes) m->VMYBGB_VADDR, &yb,
+               r = yieldblock(vmp, yieldid, (vir_bytes) m->VMYBGB_VADDR, &yb,
                        pages);
        }
 
-       if(cmp64(getid, VM_BLOCKID_NONE) != 0) {
+       if(r == OK && cmp64(getid, VM_BLOCKID_NONE) != 0) {
                /* A block was given to get. */
                r = getblock(vmp, getid, (vir_bytes) m->VMYBGB_VADDR, pages);
        }
index ceea1188ec5fa52c8e856d8ef5b7195ac1bba349..baeb47c9a4958cbdeb865249a2e4d9e8ec958126 100644 (file)
@@ -31,12 +31,22 @@ LDFLAGS.mod=        -shared         # make shared object
 # Some have an extra file
 OBJS.test57=   test57loop.o
 
+# Cache testing programs
+OBJS.test71+=  testcache.o
+OBJS.test72+=  testcache.o
+LDADD.test72+= -lminixfs
+PROGS += testvm
+OBJS.testvm+=  testcache.o
+LDADD.testvm+= -lsys -ltimers -lminlib -static
+
+FILES += testvm.conf
+
 # Tests to compile, For every architecture
 MINIX_TESTS= \
  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 \
 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
 41 42 43 44 45 46    48 49 50    52 53 54 55 56    58 59 60 \
-61       64 65 66 67 68 69
+61       64 65 66 67 68 69 70 71 72 73
 
 .if ${MACHINE_ARCH} == "i386"
 MINIX_TESTS+= \
index 3ccb6700b3d4e451083dd1748c30aee6475fe834..ca672584399099278b3f784cdfd933d844329dd0 100644 (file)
@@ -14,6 +14,8 @@
 
 int common_test_nr = -1, errct = 0, subtest;
 
+int quietflag = 1;
+
 /* provide a default max_error symbol as Max_error with a value
  * of 5. The test program can override it wit its own max_error
  * symbol if it wants that this code will then use instead.
@@ -130,3 +132,49 @@ void quit()
        exit(1);
   }
 }
+
+void
+printprogress(char *msg, int i, int max)
+{
+        int use_i = i + 1;
+        static time_t start_time, prev_time;
+        static int prev_i;
+        time_t now;
+
+       if(quietflag) return;
+
+        time(&now);
+        if(prev_i >= i) start_time = now;
+
+        if(now > start_time && prev_time < now) {
+                double i_per_sec = i / (now - start_time);
+                int remain_secs;
+
+                remain_secs = (int)((max-i) / i_per_sec);
+
+                fprintf(stderr, "%-35s  %7d/%7d  %3d%%  ETA %3ds\r", msg,
+                      use_i, (max), use_i*100/(max), remain_secs);
+                fflush(stderr);
+        }
+
+        if(use_i >= max) {
+                fprintf(stderr, "%-35s  done                                      \n", msg);
+        }
+
+        prev_i = i;
+        prev_time = now;
+}
+
+void getmem(u32_t *total, u32_t *free, u32_t *cached)
+{
+        u32_t pagesize, largest;
+        FILE *f = fopen("/proc/meminfo", "r");
+        if(!f) return;
+        if(fscanf(f, "%u %u %u %u %u", &pagesize, total, free,
+                &largest, cached) != 5) {
+               fprintf(stderr, "fscanf of meminfo failed\n");
+               exit(1);
+       }
+        fclose(f);
+}
+
index d98bf30beac1b0dd9e91aaf505027e30d77528a5..69e3237f56659595783f33aedf8f57618234f727 100644 (file)
@@ -10,6 +10,8 @@
 #define e(errn) e_f(__FILE__, __LINE__, (errn))
 #define em(errn,msg) do { fprintf(stderr, "%s\n", msg); e(errn); } while(0)
 
+#define BIGVARNAME "BIGTEST"
+
 void printprogress(char *msg, int i, int max);
 void cleanup(void);
 int does_fs_truncate(void);
@@ -19,6 +21,6 @@ void quit(void);
 void rm_rf_dir(int test_nr);
 void rm_rf_ppdir(int test_nr);
 void start(int test_nr);
+void getmem(u32_t *total, u32_t *free, u32_t *cached);
 
 extern int common_test_nr, errct, subtest;
-
index 9bf5c94cb05e65f7d3bafd10f1c66ff99760a833..7abad9d538c60af2ebd3056eb602a4303cadfd3a 100755 (executable)
--- a/test/run
+++ b/test/run
@@ -19,12 +19,12 @@ badones=                    # list of tests that failed
 
 # Tests which require setuid
 setuids="test11 test33 test43 test44 test46 test56 test60 test61 test65 \
-        test69"
+        test69 test73"
 
 alltests="   1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 \
          21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
          41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 \
-         61 62 63 64 65 66 67 68 69\
+         61 62 63 64 65 66 67 68 69 70 71 72 73 \
         sh1.sh sh2.sh interp.sh"
 tests_no=`expr 0`
 
diff --git a/test/test70.c b/test/test70.c
new file mode 100644 (file)
index 0000000..c4c584d
--- /dev/null
@@ -0,0 +1,84 @@
+/* Test 70 - regression test for m_out vfs race condition.
+ *
+ * Regression test for vfs overwriting m_out fields by competing threads.
+ * lseek() uses one of these fields too so this test performs concurrent
+ * lseek()s to trigger this situation.
+ *
+ * The program consists of 2 processes, each seeking to different ranges in
+ * a test file. The bug would return the wrong value in one of the messaeg
+ * fields so the lseek() return value would be wrong sometimes.
+ *
+ * The first instance seeks from 0 to SEEKWINDOW, the other instance seeks
+ * from SEEKWINDOW to SEEKWINDOW+SEEKWINDOW.
+ */
+
+#include <time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "common.h"
+
+#define SEEKWINDOW 1000
+
+static int
+doseeks(int seekbase)
+{
+       char template[30] = "tempfile.XXXXXXXX";
+       int iteration, fd = mkstemp(template);
+       int limit = seekbase + SEEKWINDOW;
+
+       /* make a temporary file, unlink it so it's always gone
+        * afterwards, and make it the size we need.
+        */
+       if(fd < 0) { perror("mkstemp"); e(2); return 1; }
+       if(unlink(template) < 0) { perror("unlink"); e(3); return 1; }
+       if(ftruncate(fd, limit) < 0) { perror("ftruncate"); e(4); return 1; }
+
+       /* try lseek() lots of times with different arguments and make
+        * sure we get the right return value back, while this happens
+        * in a concurrent process too.
+        */
+#define ITERATIONS 50000
+       for(iteration = 0; iteration < ITERATIONS; iteration++) {
+               int o;
+               for(o = seekbase; o < limit; o++) {
+                       int r;
+                       if((r=lseek(fd, o, SEEK_SET)) != o) {
+                               if(r < 0) perror("lseek");
+                               fprintf(stderr, "%d/%d  %d != %d\n",
+                                       iteration, ITERATIONS, r, o);
+                               e(5);
+                               return 1;
+                       }       
+               }
+       }
+
+       return 0;
+}
+
+int
+main()
+{
+  start(70);
+  pid_t f;
+  int result;
+
+  if((f=fork()) < 0) { e(1); quit(); }
+
+  if(f == 0) { exit(doseeks(0)); }
+
+  if(doseeks(SEEKWINDOW)) { e(10); }
+
+  if (waitpid(f, &result, 0) == -1) e(11);
+  if (WEXITSTATUS(result) != 0) e(12);
+
+  quit();
+
+  return(-1);                  /* impossible */
+}
+
diff --git a/test/test71.c b/test/test71.c
new file mode 100644 (file)
index 0000000..94b7b4b
--- /dev/null
@@ -0,0 +1,131 @@
+/* Test 71 - full hierachy storage test.
+ *
+ * Black box test of storage: test consistent file contents
+ * under various working sets and access patterns.
+ *
+ * Using varying working set sizes, exercise various cache
+ * layers separately.
+ *
+ * There is a 'smoke test' mode, suitable for running interactively,
+ * and a 'regression test' (big) mode, meant for batch invocation only
+ * as it takes very long.
+ */
+
+#include <sys/types.h>
+#include <sys/ioc_memory.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "common.h"
+#include "testcache.h"
+
+/* we want to flexibly split this test over multiple files
+ * - for big working sets we might run over the 2GB MFS file limit
+ * - we might want to test the FS being able to handle lots of
+ *   files / unusual metadata situations
+ */
+#define MBPERFILE 100
+#define MB (1024*1024)
+#define MAXFILES ((u64_t) MAXBLOCKS * MAXBLOCKSIZE / MB / MBPERFILE + 1)
+
+static int fds[MAXFILES];
+
+static void
+get_fd_offset(int b, int blocksize, u64_t *file_offset, int *fd)
+{
+       u64_t offset = (u64_t) b * blocksize;
+       int filenumber;
+
+       filenumber = offset / MB / MBPERFILE;
+
+       assert(filenumber >= 0 && filenumber < MAXFILES);
+       assert(fds[filenumber] > 0);
+
+       *fd = fds[filenumber];
+       *file_offset = offset - (filenumber * MBPERFILE * MB);
+}
+
+int
+dowriteblock(int b, int blocksize, u32_t seed, char *data)
+{
+       u64_t offset;
+       int fd;
+
+       get_fd_offset(b, blocksize, &offset, &fd);
+
+       if(pwrite(fd, data, blocksize, offset) < blocksize) {
+               perror("pwrite");
+               return -1;
+       }
+
+       return blocksize;
+}
+
+int
+readblock(int b, int blocksize, u32_t seed, char *data)
+{
+       u64_t offset;
+       int fd;
+
+       get_fd_offset(b, blocksize, &offset, &fd);
+
+       if(pread(fd, data, blocksize, offset) < blocksize) {
+               perror("pread");
+               return -1;
+       }
+
+       return blocksize;
+}
+
+void testend(void) { }
+
+int
+main(int argc, char *argv[])
+{
+       int f, big = !!getenv(BIGVARNAME), iter = 2;
+
+       start(71);
+
+       cachequiet(!big);
+       if(big) iter = 3;
+
+       for(f = 0; f < MAXFILES; f++) {
+               char tempfilename[] = "cachetest.XXXXXXXX";
+               fds[f] = mkstemp(tempfilename);
+               if(fds[f] < 0) { perror("mkstemp"); e(20); return 1; }
+               assert(fds[f] > 0);
+       }
+
+       /* Try various combinations working set sizes
+        * and block sizes in order to specifically 
+        * target the primary cache, then primary+secondary
+        * cache, then primary+secondary cache+secondary
+        * cache eviction.
+        */
+
+       if(dotest(PAGE_SIZE,    100, iter)) e(5);
+       if(dotest(PAGE_SIZE*2,  100, iter)) e(2);
+       if(dotest(PAGE_SIZE*3,  100, iter)) e(3);
+       if(dotest(PAGE_SIZE,  20000, iter)) e(5);
+
+       if(big) {
+               u32_t totalmem, freemem, cachedmem;
+               if(dotest(PAGE_SIZE,  150000, iter)) e(5);
+               getmem(&totalmem, &freemem, &cachedmem);
+               if(dotest(PAGE_SIZE,  totalmem*1.5, iter)) e(6);
+       }
+
+       for(f = 0; f < MAXFILES; f++) {
+               assert(fds[f] > 0);
+               close(fds[f]);
+       }
+
+       quit();
+
+       return 0;
+}
+
diff --git a/test/test72.c b/test/test72.c
new file mode 100644 (file)
index 0000000..f376497
--- /dev/null
@@ -0,0 +1,306 @@
+/* Test 72 - libminixfs unit test.
+ *
+ * Exercise the caching functionality of libminixfs in isolation.
+ */
+
+#include <minix/libminixfs.h>
+#include <minix/sysutil.h>
+#include <minix/syslib.h>
+#include <minix/vm.h>
+#include <minix/bdev.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ioc_memory.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <math.h>
+
+int max_error = 0;
+
+#include "common.h"
+#include "testcache.h"
+
+#define MYMAJOR        40      /* doesn't really matter, shouldn't be NO_DEV though */
+
+#define MYDEV  makedev(MYMAJOR, 1)
+
+static int curblocksize = -1;
+
+static char *writtenblocks[MAXBLOCKS];
+
+/* Some functions used by testcache.c */
+
+int
+dowriteblock(int b, int blocksize, u32_t seed, char *data)
+{
+       struct buf *bp;
+
+       assert(blocksize == curblocksize);
+
+       if(!(bp = lmfs_get_block(MYDEV, b, NORMAL))) {
+               e(30);
+               return 0;
+       }
+
+       memcpy(bp->data, data, blocksize);
+
+       lmfs_markdirty(bp);
+
+       lmfs_put_block(bp, FULL_DATA_BLOCK);
+
+       return blocksize;
+}
+
+int
+readblock(int b, int blocksize, u32_t seed, char *data)
+{
+       struct buf *bp;
+
+       assert(blocksize == curblocksize);
+
+       if(!(bp = lmfs_get_block(MYDEV, b, NORMAL))) {
+               e(30);
+               return 0;
+       }
+
+       memcpy(data, bp->data, blocksize);
+
+       lmfs_put_block(bp, FULL_DATA_BLOCK);
+
+       return blocksize;
+}
+
+void testend(void)
+{
+       int i;
+       for(i = 0; i < MAXBLOCKS; i++) {
+               if(writtenblocks[i]) {
+                       free(writtenblocks[i]);
+                       writtenblocks[i] = NULL;
+               }
+       }
+}
+
+/* Fake some libminixfs client functions */
+
+int
+fs_sync(void)
+{
+       return 0;
+}
+
+void
+fs_blockstats(u32_t *total, u32_t *free, u32_t *used)
+{
+       *total = *free = *used = 0;
+}
+
+static void allocate(int b)
+{
+       assert(curblocksize > 0);
+       assert(!writtenblocks[b]);
+       if(!(writtenblocks[b] = calloc(1, curblocksize))) {
+               fprintf(stderr, "out of memory allocating block %d\n", b);
+               exit(1);
+       }
+}
+
+/* Fake some libblockdriver functions */
+ssize_t
+bdev_gather(dev_t dev, u64_t pos, iovec_t *vec, int count, int flags)
+{
+       int i, block;
+       ssize_t tot = 0;
+       assert(dev == MYDEV);
+       assert(curblocksize > 0);
+       assert(!(pos % curblocksize));
+       block = pos / curblocksize;
+       for(i = 0; i < count; i++) {
+               int subblocks;
+               char *data = (char *) vec[i].iov_addr;
+               assert(vec[i].iov_size > 0);
+               assert(!(vec[i].iov_size % curblocksize));
+               subblocks = vec[i].iov_size / curblocksize;
+               while(subblocks > 0) {
+                       assert(block > 0);
+                       assert(block < MAXBLOCKS);
+                       if(!writtenblocks[block]) {
+                               allocate(block);
+                       }
+                       memcpy(data, writtenblocks[block], curblocksize);
+                       block++;
+                       subblocks--;
+                       data += curblocksize;
+                       tot += curblocksize;
+               }
+       }
+
+       return tot;
+}
+
+ssize_t
+bdev_scatter(dev_t dev, u64_t pos, iovec_t *vec, int count, int flags)
+{
+       int i, block;
+       ssize_t tot = 0;
+       assert(dev == MYDEV);
+       assert(curblocksize > 0);
+       assert(!(pos % curblocksize));
+       block = pos / curblocksize;
+       for(i = 0; i < count; i++) {
+               int subblocks;
+               char *data = (char *) vec[i].iov_addr;
+               assert(vec[i].iov_size > 0);
+               assert(!(vec[i].iov_size % curblocksize));
+               subblocks = vec[i].iov_size / curblocksize;
+               while(subblocks > 0) {
+                       assert(block >= 0);
+                       assert(block < MAXBLOCKS);
+                       if(!writtenblocks[block]) {
+                               allocate(block);
+                       }
+                       memcpy(writtenblocks[block], data, curblocksize);
+                       block++;
+                       subblocks--;
+                       data += curblocksize;
+                       tot += curblocksize;
+               }
+       }
+
+       return tot;
+}
+
+ssize_t
+bdev_read(dev_t dev, u64_t pos, char *data, size_t count, int flags)
+{
+       int block;
+       ssize_t tot = 0;
+       int subblocks;
+
+       assert(dev == MYDEV);
+       assert(curblocksize > 0);
+       assert(!(pos % curblocksize));
+       assert(count > 0);
+       assert(!(count % curblocksize));
+
+       block = pos / curblocksize;
+       subblocks = count / curblocksize;
+       while(subblocks > 0) {
+               assert(block >= 0);
+               assert(block < MAXBLOCKS);
+               if(!writtenblocks[block]) {
+                       allocate(block);
+               }
+               memcpy(data, writtenblocks[block], curblocksize);
+               block++;
+               subblocks--;
+               data += curblocksize;
+               tot += curblocksize;
+       }
+
+       return tot;
+}
+
+/* Fake some libsys functions */
+
+__dead void
+panic(const char *fmt, ...)
+{
+       va_list va;
+       va_start(va, fmt);
+       vfprintf(stderr, fmt, va);
+       va_end(va);
+
+       exit(1);
+}
+
+int vm_forgetblock(u64_t id)
+{
+       return ENOSYS;
+}
+
+int vm_yield_block_get_block(u64_t yieldid, u64_t getid, void *mem,
+        vir_bytes len)
+{
+       return ENOSYS;
+}
+
+void vm_forgetblocks(void)
+{
+       return;
+}
+
+int
+vm_info_stats(struct vm_stats_info *vsi)
+{
+       return ENOSYS;
+}
+
+void
+util_stacktrace(void)
+{
+       fprintf(stderr, "fake stacktrace\n");
+}
+
+void *alloc_contig(size_t len, int flags, phys_bytes *phys)
+{
+       return malloc(len);
+}
+
+int free_contig(void *addr, size_t len)
+{
+       free(addr);
+       return 0;
+}
+
+u32_t sqrt_approx(u32_t v)
+{
+       return (u32_t) sqrt(v);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int wss, cs, n = 0, p;
+
+#define ITER 3
+#define BLOCKS 200
+
+       start(72);
+
+       lmfs_setquiet(1);
+
+       /* Can the cache handle differently sized blocks? */
+
+       for(p = 1; p <= 3; p++) {
+               curblocksize = PAGE_SIZE*p;
+               lmfs_set_blocksize(curblocksize, MYMAJOR);
+               lmfs_buf_pool(BLOCKS);
+               if(dotest(curblocksize, BLOCKS, ITER)) e(n);
+               n++;
+       }
+       
+       /* Can the cache handle various combinations of the working set
+        * being larger and smaller than the cache?
+        */
+       for(wss = 2; wss <= 3; wss++) {
+               int wsblocks = 10*wss*wss*wss*wss*wss;
+               for(cs = wsblocks/4; cs <= wsblocks*3; cs *= 1.5) {
+                       curblocksize = PAGE_SIZE;
+                       lmfs_set_blocksize(curblocksize, MYMAJOR);
+                       lmfs_buf_pool(cs);
+                       if(dotest(curblocksize, wsblocks, ITER)) e(n);
+                       n++;
+               }
+       }
+
+       quit();
+
+       return 0;
+}
+
diff --git a/test/test73.c b/test/test73.c
new file mode 100644 (file)
index 0000000..3c5c7f4
--- /dev/null
@@ -0,0 +1,89 @@
+/* Test 73 - VM secondary cache blackbox test.
+ *
+ * Blackbox test of the VM secondary cache in isolation, implemented
+ * in testvm.c, started as a service by this test program.
+ */
+
+#include <minix/libminixfs.h>
+#include <minix/sysutil.h>
+#include <minix/syslib.h>
+#include <minix/vm.h>
+#include <minix/bdev.h>
+#include <minix/paths.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ioc_memory.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <math.h>
+
+#include "testvm.h"
+
+int max_error = 0;
+
+#include "common.h"
+#include "testcache.h"
+
+int
+main(int argc, char *argv[])
+{
+       char pipefn[30], cwd[400], cmdline[400];
+       int pipefd;
+       static struct info i;
+       ssize_t r;
+       int big = 0;
+
+#define ITER 3
+#define BLOCKS 200
+
+       start(73);
+
+       unlink(pipefn);
+
+       /* 'big' as a substring indicates to testvm that it's ok to
+        * run a long test
+        */
+       if(getenv(BIGVARNAME)) big = 1;
+
+       if(big) strcpy(pipefn, "pipe_testvm_big");
+       else strcpy(pipefn, "pipe_testvm");
+
+       umask(0);
+       if(mkfifo(pipefn, 0666) < 0) { e(1); exit(1); }
+       if(!getcwd(cwd, sizeof(cwd))) { e(2); exit(1); }
+
+       /* stop residual testvm service if any */
+       snprintf(cmdline, sizeof(cmdline), "%s down testvm >/dev/null 2>&1",
+               _PATH_SERVICE);
+       if(system(cmdline) < 0) { e(9); exit(1); }
+
+       /* start the testvm service */
+       snprintf(cmdline, sizeof(cmdline),
+               "%s up /%s/../testvm -script /etc/rs.single "
+               "-args /%s/%s -config %s/../testvm.conf",
+                       _PATH_SERVICE, cwd, cwd, pipefn, cwd);
+       if(system(cmdline) < 0) { e(10); exit(1); }
+
+       /* don't hang forever if the open or read block */
+       alarm(big ? 6000 : 600);
+
+       if((pipefd=open(pipefn, O_RDONLY)) < 0) { e(3); exit(1); }
+
+       if((r=read(pipefd, &i, sizeof(i))) != sizeof(i)) {
+               printf("read returned %d\n", r);
+               e(12);
+               exit(1);
+       }
+
+       if(i.result != 0) { e(i.result); }
+
+       quit();
+
+       return 0;
+}
+
diff --git a/test/testcache.c b/test/testcache.c
new file mode 100644 (file)
index 0000000..b25141a
--- /dev/null
@@ -0,0 +1,217 @@
+/* A general i/o consistency test library. It performs i/o
+ * using functions provided by the client (readblock, dowriteblock)
+ * with a working set size specified by the client. It checks that
+ * blocks that were written have the same contents when later read,
+ * using different access patterns. The assumption is the various
+ * cache layers so exercised are forced into many different states
+ * (reordering, eviction, etc), hopefully triggering bugs if present.
+ *
+ * Entry point: dotest()
+ */
+
+#include <sys/types.h>
+#include <sys/ioc_memory.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "testcache.h"
+#include "common.h"
+
+extern int quietflag;
+
+static void
+genblock(int b, char *blockdata, int blocksize, u32_t seed)
+{
+       u32_t *p = (u32_t *) blockdata,
+               *plimit = (u32_t *) (blockdata + blocksize),
+               i = 0;
+
+       srandom(seed ^ b);
+
+       for(p = (u32_t *) blockdata; p < plimit; p++) {
+               i++;
+               *p = random();
+       }
+}
+
+static int
+checkblock(int b, int blocksize, u32_t seed)
+{
+       static char data[MAXBLOCKSIZE], expected_data[MAXBLOCKSIZE];
+       int r;
+
+       genblock(b, expected_data, blocksize, seed);
+
+       r = readblock(b, blocksize, seed, data);
+
+       if(r == OK_BLOCK_GONE) { return 0; }
+
+       if(r != blocksize) {
+               fprintf(stderr, "readblock failed\n");
+               return 1;
+       }
+
+       if(memcmp(expected_data, data, blocksize)) {
+               fprintf(stderr, "comparison of %d failed\n", b);
+               return 1;
+       }
+
+       return 0;
+}
+
+static int
+writeblock(int b, int blocksize, u32_t seed)
+{
+       static char data[MAXBLOCKSIZE];
+
+       genblock(b, data, blocksize, seed);
+
+       if(dowriteblock(b, blocksize, seed, data) != blocksize) {
+               fprintf(stderr, "writeblock of %d failed\n", b);
+               return 0;
+       }
+
+       return blocksize;
+}
+
+static int *
+makepermutation(int nblocks, int *permutation)
+{
+       int b;
+
+       assert(nblocks > 0 && nblocks <= MAXBLOCKS);
+
+       for(b = 0; b < nblocks; b++) permutation[b] = b;
+
+       for(b = 0; b < nblocks-1; b++) {
+               int s, other = b + random() % (nblocks - b - 1);
+               assert(other >= b && other < nblocks);
+               s = permutation[other];
+               permutation[other] = permutation[b];
+               permutation[b] = s;
+       }
+
+       return permutation;
+}
+
+static int
+checkblocks(int nblocks, int blocksize, u32_t seed)
+{
+       int b;
+       int nrandom = nblocks * 3;
+       static int perm1[MAXBLOCKS];
+
+       if(!quietflag) { fprintf(stderr, "\nverifying "); fflush(stderr); }
+
+       makepermutation(nblocks, perm1);
+
+       assert(nblocks > 0 && nblocks <= MAXBLOCKS);
+       assert(blocksize > 0 && blocksize <= MAXBLOCKSIZE);
+
+       for(b = 0; b < nblocks; b++) {
+               if(checkblock(b, blocksize, seed)) { return 1; }
+       }
+
+       for(b = 0; b < nrandom; b++) {
+               if(checkblock(random() % nblocks, blocksize, seed)) { return 1; }
+       }
+
+       for(b = 0; b < nblocks; b++) {
+               if(checkblock(b, blocksize, seed)) { return 1; }
+       }
+
+       for(b = 0; b < nblocks; b++) {
+               if(checkblock(perm1[b], blocksize, seed)) { return 1; }
+       }
+
+       if(!quietflag) { fprintf(stderr, "done\n"); }
+
+       return 0;
+}
+
+int
+dotest(int blocksize, int nblocks, int iterations)
+{
+       int b, i;
+       int nrandom = nblocks * iterations;
+       static int perm1[MAXBLOCKS], perm2[MAXBLOCKS];
+       static int newblock[MAXBLOCKS];
+       u32_t seed = random(), newseed;
+       int mb;
+
+       assert(nblocks > 0 && nblocks <= MAXBLOCKS);
+
+       mb = (int) ((u64_t) blocksize * nblocks / 1024 / 1024);
+
+       if(!quietflag) { fprintf(stderr, "test: %d * %d = %dMB\n", blocksize, nblocks, mb); }
+
+       for(b = 0; b < nblocks; b++) {
+               if(writeblock(b, blocksize, seed) < blocksize) { return 1; }
+               if(checkblock(b, blocksize, seed)) { return 1; }
+               printprogress("writing sequential", b, nblocks);
+       }
+
+       if(checkblocks(nblocks, blocksize, seed)) { return 1; }
+
+       makepermutation(nblocks, perm1);
+
+       for(b = 0; b < nblocks; b++) {
+               if(writeblock(perm1[b], blocksize, seed) < blocksize) { return 1; }
+               if(checkblock(perm1[b], blocksize, seed)) { return 1; }
+               printprogress("writing permutation", b, nblocks);
+       }
+
+       if(checkblocks(nblocks, blocksize, seed)) { return 1; }
+
+       for(i = 0; i < iterations; i++) {
+               makepermutation(nblocks, perm1);
+               makepermutation(nblocks, perm2);
+               memset(newblock, 0, sizeof(newblock));
+
+               newseed = random();
+
+               if(!quietflag) { fprintf(stderr, "iteration %d/%d\n", i, iterations); }
+
+               for(b = 0; b < nblocks; b++) {
+                       int wr = perm1[b], check = perm2[b];
+                       if(writeblock(wr, blocksize, newseed) < blocksize) { return 1; }
+                       newblock[wr] = 1;
+                       if(checkblock(check, blocksize, newblock[check] ? newseed : seed)) { return 1; }
+                       printprogress("interleaved permutation read, write", b, nblocks);
+               }
+
+               seed = newseed;
+
+               if(checkblocks(nblocks, blocksize, seed)) { return 1; }
+       }
+
+       newseed = random();
+
+       memset(newblock, 0, sizeof(newblock));
+
+       for(b = 0; b < nrandom; b++) {
+               int wr = random() % nblocks, check = random() % nblocks;
+               if(writeblock(wr, blocksize, newseed) < blocksize) { return 1; }
+               newblock[wr] = 1;
+               if(checkblock(check, blocksize,
+                       newblock[check] ? newseed : seed)) { return 1; }
+               printprogress("1 random verify, 1 random write", b, nrandom);
+       }
+
+       seed = newseed;
+
+       if(!quietflag) { fprintf(stderr, "\n"); }
+       testend();
+
+       return 0;
+}
+
+void cachequiet(int quiet)
+{
+       quietflag = quiet;
+}
+
diff --git a/test/testcache.h b/test/testcache.h
new file mode 100644 (file)
index 0000000..de735f6
--- /dev/null
@@ -0,0 +1,18 @@
+
+/* Common definitions and declarations for the testcache code
+ * and the testcache clients.
+ */
+
+#include <sys/types.h>
+
+#define MAXBLOCKS 1500000
+
+#define MAXBLOCKSIZE (4*PAGE_SIZE)
+
+int dowriteblock(int b, int blocksize, u32_t seed, char *block);
+int readblock(int b, int blocksize, u32_t seed, char *block);
+void testend(void);
+int dotest(int blocksize, int nblocks, int iterations);
+void cachequiet(int quiet);
+
+#define OK_BLOCK_GONE  -999
diff --git a/test/testvm.c b/test/testvm.c
new file mode 100644 (file)
index 0000000..dacf31a
--- /dev/null
@@ -0,0 +1,171 @@
+/* testvm - service-started code that goes with test73.o
+ */
+
+#include <minix/drivers.h>
+#include <minix/chardriver.h>
+#include <minix/ds.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include "testvm.h"
+#include "common.h"
+#include "testcache.h"
+
+static char *pipefilename = NULL, *progname;
+int pipefd = -1;
+
+int memfd;
+
+static char *bdata = NULL;
+
+int dowriteblock(int b, int blocksize, u32_t seed, char *block)
+{
+       int r;
+
+       r=vm_yield_block_get_block(VM_BLOCKID_NONE, b, bdata, blocksize);
+
+       if(r != OK && r != ESRCH) {
+               printf("dowriteblock: vm_yield_block_get_block get %d\n", r);
+               exit(1);
+       }
+
+       memcpy(bdata, block, blocksize);
+
+       r=vm_yield_block_get_block(b, VM_BLOCKID_NONE, bdata, blocksize);
+
+       if(r != OK) {
+               printf("dowriteblock: vm_yield_block_get_block yield %d\n", r);
+               exit(1);
+       }
+
+       return blocksize;
+}
+
+int readblock(int b, int blocksize, u32_t seed, char *block)
+{
+       int r;
+
+       r=vm_yield_block_get_block(VM_BLOCKID_NONE, b, bdata, blocksize);
+       if(r == ESRCH) {
+               return OK_BLOCK_GONE;
+       }
+       if(r != OK) {
+               printf("readblock: vm_yield_block_get_block get %d\n", r);
+               exit(1);
+       }
+
+       memcpy(block, bdata, blocksize);
+       r=vm_yield_block_get_block(b, VM_BLOCKID_NONE, bdata, blocksize);
+       if(r != OK) {
+               printf("readblock: vm_yield_block_get_block yield %d\n", r);
+               exit(1);
+       }
+
+       return blocksize;
+}
+
+void testend(void) { vm_forgetblocks(); }
+
+static void
+writepipe(struct info *i)
+{
+       if(write(pipefd, i, sizeof(*i)) != sizeof(*i)) {
+               printf("%s: pipe write failed\n", progname);
+               exit(1);
+       }
+}
+
+static int
+testinit(void)
+{
+       struct stat st;
+       int attempts = 0;
+
+       for(attempts = 0; attempts < 5 && pipefd < 0; attempts++) {
+               if(attempts > 0) sleep(1);
+               pipefd = open(pipefilename, O_WRONLY | O_NONBLOCK);
+       }
+
+       if(pipefd < 0) {
+               printf("%s: could not open pipe %s, errno %d\n",
+                       progname, pipefilename, errno);
+               exit(1);
+       }
+
+       if(fstat(pipefd, &st) < 0) {
+               printf("%s: could not fstat pipe %s\n", progname, pipefilename);
+               exit(1);
+       }
+
+       if(!(st.st_mode & I_NAMED_PIPE)) {
+               printf("%s: file %s is not a pipe\n", progname, pipefilename);
+               exit(1);
+       }
+
+       return OK;
+}
+
+static int
+sef_cb_init(int type, sef_init_info_t *UNUSED(info))
+{
+       return OK;
+}
+
+static void
+init(void)
+{
+       /* SEF init */
+       sef_setcb_init_fresh(sef_cb_init);
+       sef_setcb_init_lu(sef_cb_init);
+       sef_setcb_init_restart(sef_cb_init);
+
+       sef_startup();
+}
+
+
+
+int
+main(int argc, char *argv[])
+{
+       struct info info;
+       int big;
+       u32_t totalmem, freemem, cachedmem;
+
+       progname = argv[0];
+
+       if(argc < 2) { printf("no args\n"); return 1; }
+
+       pipefilename=argv[1];
+
+       big = !!strstr(pipefilename, "big");
+
+       init();
+
+       info.result = time(NULL);
+
+       if(testinit() != OK) { printf("%s: testinit failed\n", progname); return 1; }
+
+       cachequiet(!big);
+
+       if(!(bdata = alloc_contig(PAGE_SIZE, 0, NULL))) {
+               printf("could not allocate block\n");
+               exit(1);
+       }
+
+       if(dotest(PAGE_SIZE,       10, 3)) { e(11); exit(1); } 
+       if(dotest(PAGE_SIZE,     1000, 3)) { e(11); exit(1); } 
+       if(dotest(PAGE_SIZE,    50000, 3)) { e(11); exit(1); } 
+       if(big) {
+               getmem(&totalmem, &freemem, &cachedmem);
+               if(dotest(PAGE_SIZE, totalmem*1.5, 3)) { e(11); exit(1); } 
+       }
+
+       info.result = 0;
+
+       writepipe(&info);
+
+       return 0;
+}
+
diff --git a/test/testvm.conf b/test/testvm.conf
new file mode 100644 (file)
index 0000000..d2c6907
--- /dev/null
@@ -0,0 +1,11 @@
+service testvm {
+       ipc     ALL;    # All system ipc targets allowed
+       system  BASIC;          # Only basic kernel calls allowed
+       vm      BASIC;
+       io      NONE;           # No I/O range allowed
+       irq     NONE;           # No IRQ allowed
+       sigmgr          rs;     # Signal manager is RS
+       scheduler    sched;     # Scheduler is sched
+       priority         5;     # priority queue 5
+       quantum        500;     # default server quantum
+};
diff --git a/test/testvm.h b/test/testvm.h
new file mode 100644 (file)
index 0000000..c47a7b0
--- /dev/null
@@ -0,0 +1,8 @@
+
+struct info {
+       /* info to testvm */
+       int big;        /* do BIG mode */
+
+       /* info from testvm */
+       int result;     /* error code */
+};