Wrap thread_data in thread_segment

No functional changes in this patch, just in preparation for having
multiple shm segments for the thread_data structures. They are getting
very large, and since it's hard to know upfront how many we need, let's
instead refactor the code a bit to allow us to add chunks of them as
needed when parsing the job file (or command line).

Signed-off-by: Jens Axboe <axboe@kernel.dk>
diff --git a/backend.c b/backend.c
index f91f3ca..d2b97db 100644
--- a/backend.c
+++ b/backend.c
@@ -63,7 +63,6 @@
 int groupid = 0;
 unsigned int thread_number = 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 +75,7 @@
 
 static void sig_int(int sig)
 {
-	if (threads) {
+	if (segments[0].threads) {
 		if (is_backend)
 			fio_server_got_signal(sig);
 		else {
diff --git a/fio.h b/fio.h
index 9d189eb..691976a 100644
--- a/fio.h
+++ b/fio.h
@@ -467,6 +467,11 @@
 
 };
 
+struct thread_segment {
+	struct thread_data *threads;
+	int shm_id;
+};
+
 /*
  * when should interactive ETA output be generated
  */
@@ -510,10 +515,13 @@
 #define __fio_stringify_1(x)	#x
 #define __fio_stringify(x)	__fio_stringify_1(x)
 
+#define REAL_MAX_JOBS		4096
+#define JOBS_PER_SEG		8
+#define REAL_MAX_SEG		(REAL_MAX_JOBS / JOBS_PER_SEG)
+
 extern bool exitall_on_terminate;
 extern unsigned int thread_number;
 extern unsigned int stat_number;
-extern int shm_id;
 extern int groupid;
 extern int output_format;
 extern int append_terse_output;
@@ -542,7 +550,7 @@
 extern long long trigger_timeout;
 extern char *aux_path;
 
-extern struct thread_data *threads;
+extern struct thread_segment segments[REAL_MAX_SEG];
 
 static inline bool is_running_backend(void)
 {
@@ -557,8 +565,6 @@
 	       !(io_u->ddir == DDIR_TRIM && !td_trim(td)));
 }
 
-#define REAL_MAX_JOBS		4096
-
 static inline bool should_fsync(struct thread_data *td)
 {
 	if (td->last_was_sync)
@@ -709,7 +715,7 @@
  * Iterates all threads/processes within all the defined jobs
  */
 #define for_each_td(td, i)	\
-	for ((i) = 0, (td) = &threads[0]; (i) < (int) thread_number; (i)++, (td)++)
+	for ((i) = 0, (td) = &segments[0].threads[0]; (i) < (int) thread_number; (i)++, (td)++)
 #define for_each_file(td, f, i)	\
 	if ((td)->files_index)						\
 		for ((i) = 0, (f) = (td)->files[0];			\
diff --git a/gettime-thread.c b/gettime-thread.c
index 953e4e6..9b82e53 100644
--- a/gettime-thread.c
+++ b/gettime-thread.c
@@ -58,7 +58,7 @@
 	 * 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 (threads) {
+	while (segments[0].threads) {
 		fio_gtod_update();
 		nop;
 	}
diff --git a/init.c b/init.c
index 7f64ce2..36b10ea 100644
--- a/init.c
+++ b/init.c
@@ -51,7 +51,7 @@
 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,17 +301,17 @@
 
 void free_threads_shm(void)
 {
-	if (threads) {
-		void *tp = threads;
+	if (segments[0].threads) {
+		void *tp = segments[0].threads;
 #ifndef CONFIG_NO_SHM
 		struct shmid_ds sbuf;
 
-		threads = NULL;
+		segments[0].threads = NULL;
 		shmdt(tp);
-		shmctl(shm_id, IPC_RMID, &sbuf);
-		shm_id = -1;
+		shmctl(segments[0].shm_id, IPC_RMID, &sbuf);
+		segments[0].shm_id = -1;
 #else
-		threads = NULL;
+		segments[0].threads = NULL;
 		free(tp);
 #endif
 	}
@@ -319,7 +319,7 @@
 
 static void free_shm(void)
 {
-	if (threads) {
+	if (segments[0].threads) {
 		flow_exit();
 		fio_debug_jobp = NULL;
 		fio_warned = NULL;
@@ -345,9 +345,10 @@
  */
 static int setup_thread_area(void)
 {
+	struct thread_segment *seg = &segments[0];
 	int i;
 
-	if (threads)
+	if (seg->threads)
 		return 0;
 
 	/*
@@ -360,16 +361,16 @@
 		size += 2 * sizeof(unsigned int);
 
 #ifndef CONFIG_NO_SHM
-		shm_id = shmget(0, size, IPC_CREAT | 0600);
-		if (shm_id != -1)
+		seg->shm_id = shmget(0, size, IPC_CREAT | 0600);
+		if (seg->shm_id != -1)
 			break;
 		if (errno != EINVAL && errno != ENOMEM && errno != ENOSPC) {
 			perror("shmget");
 			break;
 		}
 #else
-		threads = malloc(size);
-		if (threads)
+		seg->threads = malloc(size);
+		if (seg->threads)
 			break;
 #endif
 
@@ -377,22 +378,22 @@
 	} while (max_jobs);
 
 #ifndef CONFIG_NO_SHM
-	if (shm_id == -1)
+	if (seg->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));
+	memset(seg->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);
+		DRD_IGNORE_VAR(seg->threads[i]);
+	fio_debug_jobp = (unsigned int *)(seg->threads + max_jobs);
 	*fio_debug_jobp = -1;
 	fio_warned = fio_debug_jobp + 1;
 	*fio_warned = 0;
@@ -484,7 +485,7 @@
 		return NULL;
 	}
 
-	td = &threads[thread_number++];
+	td = &segments[0].threads[thread_number++];
 	*td = *parent;
 
 	INIT_FLIST_HEAD(&td->opt_list);
@@ -534,7 +535,7 @@
 	if (td->o.name)
 		free(td->o.name);
 
-	memset(&threads[td->thread_number - 1], 0, sizeof(*td));
+	memset(td, 0, sizeof(*td));
 	thread_number--;
 }
 
diff --git a/server.c b/server.c
index 248a2d4..c9b5c28 100644
--- a/server.c
+++ b/server.c
@@ -950,7 +950,7 @@
 		return 0;
 	}
 
-	td = &threads[tnumber - 1];
+	td = &segments[0].threads[tnumber - 1];
 	convert_thread_options_to_cpu(&td->o, &pdu->top);
 	send_update_job_reply(cmd->tag, 0);
 	return 0;