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 }