1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /* IPC Hub and Simple memory allocator */
27 
28 #include "config.h"
29 
30 #include <stdio.h>
31 #include <fcntl.h>
32 #include <sys/mman.h>
33 #include <sys/ipc.h>
34 #include <sys/sem.h>
35 #include <sys/errno.h>
36 #include <signal.h>
37 #include <pthread.h>
38 #include <sys/shm.h>
39 #include "filebench.h"
40 #include "fb_cvar.h"
41 
42 filebench_shm_t *filebench_shm = NULL;
43 char shmpath[128] = "/tmp/filebench-shm-XXXXXX";
44 
45 /*
46  * Interprocess Communication mechanisms. If multiple processes
47  * are used, filebench opens a shared file in memory mapped mode to hold
48  * a variety of global variables and data structures. If only using
49  * multiple threads, it just allocates a region of local memory. A
50  * region of interprocess shared memory and a set of shared semaphores
51  * are also created. Routines are provided to manage the creation,
52  * destruction, and allocation of these resoures.
53  */
54 
55 /*
56  * Locks a mutex and logs any errors.
57  */
58 int
ipc_mutex_lock(pthread_mutex_t * mutex)59 ipc_mutex_lock(pthread_mutex_t *mutex)
60 {
61 	int error;
62 
63 	error = pthread_mutex_lock(mutex);
64 
65 #ifdef HAVE_ROBUST_MUTEX
66 	if (error == EOWNERDEAD) {
67 		if (pthread_mutex_consistent_np(mutex) != 0) {
68 			filebench_log(LOG_FATAL, "mutex make consistent "
69 			    "failed: %s", strerror(error));
70 			return (-1);
71 		}
72 		return (0);
73 	}
74 #endif /* HAVE_ROBUST_MUTEX */
75 
76 	if (error != 0) {
77 		filebench_log(LOG_FATAL, "mutex lock failed: %s",
78 		    strerror(error));
79 	}
80 
81 	return (error);
82 }
83 
84 /*
85  * Unlocks a mutex and logs any errors.
86  */
87 int
ipc_mutex_unlock(pthread_mutex_t * mutex)88 ipc_mutex_unlock(pthread_mutex_t *mutex)
89 {
90 	int error;
91 
92 	error = pthread_mutex_unlock(mutex);
93 
94 #ifdef HAVE_ROBUST_MUTEX
95 	if (error == EOWNERDEAD) {
96 		if (pthread_mutex_consistent_np(mutex) != 0) {
97 			filebench_log(LOG_FATAL, "mutex make consistent "
98 			    "failed: %s", strerror(error));
99 			return (-1);
100 		}
101 		return (0);
102 	}
103 #endif /* HAVE_ROBUST_MUTEX */
104 
105 	if (error != 0) {
106 		filebench_log(LOG_FATAL, "mutex unlock failed: %s",
107 		    strerror(error));
108 	}
109 
110 	return (error);
111 }
112 
113 /*
114  * Initialize mutex attributes for the various flavors of mutexes
115  */
116 static void
ipc_mutexattr_init(int mtx_type)117 ipc_mutexattr_init(int mtx_type)
118 {
119 	pthread_mutexattr_t *mtx_attrp;
120 
121 	mtx_attrp = &(filebench_shm->shm_mutexattr[mtx_type]);
122 
123 	(void) pthread_mutexattr_init(mtx_attrp);
124 
125 #ifdef HAVE_PROCSCOPE_PTHREADS
126 	if (pthread_mutexattr_setpshared(mtx_attrp,
127 	    PTHREAD_PROCESS_SHARED) != 0) {
128 		filebench_log(LOG_ERROR, "cannot set mutex attr "
129 		    "PROCESS_SHARED on this platform");
130 //		filebench_shutdown(1);
131 	}
132 #ifdef HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL
133 	if (mtx_type & IPC_MUTEX_PRIORITY) {
134 		if (pthread_mutexattr_setprotocol(mtx_attrp,
135 		    PTHREAD_PRIO_INHERIT) != 0) {
136 			filebench_log(LOG_ERROR,
137 			    "cannot set mutex attr "
138 			    "PTHREAD_PRIO_INHERIT on this platform");
139 //			filebench_shutdown(1);
140 		}
141 	}
142 #endif /* HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL */
143 #endif /* HAVE_PROCSCOPE_PTHREADS */
144 #ifdef HAVE_ROBUST_MUTEX
145 	if (mtx_type & IPC_MUTEX_ROBUST) {
146 		if (pthread_mutexattr_setrobust_np(mtx_attrp,
147 		    PTHREAD_MUTEX_ROBUST_NP) != 0) {
148 			filebench_log(LOG_ERROR,
149 			    "cannot set mutex attr "
150 			    "PTHREAD_MUTEX_ROBUST_NP on this platform");
151 			filebench_shutdown(1);
152 		}
153 		if (pthread_mutexattr_settype(mtx_attrp,
154 		    PTHREAD_MUTEX_ERRORCHECK) != 0) {
155 			filebench_log(LOG_ERROR,
156 			    "cannot set mutex attr "
157 			    "PTHREAD_MUTEX_ERRORCHECK "
158 			    "on this platform");
159 			filebench_shutdown(1);
160 		}
161 	}
162 #endif /* HAVE_ROBUST_MUTEX */
163 }
164 
165 /*
166  * On first invocation, allocates a mutex attributes structure
167  * and initializes it with appropriate attributes. In all cases,
168  * returns a pointer to the structure.
169  */
170 pthread_mutexattr_t *
ipc_mutexattr(int mtx_type)171 ipc_mutexattr(int mtx_type)
172 {
173 	if ((mtx_type >= IPC_NUM_MUTEX_ATTRS) ||
174 	    (mtx_type < IPC_MUTEX_NORMAL)) {
175 		filebench_log(LOG_ERROR,
176 		    "ipc_mutexattr called with undefined attr selector %d",
177 		    mtx_type);
178 		return (&(filebench_shm->shm_mutexattr[IPC_MUTEX_NORMAL]));
179 	}
180 
181 	return (&(filebench_shm->shm_mutexattr[mtx_type]));
182 }
183 
184 static pthread_condattr_t *condattr = NULL;
185 
186 /*
187  * On first invocation, allocates a condition variable attributes
188  * structure and initializes it with appropriate attributes. In
189  * all cases, returns a pointer to the structure.
190  */
191 pthread_condattr_t *
ipc_condattr(void)192 ipc_condattr(void)
193 {
194 	if (condattr == NULL) {
195 		if ((condattr = malloc(sizeof (pthread_condattr_t))) == NULL) {
196 			filebench_log(LOG_ERROR, "cannot alloc cond attr");
197 			filebench_shutdown(1);
198 		}
199 		(void) pthread_condattr_init(condattr);
200 #ifdef HAVE_PROCSCOPE_PTHREADS
201 		if (pthread_condattr_setpshared(condattr,
202 		    PTHREAD_PROCESS_SHARED) != 0) {
203 			filebench_log(LOG_ERROR,
204 			    "cannot set cond attr PROCESS_SHARED");
205 //			filebench_shutdown(1);
206 		}
207 #endif /* HAVE_PROCSCOPE_PTHREADS */
208 	}
209 	return (condattr);
210 }
211 
212 static pthread_rwlockattr_t *rwlockattr = NULL;
213 
214 /*
215  * On first invocation, allocates a readers/writers attributes
216  * structure and initializes it with appropriate attributes.
217  * In all cases, returns a pointer to the structure.
218  */
219 static pthread_rwlockattr_t *
ipc_rwlockattr(void)220 ipc_rwlockattr(void)
221 {
222 	if (rwlockattr == NULL) {
223 		if ((rwlockattr =
224 		    malloc(sizeof (pthread_rwlockattr_t))) == NULL) {
225 			filebench_log(LOG_ERROR, "cannot alloc rwlock attr");
226 			filebench_shutdown(1);
227 		}
228 		(void) pthread_rwlockattr_init(rwlockattr);
229 #ifdef HAVE_PROCSCOPE_PTHREADS
230 		if (pthread_rwlockattr_setpshared(rwlockattr,
231 		    PTHREAD_PROCESS_SHARED) != 0) {
232 			filebench_log(LOG_ERROR,
233 			    "cannot set rwlock attr PROCESS_SHARED");
234 //			filebench_shutdown(1);
235 		}
236 #endif /* HAVE_PROCSCOPE_PTHREADS */
237 	}
238 	return (rwlockattr);
239 }
240 
241 /*
242  * Calls semget() to get a set of shared system V semaphores.
243  */
244 void
ipc_seminit(void)245 ipc_seminit(void)
246 {
247 	key_t key = filebench_shm->shm_semkey;
248 	int sys_semid;
249 
250 	/* Already done? */
251 	if (filebench_shm->shm_sys_semid >= 0)
252 		return;
253 
254 	if ((sys_semid = semget(key, FILEBENCH_NSEMS, IPC_CREAT |
255 	    S_IRUSR | S_IWUSR)) == -1) {
256 		filebench_log(LOG_ERROR,
257 		    "could not create sysv semaphore set "
258 		    "(need to increase sems?): %s",
259 		    strerror(errno));
260 		filebench_shutdown(1);
261 	}
262 
263 	filebench_shm->shm_sys_semid = sys_semid;
264 }
265 
266 /*
267  * Initialize the Interprocess Communication system and its associated shared
268  * memory structure. It first creates a temporary file using the mkstemp()
269  * function. It than sets the file large enough to hold the filebench_shm and an
270  * additional megabyte.  (Additional megabyte is required to make sure that all
271  * sizeof(filebench_shm) bytes plus page alignment bytes will fit in the file.)
272  * The file is then memory mapped. Once the shared memory region is created,
273  * ipc_init initializes various locks, pointers, and variables in the shared
274  * memory. It also uses ftok() to get a shared memory semaphore key for later
275  * use in allocating shared semaphores.
276  */
ipc_init(void)277 void ipc_init(void)
278 {
279 	int shmfd;
280 	char tmpbuf[MB];
281 	key_t key;
282 #ifdef HAVE_SEM_RMID
283 	int sys_semid;
284 #endif
285 
286 	shmfd = mkstemp(shmpath);
287 	if (shmfd  < 0) {
288 		filebench_log(LOG_FATAL, "Could not create shared memory "
289 			      "file %s: %s", shmpath, strerror(errno));
290 		exit(1);
291 	}
292 
293 	(void)lseek(shmfd, sizeof(filebench_shm_t), SEEK_SET);
294 	if (write(shmfd, tmpbuf, MB) != MB) {
295 		filebench_log(LOG_FATAL,
296 		    "Could not write to the shared memory "
297 		    "file: %s", strerror(errno));
298 		exit(1);
299 	}
300 
301 	if ((filebench_shm = (filebench_shm_t *)mmap(NULL,
302 	    sizeof(filebench_shm_t), PROT_READ | PROT_WRITE,
303 	    MAP_SHARED, shmfd, 0)) == MAP_FAILED) {
304 		filebench_log(LOG_FATAL, "Could not mmap the shared "
305 		"memory file: %s", strerror(errno));
306 		exit(1);
307 	}
308 
309 	(void) memset(filebench_shm, 0,
310 		 (char *)&filebench_shm->shm_marker - (char *)filebench_shm);
311 
312 	/*
313 	 * First, initialize all the structures needed for the filebench_log()
314 	 * function to work correctly with the log levels other than LOG_FATAL
315 	 */
316 	filebench_shm->shm_epoch = gethrtime();
317 	filebench_shm->shm_debug_level = LOG_INFO;
318 
319 	/* Setup mutexes for object lists */
320 	ipc_mutexattr_init(IPC_MUTEX_NORMAL);
321 	ipc_mutexattr_init(IPC_MUTEX_PRIORITY);
322 	ipc_mutexattr_init(IPC_MUTEX_ROBUST);
323 	ipc_mutexattr_init(IPC_MUTEX_PRI_ROB);
324 
325 	(void) pthread_mutex_init(&filebench_shm->shm_msg_lock,
326 	    ipc_mutexattr(IPC_MUTEX_NORMAL));
327 
328 	filebench_log(LOG_INFO, "Allocated %lldMB of shared memory",
329 			(sizeof(filebench_shm_t) + MB) / MB);
330 
331 	filebench_shm->shm_rmode = FILEBENCH_MODE_TIMEOUT;
332 	filebench_shm->shm_string_ptr = &filebench_shm->shm_strings[0];
333 	filebench_shm->shm_ptr = (char *)filebench_shm->shm_addr;
334 	filebench_shm->shm_path_ptr = &filebench_shm->shm_filesetpaths[0];
335 
336 	(void) pthread_mutex_init(&filebench_shm->shm_fileset_lock,
337 	    ipc_mutexattr(IPC_MUTEX_NORMAL));
338 	(void) pthread_mutex_init(&filebench_shm->shm_procflow_lock,
339 	    ipc_mutexattr(IPC_MUTEX_NORMAL));
340 	(void) pthread_mutex_init(&filebench_shm->shm_procs_running_lock,
341 	    ipc_mutexattr(IPC_MUTEX_NORMAL));
342 	(void) pthread_mutex_init(&filebench_shm->shm_threadflow_lock,
343 	    ipc_mutexattr(IPC_MUTEX_NORMAL));
344 	(void) pthread_mutex_init(&filebench_shm->shm_flowop_lock,
345 	    ipc_mutexattr(IPC_MUTEX_NORMAL));
346 	(void) pthread_mutex_init(&filebench_shm->shm_eventgen_lock,
347 	    ipc_mutexattr(IPC_MUTEX_PRI_ROB));
348 	(void) pthread_mutex_init(&filebench_shm->shm_malloc_lock,
349 	    ipc_mutexattr(IPC_MUTEX_NORMAL));
350 	(void) pthread_mutex_init(&filebench_shm->shm_ism_lock,
351 	    ipc_mutexattr(IPC_MUTEX_NORMAL));
352 	(void) ipc_mutex_lock(&filebench_shm->shm_ism_lock);
353 	(void) pthread_cond_init(&filebench_shm->shm_eventgen_cv,
354 	    ipc_condattr());
355 	(void) pthread_rwlock_init(&filebench_shm->shm_flowop_find_lock,
356 	    ipc_rwlockattr());
357 	(void) pthread_rwlock_init(&filebench_shm->shm_run_lock,
358 	    ipc_rwlockattr());
359 
360 	/* Create semaphore */
361 	if ((key = ftok(shmpath, 1)) < 0) {
362 		filebench_log(LOG_ERROR, "cannot create sem: %s",
363 		    strerror(errno));
364 		exit(1);
365 	}
366 
367 #ifdef HAVE_SEM_RMID
368 	if ((sys_semid = semget(key, 0, 0)) != -1)
369 		(void) semctl(sys_semid, 0, IPC_RMID);
370 #endif
371 
372 	filebench_shm->shm_semkey = key;
373 	filebench_shm->shm_sys_semid = -1;
374 	filebench_shm->shm_dump_fd = -1;
375 	filebench_shm->shm_eventgen_hz = 0;
376 	filebench_shm->shm_id = -1;
377 
378 	filebench_shm->shm_filesys_type = LOCAL_FS_PLUG;
379 }
380 
381 void
ipc_fini(void)382 ipc_fini(void)
383 {
384 #ifdef HAVE_SEM_RMID
385 	if (filebench_shm->shm_sys_semid != -1) {
386 		(void) semctl(filebench_shm->shm_sys_semid, 0, IPC_RMID);
387 		filebench_shm->shm_sys_semid = -1;
388 	}
389 #endif
390 
391 	(void) unlink(shmpath);
392 }
393 
394 /*
395  * Attach worker process to the shared memory. Used to open and mmap
396  * the shared memory region. If successful, it initializes the worker
397  * process' filebench_shm to point to the shared memory region and
398  * returns 0. Otherwise it returns -1.
399  */
400 int
ipc_attach(void * shmaddr,char * shmpath)401 ipc_attach(void *shmaddr, char *shmpath)
402 {
403 	int shmfd;
404 
405 	if ((shmfd = open(shmpath, O_RDWR)) < 0) {
406 		filebench_log(LOG_FATAL, "Could not open shared memory "
407 			      "file %s: %s", shmpath, strerror(errno));
408 		return (-1);
409 	}
410 
411 	if ((filebench_shm = (filebench_shm_t *)mmap(shmaddr,
412 	    sizeof (filebench_shm_t), PROT_READ | PROT_WRITE,
413 	    MAP_SHARED | MAP_FIXED, shmfd, 0)) == MAP_FAILED) {
414 		filebench_log(LOG_FATAL, "Could not mmap the shared "
415 		"memory file: %s", strerror(errno));
416 		return (-1);
417 	}
418 
419 	if (filebench_shm != shmaddr) {
420 		filebench_log(LOG_FATAL, "Could not mmap the shared "
421 		"memory file to the same location as master process: %s",
422 		 strerror(errno));
423 		return (-1);
424 	}
425 
426 	return (0);
427 }
428 
429 /*
430  * Returns the number of preallocated objects in the filebench_shm region
431  */
432 static int
preallocated_entries(int obj_type)433 preallocated_entries(int obj_type)
434 {
435 	int entries;
436 
437 	switch(obj_type) {
438 	case FILEBENCH_FILESET:
439 		entries = sizeof(filebench_shm->shm_fileset)
440 						/ sizeof(fileset_t);
441 		break;
442 	case FILEBENCH_FILESETENTRY:
443 		entries = sizeof(filebench_shm->shm_filesetentry)
444 						/ sizeof(filesetentry_t);
445 		break;
446 	case FILEBENCH_PROCFLOW:
447 		entries = sizeof(filebench_shm->shm_procflow)
448 						/ sizeof(procflow_t);
449 		break;
450 	case FILEBENCH_THREADFLOW:
451 		entries = sizeof(filebench_shm->shm_threadflow)
452 						/ sizeof(threadflow_t);
453 		break;
454 	case FILEBENCH_FLOWOP:
455 		entries = sizeof(filebench_shm->shm_flowop)
456 						/ sizeof(flowop_t);
457 		break;
458 	case FILEBENCH_VARIABLE:
459 		entries = sizeof(filebench_shm->shm_var)
460 						/ sizeof(var_t);
461 		break;
462 	case FILEBENCH_AVD:
463 		entries = sizeof(filebench_shm->shm_avd_ptrs)
464 						/ sizeof(avd_t);
465 		break;
466 	case FILEBENCH_RANDDIST:
467 		entries = sizeof(filebench_shm->shm_randdist)
468 						/ sizeof(randdist_t);
469 		break;
470 	case FILEBENCH_CVAR:
471 		entries = sizeof(filebench_shm->shm_cvar)
472 						/ sizeof(cvar_t);
473 		break;
474 	case FILEBENCH_CVAR_LIB_INFO:
475 		entries = sizeof(filebench_shm->shm_cvar_lib_info)
476 						/ sizeof(cvar_library_info_t);
477 		break;
478 	default:
479 		entries = -1;
480 		filebench_log(LOG_ERROR, "preallocated_entries: "
481 						"unknown object type");
482 		filebench_shutdown(1);
483 		break;
484 	}
485 
486 	return entries;
487 }
488 
489 /*
490  * Allocates filebench objects from pre allocated region of
491  * shareable memory. The memory region is partitioned into sets
492  * of objects during initialization. This routine scans for
493  * the first unallocated object of type "type" in the set of
494  * available objects, and makes it as allocated. The routine
495  * returns a pointer to the object, or NULL if all objects have
496  * been allocated.
497  */
498 void *
ipc_malloc(int obj_type)499 ipc_malloc(int obj_type)
500 {
501 	int start_idx;
502 	int max_idx;
503 	int i;
504 
505 	(void) ipc_mutex_lock(&filebench_shm->shm_malloc_lock);
506 
507 	start_idx = filebench_shm->shm_lastbitmapindex[obj_type];
508 	max_idx = preallocated_entries(obj_type) - 1;
509 
510 	i = start_idx;
511 	do {
512 		i++;
513 		if (i > max_idx)
514 			i = 0;
515 
516 		if (filebench_shm->shm_bitmap[obj_type][i] == 0)
517 			break;
518 	} while (i != start_idx);
519 
520 	if (i == start_idx) {
521 		filebench_log(LOG_ERROR, "Out of shared memory (%d)!", obj_type);
522 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
523 		return (NULL);
524 	}
525 
526 	filebench_shm->shm_bitmap[obj_type][i] = 1;
527 	filebench_shm->shm_lastbitmapindex[obj_type] = i;
528 
529 	switch (obj_type) {
530 	case FILEBENCH_FILESET:
531 		(void) memset((char *)&filebench_shm->shm_fileset[i], 0,
532 		    sizeof (fileset_t));
533 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
534 		return ((char *)&filebench_shm->shm_fileset[i]);
535 
536 	case FILEBENCH_FILESETENTRY:
537 		(void) memset((char *)&filebench_shm->shm_filesetentry[i], 0,
538 		    sizeof (filesetentry_t));
539 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
540 		return ((char *)&filebench_shm->shm_filesetentry[i]);
541 
542 	case FILEBENCH_PROCFLOW:
543 		(void) memset((char *)&filebench_shm->shm_procflow[i], 0,
544 		    sizeof (procflow_t));
545 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
546 		return ((char *)&filebench_shm->shm_procflow[i]);
547 
548 	case FILEBENCH_THREADFLOW:
549 		(void) memset((char *)&filebench_shm->shm_threadflow[i], 0,
550 		    sizeof (threadflow_t));
551 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
552 		return ((char *)&filebench_shm->shm_threadflow[i]);
553 
554 	case FILEBENCH_FLOWOP:
555 		(void) memset((char *)&filebench_shm->shm_flowop[i], 0,
556 		    sizeof (flowop_t));
557 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
558 		return ((char *)&filebench_shm->shm_flowop[i]);
559 
560 	case FILEBENCH_VARIABLE:
561 		(void) memset((char *)&filebench_shm->shm_var[i], 0,
562 		    sizeof (var_t));
563 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
564 		return ((char *)&filebench_shm->shm_var[i]);
565 
566 	case FILEBENCH_AVD:
567 		filebench_shm->shm_avd_ptrs[i].avd_type = AVD_INVALID;
568 		filebench_shm->shm_avd_ptrs[i].avd_val.varptr = NULL;
569 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
570 		return ((char *)&filebench_shm->shm_avd_ptrs[i]);
571 
572 	case FILEBENCH_RANDDIST:
573 		(void) memset((char *)&filebench_shm->shm_randdist[i], 0,
574 		    sizeof (randdist_t));
575 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
576 		return ((char *)&filebench_shm->shm_randdist[i]);
577 
578 	case FILEBENCH_CVAR:
579 		(void) memset((char *)&filebench_shm->shm_cvar[i], 0, sizeof(cvar_t));
580 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
581 		return ((char *)&filebench_shm->shm_cvar[i]);
582 
583 	case FILEBENCH_CVAR_LIB_INFO:
584 		(void) memset((char *)&filebench_shm->shm_cvar_lib_info[i], 0,
585 			sizeof(cvar_library_info_t));
586 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
587 		return ((char *)&filebench_shm->shm_cvar_lib_info[i]);
588 	}
589 
590 	filebench_log(LOG_ERROR, "Attempt to ipc_malloc unknown object type (%d)!",
591 	    obj_type);
592 	(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
593 	return (NULL);
594 }
595 
596 /*
597  * Frees a filebench object of type "type" at the location
598  * pointed to by "addr". It uses the type and address to
599  * calculate which object is being freed, and clears its
600  * allocation map entry.
601  */
602 void
ipc_free(int type,char * addr)603 ipc_free(int type, char *addr)
604 {
605 	int item;
606 	caddr_t base = 0;
607 	size_t offset;
608 	size_t size = 0;
609 
610 	if (addr == NULL) {
611 		filebench_log(LOG_ERROR, "Freeing type %d %zx", type, addr);
612 		return;
613 	}
614 
615 	switch (type) {
616 
617 	case FILEBENCH_FILESET:
618 		base = (caddr_t)&filebench_shm->shm_fileset[0];
619 		size = sizeof (fileset_t);
620 		break;
621 
622 	case FILEBENCH_FILESETENTRY:
623 		base = (caddr_t)&filebench_shm->shm_filesetentry[0];
624 		size = sizeof (filesetentry_t);
625 		break;
626 
627 	case FILEBENCH_PROCFLOW:
628 		base = (caddr_t)&filebench_shm->shm_procflow[0];
629 		size = sizeof (procflow_t);
630 		break;
631 
632 	case FILEBENCH_THREADFLOW:
633 		base = (caddr_t)&filebench_shm->shm_threadflow[0];
634 		size = sizeof (threadflow_t);
635 		break;
636 
637 	case FILEBENCH_FLOWOP:
638 		base = (caddr_t)&filebench_shm->shm_flowop[0];
639 		size = sizeof (flowop_t);
640 		break;
641 
642 	case FILEBENCH_VARIABLE:
643 		base = (caddr_t)&filebench_shm->shm_var[0];
644 		size = sizeof (var_t);
645 		break;
646 
647 	case FILEBENCH_AVD:
648 		base = (caddr_t)&filebench_shm->shm_avd_ptrs[0];
649 		size = sizeof (avd_t);
650 		break;
651 
652 	case FILEBENCH_RANDDIST:
653 		base = (caddr_t)&filebench_shm->shm_randdist[0];
654 		size = sizeof (randdist_t);
655 		break;
656 
657 	case FILEBENCH_CVAR:
658 		base = (caddr_t)&filebench_shm->shm_cvar[0];
659 		size = sizeof (cvar_t);
660 		break;
661 
662 	case FILEBENCH_CVAR_LIB_INFO:
663 		base = (caddr_t)&filebench_shm->shm_cvar_lib_info[0];
664 		size = sizeof(cvar_library_info_t);
665 		break;
666 	}
667 
668 	offset = ((size_t)addr - (size_t)base);
669 	item = offset / size;
670 
671 	(void) ipc_mutex_lock(&filebench_shm->shm_malloc_lock);
672 	filebench_shm->shm_bitmap[type][item] = 0;
673 	(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
674 }
675 
676 /*
677  * Allocate a string from filebench string memory. The length
678  * of the allocated string is the same as the length of the
679  * supplied string "string", and the contents of string are
680  * copied to the newly allocated string.
681  */
682 char *
ipc_stralloc(const char * string)683 ipc_stralloc(const char *string)
684 {
685 	char *allocstr = filebench_shm->shm_string_ptr;
686 
687 	filebench_shm->shm_string_ptr += strlen(string) + 1;
688 
689 	if ((filebench_shm->shm_string_ptr - &filebench_shm->shm_strings[0]) >
690 	    FILEBENCH_STRINGMEMORY) {
691 		filebench_log(LOG_ERROR, "Out of ipc string memory");
692 		return (NULL);
693 	}
694 
695 	(void) strncpy(allocstr, string, strlen(string));
696 
697 	return (allocstr);
698 }
699 
700 /*
701  * Allocate a path string from filebench path string memory.
702  * Specifically used for allocating fileset paths. The length
703  * of the allocated path string is the same as the length of
704  * the supplied path string "path", and the contents of path
705  * are copied to the newly allocated path string. Checks for
706  * out-of-path-string-memory condition and returns NULL if so.
707  * Otherwise it returns a pointer to the newly allocated path
708  * string.
709  */
710 char *
ipc_pathalloc(char * path)711 ipc_pathalloc(char *path)
712 {
713 	char *allocpath = filebench_shm->shm_path_ptr;
714 
715 	filebench_shm->shm_path_ptr += strlen(path) + 1;
716 
717 	if ((filebench_shm->shm_path_ptr -
718 	    &filebench_shm->shm_filesetpaths[0]) >
719 	    FILEBENCH_FILESETPATHMEMORY) {
720 		filebench_log(LOG_ERROR, "Out of fileset path memory");
721 		return (NULL);
722 	}
723 
724 	(void) strncpy(allocpath, path, strlen(path));
725 
726 	return (allocpath);
727 }
728 
729 /*
730  * This is a limited functionality deallocator for path
731  * strings - it can only free all path strings at once,
732  * in order to avoid fragmentation.
733  */
734 void
ipc_freepaths(void)735 ipc_freepaths(void)
736 {
737 	filebench_shm->shm_path_ptr = &filebench_shm->shm_filesetpaths[0];
738 }
739 
740 /*
741  * Limited functionality allocator for use by custom variables to allocate
742  * state.
743  */
744 void
ipc_cvar_heapalloc(size_t size)745 *ipc_cvar_heapalloc(size_t size)
746 {
747 	void *memory;
748 
749 	(void) ipc_mutex_lock(&filebench_shm->shm_malloc_lock);
750 
751 	if ((filebench_shm->shm_cvar_heapsize + size) <= FILEBENCH_CVAR_HEAPSIZE) {
752 		memory = filebench_shm->shm_cvar_heap +
753 				filebench_shm->shm_cvar_heapsize;
754 
755 		filebench_shm->shm_cvar_heapsize += size;
756 	} else
757 		memory = NULL;
758 
759 	(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
760 
761 	return memory;
762 }
763 
764 void
ipc_cvar_heapfree(void * ptr)765 ipc_cvar_heapfree(void *ptr)
766 {
767 	/* Since Filebench will shutdown when the allocation of a custom variable
768 	 * handle fails, there's no immediate need to implement free functionality
769 	 * here. */
770 	return;
771 }
772 
773 /*
774  * Allocates a semid from the table of semids for pre intialized
775  * semaphores. Searches for the first available semaphore, and
776  * sets the entry in the table to "1" to indicate allocation.
777  * Returns the allocated semid. Stops the run if all semaphores
778  * are already in use.
779  */
780 int
ipc_semidalloc(void)781 ipc_semidalloc(void)
782 {
783 	int semid;
784 
785 	for (semid = 0; filebench_shm->shm_semids[semid] == 1; semid++)
786 		;
787 	if (semid == FILEBENCH_NSEMS) {
788 		filebench_log(LOG_ERROR,
789 		    "Out of semaphores, increase system tunable limit");
790 		filebench_shutdown(1);
791 	}
792 	filebench_shm->shm_semids[semid] = 1;
793 	return (semid);
794 }
795 
796 /*
797  * Frees up the supplied semid by seting its position in the
798  * allocation table to "0".
799  */
800 void
ipc_semidfree(int semid)801 ipc_semidfree(int semid)
802 {
803 	filebench_shm->shm_semids[semid] = 0;
804 }
805 
806 /*
807  * Create a pool of shared memory to fit the per-thread
808  * allocations. Uses shmget() to create a shared memory region
809  * of size "size", attaches to it using shmat(), and stores
810  * the returned address of the region in filebench_shm->shm_addr.
811  * The pool is only created on the first call. The routine
812  * returns 0 if successful or the pool already exists,
813  * -1 otherwise.
814  */
815 int
ipc_ismcreate(size_t size)816 ipc_ismcreate(size_t size)
817 {
818 #ifdef HAVE_SHM_SHARE_MMU
819 	int flag = SHM_SHARE_MMU;
820 #else
821 	int flag = 0;
822 #endif /* HAVE_SHM_SHARE_MMU */
823 
824 	/* Already done? */
825 	if (filebench_shm->shm_id != -1)
826 		return (0);
827 
828 	filebench_log(LOG_VERBOSE,
829 	    "Creating %zd bytes of ISM Shared Memory...", size);
830 
831 	if ((filebench_shm->shm_id =
832 	    shmget(0, size, IPC_CREAT | 0666)) == -1) {
833 		filebench_log(LOG_ERROR,
834 		    "Failed to create %zd bytes of ISM shared memory (ret = %d)", size, errno);
835 		return (-1);
836 	}
837 
838 	if ((filebench_shm->shm_addr = (caddr_t)shmat(filebench_shm->shm_id,
839 	    0, flag)) == (void *)-1) {
840 		filebench_log(LOG_ERROR,
841 		    "Failed to attach %zd bytes of created ISM shared memory",
842 		    size);
843 		return (-1);
844 	}
845 
846 	filebench_shm->shm_ptr = (char *)filebench_shm->shm_addr;
847 
848 	filebench_log(LOG_VERBOSE,
849 	    "Allocated %zd bytes of ISM Shared Memory... at %zx",
850 	    size, filebench_shm->shm_addr);
851 
852 	/* Locked until allocated to block allocs */
853 	(void) ipc_mutex_unlock(&filebench_shm->shm_ism_lock);
854 
855 	return (0);
856 }
857 
858 /* Per addr space ism */
859 static int ism_attached = 0;
860 
861 /*
862  * Attach to interprocess shared memory. If already attached
863  * just return, otherwise use shmat() to attached to the region
864  * with ID of filebench_shm->shm_id. Returns -1 if shmat()
865  * fails, otherwise 0.
866  */
867 static int
ipc_ismattach(void)868 ipc_ismattach(void)
869 {
870 #ifdef HAVE_SHM_SHARE_MMU
871 	int flag = SHM_SHARE_MMU;
872 #else
873 	int flag = 0;
874 #endif /* HAVE_SHM_SHARE_MMU */
875 
876 
877 	if (ism_attached)
878 		return (0);
879 
880 	/* Does it exist? */
881 	if (filebench_shm->shm_id == 999)
882 		return (0);
883 
884 	if (shmat(filebench_shm->shm_id, filebench_shm->shm_addr,
885 	    flag) == NULL)
886 		return (-1);
887 
888 	ism_attached = 1;
889 
890 	return (0);
891 }
892 
893 /*
894  * Allocate from interprocess shared memory. Attaches to ism
895  * if necessary, then allocates "size" bytes, updates allocation
896  * information and returns a pointer to the allocated memory.
897  */
898 /*
899  * XXX No check is made for out-of-memory condition
900  */
901 char *
ipc_ismmalloc(size_t size)902 ipc_ismmalloc(size_t size)
903 {
904 	char *allocstr;
905 
906 	(void) ipc_mutex_lock(&filebench_shm->shm_ism_lock);
907 
908 	/* Map in shared memory */
909 	(void) ipc_ismattach();
910 
911 	allocstr = filebench_shm->shm_ptr;
912 
913 	filebench_shm->shm_ptr += size;
914 	filebench_shm->shm_allocated += size;
915 
916 	(void) ipc_mutex_unlock(&filebench_shm->shm_ism_lock);
917 
918 	return (allocstr);
919 }
920 
921 /*
922  * Deletes shared memory region and resets shared memory region
923  * information in filebench_shm.
924  */
925 void
ipc_ismdelete(void)926 ipc_ismdelete(void)
927 {
928 	if (filebench_shm->shm_id == -1)
929 		return;
930 
931 	filebench_log(LOG_VERBOSE, "Deleting ISM...");
932 
933 	(void) ipc_mutex_lock(&filebench_shm->shm_ism_lock);
934 #ifdef HAVE_SEM_RMID
935 	(void) shmctl(filebench_shm->shm_id, IPC_RMID, 0);
936 #endif
937 	filebench_shm->shm_ptr = (char *)filebench_shm->shm_addr;
938 	filebench_shm->shm_id = -1;
939 	filebench_shm->shm_allocated = 0;
940 	(void) ipc_mutex_unlock(&filebench_shm->shm_ism_lock);
941 }
942