Merge branch 'master' into gfio
authorJens Axboe <axboe@kernel.dk>
Fri, 5 Apr 2013 10:46:54 +0000 (12:46 +0200)
committerJens Axboe <axboe@kernel.dk>
Fri, 5 Apr 2013 10:46:54 +0000 (12:46 +0200)
Conflicts:
Makefile

Signed-off-by: Jens Axboe <axboe@kernel.dk>
13 files changed:
.gitignore
Makefile
backend.c
configure
engines/posixaio.c
fio.h
ioengines.c
lib/axmap.c
mutex.c
mutex.h
parse.c
stat.c
t/axmap.c

index 2457d65..3993a30 100644 (file)
@@ -1,5 +1,9 @@
-fio
-*.o
 *.d
-.depend
-cscope.out
+*.o
+/.depend
+/FIO-VERSION-FILE
+/config-host.h
+/config-host.mak
+/config.log
+/cscope.out
+/fio
index 4aa05fa..1755981 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,11 +1,3 @@
-DEBUGFLAGS = -D_FORTIFY_SOURCE=2 -DFIO_INC_DEBUG
-CPPFLAGS= -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 $(DEBUGFLAGS)
-OPTFLAGS= -O3 -g -ffast-math $(EXTFLAGS)
-CFLAGS = -std=gnu99 -Wwrite-strings -Wall $(OPTFLAGS)
-LIBS   = -lm -lz $(EXTLIBS)
-PROGS  = fio
-SCRIPTS = fio_generate_plots
-
 ifneq ($(wildcard config-host.mak),)
 all:
 include config-host.mak
@@ -20,11 +12,19 @@ all:
 include config-host.mak
 endif
 
+DEBUGFLAGS = -D_FORTIFY_SOURCE=2 -DFIO_INC_DEBUG
+CPPFLAGS= -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 $(DEBUGFLAGS)
+OPTFLAGS= -O3 -g -ffast-math
+CFLAGS = -std=gnu99 -Wwrite-strings -Wall $(OPTFLAGS) $(EXTFLAGS) $(BUILD_CFLAGS)
+LIBS   += -lm $(EXTLIBS)
+PROGS  = fio
+SCRIPTS = fio_generate_plots
+
 ifdef CONFIG_GFIO
   PROGS += gfio
 endif
 
-SOURCE := gettime.c ioengines.c init.c stat.c log.c time.c filesetup.c \
+SOURCE := gettime.c fio.c ioengines.c init.c stat.c log.c time.c filesetup.c \
                eta.c verify.c memory.c io_u.c parse.c mutex.c options.c \
                lib/rbtree.c smalloc.c filehash.c profile.c debug.c lib/rand.c \
                lib/num2str.c lib/ieee754.c $(wildcard crc/*.c) engines/cpu.c \
index 600f5ce..022122a 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -406,6 +406,15 @@ static int break_on_this_error(struct thread_data *td, enum fio_ddir ddir,
        return 0;
 }
 
+static void check_update_rusage(struct thread_data *td)
+{
+       if (td->update_rusage) {
+               td->update_rusage = 0;
+               update_rusage_stat(td);
+               fio_mutex_up(td->rusage_sem);
+       }
+}
+
 /*
  * The main verify engine. Runs over the writes we previously submitted,
  * reads the blocks back in, and checks the crc/md5 of the data.
@@ -433,6 +442,8 @@ static void do_verify(struct thread_data *td, uint64_t verify_bytes)
                        break;
        }
 
+       check_update_rusage(td);
+
        if (td->error)
                return;
 
@@ -444,6 +455,7 @@ static void do_verify(struct thread_data *td, uint64_t verify_bytes)
                int ret2, full;
 
                update_tv_cache(td);
+               check_update_rusage(td);
 
                if (runtime_exceeded(td, &td->tv_cache)) {
                        __update_tv_cache(td);
@@ -597,6 +609,8 @@ sync_done:
                        break;
        }
 
+       check_update_rusage(td);
+
        if (!td->error) {
                min_events = td->cur_depth;
 
@@ -652,6 +666,8 @@ static uint64_t do_io(struct thread_data *td)
                int ret2, full;
                enum fio_ddir ddir;
 
+               check_update_rusage(td);
+
                if (td->terminate || td->done)
                        break;
 
@@ -816,6 +832,8 @@ sync_done:
                }
        }
 
+       check_update_rusage(td);
+
        if (td->trim_entries)
                log_err("fio: %d trim entries leaked?\n", td->trim_entries);
 
@@ -884,8 +902,7 @@ static int init_io_u(struct thread_data *td)
        char *p;
 
        max_units = td->o.iodepth;
-       max_bs = max(td->o.max_bs[DDIR_READ], td->o.max_bs[DDIR_WRITE]);
-       max_bs = max(td->o.max_bs[DDIR_TRIM], max_bs);
+       max_bs = td_max_bs(td);
        min_write = td->o.min_bs[DDIR_WRITE];
        td->orig_buffer_size = (unsigned long long) max_bs
                                        * (unsigned long long) max_units;
@@ -1042,7 +1059,7 @@ static int keep_running(struct thread_data *td)
                 * are done.
                 */
                diff = td->o.size - ddir_rw_sum(td->io_bytes);
-               if (diff < td->o.rw_min_bs)
+               if (diff < td_max_bs(td))
                        return 0;
 
                return 1;
@@ -1392,6 +1409,9 @@ err:
        if (td->o.write_iolog_file)
                write_iolog_close(td);
 
+       fio_mutex_remove(td->rusage_sem);
+       td->rusage_sem = NULL;
+
        td_set_runstate(td, TD_EXITED);
        return (void *) (uintptr_t) td->error;
 }
@@ -1645,6 +1665,9 @@ static void run_threads(void)
 
                        init_disk_util(td);
 
+                       td->rusage_sem = fio_mutex_init(FIO_MUTEX_LOCKED);
+                       td->update_rusage = 0;
+
                        /*
                         * Set state to created. Thread will transition
                         * to TD_INITIALIZED when it's done setting up.
index 222befe..5dd683c 100755 (executable)
--- a/configure
+++ b/configure
@@ -38,8 +38,8 @@ fatal() {
 }
 
 # Default CFLAGS
-CFLAGS="-D_GNU_SOURCE"
-EXTFLAGS="-include config-host.h"
+CFLAGS="-D_GNU_SOURCE -include config-host.h"
+BUILD_CFLAGS=""
 
 # Print a helpful header at the top of config.log
 echo "# FIO configure log $(date)" >> config.log
@@ -236,7 +236,7 @@ CYGWIN*)
   output_sym "CONFIG_SCHED_IDLE"
   output_sym "CONFIG_TCP_NODELAY"
   echo "CC=$CC" >> $config_host_mak
-  echo "EXTFLAGS=$CFLAGS -include config-host.h -D_GNU_SOURCE" >> $config_host_mak
+  echo "BUILD_CFLAGS=$CFLAGS -include config-host.h -D_GNU_SOURCE" >> $config_host_mak
   exit 0
   ;;
 esac
@@ -815,6 +815,24 @@ if compile_prog "" "-lnuma" "libnuma"; then
 fi
 echo "libnuma                       $libnuma"
 
+##########################################
+# libnuma 2.x version API
+if test "$libnuma" = "yes" ; then
+libnuma_v2="no"
+cat > $TMPC << EOF
+#include <numa.h>
+int main(int argc, char **argv)
+{
+  struct bitmask *mask = numa_parse_nodestring(NULL);
+  return 0;
+}
+EOF
+if compile_prog "" "" "libnuma api"; then
+  libnuma_v2="yes"
+fi
+echo "libnuma v2                    $libnuma_v2"
+fi
+
 ##########################################
 # strsep() probe
 strsep="no"
@@ -1047,7 +1065,7 @@ fi
 if test "$sfaa" = "yes" ; then
   output_sym "CONFIG_SFAA"
 fi
-if test "$libverbs" = "yes" -o "rdmacm" = "yes" ; then
+if test "$libverbs" = "yes" -a "rdmacm" = "yes" ; then
   output_sym "CONFIG_RDMA"
 fi
 if test "$clock_gettime" = "yes" ; then
@@ -1094,7 +1112,7 @@ fi
 if test "$fusion_aw" = "yes" ; then
   output_sym "CONFIG_FUSION_AW"
 fi
-if test "$libnuma" = "yes" ; then
+if test "$libnuma_v2" = "yes" ; then
   output_sym "CONFIG_LIBNUMA"
 fi
 if test "$solaris_aio" = "yes" ; then
@@ -1122,4 +1140,4 @@ fi
 echo "LIBS+=$LIBS" >> $config_host_mak
 echo "CFLAGS+=$CFLAGS" >> $config_host_mak
 echo "CC=$cc" >> $config_host_mak
-echo "EXTFLAGS=$EXTFLAGS $CFLAGS" >> $config_host_mak
+echo "BUILD_CFLAGS=$BUILD_CFLAGS $CFLAGS" >> $config_host_mak
index a943e5b..1858e52 100644 (file)
@@ -98,7 +98,7 @@ static int fio_posixaio_getevents(struct thread_data *td, unsigned int min,
        struct flist_head *entry;
        struct timespec start;
        int have_timeout = 0;
-       int suspend_entries = 0;
+       int suspend_entries;
        unsigned int r;
 
        if (t && !fill_timespec(&start))
@@ -107,8 +107,9 @@ static int fio_posixaio_getevents(struct thread_data *td, unsigned int min,
                memset(&start, 0, sizeof(start));
 
        r = 0;
-       memset(suspend_list, 0, sizeof(*suspend_list));
 restart:
+       memset(suspend_list, 0, sizeof(*suspend_list));
+       suspend_entries = 0;
        flist_for_each(entry, &td->io_u_busylist) {
                struct io_u *io_u = flist_entry(entry, struct io_u, list);
                int err;
diff --git a/fio.h b/fio.h
index 05406cb..1cb44e6 100644 (file)
--- a/fio.h
+++ b/fio.h
@@ -113,6 +113,8 @@ struct thread_data {
        uint64_t stat_io_blocks[DDIR_RWDIR_CNT];
        struct timeval iops_sample_time;
 
+       volatile int update_rusage;
+       struct fio_mutex *rusage_sem;
        struct rusage ru_start;
        struct rusage ru_end;
 
@@ -568,6 +570,14 @@ static inline int should_check_rate(struct thread_data *td,
        return ret;
 }
 
+static inline unsigned int td_max_bs(struct thread_data *td)
+{
+       unsigned int max_bs;
+
+       max_bs = max(td->o.max_bs[DDIR_READ], td->o.max_bs[DDIR_WRITE]);
+       return max(td->o.max_bs[DDIR_TRIM], max_bs);
+}
+
 static inline int is_power_of_2(unsigned int val)
 {
        return (val != 0 && ((val & (val - 1)) == 0));
index f4eae04..93e7631 100644 (file)
@@ -475,7 +475,9 @@ int td_io_close_file(struct thread_data *td, struct fio_file *f)
        fio_file_set_closing(f);
 
        disk_util_dec(f->du);
-       unlock_file_all(td, f);
+
+       if (td->o.file_lock_mode != FILE_LOCK_NONE)
+               unlock_file_all(td, f);
 
        return put_file(td, f);
 }
index eeb32d4..c9f3a4f 100644 (file)
@@ -189,7 +189,6 @@ void axmap_clear(struct axmap *axmap, uint64_t bit_nr)
 struct axmap_set_data {
        unsigned int nr_bits;
        unsigned int set_bits;
-       unsigned int fail_ok;
 };
 
 static unsigned long bit_masks[] = {
@@ -229,10 +228,8 @@ static int axmap_set_fn(struct axmap_level *al, unsigned long offset,
         * Mask off any potential overlap, only sets contig regions
         */
        overlap = al->map[offset] & mask;
-       if (overlap == mask) {
-               assert(data->fail_ok);
+       if (overlap == mask)
                return 1;
-       }
 
        while (overlap) {
                unsigned long clear_mask = ~(1UL << ffz(~overlap));
@@ -273,14 +270,14 @@ static void __axmap_set(struct axmap *axmap, uint64_t bit_nr,
                axmap_handler(axmap, bit_nr, axmap_set_fn, data);
                set_bits += data->set_bits;
 
-               if (data->set_bits != (BLOCKS_PER_UNIT - nr_bits))
+               if (!data->set_bits ||
+                   data->set_bits != (BLOCKS_PER_UNIT - nr_bits))
                        break;
 
                nr_bits -= data->set_bits;
                bit_nr += data->set_bits;
 
                data->nr_bits = nr_bits;
-               data->fail_ok = 1;
        }
 
        data->set_bits = set_bits;
@@ -295,10 +292,27 @@ void axmap_set(struct axmap *axmap, uint64_t bit_nr)
 
 unsigned int axmap_set_nr(struct axmap *axmap, uint64_t bit_nr, unsigned int nr_bits)
 {
-       struct axmap_set_data data = { .nr_bits = nr_bits, };
+       unsigned int set_bits = 0;
 
-       __axmap_set(axmap, bit_nr, &data);
-       return data.set_bits;
+       do {
+               struct axmap_set_data data = { .nr_bits = nr_bits, };
+               unsigned int max_bits, this_set;
+
+               max_bits = BLOCKS_PER_UNIT - (bit_nr & BLOCKS_PER_UNIT_MASK);
+               if (max_bits < nr_bits)
+                       data.nr_bits = max_bits;
+
+               this_set = data.nr_bits;
+               __axmap_set(axmap, bit_nr, &data);
+               set_bits += data.set_bits;
+               if (data.set_bits != this_set)
+                       break;
+
+               nr_bits -= data.set_bits;
+               bit_nr += data.set_bits;
+       } while (nr_bits);
+
+       return set_bits;
 }
 
 static int axmap_isset_fn(struct axmap_level *al, unsigned long offset,
diff --git a/mutex.c b/mutex.c
index 332e9f9..e1fbb60 100644 (file)
--- a/mutex.c
+++ b/mutex.c
@@ -7,6 +7,7 @@
 #include <errno.h>
 #include <pthread.h>
 #include <sys/mman.h>
+#include <assert.h>
 
 #include "fio.h"
 #include "log.h"
@@ -19,6 +20,7 @@
 
 void fio_mutex_remove(struct fio_mutex *mutex)
 {
+       assert(mutex->magic == FIO_MUTEX_MAGIC);
        pthread_cond_destroy(&mutex->cond);
        munmap((void *) mutex, sizeof(*mutex));
 }
@@ -40,6 +42,7 @@ struct fio_mutex *fio_mutex_init(int value)
        }
 
        mutex->value = value;
+       mutex->magic = FIO_MUTEX_MAGIC;
 
        ret = pthread_mutexattr_init(&attr);
        if (ret) {
@@ -92,6 +95,8 @@ int fio_mutex_down_timeout(struct fio_mutex *mutex, unsigned int seconds)
        struct timespec t;
        int ret = 0;
 
+       assert(mutex->magic == FIO_MUTEX_MAGIC);
+
        gettimeofday(&tv_s, NULL);
        t.tv_sec = tv_s.tv_sec + seconds;
        t.tv_nsec = tv_s.tv_usec * 1000;
@@ -122,6 +127,8 @@ int fio_mutex_down_timeout(struct fio_mutex *mutex, unsigned int seconds)
 
 void fio_mutex_down(struct fio_mutex *mutex)
 {
+       assert(mutex->magic == FIO_MUTEX_MAGIC);
+
        pthread_mutex_lock(&mutex->lock);
 
        while (!mutex->value) {
@@ -136,6 +143,8 @@ void fio_mutex_down(struct fio_mutex *mutex)
 
 void fio_mutex_up(struct fio_mutex *mutex)
 {
+       assert(mutex->magic == FIO_MUTEX_MAGIC);
+
        pthread_mutex_lock(&mutex->lock);
        read_barrier();
        if (!mutex->value && mutex->waiters)
@@ -146,27 +155,32 @@ void fio_mutex_up(struct fio_mutex *mutex)
 
 void fio_rwlock_write(struct fio_rwlock *lock)
 {
+       assert(lock->magic == FIO_RWLOCK_MAGIC);
        pthread_rwlock_wrlock(&lock->lock);
 }
 
 void fio_rwlock_read(struct fio_rwlock *lock)
 {
+       assert(lock->magic == FIO_RWLOCK_MAGIC);
        pthread_rwlock_rdlock(&lock->lock);
 }
 
 void fio_rwlock_unlock(struct fio_rwlock *lock)
 {
+       assert(lock->magic == FIO_RWLOCK_MAGIC);
        pthread_rwlock_unlock(&lock->lock);
 }
 
 void fio_rwlock_remove(struct fio_rwlock *lock)
 {
+       assert(lock->magic == FIO_RWLOCK_MAGIC);
        munmap((void *) lock, sizeof(*lock));
 }
 
 struct fio_rwlock *fio_rwlock_init(void)
 {
        struct fio_rwlock *lock;
+       pthread_rwlockattr_t attr;
        int ret;
 
        lock = (void *) mmap(NULL, sizeof(struct fio_rwlock),
@@ -178,13 +192,35 @@ struct fio_rwlock *fio_rwlock_init(void)
                goto err;
        }
 
+       lock->magic = FIO_RWLOCK_MAGIC;
+
+       ret = pthread_rwlockattr_init(&attr);
+       if (ret) {
+               log_err("pthread_rwlockattr_init: %s\n", strerror(ret));
+               goto err;
+       }
+#ifdef FIO_HAVE_PSHARED_MUTEX
+       ret = pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+       if (ret) {
+               log_err("pthread_rwlockattr_setpshared: %s\n", strerror(ret));
+               goto destroy_attr;
+       }
+
+       ret = pthread_rwlock_init(&lock->lock, &attr);
+#else
        ret = pthread_rwlock_init(&lock->lock, NULL);
+#endif
+
        if (ret) {
                log_err("pthread_rwlock_init: %s\n", strerror(ret));
-               goto err;
+               goto destroy_attr;
        }
 
+       pthread_rwlockattr_destroy(&attr);
+
        return lock;
+destroy_attr:
+       pthread_rwlockattr_destroy(&attr);
 err:
        if (lock)
                fio_rwlock_remove(lock);
diff --git a/mutex.h b/mutex.h
index 49a66e3..4f3486d 100644 (file)
--- a/mutex.h
+++ b/mutex.h
@@ -3,15 +3,20 @@
 
 #include <pthread.h>
 
+#define FIO_MUTEX_MAGIC                0x4d555445U
+#define FIO_RWLOCK_MAGIC       0x52574c4fU
+
 struct fio_mutex {
        pthread_mutex_t lock;
        pthread_cond_t cond;
        int value;
        int waiters;
+       int magic;
 };
 
 struct fio_rwlock {
        pthread_rwlock_t lock;
+       int magic;
 };
 
 enum {
diff --git a/parse.c b/parse.c
index 9250133..a701a5b 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -139,6 +139,19 @@ static unsigned long get_mult_time(char c)
        }
 }
 
+static int is_separator(char c)
+{
+       switch (c) {
+       case ':':
+       case '-':
+       case ',':
+       case '/':
+               return 1;
+       default:
+               return 0;
+       }
+}
+
 static unsigned long long __get_mult_bytes(const char *p, void *data,
                                           int *percent)
 {
@@ -152,8 +165,13 @@ static unsigned long long __get_mult_bytes(const char *p, void *data,
 
        c = strdup(p);
 
-       for (i = 0; i < strlen(c); i++)
+       for (i = 0; i < strlen(c); i++) {
                c[i] = tolower(c[i]);
+               if (is_separator(c[i])) {
+                       c[i] = '\0';
+                       break;
+               }
+       }
 
        if (!strncmp("pib", c, 3)) {
                pow = 5;
diff --git a/stat.c b/stat.c
index c3d3c4a..402a73f 100644 (file)
--- a/stat.c
+++ b/stat.c
@@ -734,7 +734,7 @@ static void add_ddir_status_json(struct thread_stat *ts,
        if (ovals)
                free(ovals);
 
-       if (!calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) {
+       if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) {
                if (rs->agg[ddir]) {
                        p_of_agg = mean * 100 / (double) rs->agg[ddir];
                        if (p_of_agg > 100.0)
@@ -1350,13 +1350,21 @@ static void *__show_running_run_stats(void *arg)
                if (td_trim(td) && td->io_bytes[DDIR_TRIM])
                        td->ts.runtime[DDIR_TRIM] += rt[i];
 
-               update_rusage_stat(td);
+               td->update_rusage = 1;
                td->ts.io_bytes[DDIR_READ] = td->io_bytes[DDIR_READ];
                td->ts.io_bytes[DDIR_WRITE] = td->io_bytes[DDIR_WRITE];
                td->ts.io_bytes[DDIR_TRIM] = td->io_bytes[DDIR_TRIM];
                td->ts.total_run_time = mtime_since(&td->epoch, &tv);
        }
 
+       for_each_td(td, i) {
+               if (td->rusage_sem) {
+                       td->update_rusage = 1;
+                       fio_mutex_down(td->rusage_sem);
+               }
+               td->update_rusage = 0;
+       }
+
        show_run_stats();
 
        for_each_td(td, i) {
index 7ab500f..57c585b 100644 (file)
--- a/t/axmap.c
+++ b/t/axmap.c
@@ -18,49 +18,125 @@ void sfree(void *ptr)
        free(ptr);
 }
 
-int main(int argc, char *argv[])
+static int test_regular(size_t size, int seed)
 {
        struct fio_lfsr lfsr;
-       size_t osize, size = (1UL << 28) - 200;
        struct axmap *map;
+       size_t osize;
        uint64_t ff;
-       int seed = 1;
+       int err;
 
-       if (argc > 1) {
-               size = strtoul(argv[1], NULL, 10);
-               if (argc > 2)
-                       seed = strtoul(argv[2], NULL, 10);
-       }
-
-       printf("Using %llu entries\n", (unsigned long long) size);
+       printf("Using %llu entries...", (unsigned long long) size);
+       fflush(stdout);
 
        lfsr_init(&lfsr, size, seed, seed & 0xF);
        map = axmap_new(size);
        osize = size;
+       err = 0;
 
        while (size--) {
                uint64_t val;
 
                if (lfsr_next(&lfsr, &val, osize)) {
                        printf("lfsr: short loop\n");
+                       err = 1;
                        break;
                }
                if (axmap_isset(map, val)) {
                        printf("bit already set\n");
+                       err = 1;
                        break;
                }
                axmap_set(map, val);
                if (!axmap_isset(map, val)) {
                        printf("bit not set\n");
+                       err = 1;
                        break;
                }
        }
 
+       if (err)
+               return err;
+
        ff = axmap_next_free(map, osize);
        if (ff != (uint64_t) -1ULL) {
                printf("axmap_next_free broken: got %llu\n", (unsigned long long) ff);
                return 1;
        }
 
+       printf("pass!\n");
+       axmap_free(map);
+       return 0;
+}
+
+static int test_multi(size_t size, unsigned int bit_off)
+{
+       unsigned int map_size = size;
+       struct axmap *map;
+       uint64_t val = bit_off;
+       int i, err;
+
+       printf("Test multi %llu entries %u offset...", (unsigned long long) size, bit_off);
+       fflush(stdout);
+
+       map = axmap_new(map_size);
+       while (val + 128 <= map_size) {
+               err = 0;
+               for (i = val; i < val + 128; i++) {
+                       if (axmap_isset(map, val + i)) {
+                               printf("bit already set\n");
+                               err = 1;
+                               break;
+                       }
+               }
+
+               if (err)
+                       break;
+
+               err = axmap_set_nr(map, val, 128);
+               if (err != 128) {
+                       printf("only set %u bits\n", err);
+                       break;
+               }
+
+               err = 0;
+               for (i = 0; i < 128; i++) {
+                       if (!axmap_isset(map, val + i)) {
+                               printf("bit not set: %llu\n", (unsigned long long) val + i);
+                               err = 1;
+                               break;
+                       }
+               }
+
+               val += 128;
+               if (err)
+                       break;
+       }
+
+       if (!err)
+               printf("pass!\n");
+
+       axmap_free(map);
+       return err;
+}
+
+int main(int argc, char *argv[])
+{
+       size_t size = (1UL << 23) - 200;
+       int seed = 1;
+
+       if (argc > 1) {
+               size = strtoul(argv[1], NULL, 10);
+               if (argc > 2)
+                       seed = strtoul(argv[2], NULL, 10);
+       }
+
+       if (test_regular(size, seed))
+               return 1;
+       if (test_multi(size, 0))
+               return 2;
+       if (test_multi(size, 17))
+               return 3;
+
        return 0;
 }