xref: /onnv-gate/usr/src/cmd/filebench/common/ipc.c (revision 6212:d9dcad6dd79c)
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->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->string_ptr = &filebench_shm->strings[0];
328 	filebench_shm->shm_ptr = (char *)filebench_shm->shm_addr;
329 	filebench_shm->path_ptr = &filebench_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->malloc_lock, ipc_mutexattr());
343 	(void) pthread_mutex_init(&filebench_shm->ism_lock, ipc_mutexattr());
344 	(void) pthread_cond_init(&filebench_shm->eventgen_cv, ipc_condattr());
345 	(void) pthread_rwlock_init(&filebench_shm->flowop_find_lock,
346 	    ipc_rwlockattr());
347 	(void) pthread_rwlock_init(&filebench_shm->run_lock, ipc_rwlockattr());
348 	(void) pthread_rwlock_rdlock(&filebench_shm->run_lock);
349 
350 	(void) ipc_mutex_lock(&filebench_shm->ism_lock);
351 
352 	/* Create semaphore */
353 	if ((key = ftok(shmpath, 1)) < 0) {
354 		filebench_log(LOG_ERROR, "cannot create sem: %s",
355 		    strerror(errno));
356 		exit(1);
357 	}
358 
359 #ifdef HAVE_SEM_RMID
360 	if ((semid = semget(key, 0, 0)) != -1)
361 		(void) semctl(semid, 0, IPC_RMID);
362 #endif
363 
364 	filebench_shm->semkey = key;
365 	filebench_shm->log_fd = -1;
366 	filebench_shm->dump_fd = -1;
367 	filebench_shm->eventgen_hz = 0;
368 	filebench_shm->shm_id = -1;
369 
370 	free(buf);
371 }
372 
373 /*
374  * If compiled to use process model, just unlinks the shmpath.
375  * Otherwise a no-op.
376  */
377 void
378 ipc_cleanup(void)
379 {
380 #ifdef USE_PROCESS_MODEL
381 	(void) unlink(shmpath);
382 #endif /* USE_PROCESS_MODEL */
383 }
384 
385 /*
386  * Attach to shared memory. Used by worker processes to open
387  * and mmap the shared memory region. If successful, it
388  * initializes the worker process' filebench_shm to point to
389  * the region and returns 0. Otherwise it returns -1.
390  */
391 int
392 ipc_attach(caddr_t shmaddr)
393 {
394 	if ((shmfd = open(shmpath, O_RDWR, 0666)) < 0) {
395 		filebench_log(LOG_ERROR, "Cannot open shm");
396 		return (-1);
397 	}
398 
399 	/* LINTED E_BAD_PTR_CAST_ALIGN */
400 	if ((filebench_shm = (filebench_shm_t *)mmap(shmaddr,
401 	    sizeof (filebench_shm_t), PROT_READ | PROT_WRITE,
402 	    MAP_SHARED | MAP_FIXED, shmfd, 0)) == NULL) {
403 		filebench_log(LOG_ERROR, "Cannot mmap shm");
404 		return (-1);
405 	}
406 
407 	filebench_log(LOG_DEBUG_IMPL, "addr = %zx", filebench_shm);
408 
409 	return (0);
410 }
411 
412 static int filebench_sizes[] = {
413 	FILEBENCH_NPROCFLOWS,		/* number of procflows */
414 	FILEBENCH_NTHREADFLOWS,		/* number of threadflows */
415 	FILEBENCH_NFLOWOPS,		/* number of flowops */
416 	(FILEBENCH_NVARS * 2),		/* number of attribute value dscrs */
417 	FILEBENCH_NVARS,		/* number of variables */
418 	FILEBENCH_NFILESETS,		/* number of filesets */
419 	FILEBENCH_NFILESETENTRIES,	/* number of fileset entries */
420 	FILEBENCH_NRANDDISTS};		/* number of random distributions */
421 
422 /*
423  * Allocates filebench objects from pre allocated region of
424  * shareable memory. The memory region is partitioned into sets
425  * of objects during initialization. This routine scans for
426  * the first unallocated object of type "type" in the set of
427  * available objects, and makes it as allocated. The routine
428  * returns a pointer to the object, or NULL if all objects have
429  * been allocated.
430  */
431 void *
432 ipc_malloc(int type)
433 {
434 	int i;
435 	int max = filebench_sizes[type];
436 
437 	(void) ipc_mutex_lock(&filebench_shm->malloc_lock);
438 
439 	for (i = 0; i < max; i++) {
440 		if (filebench_shm->bitmap[type][i] == 0)
441 			break;
442 	}
443 
444 	if (i >= max) {
445 		filebench_log(LOG_ERROR, "Out of shared memory (%d)!", type);
446 		(void) ipc_mutex_unlock(&filebench_shm->malloc_lock);
447 		return (NULL);
448 	}
449 
450 	filebench_shm->bitmap[type][i] = 1;
451 
452 	switch (type) {
453 	case FILEBENCH_FILESET:
454 		(void) memset((char *)&filebench_shm->fileset[i], 0,
455 		    sizeof (fileset_t));
456 		(void) ipc_mutex_unlock(&filebench_shm->malloc_lock);
457 		return ((char *)&filebench_shm->fileset[i]);
458 
459 	case FILEBENCH_FILESETENTRY:
460 		(void) memset((char *)&filebench_shm->filesetentry[i], 0,
461 		    sizeof (filesetentry_t));
462 		(void) ipc_mutex_unlock(&filebench_shm->malloc_lock);
463 		return ((char *)&filebench_shm->filesetentry[i]);
464 
465 	case FILEBENCH_PROCFLOW:
466 		(void) memset((char *)&filebench_shm->procflow[i], 0,
467 		    sizeof (procflow_t));
468 		(void) ipc_mutex_unlock(&filebench_shm->malloc_lock);
469 		return ((char *)&filebench_shm->procflow[i]);
470 
471 	case FILEBENCH_THREADFLOW:
472 		(void) memset((char *)&filebench_shm->threadflow[i], 0,
473 		    sizeof (threadflow_t));
474 		(void) ipc_mutex_unlock(&filebench_shm->malloc_lock);
475 		return ((char *)&filebench_shm->threadflow[i]);
476 
477 	case FILEBENCH_FLOWOP:
478 		(void) memset((char *)&filebench_shm->flowop[i], 0,
479 		    sizeof (flowop_t));
480 		(void) ipc_mutex_unlock(&filebench_shm->malloc_lock);
481 		return ((char *)&filebench_shm->flowop[i]);
482 
483 	case FILEBENCH_AVD:
484 		filebench_shm->shm_avd_ptrs[i].avd_type = AVD_INVALID;
485 		filebench_shm->shm_avd_ptrs[i].avd_val.varptr = NULL;
486 		(void) ipc_mutex_unlock(&filebench_shm->malloc_lock);
487 		return ((char *)&filebench_shm->shm_avd_ptrs[i]);
488 
489 	case FILEBENCH_VARIABLE:
490 		(void) memset((char *)&filebench_shm->var[i], 0,
491 		    sizeof (var_t));
492 		(void) ipc_mutex_unlock(&filebench_shm->malloc_lock);
493 		return ((char *)&filebench_shm->var[i]);
494 
495 	case FILEBENCH_RANDDIST:
496 		(void) memset((char *)&filebench_shm->shm_randdist[i], 0,
497 		    sizeof (randdist_t));
498 		(void) ipc_mutex_unlock(&filebench_shm->malloc_lock);
499 		return ((char *)&filebench_shm->shm_randdist[i]);
500 	}
501 
502 	filebench_log(LOG_ERROR, "Attempt to ipc_malloc unknown type (%d)!",
503 	    type);
504 	return (NULL);
505 }
506 
507 /*
508  * Frees a filebench object of type "type" at the location
509  * pointed to by "addr". It uses the type and address to
510  * calculate which object is being freed, and clears its
511  * allocation map entry.
512  */
513 void
514 ipc_free(int type, char *addr)
515 {
516 	int item;
517 	caddr_t base;
518 	size_t offset;
519 	size_t size;
520 
521 	if (addr == NULL) {
522 		filebench_log(LOG_ERROR, "Freeing type %d %zx", type, addr);
523 		return;
524 	}
525 
526 	switch (type) {
527 
528 	case FILEBENCH_FILESET:
529 		base = (caddr_t)&filebench_shm->fileset[0];
530 		size = sizeof (fileset_t);
531 		break;
532 
533 	case FILEBENCH_FILESETENTRY:
534 		base = (caddr_t)&filebench_shm->filesetentry[0];
535 		size = sizeof (filesetentry_t);
536 		break;
537 
538 	case FILEBENCH_PROCFLOW:
539 		base = (caddr_t)&filebench_shm->procflow[0];
540 		size = sizeof (procflow_t);
541 		break;
542 
543 	case FILEBENCH_THREADFLOW:
544 		base = (caddr_t)&filebench_shm->threadflow[0];
545 		size = sizeof (threadflow_t);
546 		break;
547 
548 	case FILEBENCH_FLOWOP:
549 		base = (caddr_t)&filebench_shm->flowop[0];
550 		size = sizeof (flowop_t);
551 		break;
552 
553 	case FILEBENCH_AVD:
554 		base = (caddr_t)&filebench_shm->shm_avd_ptrs[0];
555 		size = sizeof (avd_t);
556 		break;
557 
558 	case FILEBENCH_VARIABLE:
559 		base = (caddr_t)&filebench_shm->var[0];
560 		size = sizeof (var_t);
561 		break;
562 
563 	case FILEBENCH_RANDDIST:
564 		base = (caddr_t)&filebench_shm->shm_randdist[0];
565 		size = sizeof (randdist_t);
566 		break;
567 	}
568 
569 	offset = ((size_t)addr - (size_t)base);
570 	item = offset / size;
571 
572 	(void) ipc_mutex_lock(&filebench_shm->malloc_lock);
573 	filebench_shm->bitmap[type][item] = 0;
574 	(void) ipc_mutex_unlock(&filebench_shm->malloc_lock);
575 }
576 
577 /*
578  * Allocate a string from filebench string memory. The length
579  * of the allocated string is the same as the length of the
580  * supplied string "string", and the contents of string are
581  * copied to the newly allocated string.
582  */
583 char *
584 ipc_stralloc(char *string)
585 {
586 	char *allocstr = filebench_shm->string_ptr;
587 
588 	filebench_shm->string_ptr += strlen(string) + 1;
589 
590 	if ((filebench_shm->string_ptr - &filebench_shm->strings[0]) >
591 	    FILEBENCH_STRINGMEMORY) {
592 		filebench_log(LOG_ERROR, "Out of ipc string memory");
593 		return (NULL);
594 	}
595 
596 	(void) strncpy(allocstr, string, strlen(string));
597 
598 	return (allocstr);
599 }
600 
601 /*
602  * Allocate a path string from filebench path string memory.
603  * Specifically used for allocating fileset paths. The length
604  * of the allocated path string is the same as the length of
605  * the supplied path string "path", and the contents of path
606  * are copied to the newly allocated path string. Checks for
607  * out-of-path-string-memory condition and returns NULL if so.
608  * Otherwise it returns a pointer to the newly allocated path
609  * string.
610  */
611 char *
612 ipc_pathalloc(char *path)
613 {
614 	char *allocpath = filebench_shm->path_ptr;
615 
616 	filebench_shm->path_ptr += strlen(path) + 1;
617 
618 	if ((filebench_shm->path_ptr - &filebench_shm->filesetpaths[0]) >
619 	    FILEBENCH_FILESETPATHMEMORY) {
620 		filebench_log(LOG_ERROR, "Out of fileset path memory");
621 		return (NULL);
622 	}
623 
624 	(void) strncpy(allocpath, path, strlen(path));
625 
626 	return (allocpath);
627 }
628 
629 /*
630  * This is a limited functionality deallocator for path
631  * strings - it can only free all path strings at once,
632  * in order to avoid fragmentation.
633  */
634 void
635 ipc_freepaths(void)
636 {
637 	filebench_shm->path_ptr = &filebench_shm->filesetpaths[0];
638 }
639 
640 /*
641  * Allocates a semid from the table of semids for pre intialized
642  * semaphores. Searches for the first available semaphore, and
643  * sets the entry in the table to "1" to indicate allocation.
644  * Returns the allocated semid. Stops the run if all semaphores
645  * are already in use.
646  */
647 int
648 ipc_semidalloc(void)
649 {
650 	int semid;
651 
652 	for (semid = 0; filebench_shm->semids[semid] == 1; semid++)
653 		;
654 	if (semid == FILEBENCH_NSEMS) {
655 		filebench_log(LOG_ERROR,
656 		    "Out of semaphores, increase system tunable limit");
657 		filebench_shutdown(1);
658 	}
659 	filebench_shm->semids[semid] = 1;
660 	return (semid);
661 }
662 
663 /*
664  * Frees up the supplied semid by seting its position in the
665  * allocation table to "0".
666  */
667 void
668 ipc_semidfree(int semid)
669 {
670 	filebench_shm->semids[semid] = 0;
671 }
672 
673 /*
674  * Create a pool of shared memory to fit the per-thread
675  * allocations. Uses shmget() to create a shared memory region
676  * of size "size", attaches to it using shmat(), and stores
677  * the returned address of the region in filebench_shm->shm_addr.
678  * The pool is only created on the first call. The routine
679  * returns 0 if successful or the pool already exists,
680  * -1 otherwise.
681  */
682 int
683 ipc_ismcreate(size_t size)
684 {
685 #ifdef HAVE_SHM_SHARE_MMU
686 	int flag = SHM_SHARE_MMU;
687 #else
688 	int flag = 0;
689 #endif /* HAVE_SHM_SHARE_MMU */
690 
691 	/* Already done? */
692 	if (filebench_shm->shm_id != -1)
693 		return (0);
694 
695 	filebench_log(LOG_VERBOSE,
696 	    "Creating %zd bytes of ISM Shared Memory...", size);
697 
698 	if ((filebench_shm->shm_id =
699 	    shmget(0, size, IPC_CREAT | 0666)) == -1) {
700 		filebench_log(LOG_ERROR,
701 		    "Failed to create %zd bytes of ISM shared memory", size);
702 		return (-1);
703 	}
704 
705 	if ((filebench_shm->shm_addr = (caddr_t)shmat(filebench_shm->shm_id,
706 	    0, flag)) == (void *)-1) {
707 		filebench_log(LOG_ERROR,
708 		    "Failed to attach %zd bytes of created ISM shared memory",
709 		    size);
710 		return (-1);
711 	}
712 
713 	filebench_shm->shm_ptr = (char *)filebench_shm->shm_addr;
714 
715 	filebench_log(LOG_VERBOSE,
716 	    "Allocated %zd bytes of ISM Shared Memory... at %zx",
717 	    size, filebench_shm->shm_addr);
718 
719 	/* Locked until allocated to block allocs */
720 	(void) ipc_mutex_unlock(&filebench_shm->ism_lock);
721 
722 	return (0);
723 }
724 
725 /* Per addr space ism */
726 static int ism_attached = 0;
727 
728 /*
729  * Attach to interprocess shared memory. If already attached
730  * just return, otherwise use shmat() to attached to the region
731  * with ID of filebench_shm->shm_id. Returns -1 if shmat()
732  * fails, otherwise 0.
733  */
734 static int
735 ipc_ismattach(void)
736 {
737 #ifdef HAVE_SHM_SHARE_MMU
738 	int flag = SHM_SHARE_MMU;
739 #else
740 	int flag = 0;
741 #endif /* HAVE_SHM_SHARE_MMU */
742 
743 
744 	if (ism_attached)
745 		return (0);
746 
747 	/* Does it exist? */
748 	if (filebench_shm->shm_id == 999)
749 		return (0);
750 
751 	if (shmat(filebench_shm->shm_id, filebench_shm->shm_addr,
752 	    flag) == NULL)
753 		return (-1);
754 
755 	ism_attached = 1;
756 
757 	return (0);
758 }
759 
760 /*
761  * Allocate from interprocess shared memory. Attaches to ism
762  * if necessary, then allocates "size" bytes, updates allocation
763  * information and returns a pointer to the allocated memory.
764  */
765 /*
766  * XXX No check is made for out-of-memory condition
767  */
768 char *
769 ipc_ismmalloc(size_t size)
770 {
771 	char *allocstr;
772 
773 	(void) ipc_mutex_lock(&filebench_shm->ism_lock);
774 
775 	/* Map in shared memory */
776 	(void) ipc_ismattach();
777 
778 	allocstr = filebench_shm->shm_ptr;
779 
780 	filebench_shm->shm_ptr += size;
781 	filebench_shm->shm_allocated += size;
782 
783 	(void) ipc_mutex_unlock(&filebench_shm->ism_lock);
784 
785 	return (allocstr);
786 }
787 
788 /*
789  * Deletes shared memory region and resets shared memory region
790  * information in filebench_shm.
791  */
792 void
793 ipc_ismdelete(void)
794 {
795 	if (filebench_shm->shm_id == -1)
796 		return;
797 
798 	filebench_log(LOG_VERBOSE, "Deleting ISM...");
799 
800 	(void) ipc_mutex_lock(&filebench_shm->ism_lock);
801 #ifdef HAVE_SEM_RMID
802 	(void) shmctl(filebench_shm->shm_id, IPC_RMID, 0);
803 #endif
804 	filebench_shm->shm_ptr = (char *)filebench_shm->shm_addr;
805 	filebench_shm->shm_id = -1;
806 	filebench_shm->shm_allocated = 0;
807 	(void) ipc_mutex_unlock(&filebench_shm->ism_lock);
808 }
809