Add thread_segments as needed
authorJens Axboe <axboe@kernel.dk>
Fri, 13 Nov 2020 16:02:35 +0000 (09:02 -0700)
committerJens Axboe <axboe@kernel.dk>
Fri, 13 Nov 2020 16:57:22 +0000 (09:57 -0700)
Setup segments in units of 8, and add extra ones as needed. This avoids
having to setup one huge segment upfront for the maximum number of jobs.

Signed-off-by: Jens Axboe <axboe@kernel.dk>
backend.c
fio.h
gettime-thread.c
init.c
libfio.c
server.c

index d2b97db07d6421372a0f2f87eee1b4528fbbe337..2e6a377cfb4cb4317294eb854cd61712f402caf7 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -62,6 +62,8 @@ 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 temp_stall_ts;
 unsigned long done_secs = 0;
@@ -75,7 +77,7 @@ pthread_mutex_t overlap_check = PTHREAD_MUTEX_INITIALIZER;
 
 static void sig_int(int sig)
 {
-       if (segments[0].threads) {
+       if (nr_segments) {
                if (is_backend)
                        fio_server_got_signal(sig);
                else {
diff --git a/fio.h b/fio.h
index 691976a3b58a8ebe4695800a018474247acdb4b9..fffec0014a4212e9183ce3bbeca963aa35492a43 100644 (file)
--- a/fio.h
+++ b/fio.h
@@ -470,6 +470,7 @@ struct thread_data {
 struct thread_segment {
        struct thread_data *threads;
        int shm_id;
+       int nr_threads;
 };
 
 /*
@@ -522,6 +523,8 @@ enum {
 extern bool exitall_on_terminate;
 extern unsigned int thread_number;
 extern unsigned int stat_number;
+extern unsigned int nr_segments;
+extern unsigned int cur_segment;
 extern int groupid;
 extern int output_format;
 extern int append_terse_output;
@@ -552,6 +555,14 @@ extern char *aux_path;
 
 extern struct thread_segment segments[REAL_MAX_SEG];
 
+static inline struct thread_data *tnumber_to_td(unsigned int tnumber)
+{
+       struct thread_segment *seg;
+
+       seg = &segments[tnumber / JOBS_PER_SEG];
+       return &seg->threads[tnumber & (JOBS_PER_SEG - 1)];
+}
+
 static inline bool is_running_backend(void)
 {
        return is_backend || is_local_backend;
@@ -715,7 +726,7 @@ extern void lat_target_reset(struct thread_data *);
  * Iterates all threads/processes within all the defined jobs
  */
 #define for_each_td(td, i)     \
-       for ((i) = 0, (td) = &segments[0].threads[0]; (i) < (int) thread_number; (i)++, (td)++)
+       for ((i) = 0, (td) = &segments[0].threads[0]; (i) < (int) thread_number; (i)++, (td) = tnumber_to_td((i)))
 #define for_each_file(td, f, i)        \
        if ((td)->files_index)                                          \
                for ((i) = 0, (f) = (td)->files[0];                     \
index 9b82e537220e703d2c00c7342ce8bfef3223a656..86c2e2efdcdcd796bd280951b5f0170ebe5a8db8 100644 (file)
@@ -58,7 +58,7 @@ static void *gtod_thread_main(void *data)
         * but I'm not sure what to use outside of a simple CPU nop to relax
         * it - we don't want to lose precision.
         */
-       while (segments[0].threads) {
+       while (nr_segments) {
                fio_gtod_update();
                nop;
        }
diff --git a/init.c b/init.c
index 36b10eaad13025d95cab46368323af2440e52d52..c3442849c0900a89ee1e26260682bb5642384989 100644 (file)
--- a/init.c
+++ b/init.c
@@ -301,25 +301,34 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
 
 void free_threads_shm(void)
 {
-       if (segments[0].threads) {
-               void *tp = segments[0].threads;
+       int i;
+
+       for (i = 0; i < nr_segments; i++) {
+               struct thread_segment *seg = &segments[i];
+
+               if (seg->threads) {
+                       void *tp = seg->threads;
 #ifndef CONFIG_NO_SHM
-               struct shmid_ds sbuf;
+                       struct shmid_ds sbuf;
 
-               segments[0].threads = NULL;
-               shmdt(tp);
-               shmctl(segments[0].shm_id, IPC_RMID, &sbuf);
-               segments[0].shm_id = -1;
+                       seg->threads = NULL;
+                       shmdt(tp);
+                       shmctl(seg->shm_id, IPC_RMID, &sbuf);
+                       seg->shm_id = -1;
 #else
-               segments[0].threads = NULL;
-               free(tp);
+                       seg->threads = NULL;
+                       free(tp);
 #endif
+               }
        }
+
+       nr_segments = 0;
+       cur_segment = 0;
 }
 
 static void free_shm(void)
 {
-       if (segments[0].threads) {
+       if (nr_segments) {
                flow_exit();
                fio_debug_jobp = NULL;
                fio_warned = NULL;
@@ -337,50 +346,31 @@ static void free_shm(void)
        scleanup();
 }
 
-/*
- * 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. We use the end of the region for keeping track of
- * open files across jobs, for file sharing.
- */
-static int setup_thread_area(void)
+static int add_thread_segment(void)
 {
-       struct thread_segment *seg = &segments[0];
+       struct thread_segment *seg = &segments[nr_segments];
+       size_t size = JOBS_PER_SEG * sizeof(struct thread_data);
        int i;
 
-       if (seg->threads)
-               return 0;
-
-       /*
-        * 1024 is too much on some machines, scale max_jobs if
-        * we get a failure that looks like too large a shm segment
-        */
-       do {
-               size_t size = max_jobs * sizeof(struct thread_data);
+       if (nr_segments + 1 >= REAL_MAX_SEG)
+               return -1;
 
-               size += 2 * sizeof(unsigned int);
+       size += 2 * sizeof(unsigned int);
 
 #ifndef CONFIG_NO_SHM
-               seg->shm_id = shmget(0, size, IPC_CREAT | 0600);
-               if (seg->shm_id != -1)
-                       break;
-               if (errno != EINVAL && errno != ENOMEM && errno != ENOSPC) {
+       seg->shm_id = shmget(0, size, IPC_CREAT | 0600);
+       if (seg->shm_id == -1) {
+               if (errno != EINVAL && errno != ENOMEM && errno != ENOSPC)
                        perror("shmget");
-                       break;
-               }
+               return -1;
+       }
 #else
-               seg->threads = malloc(size);
-               if (seg->threads)
-                       break;
+       seg->threads = malloc(size);
+       if (!seg->threads)
+               return -1;
 #endif
 
-               max_jobs >>= 1;
-       } while (max_jobs);
-
 #ifndef CONFIG_NO_SHM
-       if (seg->shm_id == -1)
-               return 1;
-
        seg->threads = shmat(seg->shm_id, NULL, 0);
        if (seg->threads == (void *) -1) {
                perror("shmat");
@@ -390,19 +380,43 @@ static int setup_thread_area(void)
                shmctl(seg->shm_id, IPC_RMID, NULL);
 #endif
 
-       memset(seg->threads, 0, max_jobs * sizeof(struct thread_data));
-       for (i = 0; i < max_jobs; i++)
+       nr_segments++;
+
+       memset(seg->threads, 0, JOBS_PER_SEG * sizeof(struct thread_data));
+       for (i = 0; i < JOBS_PER_SEG; i++)
                DRD_IGNORE_VAR(seg->threads[i]);
-       fio_debug_jobp = (unsigned int *)(seg->threads + max_jobs);
+       seg->nr_threads = 0;
+
+       /* Not first segment, we're done */
+       if (nr_segments != 1) {
+               cur_segment++;
+               return 0;
+       }
+
+       fio_debug_jobp = (unsigned int *)(seg->threads + JOBS_PER_SEG);
        *fio_debug_jobp = -1;
        fio_warned = fio_debug_jobp + 1;
        *fio_warned = 0;
 
        flow_init();
-
        return 0;
 }
 
+/*
+ * The thread areas are shared between the main process and the job
+ * threads/processes, and is split into chunks of JOBS_PER_SEG. If the current
+ * segment has no more room, add a new chunk.
+ */
+static int expand_thread_area(void)
+{
+       struct thread_segment *seg = &segments[cur_segment];
+
+       if (nr_segments && seg->nr_threads < JOBS_PER_SEG)
+               return 0;
+
+       return add_thread_segment();
+}
+
 static void dump_print_option(struct print_option *p)
 {
        const char *delim;
@@ -471,11 +485,12 @@ static void copy_opt_list(struct thread_data *dst, struct thread_data *src)
 static struct thread_data *get_new_job(bool global, struct thread_data *parent,
                                       bool preserve_eo, const char *jobname)
 {
+       struct thread_segment *seg;
        struct thread_data *td;
 
        if (global)
                return &def_thread;
-       if (setup_thread_area()) {
+       if (expand_thread_area()) {
                log_err("error: failed to setup shm segment\n");
                return NULL;
        }
@@ -485,7 +500,9 @@ static struct thread_data *get_new_job(bool global, struct thread_data *parent,
                return NULL;
        }
 
-       td = &segments[0].threads[thread_number++];
+       seg = &segments[cur_segment];
+       td = &seg->threads[seg->nr_threads++];
+       thread_number++;
        *td = *parent;
 
        INIT_FLIST_HEAD(&td->opt_list);
@@ -536,6 +553,7 @@ static void put_job(struct thread_data *td)
                free(td->o.name);
 
        memset(td, 0, sizeof(*td));
+       segments[cur_segment].nr_threads--;
        thread_number--;
 }
 
index 7348b16491ff266cadb7ba46d75e503984ccbc95..6144a474738fe9a51a530ffad691aa234138d665 100644 (file)
--- a/libfio.c
+++ b/libfio.c
@@ -156,8 +156,13 @@ void reset_all_stats(struct thread_data *td)
 
 void reset_fio_state(void)
 {
+       int i;
+
        groupid = 0;
        thread_number = 0;
+       cur_segment = 0;
+       for (i = 0; i < nr_segments; i++)
+               segments[i].nr_threads = 0;
        stat_number = 0;
        done_secs = 0;
 }
index c9b5c2856a80295d529d47e98cab93176149c09e..1b65297ec25feb166e3f39e6b01b7c081a96fa42 100644 (file)
--- a/server.c
+++ b/server.c
@@ -950,7 +950,7 @@ static int handle_update_job_cmd(struct fio_net_cmd *cmd)
                return 0;
        }
 
-       td = &segments[0].threads[tnumber - 1];
+       td = tnumber_to_td(tnumber);
        convert_thread_options_to_cpu(&td->o, &pdu->top);
        send_update_job_reply(cmd->tag, 0);
        return 0;