Merge branch 'fixes_1290' of https://github.com/rthardin/fio
authorJens Axboe <axboe@kernel.dk>
Tue, 19 Oct 2021 01:29:46 +0000 (19:29 -0600)
committerJens Axboe <axboe@kernel.dk>
Tue, 19 Oct 2021 01:29:46 +0000 (19:29 -0600)
* 'fixes_1290' of https://github.com/rthardin/fio:
  Use min_bs in rate_process=poisson

1  2 
backend.c

diff --combined backend.c
index 86fa6d41f860649e0f83c3aaf740ca9720e6c930,e2ffec513c67b5b44d24c685289cf98e8feb4412..c167f908625f39fd8e344c9f75c705f7fd71adf4
+++ b/backend.c
@@@ -62,22 -62,17 +62,22 @@@ struct io_log *agg_io_log[DDIR_RWDIR_CN
  
  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
 +pthread_mutex_t overlap_check = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
 +#else
  pthread_mutex_t overlap_check = PTHREAD_MUTEX_INITIALIZER;
 +#endif
  
  #define JOB_START_TIMEOUT     (5 * 1000)
  
  static void sig_int(int sig)
  {
 -      if (threads) {
 +      if (nr_segments) {
                if (is_backend)
                        fio_server_got_signal(sig);
                else {
@@@ -86,7 -81,7 +86,7 @@@
                        exit_value = 128;
                }
  
 -              fio_terminate_threads(TERMINATE_ALL);
 +              fio_terminate_threads(TERMINATE_ALL, TERMINATE_ALL);
        }
  }
  
@@@ -139,8 -134,8 +139,8 @@@ static bool __check_min_rate(struct thr
        unsigned long long bytes = 0;
        unsigned long iops = 0;
        unsigned long spent;
 -      unsigned long rate;
 -      unsigned int ratemin = 0;
 +      unsigned long long rate;
 +      unsigned long long ratemin = 0;
        unsigned int rate_iops = 0;
        unsigned int rate_iops_min = 0;
  
                         * check bandwidth specified rate
                         */
                        if (bytes < td->rate_bytes[ddir]) {
 -                              log_err("%s: rate_min=%uB/s not met, only transferred %lluB\n",
 +                              log_err("%s: rate_min=%lluB/s not met, only transferred %lluB\n",
                                        td->o.name, ratemin, bytes);
                                return true;
                        } else {
  
                                if (rate < ratemin ||
                                    bytes < td->rate_bytes[ddir]) {
 -                                      log_err("%s: rate_min=%uB/s not met, got %luB/s\n",
 +                                      log_err("%s: rate_min=%lluB/s not met, got %lluB/s\n",
                                                td->o.name, ratemin, rate);
                                        return true;
                                }
  
                                if (rate < rate_iops_min ||
                                    iops < td->rate_blocks[ddir]) {
 -                                      log_err("%s: rate_iops_min=%u not met, got %lu IOPS\n",
 +                                      log_err("%s: rate_iops_min=%u not met, got %llu IOPS\n",
                                                td->o.name, rate_iops_min, rate);
                                        return true;
                                }
@@@ -224,10 -219,12 +224,10 @@@ static bool check_min_rate(struct threa
  {
        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;
  }
@@@ -240,10 -237,15 +240,10 @@@ static void cleanup_pending_aio(struct 
  {
        int r;
  
 -      if (td->error)
 -              return;
 -
        /*
         * get immediately available events, if any
         */
        r = io_u_queued_complete(td, 0);
 -      if (r < 0)
 -              return;
  
        /*
         * now cancel remaining active events
@@@ -279,7 -281,6 +279,7 @@@ static bool fio_io_sync(struct thread_d
  
        io_u->ddir = DDIR_SYNC;
        io_u->file = f;
 +      io_u_set(td, io_u, IO_U_F_NO_FILE_PUT);
  
        if (td_io_prep(td, io_u)) {
                put_io_u(td, io_u);
@@@ -313,7 -314,7 +313,7 @@@ requeue
  
  static int fio_file_fsync(struct thread_data *td, struct fio_file *f)
  {
 -      int ret;
 +      int ret, ret2;
  
        if (fio_file_open(f))
                return fio_io_sync(td, f);
                return 1;
  
        ret = fio_io_sync(td, f);
 -      td_io_close_file(td, f);
 -      return ret;
 +      ret2 = 0;
 +      if (fio_file_open(f))
 +              ret2 = td_io_close_file(td, f);
 +      return (ret || ret2);
  }
  
  static inline void __update_ts_cache(struct thread_data *td)
@@@ -393,7 -392,7 +393,7 @@@ static bool break_on_this_error(struct 
                        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.
@@@ -439,7 -438,7 +439,7 @@@ static int wait_for_completions(struct 
        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 {
@@@ -494,7 -493,7 +494,7 @@@ int io_queue_event(struct thread_data *
                        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);
@@@ -837,7 -836,7 +837,7 @@@ static long long usec_for_io(struct thr
        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) {
        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);
                /* 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;
  }
  
  /*
@@@ -1049,6 -1008,12 +1049,6 @@@ static void do_io(struct thread_data *t
                if (td->o.verify != VERIFY_NONE && io_u->ddir == DDIR_READ &&
                    ((io_u->flags & IO_U_F_VER_LIST) || !td_rw(td))) {
  
 -                      if (!td->o.verify_pattern_bytes) {
 -                              io_u->rand_seed = __rand(&td->verify_state);
 -                              if (sizeof(int) != sizeof(long *))
 -                                      io_u->rand_seed *= __rand(&td->verify_state);
 -                      }
 -
                        if (verify_state_should_stop(td, io_u)) {
                                put_io_u(td, io_u);
                                break;
@@@ -1116,10 -1081,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;
                if (!in_ramp_time(td) && should_check_rate(td)) {
                        if (check_min_rate(td, &comp_time)) {
                                if (exitall_on_terminate || td->o.exitall_error)
 -                                      fio_terminate_threads(td->groupid);
 +                                      fio_terminate_threads(td->groupid, td->o.exit_what);
                                td_verror(td, EIO, "check_min_rate");
                                break;
                        }
                }
                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);
        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);
        }
  
                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;
                }
  
 -              if (should_fsync(td) && td->o.end_fsync) {
 +              if (should_fsync(td) && (td->o.end_fsync || td->o.fsync_on_close)) {
                        td_set_runstate(td, TD_FSYNCING);
  
                        for_each_file(td, f, i) {
@@@ -1270,7 -1233,7 +1270,7 @@@ static int init_io_u(struct thread_dat
                ptr = fio_memalign(cl_align, sizeof(*io_u), td_offload_overlap(td));
                if (!ptr) {
                        log_err("fio: unable to allocate aligned memory\n");
 -                      break;
 +                      return 1;
                }
  
                io_u = ptr;
@@@ -1378,19 -1341,22 +1378,19 @@@ int init_io_u_buffers(struct thread_dat
        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) {
  
        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;
  
        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;
        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) + 9 + 1;
        int ret;
        char *str;
  
 -      str = malloc(newlen);
 -      sprintf(str, "%s &> %s.%s.txt", 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);
@@@ -1622,7 -1543,7 +1622,7 @@@ static void *thread_main(void *data
        uint64_t bytes_done[DDIR_RWDIR_CNT];
        int deadlock_loop_cnt;
        bool clear_state;
 -      int ret;
 +      int res, ret;
  
        sk_out_assign(sk_out);
        free(fd);
                        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))
        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))
        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,
         * offload mode so that we don't clean up this job while
         * another thread is checking its io_u's for overlap
         */
 -      if (td_offload_overlap(td))
 -              pthread_mutex_lock(&overlap_check);
 +      if (td_offload_overlap(td)) {
 +              int res = pthread_mutex_lock(&overlap_check);
 +              assert(res == 0);
 +      }
        td_set_runstate(td, TD_FINISHING);
 -      if (td_offload_overlap(td))
 -              pthread_mutex_unlock(&overlap_check);
 +      if (td_offload_overlap(td)) {
 +              res = pthread_mutex_unlock(&overlap_check);
 +              assert(res == 0);
 +      }
  
        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)))
        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);
 +              fio_terminate_threads(td->groupid, td->o.exit_what);
  
  err:
        if (td->error)
@@@ -2130,11 -2044,10 +2130,11 @@@ reaped
  
                done_secs += mtime_since_now(&td->epoch) / 1000;
                profile_td_exit(td);
 +              flow_exit_job(td);
        }
  
        if (*nr_running == cputhreads && !pending && realthreads)
 -              fio_terminate_threads(TERMINATE_ALL);
 +              fio_terminate_threads(TERMINATE_ALL, TERMINATE_ALL);
  }
  
  static bool __check_trigger_file(void)
@@@ -2184,7 -2097,7 +2184,7 @@@ void check_trigger_file(void
                        fio_clients_send_trigger(trigger_remote_cmd);
                else {
                        verify_save_state(IO_LIST_ALL);
 -                      fio_terminate_threads(TERMINATE_ALL);
 +                      fio_terminate_threads(TERMINATE_ALL, TERMINATE_ALL);
                        exec_trigger(trigger_cmd);
                }
        }
@@@ -2204,16 -2117,8 +2204,16 @@@ static int fio_verify_load_state(struc
                                        td->thread_number - 1, &data);
                if (!ret)
                        verify_assign_state(td, data);
 -      } else
 -              ret = verify_load_state(td, "local");
 +      } else {
 +              char prefix[PATH_MAX];
 +
 +              if (aux_path)
 +                      sprintf(prefix, "%s%clocal", aux_path,
 +                                      FIO_OS_PATH_SEPARATOR);
 +              else
 +                      strcpy(prefix, "local");
 +              ret = verify_load_state(td, prefix);
 +      }
  
        return ret;
  }
@@@ -2465,7 -2370,7 +2465,7 @@@ reap
                        dprint(FD_MUTEX, "wait on startup_sem\n");
                        if (fio_sem_down_timeout(startup_sem, 10000)) {
                                log_err("fio: job startup hung? exiting.\n");
 -                              fio_terminate_threads(TERMINATE_ALL);
 +                              fio_terminate_threads(TERMINATE_ALL, TERMINATE_ALL);
                                fio_abort = true;
                                nr_started--;
                                free(fd);
@@@ -2587,8 -2492,7 +2587,8 @@@ int fio_backend(struct sk_out *sk_out
  
        set_genesis_time();
        stat_init();
 -      helper_thread_create(startup_sem, sk_out);
 +      if (helper_thread_create(startup_sem, sk_out))
 +              log_err("fio: failed to create helper thread\n");
  
        cgroup_list = smalloc(sizeof(*cgroup_list));
        if (cgroup_list)
        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;