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