Merge branch 'segmented-threads'
[fio.git] / init.c
diff --git a/init.c b/init.c
index 7f64ce21a36aec1bedf4d609f7cb63866923643a..f9c20bdb17993e6f5c1003ace756af91d914c47f 100644 (file)
--- a/init.c
+++ b/init.c
@@ -45,13 +45,12 @@ const char fio_version_string[] = FIO_VERSION;
 #define FIO_RANDSEED           (0xb1899bedUL)
 
 static char **ini_file;
-static int max_jobs = FIO_MAX_JOBS;
 static bool dump_cmdline;
 static bool parse_only;
 static bool merge_blktrace_only;
 
 static struct thread_data def_thread;
-struct thread_data *threads = NULL;
+struct thread_segment segments[REAL_MAX_SEG];
 static char **job_sections;
 static int nr_job_sections;
 
@@ -301,25 +300,34 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
 
 void free_threads_shm(void)
 {
-       if (threads) {
-               void *tp = 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;
 
-               threads = NULL;
-               shmdt(tp);
-               shmctl(shm_id, IPC_RMID, &sbuf);
-               shm_id = -1;
+                       seg->threads = NULL;
+                       shmdt(tp);
+                       shmctl(seg->shm_id, IPC_RMID, &sbuf);
+                       seg->shm_id = -1;
 #else
-               threads = NULL;
-               free(tp);
+                       seg->threads = NULL;
+                       free(tp);
 #endif
+               }
        }
+
+       nr_segments = 0;
+       cur_segment = 0;
 }
 
 static void free_shm(void)
 {
-       if (threads) {
+       if (nr_segments) {
                flow_exit();
                fio_debug_jobp = NULL;
                fio_warned = NULL;
@@ -337,71 +345,79 @@ 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[nr_segments];
+       size_t size = JOBS_PER_SEG * sizeof(struct thread_data);
        int i;
 
-       if (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) {
+               log_err("error: maximum number of jobs reached.\n");
+               return -1;
+       }
 
-               size += 2 * sizeof(unsigned int);
+       size += 2 * sizeof(unsigned int);
 
 #ifndef CONFIG_NO_SHM
-               shm_id = shmget(0, size, IPC_CREAT | 0600);
-               if (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
-               threads = malloc(size);
-               if (threads)
-                       break;
+       seg->threads = malloc(size);
+       if (!seg->threads)
+               return -1;
 #endif
 
-               max_jobs >>= 1;
-       } while (max_jobs);
-
 #ifndef CONFIG_NO_SHM
-       if (shm_id == -1)
-               return 1;
-
-       threads = shmat(shm_id, NULL, 0);
-       if (threads == (void *) -1) {
+       seg->threads = shmat(seg->shm_id, NULL, 0);
+       if (seg->threads == (void *) -1) {
                perror("shmat");
                return 1;
        }
        if (shm_attach_to_open_removed())
-               shmctl(shm_id, IPC_RMID, NULL);
+               shmctl(seg->shm_id, IPC_RMID, NULL);
 #endif
 
-       memset(threads, 0, max_jobs * sizeof(struct thread_data));
-       for (i = 0; i < max_jobs; i++)
-               DRD_IGNORE_VAR(threads[i]);
-       fio_debug_jobp = (unsigned int *)(threads + max_jobs);
+       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]);
+       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;
@@ -470,21 +486,19 @@ 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;
        }
-       if (thread_number >= max_jobs) {
-               log_err("error: maximum number of jobs (%d) reached.\n",
-                               max_jobs);
-               return NULL;
-       }
 
-       td = &threads[thread_number++];
+       seg = &segments[cur_segment];
+       td = &seg->threads[seg->nr_threads++];
+       thread_number++;
        *td = *parent;
 
        INIT_FLIST_HEAD(&td->opt_list);
@@ -534,7 +548,8 @@ static void put_job(struct thread_data *td)
        if (td->o.name)
                free(td->o.name);
 
-       memset(&threads[td->thread_number - 1], 0, sizeof(*td));
+       memset(td, 0, sizeof(*td));
+       segments[cur_segment].nr_threads--;
        thread_number--;
 }
 
@@ -2722,12 +2737,7 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        warnings_fatal = 1;
                        break;
                case 'j':
-                       max_jobs = atoi(optarg);
-                       if (!max_jobs || max_jobs > REAL_MAX_JOBS) {
-                               log_err("fio: invalid max jobs: %d\n", max_jobs);
-                               do_exit++;
-                               exit_val = 1;
-                       }
+                       /* we don't track/need this anymore, ignore it */
                        break;
                case 'S':
                        did_arg = true;