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