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