many new tests
. 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
This commit is contained in:
parent
072d916c1c
commit
32a4e0d84d
17 changed files with 1115 additions and 8 deletions
|
@ -4623,12 +4623,18 @@
|
|||
./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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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+= \
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
4
test/run
4
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`
|
||||
|
||||
|
|
84
test/test70.c
Normal file
84
test/test70.c
Normal file
|
@ -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 */
|
||||
}
|
||||
|
131
test/test71.c
Normal file
131
test/test71.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
306
test/test72.c
Normal file
306
test/test72.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
89
test/test73.c
Normal file
89
test/test73.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
217
test/testcache.c
Normal file
217
test/testcache.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
18
test/testcache.h
Normal file
18
test/testcache.h
Normal file
|
@ -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
|
171
test/testvm.c
Normal file
171
test/testvm.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
11
test/testvm.conf
Normal file
11
test/testvm.conf
Normal file
|
@ -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
|
||||
};
|
8
test/testvm.h
Normal file
8
test/testvm.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
struct info {
|
||||
/* info to testvm */
|
||||
int big; /* do BIG mode */
|
||||
|
||||
/* info from testvm */
|
||||
int result; /* error code */
|
||||
};
|
Loading…
Reference in a new issue