Merge branch 'patch-1' of https://github.com/Nikratio/fio
[fio.git] / backend.c
index 0e454cdd1e9a4f2979320e7b913cc5f242d4b362..c167f908625f39fd8e344c9f75c705f7fd71adf4 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -62,8 +62,9 @@ struct io_log *agg_io_log[DDIR_RWDIR_CNT];
 
 int groupid = 0;
 unsigned int thread_number = 0;
+unsigned int nr_segments = 0;
+unsigned int cur_segment = 0;
 unsigned int stat_number = 0;
-int shm_id = 0;
 int temp_stall_ts;
 unsigned long done_secs = 0;
 #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
@@ -76,7 +77,7 @@ pthread_mutex_t overlap_check = PTHREAD_MUTEX_INITIALIZER;
 
 static void sig_int(int sig)
 {
-       if (threads) {
+       if (nr_segments) {
                if (is_backend)
                        fio_server_got_signal(sig);
                else {
@@ -223,12 +224,10 @@ static bool check_min_rate(struct thread_data *td, struct timespec *now)
 {
        bool ret = false;
 
-       if (td->bytes_done[DDIR_READ])
-               ret |= __check_min_rate(td, now, DDIR_READ);
-       if (td->bytes_done[DDIR_WRITE])
-               ret |= __check_min_rate(td, now, DDIR_WRITE);
-       if (td->bytes_done[DDIR_TRIM])
-               ret |= __check_min_rate(td, now, DDIR_TRIM);
+       for_each_rw_ddir(ddir) {
+               if (td->bytes_done[ddir])
+                       ret |= __check_min_rate(td, now, ddir);
+       }
 
        return ret;
 }
@@ -394,7 +393,7 @@ static bool break_on_this_error(struct thread_data *td, enum fio_ddir ddir,
                        td_clear_error(td);
                        *retptr = 0;
                        return false;
-               } else if (td->o.fill_device && err == ENOSPC) {
+               } else if (td->o.fill_device && (err == ENOSPC || err == EDQUOT)) {
                        /*
                         * We expect to hit this error if
                         * fill_device option is set.
@@ -440,7 +439,7 @@ static int wait_for_completions(struct thread_data *td, struct timespec *time)
        if ((full && !min_evts) || !td->o.iodepth_batch_complete_min)
                min_evts = 1;
 
-       if (time && __should_check_rate(td))
+       if (time && should_check_rate(td))
                fio_gettime(time, NULL);
 
        do {
@@ -495,7 +494,7 @@ int io_queue_event(struct thread_data *td, struct io_u *io_u, int *ret,
                        requeue_io_u(td, &io_u);
                } else {
 sync_done:
-                       if (comp_time && __should_check_rate(td))
+                       if (comp_time && should_check_rate(td))
                                fio_gettime(comp_time, NULL);
 
                        *ret = io_u_sync_complete(td, io_u);
@@ -838,7 +837,7 @@ static long long usec_for_io(struct thread_data *td, enum fio_ddir ddir)
        if (td->o.rate_process == RATE_PROCESS_POISSON) {
                uint64_t val, iops;
 
-               iops = bps / td->o.bs[ddir];
+               iops = bps / td->o.min_bs[ddir];
                val = (int64_t) (1000000 / iops) *
                                -logf(__rand_0_1(&td->poisson_state[ddir]));
                if (val) {
@@ -859,14 +858,47 @@ static long long usec_for_io(struct thread_data *td, enum fio_ddir ddir)
        return 0;
 }
 
-static void handle_thinktime(struct thread_data *td, enum fio_ddir ddir)
+static void init_thinktime(struct thread_data *td)
+{
+       if (td->o.thinktime_blocks_type == THINKTIME_BLOCKS_TYPE_COMPLETE)
+               td->thinktime_blocks_counter = td->io_blocks;
+       else
+               td->thinktime_blocks_counter = td->io_issues;
+       td->last_thinktime = td->epoch;
+       td->last_thinktime_blocks = 0;
+}
+
+static void handle_thinktime(struct thread_data *td, enum fio_ddir ddir,
+                            struct timespec *time)
 {
        unsigned long long b;
        uint64_t total;
        int left;
+       struct timespec now;
+       bool stall = false;
+
+       if (td->o.thinktime_iotime) {
+               fio_gettime(&now, NULL);
+               if (utime_since(&td->last_thinktime, &now)
+                   >= td->o.thinktime_iotime + td->o.thinktime) {
+                       stall = true;
+               } else if (!fio_option_is_set(&td->o, thinktime_blocks)) {
+                       /*
+                        * When thinktime_iotime is set and thinktime_blocks is
+                        * not set, skip the thinktime_blocks check, since
+                        * thinktime_blocks default value 1 does not work
+                        * together with thinktime_iotime.
+                        */
+                       return;
+               }
+
+       }
+
+       b = ddir_rw_sum(td->thinktime_blocks_counter);
+       if (b >= td->last_thinktime_blocks + td->o.thinktime_blocks)
+               stall = true;
 
-       b = ddir_rw_sum(td->io_blocks);
-       if (b % td->o.thinktime_blocks)
+       if (!stall)
                return;
 
        io_u_quiesce(td);
@@ -899,6 +931,13 @@ static void handle_thinktime(struct thread_data *td, enum fio_ddir ddir)
                /* adjust for rate_process=poisson */
                td->last_usec[ddir] += total;
        }
+
+       if (time && should_check_rate(td))
+               fio_gettime(time, NULL);
+
+       td->last_thinktime_blocks = b;
+       if (td->o.thinktime_iotime)
+               td->last_thinktime = now;
 }
 
 /*
@@ -1077,6 +1116,10 @@ reap:
                }
                if (ret < 0)
                        break;
+
+               if (ddir_rw(ddir) && td->o.thinktime)
+                       handle_thinktime(td, ddir, &comp_time);
+
                if (!ddir_rw_sum(td->bytes_done) &&
                    !td_ioengine_flagged(td, FIO_NOIO))
                        continue;
@@ -1091,9 +1134,6 @@ reap:
                }
                if (!in_ramp_time(td) && td->o.latency_target)
                        lat_target_check(td);
-
-               if (ddir_rw(ddir) && td->o.thinktime)
-                       handle_thinktime(td, ddir);
        }
 
        check_update_rusage(td);
@@ -1101,7 +1141,7 @@ reap:
        if (td->trim_entries)
                log_err("fio: %lu trim entries leaked?\n", td->trim_entries);
 
-       if (td->o.fill_device && td->error == ENOSPC) {
+       if (td->o.fill_device && (td->error == ENOSPC || td->error == EDQUOT)) {
                td->error = 0;
                fio_mark_td_terminate(td);
        }
@@ -1116,7 +1156,8 @@ reap:
 
                if (i) {
                        ret = io_u_queued_complete(td, i);
-                       if (td->o.fill_device && td->error == ENOSPC)
+                       if (td->o.fill_device &&
+                           (td->error == ENOSPC || td->error == EDQUOT))
                                td->error = 0;
                }
 
@@ -1337,22 +1378,19 @@ int init_io_u_buffers(struct thread_data *td)
        return 0;
 }
 
+#ifdef FIO_HAVE_IOSCHED_SWITCH
 /*
- * This function is Linux specific.
+ * These functions are Linux specific.
  * FIO_HAVE_IOSCHED_SWITCH enabled currently means it's Linux.
  */
-static int switch_ioscheduler(struct thread_data *td)
+static int set_ioscheduler(struct thread_data *td, struct fio_file *file)
 {
-#ifdef FIO_HAVE_IOSCHED_SWITCH
        char tmp[256], tmp2[128], *p;
        FILE *f;
        int ret;
 
-       if (td_ioengine_flagged(td, FIO_DISKLESSIO))
-               return 0;
-
-       assert(td->files && td->files[0]);
-       sprintf(tmp, "%s/queue/scheduler", td->files[0]->du->sysfs_root);
+       assert(file->du && file->du->sysfs_root);
+       sprintf(tmp, "%s/queue/scheduler", file->du->sysfs_root);
 
        f = fopen(tmp, "r+");
        if (!f) {
@@ -1405,7 +1443,7 @@ static int switch_ioscheduler(struct thread_data *td)
 
        sprintf(tmp2, "[%s]", td->o.ioscheduler);
        if (!strstr(tmp, tmp2)) {
-               log_err("fio: io scheduler %s not found\n", td->o.ioscheduler);
+               log_err("fio: unable to set io scheduler to %s\n", td->o.ioscheduler);
                td_verror(td, EINVAL, "iosched_switch");
                fclose(f);
                return 1;
@@ -1413,11 +1451,55 @@ static int switch_ioscheduler(struct thread_data *td)
 
        fclose(f);
        return 0;
+}
+
+static int switch_ioscheduler(struct thread_data *td)
+{
+       struct fio_file *f;
+       unsigned int i;
+       int ret = 0;
+
+       if (td_ioengine_flagged(td, FIO_DISKLESSIO))
+               return 0;
+
+       assert(td->files && td->files[0]);
+
+       for_each_file(td, f, i) {
+
+               /* Only consider regular files and block device files */
+               switch (f->filetype) {
+               case FIO_TYPE_FILE:
+               case FIO_TYPE_BLOCK:
+                       /*
+                        * Make sure that the device hosting the file could
+                        * be determined.
+                        */
+                       if (!f->du)
+                               continue;
+                       break;
+               case FIO_TYPE_CHAR:
+               case FIO_TYPE_PIPE:
+               default:
+                       continue;
+               }
+
+               ret = set_ioscheduler(td, f);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
 #else
+
+static int switch_ioscheduler(struct thread_data *td)
+{
        return 0;
-#endif
 }
 
+#endif /* FIO_HAVE_IOSCHED_SWITCH */
+
 static bool keep_running(struct thread_data *td)
 {
        unsigned long long limit;
@@ -1460,16 +1542,17 @@ static bool keep_running(struct thread_data *td)
        return false;
 }
 
-static int exec_string(struct thread_options *o, const char *string, const char *mode)
+static int exec_string(struct thread_options *o, const char *string,
+                      const char *mode)
 {
-       size_t newlen = strlen(string) + strlen(o->name) + strlen(mode) + 13 + 1;
        int ret;
        char *str;
 
-       str = malloc(newlen);
-       sprintf(str, "%s > %s.%s.txt 2>&1", string, o->name, mode);
+       if (asprintf(&str, "%s > %s.%s.txt 2>&1", string, o->name, mode) < 0)
+               return -1;
 
-       log_info("%s : Saving output of %s in %s.%s.txt\n",o->name, mode, o->name, mode);
+       log_info("%s : Saving output of %s in %s.%s.txt\n", o->name, mode,
+                o->name, mode);
        ret = system(str);
        if (ret == -1)
                log_err("fio: exec of cmd <%s> failed\n", str);
@@ -1713,6 +1796,7 @@ static void *thread_main(void *data)
                        td_verror(td, errno, "ioprio_set");
                        goto err;
                }
+               td->ioprio = ioprio_value(o->ioprio_class, o->ioprio);
        }
 
        if (o->cgroup && cgroup_setup(td, cgroup_list, &cgroup_mnt))
@@ -1733,7 +1817,7 @@ static void *thread_main(void *data)
        if (!init_random_map(td))
                goto err;
 
-       if (o->exec_prerun && exec_string(o, o->exec_prerun, (const char *)"prerun"))
+       if (o->exec_prerun && exec_string(o, o->exec_prerun, "prerun"))
                goto err;
 
        if (o->pre_read && !pre_read_files(td))
@@ -1750,6 +1834,8 @@ static void *thread_main(void *data)
        memcpy(&td->iops_sample_time, &td->epoch, sizeof(td->epoch));
        memcpy(&td->ss.prev_time, &td->epoch, sizeof(td->epoch));
 
+       init_thinktime(td);
+
        if (o->ratemin[DDIR_READ] || o->ratemin[DDIR_WRITE] ||
                        o->ratemin[DDIR_TRIM]) {
                memcpy(&td->lastrate[DDIR_READ], &td->bw_sample_time,
@@ -1876,9 +1962,9 @@ static void *thread_main(void *data)
 
        update_rusage_stat(td);
        td->ts.total_run_time = mtime_since_now(&td->epoch);
-       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];
+       for_each_rw_ddir(ddir) {
+               td->ts.io_bytes[ddir] = td->io_bytes[ddir];
+       }
 
        if (td->o.verify_state_save && !(td->flags & TD_F_VSTATE_SAVED) &&
            (td->o.verify != VERIFY_NONE && td_write(td)))
@@ -1892,7 +1978,7 @@ static void *thread_main(void *data)
        rate_submit_exit(td);
 
        if (o->exec_postrun)
-               exec_string(o, o->exec_postrun, (const char *)"postrun");
+               exec_string(o, o->exec_postrun, "postrun");
 
        if (exitall_on_terminate || (o->exitall_error && td->error))
                fio_terminate_threads(td->groupid, td->o.exit_what);
@@ -2044,6 +2130,7 @@ reaped:
 
                done_secs += mtime_since_now(&td->epoch) / 1000;
                profile_td_exit(td);
+               flow_exit_job(td);
        }
 
        if (*nr_running == cputhreads && !pending && realthreads)
@@ -2526,6 +2613,7 @@ int fio_backend(struct sk_out *sk_out)
        for_each_td(td, i) {
                steadystate_free(td);
                fio_options_free(td);
+               fio_dump_options_free(td);
                if (td->rusage_sem) {
                        fio_sem_remove(td->rusage_sem);
                        td->rusage_sem = NULL;