[PATCH] Various little fixes and lots of commenting
authorJens Axboe <axboe@suse.de>
Tue, 13 Jun 2006 07:37:56 +0000 (09:37 +0200)
committerJens Axboe <axboe@suse.de>
Tue, 13 Jun 2006 07:37:56 +0000 (09:37 +0200)
README
fio.c
fio.h
init.c

diff --git a/README b/README
index d6ff109..4ed6078 100644 (file)
--- a/README
+++ b/README
@@ -102,7 +102,9 @@ The job file parameters are:
        cpumask=x       Only allow job to run on CPUs defined by mask.
        fsync=x         If writing, fsync after every x blocks have been written
        startdelay=x    Start this thread x seconds after startup
-       timeout=x       Terminate x seconds after startup
+       timeout=x       Terminate x seconds after startup. Can include a
+                       normal time suffix if not given in seconds, such as
+                       'm' for minutes, 'h' for hours, and 'd' for days.
        offset=x        Start io at offset x (x string can include k/m/g)
        invalidate=x    Invalidate page cache for file prior to doing io
        sync=x          Use sync writes if x and writing
diff --git a/fio.c b/fio.c
index 5bced24..412f002 100644 (file)
--- a/fio.c
+++ b/fio.c
@@ -103,6 +103,10 @@ static void sig_handler(int sig)
        }
 }
 
+/*
+ * The ->file_map[] contains a map of blocks we have or have not done io
+ * to yet. Used to make sure we cover the entire range in a fair fashion.
+ */
 static int random_map_free(struct thread_data *td, unsigned long long block)
 {
        unsigned int idx = RAND_MAP_IDX(td, block);
@@ -111,6 +115,9 @@ static int random_map_free(struct thread_data *td, unsigned long long block)
        return (td->file_map[idx] & (1UL << bit)) == 0;
 }
 
+/*
+ * Return the next free block in the map.
+ */
 static int get_next_free_block(struct thread_data *td, unsigned long long *b)
 {
        int i;
@@ -130,6 +137,9 @@ static int get_next_free_block(struct thread_data *td, unsigned long long *b)
        return 1;
 }
 
+/*
+ * Mark a given offset as used in the map.
+ */
 static void mark_random_map(struct thread_data *td, struct io_u *io_u)
 {
        unsigned long long block = io_u->offset / (unsigned long long) td->min_bs;
@@ -155,6 +165,11 @@ static void mark_random_map(struct thread_data *td, struct io_u *io_u)
                io_u->buflen = blocks * td->min_bs;
 }
 
+/*
+ * For random io, generate a random new block and see if it's used. Repeat
+ * until we find a free one. For sequential io, just return the end of
+ * the last io issued.
+ */
 static int get_next_offset(struct thread_data *td, unsigned long long *offset)
 {
        unsigned long long b, rb;
@@ -204,6 +219,9 @@ static unsigned int get_next_buflen(struct thread_data *td)
        return buflen;
 }
 
+/*
+ * Check if we are above the minimum rate given.
+ */
 static int check_min_rate(struct thread_data *td, struct timeval *now)
 {
        unsigned long spent;
@@ -352,6 +370,11 @@ static void fill_md5(struct verify_header *hdr, void *p, unsigned int len)
        memcpy(hdr->md5_digest, md5_ctx.hash, sizeof(md5_ctx.hash));
 }
 
+/*
+ * Return the data direction for the next io_u. If the job is a
+ * mixed read/write workload, check the rwmix cycle and switch if
+ * necessary.
+ */
 static int get_rw_ddir(struct thread_data *td)
 {
        if (td_rw(td)) {
@@ -472,6 +495,10 @@ struct io_u *__get_io_u(struct thread_data *td)
        return io_u;
 }
 
+/*
+ * Return an io_u to be processed. Gets a buflen and offset, sets direction,
+ * etc. The returned io_u is fully ready to be prepped and submitted.
+ */
 static struct io_u *get_io_u(struct thread_data *td)
 {
        struct io_u *io_u;
@@ -608,6 +635,10 @@ static void ios_completed(struct thread_data *td,struct io_completion_data *icd)
        }
 }
 
+/*
+ * When job exits, we can cancel the in-flight IO if we are using async
+ * io. Attempt to do so.
+ */
 static void cleanup_pending_aio(struct thread_data *td)
 {
        struct timespec ts = { .tv_sec = 0, .tv_nsec = 0};
@@ -661,6 +692,10 @@ static int do_io_u_verify(struct thread_data *td, struct io_u **io_u)
        return ret;
 }
 
+/*
+ * The main verify engine. Runs over the writes we previusly submitted,
+ * reads the blocks back in, and checks the crc/md5 of the data.
+ */
 static void do_verify(struct thread_data *td)
 {
        struct timeval t;
@@ -742,7 +777,7 @@ static void do_verify(struct thread_data *td)
 }
 
 /*
- * Main IO worker functions. It retrieves io_u's to process and queues
+ * Main IO worker function. It retrieves io_u's to process and queues
  * and reaps them, checking for rate and errors along the way.
  */
 static void do_io(struct thread_data *td)
@@ -1281,6 +1316,10 @@ static void clear_io_state(struct thread_data *td)
                memset(td->file_map, 0, td->num_maps * sizeof(long));
 }
 
+/*
+ * Entry point for the thread based jobs. The process based jobs end up
+ * here as well, after a little setup.
+ */
 static void *thread_main(void *data)
 {
        struct thread_data *td = data;
@@ -1403,6 +1442,10 @@ err:
 
 }
 
+/*
+ * We cannot pass the td data into a forked process, so attach the td and
+ * pass it to the thread worker.
+ */
 static void *fork_main(int shmid, int offset)
 {
        struct thread_data *td;
@@ -1420,6 +1463,9 @@ static void *fork_main(int shmid, int offset)
        return NULL;
 }
 
+/*
+ * Sets the status of the 'td' in the printed status map.
+ */
 static void check_str_update(struct thread_data *td)
 {
        char c = run_str[td->thread_number - 1];
@@ -1471,6 +1517,9 @@ static void check_str_update(struct thread_data *td)
        run_str[td->thread_number - 1] = c;
 }
 
+/*
+ * Convert seconds to a printable string.
+ */
 static void eta_to_str(char *str, int eta_sec)
 {
        unsigned int d, h, m, s;
@@ -1499,6 +1548,9 @@ static void eta_to_str(char *str, int eta_sec)
        str += sprintf(str, "%02ds", s);
 }
 
+/*
+ * Best effort calculation of the estimated pending runtime of a job.
+ */
 static int thread_eta(struct thread_data *td, unsigned long elapsed)
 {
        unsigned long long bytes_total, bytes_done;
@@ -1564,6 +1616,10 @@ static int thread_eta(struct thread_data *td, unsigned long elapsed)
        return eta_sec;
 }
 
+/*
+ * Print status of the jobs we know about. This includes rate estimates,
+ * ETA, thread state, etc.
+ */
 static void print_thread_status(void)
 {
        unsigned long elapsed = time_since_now(&genesis);
@@ -1632,6 +1688,9 @@ static void print_thread_status(void)
        free(eta_secs);
 }
 
+/*
+ * Run over the job map and reap the threads that have exited, if any.
+ */
 static void reap_threads(int *nr_running, int *t_rate, int *m_rate)
 {
        int i;
@@ -1703,6 +1762,9 @@ static void *fio_pin_memory(void)
        return ptr;
 }
 
+/*
+ * Main function for kicking off and reaping jobs, as needed.
+ */
 static void run_threads(void)
 {
        struct thread_data *td;
diff --git a/fio.h b/fio.h
index 39d9587..cc30a15 100644 (file)
--- a/fio.h
+++ b/fio.h
@@ -177,7 +177,7 @@ struct thread_data {
        unsigned int thinktime;
        unsigned int fsync_blocks;
        unsigned int start_delay;
-       unsigned int timeout;
+       unsigned long timeout;
        enum fio_iotype io_engine;
        unsigned int overwrite;
        unsigned int bw_avg_time;
diff --git a/init.c b/init.c
index 32d1e5c..85f0e3e 100644 (file)
--- a/init.c
+++ b/init.c
@@ -1,3 +1,7 @@
+/*
+ * This file contains the ini and command liner parser. It will create
+ * and initialize the specified jobs.
+ */
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -12,6 +16,9 @@
 
 #include "fio.h"
 
+/*
+ * The default options
+ */
 #define DEF_BS                 (4096)
 #define DEF_TIMEOUT            (0)
 #define DEF_RATE_CYCLE         (1000)
@@ -59,6 +66,9 @@ unsigned long long mlock_size = 0;
 FILE *f_out = NULL;
 FILE *f_err = NULL;
 
+/*
+ * Return a free job structure.
+ */
 static struct thread_data *get_new_job(int global, struct thread_data *parent)
 {
        struct thread_data *td;
@@ -83,6 +93,11 @@ static void put_job(struct thread_data *td)
        thread_number--;
 }
 
+/*
+ * Adds a job to the list of things todo. Sanitizes the various options
+ * to make sure we don't have conflicts, and initializes various
+ * members of td.
+ */
 static int add_job(struct thread_data *td, const char *jobname, int job_add_num)
 {
        char *ddir_str[] = { "read", "write", "randread", "randwrite",
@@ -211,6 +226,10 @@ err:
        return -1;
 }
 
+/*
+ * Initialize the various random states we need (random io, block size ranges,
+ * read/write mix, etc).
+ */
 int init_random_state(struct thread_data *td)
 {
        unsigned long seeds[4];
@@ -264,7 +283,24 @@ static void fill_cpu_mask(os_cpu_mask_t cpumask, int cpu)
 #endif
 }
 
-static unsigned long get_mult(char c)
+static unsigned long get_mult_time(char c)
+{
+       switch (c) {
+               case 'm':
+               case 'M':
+                       return 60;
+               case 'h':
+               case 'H':
+                       return 60 * 60;
+               case 'd':
+               case 'D':
+                       return 24 * 60 * 60;
+               default:
+                       return 1;
+       }
+}
+
+static unsigned long get_mult_bytes(char c)
 {
        switch (c) {
                case 'k':
@@ -284,7 +320,7 @@ static unsigned long get_mult(char c)
 /*
  * convert string after '=' into decimal value, noting any size suffix
  */
-static int str_cnv(char *p, unsigned long long *val)
+static int str_to_decimal(char *p, unsigned long long *val, int kilo)
 {
        char *str;
        int len;
@@ -300,16 +336,27 @@ static int str_cnv(char *p, unsigned long long *val)
        if (*val == ULONG_MAX && errno == ERANGE)
                return 1;
 
-       *val *= get_mult(str[len - 1]);
+       if (kilo)
+               *val *= get_mult_bytes(str[len - 1]);
+       else
+               *val *= get_mult_time(str[len - 1]);
        return 0;
 }
 
-static int check_strcnv(char *p, char *name, unsigned long long *val)
+static int check_str_bytes(char *p, char *name, unsigned long long *val)
 {
        if (strncmp(p, name, strlen(name) - 1))
                return 1;
 
-       return str_cnv(p, val);
+       return str_to_decimal(p, val, 1);
+}
+
+static int check_str_time(char *p, char *name, unsigned long long *val)
+{
+       if (strncmp(p, name, strlen(name) - 1))
+               return 1;
+
+       return str_to_decimal(p, val, 0);
 }
 
 static void strip_blank_front(char **p)
@@ -374,12 +421,12 @@ static int check_strstore(char *p, char *name, char *dest)
        return 0;
 }
 
-static int __check_range(char *str, unsigned long *val)
+static int __check_range_bytes(char *str, unsigned long *val)
 {
        char suffix;
 
        if (sscanf(str, "%lu%c", val, &suffix) == 2) {
-               *val *= get_mult(suffix);
+               *val *= get_mult_bytes(suffix);
                return 0;
        }
 
@@ -389,7 +436,8 @@ static int __check_range(char *str, unsigned long *val)
        return 1;
 }
 
-static int check_range(char *p, char *name, unsigned long *s, unsigned long *e)
+static int check_range_bytes(char *p, char *name, unsigned long *s,
+                            unsigned long *e)
 {
        char option[128];
        char *str, *p1, *p2;
@@ -425,7 +473,7 @@ static int check_range(char *p, char *name, unsigned long *s, unsigned long *e)
        p2 = p + 1;
        *p = '\0';
 
-       if (!__check_range(p1, s) && !__check_range(p2, e))
+       if (!__check_range_bytes(p1, s) && !__check_range_bytes(p2, e))
                return 0;
 
        return 1;
@@ -700,7 +748,8 @@ int parse_jobs_ini(char *file)
                                fgetpos(f, &off);
                                continue;
                        }
-                       if (!check_int(p, "timeout", &td->timeout)) {
+                       if (!check_str_time(p, "timeout", &ull)) {
+                               td->timeout = ul1;
                                fgetpos(f, &off);
                                continue;
                        }
@@ -772,7 +821,7 @@ int parse_jobs_ini(char *file)
                                fgetpos(f, &off);
                                continue;
                        }
-                       if (!check_range(p, "bsrange", &ul1, &ul2)) {
+                       if (!check_range_bytes(p, "bsrange", &ul1, &ul2)) {
                                if (ul1 > ul2) {
                                        td->max_bs = ul1;
                                        td->min_bs = ul2;
@@ -783,28 +832,28 @@ int parse_jobs_ini(char *file)
                                fgetpos(f, &off);
                                continue;
                        }
-                       if (!check_strcnv(p, "bs", &ull)) {
+                       if (!check_str_bytes(p, "bs", &ull)) {
                                td->bs = ull;
                                fgetpos(f, &off);
                                continue;
                        }
-                       if (!check_strcnv(p, "size", &td->file_size)) {
+                       if (!check_str_bytes(p, "size", &td->file_size)) {
                                fgetpos(f, &off);
                                continue;
                        }
-                       if (!check_strcnv(p, "offset", &td->file_offset)) {
+                       if (!check_str_bytes(p, "offset", &td->file_offset)) {
                                fgetpos(f, &off);
                                continue;
                        }
-                       if (!check_strcnv(p, "zonesize", &td->zone_size)) {
+                       if (!check_str_bytes(p, "zonesize", &td->zone_size)) {
                                fgetpos(f, &off);
                                continue;
                        }
-                       if (!check_strcnv(p, "zoneskip", &td->zone_skip)) {
+                       if (!check_str_bytes(p, "zoneskip", &td->zone_skip)) {
                                fgetpos(f, &off);
                                continue;
                        }
-                       if (!check_strcnv(p, "lockmem", &mlock_size)) {
+                       if (!check_str_bytes(p, "lockmem", &mlock_size)) {
                                fgetpos(f, &off);
                                continue;
                        }
@@ -1051,6 +1100,11 @@ static void free_shm(void)
        }
 }
 
+/*
+ * The thread area is shared between the main process and the job
+ * threads/processes. So setup a shared memory segment that will hold
+ * all the job info.
+ */
 static int setup_thread_area(void)
 {
        /*
@@ -1058,9 +1112,9 @@ static int setup_thread_area(void)
         * we get a failure that looks like too large a shm segment
         */
        do {
-               int s = max_jobs * sizeof(struct thread_data);
+               size_t size = max_jobs * sizeof(struct thread_data);
 
-               shm_id = shmget(0, s, IPC_CREAT | 0600);
+               shm_id = shmget(0, size, IPC_CREAT | 0600);
                if (shm_id != -1)
                        break;
                if (errno != EINVAL) {