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