xref: /onnv-gate/usr/src/cmd/filebench/common/ipc.c (revision 6305:ce48ff893c37)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
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 
41 /* IPC Hub and Simple memory allocator */
42 
43 static int shmfd;
44 filebench_shm_t *filebench_shm = NULL;
45 static pthread_mutexattr_t *mutexattr = NULL;
46 
47 /*
48  * Interprocess Communication mechanisms. If multiple processes
49  * are used, filebench opens a shared file in memory mapped mode to hold
50  * a variety of global variables and data structures. If only using
51  * multiple threads, it just allocates a region of local memory. A
52  * region of interprocess shared memory and a set of shared semaphores
53  * are also created. Routines are provided to manage the creation,
54  * destruction, and allocation of these resoures.
55  */
56 
57 
58 /*
59  * Locks a mutex and logs any errors.
60  */
61 int
62 ipc_mutex_lock(pthread_mutex_t *mutex)
63 {
64 	int error;
65 
66 	error = pthread_mutex_lock(mutex);
67 
68 #ifdef HAVE_ROBUST_MUTEX
69 	if (error == EOWNERDEAD) {
70 		if (pthread_mutex_consistent_np(mutex) != 0) {
71 			filebench_log(LOG_FATAL, "mutex make consistent "
72 			    "failed: %s", strerror(error));
73 			return (-1);
74 		}
75 		return (0);
76 	}
77 #endif /* HAVE_ROBUST_MUTEX */
78 
79 	if (error != 0) {
80 		filebench_log(LOG_FATAL, "mutex lock failed: %s",
81 		    strerror(error));
82 	}
83 
84 	return (error);
85 }
86 
87 /*
88  * Unlocks a mutex and logs any errors.
89  */
90 int
91 ipc_mutex_unlock(pthread_mutex_t *mutex)
92 {
93 	int error;
94 
95 	error = pthread_mutex_unlock(mutex);
96 
97 #ifdef HAVE_ROBUST_MUTEX
98 	if (error == EOWNERDEAD) {
99 		if (pthread_mutex_consistent_np(mutex) != 0) {
100 			filebench_log(LOG_FATAL, "mutex make consistent "
101 			    "failed: %s", strerror(error));
102 			return (-1);
103 		}
104 		return (0);
105 	}
106 #endif /* HAVE_ROBUST_MUTEX */
107 
108 	if (error != 0) {
109 		filebench_log(LOG_FATAL, "mutex unlock failed: %s",
110 		    strerror(error));
111 	}
112 
113 	return (error);
114 }
115 
116 /*
117  * On first invocation, allocates a mutex attributes structure
118  * and initializes it with appropriate attributes. In all cases,
119  * returns a pointer to the structure.
120  */
121 pthread_mutexattr_t *
122 ipc_mutexattr(void)
123 {
124 #ifdef USE_PROCESS_MODEL
125 	if (mutexattr == NULL) {
126 		if ((mutexattr =
127 		    malloc(sizeof (pthread_mutexattr_t))) == NULL) {
128 			filebench_log(LOG_ERROR, "cannot alloc mutex attr");
129 			filebench_shutdown(1);
130 		}
131 #ifdef HAVE_PROCSCOPE_PTHREADS
132 		(void) pthread_mutexattr_init(mutexattr);
133 		if (pthread_mutexattr_setpshared(mutexattr,
134 		    PTHREAD_PROCESS_SHARED) != 0) {
135 			filebench_log(LOG_ERROR, "cannot set mutex attr "
136 			    "PROCESS_SHARED on this platform");
137 			filebench_shutdown(1);
138 		}
139 #ifdef HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL
140 		if (pthread_mutexattr_setprotocol(mutexattr,
141 		    PTHREAD_PRIO_INHERIT) != 0) {
142 			filebench_log(LOG_ERROR, "cannot set mutex attr "
143 			    "PTHREAD_PRIO_INHERIT on this platform");
144 			filebench_shutdown(1);
145 		}
146 #endif /* HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL */
147 #endif /* HAVE_PROCSCOPE_PTHREADS */
148 #ifdef HAVE_ROBUST_MUTEX
149 		if (pthread_mutexattr_setrobust_np(mutexattr,
150 		    PTHREAD_MUTEX_ROBUST_NP) != 0) {
151 			filebench_log(LOG_ERROR, "cannot set mutex attr "
152 			    "PTHREAD_MUTEX_ROBUST_NP on this platform");
153 			filebench_shutdown(1);
154 		}
155 		if (pthread_mutexattr_settype(mutexattr,
156 		    PTHREAD_MUTEX_ERRORCHECK) != 0) {
157 			filebench_log(LOG_ERROR, "cannot set mutex attr "
158 			    "PTHREAD_MUTEX_ERRORCHECK on this platform");
159 			filebench_shutdown(1);
160 		}
161 #endif /* HAVE_ROBUST_MUTEX */
162 
163 	}
164 #endif /* USE_PROCESS_MODEL */
165 	return (mutexattr);
166 }
167 
168 static pthread_condattr_t *condattr = NULL;
169 
170 /*
171  * On first invocation, allocates a condition variable attributes
172  * structure and initializes it with appropriate attributes. In
173  * all cases, returns a pointer to the structure.
174  */
175 pthread_condattr_t *
176 ipc_condattr(void)
177 {
178 #ifdef USE_PROCESS_MODEL
179 	if (condattr == NULL) {
180 		if ((condattr = malloc(sizeof (pthread_condattr_t))) == NULL) {
181 			filebench_log(LOG_ERROR, "cannot alloc cond attr");
182 			filebench_shutdown(1);
183 		}
184 #ifdef HAVE_PROCSCOPE_PTHREADS
185 		(void) pthread_condattr_init(condattr);
186 		if (pthread_condattr_setpshared(condattr,
187 		    PTHREAD_PROCESS_SHARED) != 0) {
188 			filebench_log(LOG_ERROR,
189 			    "cannot set cond attr PROCESS_SHARED");
190 			filebench_shutdown(1);
191 		}
192 #endif /* HAVE_PROCSCOPE_PTHREADS */
193 	}
194 #endif /* USE_PROCESS_MODEL */
195 	return (condattr);
196 }
197 
198 static pthread_rwlockattr_t *rwlockattr = NULL;
199 
200 /*
201  * On first invocation, allocates a readers/writers attributes
202  * structure and initializes it with appropriate attributes.
203  * In all cases, returns a pointer to the structure.
204  */
205 static pthread_rwlockattr_t *
206 ipc_rwlockattr(void)
207 {
208 #ifdef USE_PROCESS_MODEL
209 	if (rwlockattr == NULL) {
210 		if ((rwlockattr =
211 		    malloc(sizeof (pthread_rwlockattr_t))) == NULL) {
212 			filebench_log(LOG_ERROR, "cannot alloc rwlock attr");
213 			filebench_shutdown(1);
214 		}
215 #ifdef HAVE_PROCSCOPE_PTHREADS
216 		(void) pthread_rwlockattr_init(rwlockattr);
217 		if (pthread_rwlockattr_setpshared(rwlockattr,
218 		    PTHREAD_PROCESS_SHARED) != 0) {
219 			filebench_log(LOG_ERROR,
220 			    "cannot set rwlock attr PROCESS_SHARED");
221 			filebench_shutdown(1);
222 		}
223 #endif /* HAVE_PROCSCOPE_PTHREADS */
224 	}
225 #endif /* USE_PROCESS_MODEL */
226 	return (rwlockattr);
227 }
228 
229 char *shmpath = NULL;
230 
231 /*
232  * Calls semget() to get a set of shared system V semaphores.
233  */
234 void
235 ipc_seminit(void)
236 {
237 	key_t key = filebench_shm->semkey;
238 
239 	/* Already done? */
240 	if (filebench_shm->seminit)
241 		return;
242 
243 	if ((semget(key, FILEBENCH_NSEMS, IPC_CREAT |
244 	    S_IRUSR | S_IWUSR)) == -1) {
245 		filebench_log(LOG_ERROR,
246 		    "could not create sysv semaphore set "
247 		    "(need to increase sems?): %s",
248 		    strerror(errno));
249 		exit(1);
250 	}
251 }
252 
253 /*
254  * Initialize the Interprocess Communication system and its
255  * associated shared memory structure. It first creates a
256  * temporary file using either the mkstemp() function or the
257  * tempnam() and open() functions. If the process model is in
258  * use,it than sets the file large enough to hold the
259  * filebench_shm and an additional Megabyte. The file is then
260  * memory mapped. If the process model is not in use, it simply
261  * mallocs a region of sizeof (filebench_shm_t).
262  *
263  * Once the shared memory region / file is created, ipc_init
264  * initializes various locks pointers, and variables in the
265  * shared memory. It also uses ftok() to get a shared memory
266  * semaphore key for later use in allocating shared semaphores.
267  */
268 void
269 ipc_init(void)
270 {
271 	filebench_shm_t *buf = malloc(MB);
272 	key_t key;
273 	caddr_t c1;
274 	caddr_t c2;
275 #ifdef HAVE_SEM_RMID
276 	int semid;
277 #endif
278 
279 #ifdef HAVE_MKSTEMP
280 	shmpath = (char *)malloc(128);
281 	(void) strcpy(shmpath, "/var/tmp/fbenchXXXXXX");
282 	shmfd = mkstemp(shmpath);
283 #else
284 	shmfd   = open(shmpath, O_CREAT | O_RDWR | O_TRUNC, 0666);
285 	shmpath = tempnam("/var/tmp", "fbench");
286 #endif	/* HAVE_MKSTEMP */
287 
288 #ifdef USE_PROCESS_MODEL
289 
290 	if (shmfd  < 0) {
291 		filebench_log(LOG_FATAL, "Cannot open shm %s: %s",
292 		    shmpath,
293 		    strerror(errno));
294 		exit(1);
295 	}
296 
297 	(void) lseek(shmfd, sizeof (filebench_shm_t), SEEK_SET);
298 	if (write(shmfd, buf, MB) != MB) {
299 		filebench_log(LOG_FATAL,
300 		    "Cannot allocate shm: %s", strerror(errno));
301 		exit(1);
302 	}
303 
304 	/* LINTED E_BAD_PTR_CAST_ALIGN */
305 	if ((filebench_shm = (filebench_shm_t *)mmap((caddr_t)0,
306 	    sizeof (filebench_shm_t), PROT_READ | PROT_WRITE,
307 	    MAP_SHARED, shmfd, 0)) == NULL) {
308 		filebench_log(LOG_FATAL, "Cannot mmap shm");
309 		exit(1);
310 	}
311 
312 #else
313 	if ((filebench_shm =
314 	    (filebench_shm_t *)malloc(sizeof (filebench_shm_t))) == NULL) {
315 		filebench_log(LOG_FATAL, "Cannot malloc shm");
316 		exit(1);
317 	}
318 #endif /* USE_PROCESS_MODEL */
319 
320 	c1 = (caddr_t)filebench_shm;
321 	c2 = (caddr_t)&filebench_shm->shm_marker;
322 
323 	(void) memset(filebench_shm, 0, c2 - c1);
324 	filebench_shm->epoch = gethrtime();
325 	filebench_shm->debug_level = LOG_VERBOSE;
326 	filebench_shm->shm_rmode = FILEBENCH_MODE_TIMEOUT;
327 	filebench_shm->shm_string_ptr = &filebench_shm->shm_strings[0];
328 	filebench_shm->shm_ptr = (char *)filebench_shm->shm_addr;
329 	filebench_shm->shm_path_ptr = &filebench_shm->shm_filesetpaths[0];
330 
331 	/* Setup mutexes for object lists */
332 	(void) pthread_mutex_init(&filebench_shm->fileset_lock,
333 	    ipc_mutexattr());
334 	(void) pthread_mutex_init(&filebench_shm->procflow_lock,
335 	    ipc_mutexattr());
336 	(void) pthread_mutex_init(&filebench_shm->threadflow_lock,
337 	    ipc_mutexattr());
338 	(void) pthread_mutex_init(&filebench_shm->flowop_lock, ipc_mutexattr());
339 	(void) pthread_mutex_init(&filebench_shm->msg_lock, ipc_mutexattr());
340 	(void) pthread_mutex_init(&filebench_shm->eventgen_lock,
341 	    ipc_mutexattr());
342 	(void) pthread_mutex_init(&filebench_shm->shm_malloc_lock,
343 	    ipc_mutexattr());
344 	(void) pthread_mutex_init(&filebench_shm->shm_ism_lock,
345 	    ipc_mutexattr());
346 	(void) pthread_cond_init(&filebench_shm->eventgen_cv, ipc_condattr());
347 	(void) pthread_rwlock_init(&filebench_shm->flowop_find_lock,
348 	    ipc_rwlockattr());
349 	(void) pthread_rwlock_init(&filebench_shm->run_lock, ipc_rwlockattr());
350 	(void) pthread_rwlock_rdlock(&filebench_shm->run_lock);
351 
352 	(void) ipc_mutex_lock(&filebench_shm->shm_ism_lock);
353 
354 	/* Create semaphore */
355 	if ((key = ftok(shmpath, 1)) < 0) {
356 		filebench_log(LOG_ERROR, "cannot create sem: %s",
357 		    strerror(errno));
358 		exit(1);
359 	}
360 
361 #ifdef HAVE_SEM_RMID
362 	if ((semid = semget(key, 0, 0)) != -1)
363 		(void) semctl(semid, 0, IPC_RMID);
364 #endif
365 
366 	filebench_shm->semkey = key;
367 	filebench_shm->log_fd = -1;
368 	filebench_shm->dump_fd = -1;
369 	filebench_shm->eventgen_hz = 0;
370 	filebench_shm->shm_id = -1;
371 
372 	free(buf);
373 }
374 
375 /*
376  * If compiled to use process model, just unlinks the shmpath.
377  * Otherwise a no-op.
378  */
379 void
380 ipc_cleanup(void)
381 {
382 #ifdef USE_PROCESS_MODEL
383 	(void) unlink(shmpath);
384 #endif /* USE_PROCESS_MODEL */
385 }
386 
387 /*
388  * Attach to shared memory. Used by worker processes to open
389  * and mmap the shared memory region. If successful, it
390  * initializes the worker process' filebench_shm to point to
391  * the region and returns 0. Otherwise it returns -1.
392  */
393 int
394 ipc_attach(caddr_t shmaddr)
395 {
396 	if ((shmfd = open(shmpath, O_RDWR, 0666)) < 0) {
397 		filebench_log(LOG_ERROR, "Cannot open shm");
398 		return (-1);
399 	}
400 
401 	/* LINTED E_BAD_PTR_CAST_ALIGN */
402 	if ((filebench_shm = (filebench_shm_t *)mmap(shmaddr,
403 	    sizeof (filebench_shm_t), PROT_READ | PROT_WRITE,
404 	    MAP_SHARED | MAP_FIXED, shmfd, 0)) == NULL) {
405 		filebench_log(LOG_ERROR, "Cannot mmap shm");
406 		return (-1);
407 	}
408 
409 	filebench_log(LOG_DEBUG_IMPL, "addr = %zx", filebench_shm);
410 
411 	return (0);
412 }
413 
414 static int filebench_sizes[] = {
415 	FILEBENCH_NPROCFLOWS,		/* number of procflows */
416 	FILEBENCH_NTHREADFLOWS,		/* number of threadflows */
417 	FILEBENCH_NFLOWOPS,		/* number of flowops */
418 	(FILEBENCH_NVARS * 2),		/* number of attribute value dscrs */
419 	FILEBENCH_NVARS,		/* number of variables */
420 	FILEBENCH_NFILESETS,		/* number of filesets */
421 	FILEBENCH_NFILESETENTRIES,	/* number of fileset entries */
422 	FILEBENCH_NRANDDISTS};		/* number of random distributions */
423 
424 /*
425  * Allocates filebench objects from pre allocated region of
426  * shareable memory. The memory region is partitioned into sets
427  * of objects during initialization. This routine scans for
428  * the first unallocated object of type "type" in the set of
429  * available objects, and makes it as allocated. The routine
430  * returns a pointer to the object, or NULL if all objects have
431  * been allocated.
432  */
433 void *
434 ipc_malloc(int type)
435 {
436 	int i;
437 	int max = filebench_sizes[type];
438 	int start_idx = filebench_shm->shm_lastbitmapindex[type];
439 
440 	(void) ipc_mutex_lock(&filebench_shm->shm_malloc_lock);
441 
442 	i = start_idx;
443 	do {
444 		i++;
445 		if (i >= max)
446 			i = 0;
447 
448 		if (filebench_shm->shm_bitmap[type][i] == 0)
449 			break;
450 
451 	} while (i != start_idx);
452 
453 	if (i == start_idx) {
454 		filebench_log(LOG_ERROR, "Out of shared memory (%d)!", type);
455 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
456 		return (NULL);
457 	}
458 
459 	filebench_shm->shm_bitmap[type][i] = 1;
460 	filebench_shm->shm_lastbitmapindex[type] = i;
461 
462 	switch (type) {
463 	case FILEBENCH_FILESET:
464 		(void) memset((char *)&filebench_shm->shm_fileset[i], 0,
465 		    sizeof (fileset_t));
466 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
467 		return ((char *)&filebench_shm->shm_fileset[i]);
468 
469 	case FILEBENCH_FILESETENTRY:
470 		(void) memset((char *)&filebench_shm->shm_filesetentry[i], 0,
471 		    sizeof (filesetentry_t));
472 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
473 		return ((char *)&filebench_shm->shm_filesetentry[i]);
474 
475 	case FILEBENCH_PROCFLOW:
476 		(void) memset((char *)&filebench_shm->shm_procflow[i], 0,
477 		    sizeof (procflow_t));
478 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
479 		return ((char *)&filebench_shm->shm_procflow[i]);
480 
481 	case FILEBENCH_THREADFLOW:
482 		(void) memset((char *)&filebench_shm->shm_threadflow[i], 0,
483 		    sizeof (threadflow_t));
484 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
485 		return ((char *)&filebench_shm->shm_threadflow[i]);
486 
487 	case FILEBENCH_FLOWOP:
488 		(void) memset((char *)&filebench_shm->shm_flowop[i], 0,
489 		    sizeof (flowop_t));
490 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
491 		return ((char *)&filebench_shm->shm_flowop[i]);
492 
493 	case FILEBENCH_AVD:
494 		filebench_shm->shm_avd_ptrs[i].avd_type = AVD_INVALID;
495 		filebench_shm->shm_avd_ptrs[i].avd_val.varptr = NULL;
496 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
497 		return ((char *)&filebench_shm->shm_avd_ptrs[i]);
498 
499 	case FILEBENCH_VARIABLE:
500 		(void) memset((char *)&filebench_shm->shm_var[i], 0,
501 		    sizeof (var_t));
502 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
503 		return ((char *)&filebench_shm->shm_var[i]);
504 
505 	case FILEBENCH_RANDDIST:
506 		(void) memset((char *)&filebench_shm->shm_randdist[i], 0,
507 		    sizeof (randdist_t));
508 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
509 		return ((char *)&filebench_shm->shm_randdist[i]);
510 	}
511 
512 	filebench_log(LOG_ERROR, "Attempt to ipc_malloc unknown type (%d)!",
513 	    type);
514 	return (NULL);
515 }
516 
517 /*
518  * Frees a filebench object of type "type" at the location
519  * pointed to by "addr". It uses the type and address to
520  * calculate which object is being freed, and clears its
521  * allocation map entry.
522  */
523 void
524 ipc_free(int type, char *addr)
525 {
526 	int item;
527 	caddr_t base;
528 	size_t offset;
529 	size_t size;
530 
531 	if (addr == NULL) {
532 		filebench_log(LOG_ERROR, "Freeing type %d %zx", type, addr);
533 		return;
534 	}
535 
536 	switch (type) {
537 
538 	case FILEBENCH_FILESET:
539 		base = (caddr_t)&filebench_shm->shm_fileset[0];
540 		size = sizeof (fileset_t);
541 		break;
542 
543 	case FILEBENCH_FILESETENTRY:
544 		base = (caddr_t)&filebench_shm->shm_filesetentry[0];
545 		size = sizeof (filesetentry_t);
546 		break;
547 
548 	case FILEBENCH_PROCFLOW:
549 		base = (caddr_t)&filebench_shm->shm_procflow[0];
550 		size = sizeof (procflow_t);
551 		break;
552 
553 	case FILEBENCH_THREADFLOW:
554 		base = (caddr_t)&filebench_shm->shm_threadflow[0];
555 		size = sizeof (threadflow_t);
556 		break;
557 
558 	case FILEBENCH_FLOWOP:
559 		base = (caddr_t)&filebench_shm->shm_flowop[0];
560 		size = sizeof (flowop_t);
561 		break;
562 
563 	case FILEBENCH_AVD:
564 		base = (caddr_t)&filebench_shm->shm_avd_ptrs[0];
565 		size = sizeof (avd_t);
566 		break;
567 
568 	case FILEBENCH_VARIABLE:
569 		base = (caddr_t)&filebench_shm->shm_var[0];
570 		size = sizeof (var_t);
571 		break;
572 
573 	case FILEBENCH_RANDDIST:
574 		base = (caddr_t)&filebench_shm->shm_randdist[0];
575 		size = sizeof (randdist_t);
576 		break;
577 	}
578 
579 	offset = ((size_t)addr - (size_t)base);
580 	item = offset / size;
581 
582 	(void) ipc_mutex_lock(&filebench_shm->shm_malloc_lock);
583 	filebench_shm->shm_bitmap[type][item] = 0;
584 	(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
585 }
586 
587 /*
588  * Allocate a string from filebench string memory. The length
589  * of the allocated string is the same as the length of the
590  * supplied string "string", and the contents of string are
591  * copied to the newly allocated string.
592  */
593 char *
594 ipc_stralloc(char *string)
595 {
596 	char *allocstr = filebench_shm->shm_string_ptr;
597 
598 	filebench_shm->shm_string_ptr += strlen(string) + 1;
599 
600 	if ((filebench_shm->shm_string_ptr - &filebench_shm->shm_strings[0]) >
601 	    FILEBENCH_STRINGMEMORY) {
602 		filebench_log(LOG_ERROR, "Out of ipc string memory");
603 		return (NULL);
604 	}
605 
606 	(void) strncpy(allocstr, string, strlen(string));
607 
608 	return (allocstr);
609 }
610 
611 /*
612  * Allocate a path string from filebench path string memory.
613  * Specifically used for allocating fileset paths. The length
614  * of the allocated path string is the same as the length of
615  * the supplied path string "path", and the contents of path
616  * are copied to the newly allocated path string. Checks for
617  * out-of-path-string-memory condition and returns NULL if so.
618  * Otherwise it returns a pointer to the newly allocated path
619  * string.
620  */
621 char *
622 ipc_pathalloc(char *path)
623 {
624 	char *allocpath = filebench_shm->shm_path_ptr;
625 
626 	filebench_shm->shm_path_ptr += strlen(path) + 1;
627 
628 	if ((filebench_shm->shm_path_ptr -
629 	    &filebench_shm->shm_filesetpaths[0]) >
630 	    FILEBENCH_FILESETPATHMEMORY) {
631 		filebench_log(LOG_ERROR, "Out of fileset path memory");
632 		return (NULL);
633 	}
634 
635 	(void) strncpy(allocpath, path, strlen(path));
636 
637 	return (allocpath);
638 }
639 
640 /*
641  * This is a limited functionality deallocator for path
642  * strings - it can only free all path strings at once,
643  * in order to avoid fragmentation.
644  */
645 void
646 ipc_freepaths(void)
647 {
648 	filebench_shm->shm_path_ptr = &filebench_shm->shm_filesetpaths[0];
649 }
650 
651 /*
652  * Allocates a semid from the table of semids for pre intialized
653  * semaphores. Searches for the first available semaphore, and
654  * sets the entry in the table to "1" to indicate allocation.
655  * Returns the allocated semid. Stops the run if all semaphores
656  * are already in use.
657  */
658 int
659 ipc_semidalloc(void)
660 {
661 	int semid;
662 
663 	for (semid = 0; filebench_shm->semids[semid] == 1; semid++)
664 		;
665 	if (semid == FILEBENCH_NSEMS) {
666 		filebench_log(LOG_ERROR,
667 		    "Out of semaphores, increase system tunable limit");
668 		filebench_shutdown(1);
669 	}
670 	filebench_shm->semids[semid] = 1;
671 	return (semid);
672 }
673 
674 /*
675  * Frees up the supplied semid by seting its position in the
676  * allocation table to "0".
677  */
678 void
679 ipc_semidfree(int semid)
680 {
681 	filebench_shm->semids[semid] = 0;
682 }
683 
684 /*
685  * Create a pool of shared memory to fit the per-thread
686  * allocations. Uses shmget() to create a shared memory region
687  * of size "size", attaches to it using shmat(), and stores
688  * the returned address of the region in filebench_shm->shm_addr.
689  * The pool is only created on the first call. The routine
690  * returns 0 if successful or the pool already exists,
691  * -1 otherwise.
692  */
693 int
694 ipc_ismcreate(size_t size)
695 {
696 #ifdef HAVE_SHM_SHARE_MMU
697 	int flag = SHM_SHARE_MMU;
698 #else
699 	int flag = 0;
700 #endif /* HAVE_SHM_SHARE_MMU */
701 
702 	/* Already done? */
703 	if (filebench_shm->shm_id != -1)
704 		return (0);
705 
706 	filebench_log(LOG_VERBOSE,
707 	    "Creating %zd bytes of ISM Shared Memory...", size);
708 
709 	if ((filebench_shm->shm_id =
710 	    shmget(0, size, IPC_CREAT | 0666)) == -1) {
711 		filebench_log(LOG_ERROR,
712 		    "Failed to create %zd bytes of ISM shared memory", size);
713 		return (-1);
714 	}
715 
716 	if ((filebench_shm->shm_addr = (caddr_t)shmat(filebench_shm->shm_id,
717 	    0, flag)) == (void *)-1) {
718 		filebench_log(LOG_ERROR,
719 		    "Failed to attach %zd bytes of created ISM shared memory",
720 		    size);
721 		return (-1);
722 	}
723 
724 	filebench_shm->shm_ptr = (char *)filebench_shm->shm_addr;
725 
726 	filebench_log(LOG_VERBOSE,
727 	    "Allocated %zd bytes of ISM Shared Memory... at %zx",
728 	    size, filebench_shm->shm_addr);
729 
730 	/* Locked until allocated to block allocs */
731 	(void) ipc_mutex_unlock(&filebench_shm->shm_ism_lock);
732 
733 	return (0);
734 }
735 
736 /* Per addr space ism */
737 static int ism_attached = 0;
738 
739 /*
740  * Attach to interprocess shared memory. If already attached
741  * just return, otherwise use shmat() to attached to the region
742  * with ID of filebench_shm->shm_id. Returns -1 if shmat()
743  * fails, otherwise 0.
744  */
745 static int
746 ipc_ismattach(void)
747 {
748 #ifdef HAVE_SHM_SHARE_MMU
749 	int flag = SHM_SHARE_MMU;
750 #else
751 	int flag = 0;
752 #endif /* HAVE_SHM_SHARE_MMU */
753 
754 
755 	if (ism_attached)
756 		return (0);
757 
758 	/* Does it exist? */
759 	if (filebench_shm->shm_id == 999)
760 		return (0);
761 
762 	if (shmat(filebench_shm->shm_id, filebench_shm->shm_addr,
763 	    flag) == NULL)
764 		return (-1);
765 
766 	ism_attached = 1;
767 
768 	return (0);
769 }
770 
771 /*
772  * Allocate from interprocess shared memory. Attaches to ism
773  * if necessary, then allocates "size" bytes, updates allocation
774  * information and returns a pointer to the allocated memory.
775  */
776 /*
777  * XXX No check is made for out-of-memory condition
778  */
779 char *
780 ipc_ismmalloc(size_t size)
781 {
782 	char *allocstr;
783 
784 	(void) ipc_mutex_lock(&filebench_shm->shm_ism_lock);
785 
786 	/* Map in shared memory */
787 	(void) ipc_ismattach();
788 
789 	allocstr = filebench_shm->shm_ptr;
790 
791 	filebench_shm->shm_ptr += size;
792 	filebench_shm->shm_allocated += size;
793 
794 	(void) ipc_mutex_unlock(&filebench_shm->shm_ism_lock);
795 
796 	return (allocstr);
797 }
798 
799 /*
800  * Deletes shared memory region and resets shared memory region
801  * information in filebench_shm.
802  */
803 void
804 ipc_ismdelete(void)
805 {
806 	if (filebench_shm->shm_id == -1)
807 		return;
808 
809 	filebench_log(LOG_VERBOSE, "Deleting ISM...");
810 
811 	(void) ipc_mutex_lock(&filebench_shm->shm_ism_lock);
812 #ifdef HAVE_SEM_RMID
813 	(void) shmctl(filebench_shm->shm_id, IPC_RMID, 0);
814 #endif
815 	filebench_shm->shm_ptr = (char *)filebench_shm->shm_addr;
816 	filebench_shm->shm_id = -1;
817 	filebench_shm->shm_allocated = 0;
818 	(void) ipc_mutex_unlock(&filebench_shm->shm_ism_lock);
819 }
820