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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "@(#)shm_shm_ops.c 1.16 09/05/26 SMI" 28 29 /* 30 * shared memory operations. They support the attaching and 31 * detaching of shared memory, to reduce the overall address space 32 * usage on a 32 bit system. 33 * 34 * It also allows diskomizer to better cope with the default values 35 * of the shmsys settings in /etc/system. 36 */ 37 /* 38 * To use shared memory you have to tweak the following in /etc/system 39 * 40 * set shmsys:shminfo_shmmax=0x100000000 41 * set shmsys:shminfo_shmmin=0x10 42 * set shmsys:shminfo_shmseg=20000 43 * set shmsys:shminfo_shmmni=20000 44 */ 45 #include <unistd.h> 46 #include <stdlib.h> 47 #include <sys/types.h> 48 #include <sys/shm.h> 49 #include <errno.h> 50 #include <string.h> 51 #include <stdio.h> 52 #include <errno.h> 53 #include "args.h" 54 #include <diskomizer/log.h> 55 #include "list.h" 56 57 #define MAX(A, B) ((A) > (B) ? (A) : (B)) 58 #define MIN(A, B) ((A) < (B) ? (A) : (B)) 59 60 #include "shm_ops.h" 61 #define USE_ISM 0x1 62 #define SHM_BEST 0x2 63 #define USE_SHM_PAGEABLE 0x4 64 65 /* 66 * Stolen from the SunOS 5.8 header file so that this will build on 5.6 and 5.7 67 */ 68 #ifndef SHM_PAGEABLE 69 #define SHM_PAGEABLE 0100000 70 #endif 71 72 struct list_head { 73 struct shm_seg *head; 74 struct shm_seg *tail; 75 }; 76 struct shm_seg { 77 struct shm_seg *next; 78 struct shm_seg *prev; 79 struct shm_seg *full_list; 80 shm_flags flags; 81 long len; 82 long used; /* len - used == free */ 83 int number_of_users; 84 int ref; 85 int shmid; 86 pid_t pid; 87 void *addr; 88 void *oldaddr; 89 }; 90 91 struct shm_shm { 92 struct shm_seg *seg; 93 /* shm_flags flags; */ 94 long len; 95 void *addr; 96 ulong_t offset; 97 }; 98 99 static struct list_head free_list, attach_list; 100 static struct shm_seg *full_list; 101 static int hasfailed; 102 103 static struct shm_seg * 104 seg_init(long wanted, shm_flags flags) 105 { 106 struct shm_seg *this; 107 struct shm_seg *found = NULL; 108 int shmflg = 0700 | 109 ((flags.private & (USE_ISM|SHM_BEST)) ? SHM_SHARE_MMU : 0) | 110 ((flags.private & USE_SHM_PAGEABLE) ? SHM_PAGEABLE : 0); 111 112 if (wanted > opts.shminfo_shmmax) { 113 errno = ENOMEM; 114 return (NULL); 115 } 116 117 for (this = full_list; this != NULL; this = this->full_list) { 118 if (flags.allow_detach == this->flags.allow_detach && 119 wanted < (this->len - this->used)) { 120 /* 121 * bingo now onlu use it if it is the smallest one 122 * that will do. 123 */ 124 if (found == NULL || (found->len - found->used) > 125 (this->len - this->used)) { 126 found = this; 127 } 128 } 129 } 130 if (found != NULL) { 131 found->number_of_users++; 132 found->used += wanted; 133 /* Fix the alignment for the largest type */ 134 found->used += (found->used % sizeof (long long)); 135 return (found); 136 } 137 if ((this = calloc(1, sizeof (struct shm_seg))) == NULL) { 138 free(this); 139 return (NULL); 140 } 141 this->shmid = -1; 142 this->next = this->prev = NULL; 143 this->flags = flags; 144 this->pid = getpid(); 145 this->addr = NULL; 146 this->len = opts.shminfo_shmmax; 147 this->number_of_users = 1; 148 this->used = wanted; 149 /* Fix the alignment for the largest type */ 150 this->used += (this->used % sizeof (long long)); 151 this->full_list = full_list; 152 full_list = this; 153 this->ref = 0; 154 155 this->shmid = shmget(IPC_PRIVATE, opts.shminfo_shmmax, shmflg); 156 if (this->shmid == -1) { 157 this->shmid = shmget(IPC_PRIVATE, wanted, shmflg); 158 if (this->shmid != -1) { 159 this->len = wanted; 160 this->used = wanted; 161 } else if (flags.private & SHM_BEST) { 162 this->shmid = shmget(IPC_PRIVATE, opts.shminfo_shmmax, 163 0700); 164 if (this->shmid == -1) { 165 full_list = this->full_list; 166 free(this); 167 return (NULL); 168 } 169 } 170 } 171 LIST_ADD(&free_list, this); 172 assert(this->addr != (void*)-1); 173 return (this); 174 } 175 176 static void * 177 seg_attach(struct shm_seg *seg) 178 { 179 struct shm_seg *x; 180 if (seg->addr == NULL) { 181 int shmflg; 182 183 shmflg = ((seg->flags.private & (USE_ISM|SHM_BEST)) ? 184 SHM_SHARE_MMU : 0) | 185 ((seg->flags.private & USE_SHM_PAGEABLE) ? 186 SHM_PAGEABLE : 0); 187 188 seg->addr = shmat(seg->shmid, NULL, shmflg); 189 if (errno != EMFILE && seg->addr == (void *)-1L && 190 seg->flags.private & SHM_BEST) { 191 seg->addr = shmat(seg->shmid, NULL, 0); 192 } 193 194 for (x = free_list.tail; x != NULL && seg->addr == (void *)-1L; 195 x = x->prev) { 196 if (x != seg && x->flags.allow_detach && 197 x->ref == 0 && x->addr != NULL) { 198 assert(x->addr != (void *)-1L); 199 if (shmdt(x->addr) == 0) { 200 x->oldaddr = x->addr; 201 x->addr = NULL; 202 seg->addr = 203 shmat(seg->shmid, NULL, shmflg); 204 if (errno != EMFILE && 205 seg->addr == (void *)-1L && 206 seg->flags.private & SHM_BEST) { 207 seg->addr = shmat(seg->shmid, 208 NULL, 0); 209 } 210 } 211 } 212 } 213 if (seg->addr == (void *)-1L) { 214 seg->addr = NULL; 215 return (NULL); 216 } 217 } 218 219 if ((seg->ref)++ == 0) { 220 LIST_REMOVE(&free_list, seg); 221 LIST_ADD(&attach_list, seg); 222 } 223 return (seg->addr); 224 } 225 static shm_det_status 226 seg_detach(struct shm_seg *seg) 227 { 228 if (seg->flags.allow_detach) { 229 if (--(seg->ref) == 0) { 230 LIST_REMOVE(&attach_list, seg); 231 LIST_ADD(&free_list, seg); 232 if (seg->flags.always_detach) { 233 if (shmdt(seg->addr) == 0) { 234 seg->oldaddr = seg->addr; 235 seg->addr = NULL; 236 return (SHM_DETACH_DONE); 237 } else { 238 return (SHM_DETACH_ERR); 239 } 240 } 241 } 242 } 243 assert(seg->addr != (void *)-1L); 244 return (SHM_DETACH_OK); 245 } 246 static void 247 seg_destroy(struct shm_seg *seg) 248 { 249 if (--(seg->number_of_users) == 0) { 250 struct shm_seg *tmp; 251 assert(seg->ref == 0); 252 LIST_REMOVE(&free_list, seg); 253 254 if (seg == full_list) { 255 full_list = full_list->full_list; 256 } else { 257 for (tmp = full_list; tmp->full_list != seg; 258 tmp = tmp->full_list) 259 /* empty */; 260 tmp->full_list = tmp->full_list->full_list; 261 } 262 if (seg->addr != NULL) 263 if (shmdt(seg->addr) != NULL) 264 pperror("shmdt(%#lx)", (ulong_t)seg->addr); 265 if (shmctl(seg->shmid, IPC_RMID, NULL) == -1) 266 pperror("shmctl(%d, IPC_RMID, NULL)", seg->shmid); 267 free(seg); 268 } 269 } 270 271 int 272 onlist(struct list_head *list_head, struct shm_seg *entry) 273 { 274 struct shm_seg *x; 275 276 for (x = list_head->head; x != NULL; x = x->next) 277 if (x == entry) 278 return (1); 279 return (0); 280 } 281 282 static void 283 shm_fini(void) 284 { 285 pid_t pid = getpid(); 286 struct shm_seg *q; 287 for (q = full_list; q != NULL; q = q->full_list) { 288 if (q->pid == pid && q->shmid != -1) { 289 if (shmctl(q->shmid, IPC_RMID, NULL) == -1) 290 pperror("shmctl(%d, IPC_RMID, NULL)", 291 q->shmid); 292 else 293 q->shmid = -1; 294 } 295 } 296 } 297 298 #ifdef SEGV_TEST 299 void 300 segv(void) 301 { 302 int *x; 303 x = segv; 304 *x = 0; 305 } 306 #endif 307 308 void * 309 shm_init(long a, long b, shm_flags flags) 310 { 311 struct shm_shm *x; 312 313 #ifdef SEGV_TEST 314 segv(); /* Debugging */ 315 #endif 316 317 x = calloc(1, sizeof (struct shm_shm)); 318 if (x == NULL) 319 return (NULL); 320 x->len = a * b; 321 if ((x->seg = seg_init(x->len, flags)) == NULL) { 322 free(x); 323 return (NULL); 324 } 325 x->offset = x->seg->used - (x->len); 326 327 328 if (!flags.allow_detach) { 329 if (seg_attach(x->seg) == NULL) { 330 free(x); 331 return (NULL); 332 } 333 x->addr = (void *)((ulong_t)x->seg->addr + x->offset); 334 } else { 335 x->addr = NULL; 336 } 337 return (x); 338 } 339 void * 340 shm_attach(void * arg) 341 { 342 struct shm_shm *x = (struct shm_shm *)arg; 343 void *addr; /* the base address of this segment */ 344 345 addr = seg_attach(x->seg); 346 347 if (addr == NULL) 348 return (NULL); 349 350 if (x->addr == NULL) { 351 x->addr = (void *)((ulong_t)x->seg->addr + x->offset); 352 } 353 assert(x->addr == (void *)((ulong_t)x->seg->addr + x->offset)); 354 assert(((ulong_t)x->addr - (ulong_t)addr) + x->len <= x->seg->len); 355 assert(x->addr != (void *)-1L); 356 return (x->addr); 357 } 358 shm_det_status 359 shm_detach(void *arg) 360 { 361 struct shm_shm *x = (struct shm_shm *)arg; 362 x->addr = NULL; 363 return (seg_detach(x->seg)); 364 } 365 void 366 shm_destroy(void *arg) 367 { 368 struct shm_shm *x = (struct shm_shm *)arg; 369 seg_destroy(x->seg); 370 free(arg); 371 } 372 373 void * 374 shm_best_init(long a, long b, shm_flags flags) 375 { 376 flags.private = USE_ISM | SHM_BEST | 377 (opts.shm_pageable ? USE_SHM_PAGEABLE : 0); 378 379 return (shm_init(a, b, flags)); 380 } 381 382 void * 383 ism_init(long a, long b, shm_flags flags) 384 { 385 flags.private = USE_ISM | 386 (opts.shm_pageable ? USE_SHM_PAGEABLE : 0); 387 388 return (shm_init(a, b, flags)); 389 } 390 391 /*ARGSUSED*/ 392 const char * 393 ism_name(void *x) 394 { 395 static char ismname[] = "ism"; 396 return (&ismname[0]); 397 } 398 /*ARGSUSED*/ 399 const char * 400 ism_longname(void *x) 401 { 402 static char ismname[] = "intimate shared memory"; 403 return (&ismname[0]); 404 } 405 /*ARGSUSED*/ 406 const char * 407 shm_best_name(void * arg) 408 { 409 static char name[] = "best_shm"; 410 return (&name[0]); 411 } 412 /*ARGSUSED*/ 413 const char * 414 shm_best_longname(void * arg) 415 { 416 static char name[] = "best shared memory"; 417 return (&name[0]); 418 } 419 /*ARGSUSED*/ 420 const char * 421 shm_name(void * arg) 422 { 423 static char name[] = "shm"; 424 return (&name[0]); 425 } 426 /*ARGSUSED*/ 427 const char * 428 shm_longname(void * arg) 429 { 430 static char name[] = "shared memory"; 431 return (&name[0]); 432 } 433 ulong_t 434 shm_max_size(void) 435 { 436 return (opts.shm_max_size); 437 } 438 static int 439 shm_is_short_of_mem(void) 440 { 441 return (hasfailed); 442 } 443 /*ARGSUSED*/ 444 static void 445 shm_complete(void *handle) 446 { 447 static char max_wasted_str[] = "shm_complete max_bytes wasted"; 448 static char wasted_str[] = "shm_complete bytes wasted"; 449 static char allocated_str[] = "shm_complete bytes allocated"; 450 struct shm_seg *this; 451 long long wasted = 0; 452 long long max_wasted = 0; 453 long long min_wasted = LONGLONG_MAX; 454 long long allocated = 0; 455 456 for (this = full_list; this != NULL; this = this->full_list) { 457 wasted += (this->len - this->used); 458 max_wasted = MAX(max_wasted, (this->len - this->used)); 459 min_wasted = MIN(min_wasted, (this->len - this->used)); 460 allocated += this->len; 461 } 462 plog_number_of_bytes(LOG_NOTICE, allocated, allocated_str, 463 allocated_str); 464 plog_number_of_bytes(LOG_NOTICE, wasted, wasted_str, wasted_str); 465 plog_number_of_bytes(LOG_NOTICE, max_wasted, 466 max_wasted_str, max_wasted_str); 467 } 468 469 struct shm_ops shm_bestshm_ops = { 470 shm_best_name, 471 shm_best_longname, 472 shm_best_init, 473 shm_attach, 474 shm_detach, 475 shm_destroy, 476 shm_max_size, 477 shm_is_short_of_mem, 478 shm_complete, 479 shm_fini 480 }; 481 struct shm_ops shm_ism_ops = { 482 ism_name, 483 ism_longname, 484 ism_init, 485 shm_attach, 486 shm_detach, 487 shm_destroy, 488 shm_max_size, 489 shm_is_short_of_mem, 490 shm_complete, 491 shm_fini 492 }; 493 struct shm_ops shm_shm_ops = { 494 shm_name, 495 shm_longname, 496 shm_init, 497 shm_attach, 498 shm_detach, 499 shm_destroy, 500 shm_max_size, 501 shm_is_short_of_mem, 502 shm_complete, 503 shm_fini 504 }; 505