Home | History | Annotate | Download | only in common
      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
     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
     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
    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 *
    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 *
    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 *
    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
    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
    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
    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
    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 *
    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
    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 *
    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 *
    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
    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
    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
    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
    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
    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 *
    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
    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