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