xref: /onnv-gate/usr/src/cmd/filebench/common/ipc.c (revision 6701:4213fadfdec4)
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->shm_semkey;
238 	int sys_semid;
239 
240 	/* Already done? */
241 	if (filebench_shm->shm_sys_semid >= 0)
242 		return;
243 
244 	if ((sys_semid = semget(key, FILEBENCH_NSEMS, IPC_CREAT |
245 	    S_IRUSR | S_IWUSR)) == -1) {
246 		filebench_log(LOG_ERROR,
247 		    "could not create sysv semaphore set "
248 		    "(need to increase sems?): %s",
249 		    strerror(errno));
250 		filebench_shutdown(1);
251 	}
252 
253 	filebench_shm->shm_sys_semid = sys_semid;
254 }
255 
256 /*
257  * Initialize the Interprocess Communication system and its
258  * associated shared memory structure. It first creates a
259  * temporary file using either the mkstemp() function or the
260  * tempnam() and open() functions. If the process model is in
261  * use,it than sets the file large enough to hold the
262  * filebench_shm and an additional Megabyte. The file is then
263  * memory mapped. If the process model is not in use, it simply
264  * mallocs a region of sizeof (filebench_shm_t).
265  *
266  * Once the shared memory region / file is created, ipc_init
267  * initializes various locks pointers, and variables in the
268  * shared memory. It also uses ftok() to get a shared memory
269  * semaphore key for later use in allocating shared semaphores.
270  */
271 void
272 ipc_init(void)
273 {
274 	filebench_shm_t *buf = malloc(MB);
275 	key_t key;
276 	caddr_t c1;
277 	caddr_t c2;
278 #ifdef HAVE_SEM_RMID
279 	int sys_semid;
280 #endif
281 
282 #ifdef HAVE_MKSTEMP
283 	shmpath = (char *)malloc(128);
284 	(void) strcpy(shmpath, "/var/tmp/fbenchXXXXXX");
285 	shmfd = mkstemp(shmpath);
286 #else
287 	shmfd   = open(shmpath, O_CREAT | O_RDWR | O_TRUNC, 0666);
288 	shmpath = tempnam("/var/tmp", "fbench");
289 #endif	/* HAVE_MKSTEMP */
290 
291 #ifdef USE_PROCESS_MODEL
292 
293 	if (shmfd  < 0) {
294 		filebench_log(LOG_FATAL, "Cannot open shm %s: %s",
295 		    shmpath,
296 		    strerror(errno));
297 		exit(1);
298 	}
299 
300 	(void) lseek(shmfd, sizeof (filebench_shm_t), SEEK_SET);
301 	if (write(shmfd, buf, MB) != MB) {
302 		filebench_log(LOG_FATAL,
303 		    "Cannot allocate shm: %s", strerror(errno));
304 		exit(1);
305 	}
306 
307 	/* LINTED E_BAD_PTR_CAST_ALIGN */
308 	if ((filebench_shm = (filebench_shm_t *)mmap((caddr_t)0,
309 	    sizeof (filebench_shm_t), PROT_READ | PROT_WRITE,
310 	    MAP_SHARED, shmfd, 0)) == NULL) {
311 		filebench_log(LOG_FATAL, "Cannot mmap shm");
312 		exit(1);
313 	}
314 
315 #else
316 	if ((filebench_shm =
317 	    (filebench_shm_t *)malloc(sizeof (filebench_shm_t))) == NULL) {
318 		filebench_log(LOG_FATAL, "Cannot malloc shm");
319 		exit(1);
320 	}
321 #endif /* USE_PROCESS_MODEL */
322 
323 	c1 = (caddr_t)filebench_shm;
324 	c2 = (caddr_t)&filebench_shm->shm_marker;
325 
326 	(void) memset(filebench_shm, 0, c2 - c1);
327 	filebench_shm->shm_epoch = gethrtime();
328 	filebench_shm->shm_debug_level = LOG_VERBOSE;
329 	filebench_shm->shm_rmode = FILEBENCH_MODE_TIMEOUT;
330 	filebench_shm->shm_string_ptr = &filebench_shm->shm_strings[0];
331 	filebench_shm->shm_ptr = (char *)filebench_shm->shm_addr;
332 	filebench_shm->shm_path_ptr = &filebench_shm->shm_filesetpaths[0];
333 
334 	/* Setup mutexes for object lists */
335 	(void) pthread_mutex_init(&filebench_shm->shm_fileset_lock,
336 	    ipc_mutexattr());
337 	(void) pthread_mutex_init(&filebench_shm->shm_procflow_lock,
338 	    ipc_mutexattr());
339 	(void) pthread_mutex_init(&filebench_shm->shm_procs_running_lock,
340 	    ipc_mutexattr());
341 	(void) pthread_mutex_init(&filebench_shm->shm_threadflow_lock,
342 	    ipc_mutexattr());
343 	(void) pthread_mutex_init(&filebench_shm->shm_flowop_lock,
344 	    ipc_mutexattr());
345 	(void) pthread_mutex_init(&filebench_shm->shm_msg_lock,
346 	    ipc_mutexattr());
347 	(void) pthread_mutex_init(&filebench_shm->shm_eventgen_lock,
348 	    ipc_mutexattr());
349 	(void) pthread_mutex_init(&filebench_shm->shm_malloc_lock,
350 	    ipc_mutexattr());
351 	(void) pthread_mutex_init(&filebench_shm->shm_ism_lock,
352 	    ipc_mutexattr());
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 	(void) pthread_rwlock_rdlock(&filebench_shm->shm_run_lock);
360 
361 	(void) ipc_mutex_lock(&filebench_shm->shm_ism_lock);
362 
363 	/* Create semaphore */
364 	if ((key = ftok(shmpath, 1)) < 0) {
365 		filebench_log(LOG_ERROR, "cannot create sem: %s",
366 		    strerror(errno));
367 		exit(1);
368 	}
369 
370 #ifdef HAVE_SEM_RMID
371 	if ((sys_semid = semget(key, 0, 0)) != -1)
372 		(void) semctl(sys_semid, 0, IPC_RMID);
373 #endif
374 
375 	filebench_shm->shm_semkey = key;
376 	filebench_shm->shm_sys_semid = -1;
377 	filebench_shm->shm_log_fd = -1;
378 	filebench_shm->shm_dump_fd = -1;
379 	filebench_shm->shm_eventgen_hz = 0;
380 	filebench_shm->shm_id = -1;
381 
382 	free(buf);
383 }
384 
385 /*
386  * If compiled to use process model, just unlinks the shmpath.
387  * Otherwise a no-op.
388  */
389 void
390 ipc_cleanup(void)
391 {
392 #ifdef USE_PROCESS_MODEL
393 	(void) unlink(shmpath);
394 #endif /* USE_PROCESS_MODEL */
395 }
396 
397 /*
398  * Attach to shared memory. Used by worker processes to open
399  * and mmap the shared memory region. If successful, it
400  * initializes the worker process' filebench_shm to point to
401  * the region and returns 0. Otherwise it returns -1.
402  */
403 int
404 ipc_attach(caddr_t shmaddr)
405 {
406 	if ((shmfd = open(shmpath, O_RDWR, 0666)) < 0) {
407 		filebench_log(LOG_ERROR, "Cannot open shm");
408 		return (-1);
409 	}
410 
411 	/* LINTED E_BAD_PTR_CAST_ALIGN */
412 	if ((filebench_shm = (filebench_shm_t *)mmap(shmaddr,
413 	    sizeof (filebench_shm_t), PROT_READ | PROT_WRITE,
414 	    MAP_SHARED | MAP_FIXED, shmfd, 0)) == NULL) {
415 		filebench_log(LOG_ERROR, "Cannot mmap shm");
416 		return (-1);
417 	}
418 
419 	filebench_log(LOG_DEBUG_IMPL, "addr = %zx", filebench_shm);
420 
421 	return (0);
422 }
423 
424 static int filebench_sizes[] = {
425 	FILEBENCH_NPROCFLOWS,		/* number of procflows */
426 	FILEBENCH_NTHREADFLOWS,		/* number of threadflows */
427 	FILEBENCH_NFLOWOPS,		/* number of flowops */
428 	(FILEBENCH_NVARS * 2),		/* number of attribute value dscrs */
429 	FILEBENCH_NVARS,		/* number of variables */
430 	FILEBENCH_NFILESETS,		/* number of filesets */
431 	FILEBENCH_NFILESETENTRIES,	/* number of fileset entries */
432 	FILEBENCH_NRANDDISTS};		/* number of random distributions */
433 
434 /*
435  * Allocates filebench objects from pre allocated region of
436  * shareable memory. The memory region is partitioned into sets
437  * of objects during initialization. This routine scans for
438  * the first unallocated object of type "type" in the set of
439  * available objects, and makes it as allocated. The routine
440  * returns a pointer to the object, or NULL if all objects have
441  * been allocated.
442  */
443 void *
444 ipc_malloc(int type)
445 {
446 	int i;
447 	int max = filebench_sizes[type];
448 	int start_idx = filebench_shm->shm_lastbitmapindex[type];
449 
450 	(void) ipc_mutex_lock(&filebench_shm->shm_malloc_lock);
451 
452 	i = start_idx;
453 	do {
454 		i++;
455 		if (i >= max)
456 			i = 0;
457 
458 		if (filebench_shm->shm_bitmap[type][i] == 0)
459 			break;
460 
461 	} while (i != start_idx);
462 
463 	if (i == start_idx) {
464 		filebench_log(LOG_ERROR, "Out of shared memory (%d)!", type);
465 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
466 		return (NULL);
467 	}
468 
469 	filebench_shm->shm_bitmap[type][i] = 1;
470 	filebench_shm->shm_lastbitmapindex[type] = i;
471 
472 	switch (type) {
473 	case FILEBENCH_FILESET:
474 		(void) memset((char *)&filebench_shm->shm_fileset[i], 0,
475 		    sizeof (fileset_t));
476 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
477 		return ((char *)&filebench_shm->shm_fileset[i]);
478 
479 	case FILEBENCH_FILESETENTRY:
480 		(void) memset((char *)&filebench_shm->shm_filesetentry[i], 0,
481 		    sizeof (filesetentry_t));
482 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
483 		return ((char *)&filebench_shm->shm_filesetentry[i]);
484 
485 	case FILEBENCH_PROCFLOW:
486 		(void) memset((char *)&filebench_shm->shm_procflow[i], 0,
487 		    sizeof (procflow_t));
488 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
489 		return ((char *)&filebench_shm->shm_procflow[i]);
490 
491 	case FILEBENCH_THREADFLOW:
492 		(void) memset((char *)&filebench_shm->shm_threadflow[i], 0,
493 		    sizeof (threadflow_t));
494 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
495 		return ((char *)&filebench_shm->shm_threadflow[i]);
496 
497 	case FILEBENCH_FLOWOP:
498 		(void) memset((char *)&filebench_shm->shm_flowop[i], 0,
499 		    sizeof (flowop_t));
500 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
501 		return ((char *)&filebench_shm->shm_flowop[i]);
502 
503 	case FILEBENCH_AVD:
504 		filebench_shm->shm_avd_ptrs[i].avd_type = AVD_INVALID;
505 		filebench_shm->shm_avd_ptrs[i].avd_val.varptr = NULL;
506 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
507 		return ((char *)&filebench_shm->shm_avd_ptrs[i]);
508 
509 	case FILEBENCH_VARIABLE:
510 		(void) memset((char *)&filebench_shm->shm_var[i], 0,
511 		    sizeof (var_t));
512 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
513 		return ((char *)&filebench_shm->shm_var[i]);
514 
515 	case FILEBENCH_RANDDIST:
516 		(void) memset((char *)&filebench_shm->shm_randdist[i], 0,
517 		    sizeof (randdist_t));
518 		(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
519 		return ((char *)&filebench_shm->shm_randdist[i]);
520 	}
521 
522 	filebench_log(LOG_ERROR, "Attempt to ipc_malloc unknown type (%d)!",
523 	    type);
524 	return (NULL);
525 }
526 
527 /*
528  * Frees a filebench object of type "type" at the location
529  * pointed to by "addr". It uses the type and address to
530  * calculate which object is being freed, and clears its
531  * allocation map entry.
532  */
533 void
534 ipc_free(int type, char *addr)
535 {
536 	int item;
537 	caddr_t base;
538 	size_t offset;
539 	size_t size;
540 
541 	if (addr == NULL) {
542 		filebench_log(LOG_ERROR, "Freeing type %d %zx", type, addr);
543 		return;
544 	}
545 
546 	switch (type) {
547 
548 	case FILEBENCH_FILESET:
549 		base = (caddr_t)&filebench_shm->shm_fileset[0];
550 		size = sizeof (fileset_t);
551 		break;
552 
553 	case FILEBENCH_FILESETENTRY:
554 		base = (caddr_t)&filebench_shm->shm_filesetentry[0];
555 		size = sizeof (filesetentry_t);
556 		break;
557 
558 	case FILEBENCH_PROCFLOW:
559 		base = (caddr_t)&filebench_shm->shm_procflow[0];
560 		size = sizeof (procflow_t);
561 		break;
562 
563 	case FILEBENCH_THREADFLOW:
564 		base = (caddr_t)&filebench_shm->shm_threadflow[0];
565 		size = sizeof (threadflow_t);
566 		break;
567 
568 	case FILEBENCH_FLOWOP:
569 		base = (caddr_t)&filebench_shm->shm_flowop[0];
570 		size = sizeof (flowop_t);
571 		break;
572 
573 	case FILEBENCH_AVD:
574 		base = (caddr_t)&filebench_shm->shm_avd_ptrs[0];
575 		size = sizeof (avd_t);
576 		break;
577 
578 	case FILEBENCH_VARIABLE:
579 		base = (caddr_t)&filebench_shm->shm_var[0];
580 		size = sizeof (var_t);
581 		break;
582 
583 	case FILEBENCH_RANDDIST:
584 		base = (caddr_t)&filebench_shm->shm_randdist[0];
585 		size = sizeof (randdist_t);
586 		break;
587 	}
588 
589 	offset = ((size_t)addr - (size_t)base);
590 	item = offset / size;
591 
592 	(void) ipc_mutex_lock(&filebench_shm->shm_malloc_lock);
593 	filebench_shm->shm_bitmap[type][item] = 0;
594 	(void) ipc_mutex_unlock(&filebench_shm->shm_malloc_lock);
595 }
596 
597 /*
598  * Allocate a string from filebench string memory. The length
599  * of the allocated string is the same as the length of the
600  * supplied string "string", and the contents of string are
601  * copied to the newly allocated string.
602  */
603 char *
604 ipc_stralloc(char *string)
605 {
606 	char *allocstr = filebench_shm->shm_string_ptr;
607 
608 	filebench_shm->shm_string_ptr += strlen(string) + 1;
609 
610 	if ((filebench_shm->shm_string_ptr - &filebench_shm->shm_strings[0]) >
611 	    FILEBENCH_STRINGMEMORY) {
612 		filebench_log(LOG_ERROR, "Out of ipc string memory");
613 		return (NULL);
614 	}
615 
616 	(void) strncpy(allocstr, string, strlen(string));
617 
618 	return (allocstr);
619 }
620 
621 /*
622  * Allocate a path string from filebench path string memory.
623  * Specifically used for allocating fileset paths. The length
624  * of the allocated path string is the same as the length of
625  * the supplied path string "path", and the contents of path
626  * are copied to the newly allocated path string. Checks for
627  * out-of-path-string-memory condition and returns NULL if so.
628  * Otherwise it returns a pointer to the newly allocated path
629  * string.
630  */
631 char *
632 ipc_pathalloc(char *path)
633 {
634 	char *allocpath = filebench_shm->shm_path_ptr;
635 
636 	filebench_shm->shm_path_ptr += strlen(path) + 1;
637 
638 	if ((filebench_shm->shm_path_ptr -
639 	    &filebench_shm->shm_filesetpaths[0]) >
640 	    FILEBENCH_FILESETPATHMEMORY) {
641 		filebench_log(LOG_ERROR, "Out of fileset path memory");
642 		return (NULL);
643 	}
644 
645 	(void) strncpy(allocpath, path, strlen(path));
646 
647 	return (allocpath);
648 }
649 
650 /*
651  * This is a limited functionality deallocator for path
652  * strings - it can only free all path strings at once,
653  * in order to avoid fragmentation.
654  */
655 void
656 ipc_freepaths(void)
657 {
658 	filebench_shm->shm_path_ptr = &filebench_shm->shm_filesetpaths[0];
659 }
660 
661 /*
662  * Allocates a semid from the table of semids for pre intialized
663  * semaphores. Searches for the first available semaphore, and
664  * sets the entry in the table to "1" to indicate allocation.
665  * Returns the allocated semid. Stops the run if all semaphores
666  * are already in use.
667  */
668 int
669 ipc_semidalloc(void)
670 {
671 	int semid;
672 
673 	for (semid = 0; filebench_shm->shm_semids[semid] == 1; semid++)
674 		;
675 	if (semid == FILEBENCH_NSEMS) {
676 		filebench_log(LOG_ERROR,
677 		    "Out of semaphores, increase system tunable limit");
678 		filebench_shutdown(1);
679 	}
680 	filebench_shm->shm_semids[semid] = 1;
681 	return (semid);
682 }
683 
684 /*
685  * Frees up the supplied semid by seting its position in the
686  * allocation table to "0".
687  */
688 void
689 ipc_semidfree(int semid)
690 {
691 	filebench_shm->shm_semids[semid] = 0;
692 }
693 
694 /*
695  * Create a pool of shared memory to fit the per-thread
696  * allocations. Uses shmget() to create a shared memory region
697  * of size "size", attaches to it using shmat(), and stores
698  * the returned address of the region in filebench_shm->shm_addr.
699  * The pool is only created on the first call. The routine
700  * returns 0 if successful or the pool already exists,
701  * -1 otherwise.
702  */
703 int
704 ipc_ismcreate(size_t size)
705 {
706 #ifdef HAVE_SHM_SHARE_MMU
707 	int flag = SHM_SHARE_MMU;
708 #else
709 	int flag = 0;
710 #endif /* HAVE_SHM_SHARE_MMU */
711 
712 	/* Already done? */
713 	if (filebench_shm->shm_id != -1)
714 		return (0);
715 
716 	filebench_log(LOG_VERBOSE,
717 	    "Creating %zd bytes of ISM Shared Memory...", size);
718 
719 	if ((filebench_shm->shm_id =
720 	    shmget(0, size, IPC_CREAT | 0666)) == -1) {
721 		filebench_log(LOG_ERROR,
722 		    "Failed to create %zd bytes of ISM shared memory", size);
723 		return (-1);
724 	}
725 
726 	if ((filebench_shm->shm_addr = (caddr_t)shmat(filebench_shm->shm_id,
727 	    0, flag)) == (void *)-1) {
728 		filebench_log(LOG_ERROR,
729 		    "Failed to attach %zd bytes of created ISM shared memory",
730 		    size);
731 		return (-1);
732 	}
733 
734 	filebench_shm->shm_ptr = (char *)filebench_shm->shm_addr;
735 
736 	filebench_log(LOG_VERBOSE,
737 	    "Allocated %zd bytes of ISM Shared Memory... at %zx",
738 	    size, filebench_shm->shm_addr);
739 
740 	/* Locked until allocated to block allocs */
741 	(void) ipc_mutex_unlock(&filebench_shm->shm_ism_lock);
742 
743 	return (0);
744 }
745 
746 /* Per addr space ism */
747 static int ism_attached = 0;
748 
749 /*
750  * Attach to interprocess shared memory. If already attached
751  * just return, otherwise use shmat() to attached to the region
752  * with ID of filebench_shm->shm_id. Returns -1 if shmat()
753  * fails, otherwise 0.
754  */
755 static int
756 ipc_ismattach(void)
757 {
758 #ifdef HAVE_SHM_SHARE_MMU
759 	int flag = SHM_SHARE_MMU;
760 #else
761 	int flag = 0;
762 #endif /* HAVE_SHM_SHARE_MMU */
763 
764 
765 	if (ism_attached)
766 		return (0);
767 
768 	/* Does it exist? */
769 	if (filebench_shm->shm_id == 999)
770 		return (0);
771 
772 	if (shmat(filebench_shm->shm_id, filebench_shm->shm_addr,
773 	    flag) == NULL)
774 		return (-1);
775 
776 	ism_attached = 1;
777 
778 	return (0);
779 }
780 
781 /*
782  * Allocate from interprocess shared memory. Attaches to ism
783  * if necessary, then allocates "size" bytes, updates allocation
784  * information and returns a pointer to the allocated memory.
785  */
786 /*
787  * XXX No check is made for out-of-memory condition
788  */
789 char *
790 ipc_ismmalloc(size_t size)
791 {
792 	char *allocstr;
793 
794 	(void) ipc_mutex_lock(&filebench_shm->shm_ism_lock);
795 
796 	/* Map in shared memory */
797 	(void) ipc_ismattach();
798 
799 	allocstr = filebench_shm->shm_ptr;
800 
801 	filebench_shm->shm_ptr += size;
802 	filebench_shm->shm_allocated += size;
803 
804 	(void) ipc_mutex_unlock(&filebench_shm->shm_ism_lock);
805 
806 	return (allocstr);
807 }
808 
809 /*
810  * Deletes shared memory region and resets shared memory region
811  * information in filebench_shm.
812  */
813 void
814 ipc_ismdelete(void)
815 {
816 	if (filebench_shm->shm_id == -1)
817 		return;
818 
819 	filebench_log(LOG_VERBOSE, "Deleting ISM...");
820 
821 	(void) ipc_mutex_lock(&filebench_shm->shm_ism_lock);
822 #ifdef HAVE_SEM_RMID
823 	(void) shmctl(filebench_shm->shm_id, IPC_RMID, 0);
824 #endif
825 	filebench_shm->shm_ptr = (char *)filebench_shm->shm_addr;
826 	filebench_shm->shm_id = -1;
827 	filebench_shm->shm_allocated = 0;
828 	(void) ipc_mutex_unlock(&filebench_shm->shm_ism_lock);
829 }
830