Sync with the current sources from onnv-gate.

The solaris sources use a non-portable create-thread-suspended flag when
spawning the update thread; I've thrown together a pthreads portable
equivalent.  This has not had any real level of testing.

These changes include a lock around the underlying brk() call; the lack of lock
in earlier revisions of this repo may be the reason that
UMEM_OPTIONS=backend=sbrk was flaky.
This commit is contained in:
Wez Furlong 2010-06-26 16:02:39 +00:00
parent b9dc821378
commit 1028ce923e
13 changed files with 460 additions and 183 deletions

View file

@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
@ -115,6 +115,9 @@ static char *safe_getenv(const char *name)
static arg_process_t umem_log_process;
static size_t umem_size_tempval;
static arg_process_t umem_size_process;
const char *____umem_environ_msg_options = "-- UMEM_OPTIONS --";
static umem_env_item_t umem_options_items[] = {
@ -144,8 +147,27 @@ static umem_env_item_t umem_options_items[] = {
NULL, 0, &umem_reap_interval
},
{ "size_add", "Private", ITEM_SPECIAL,
"add a size to the cache size table",
NULL, 0, NULL,
&umem_size_tempval, &umem_size_process
},
{ "size_clear", "Private", ITEM_SPECIAL,
"clear all but the largest size from the cache size table",
NULL, 0, NULL,
&umem_size_tempval, &umem_size_process
},
{ "size_remove", "Private", ITEM_SPECIAL,
"remove a size from the cache size table",
NULL, 0, NULL,
&umem_size_tempval, &umem_size_process
},
#ifndef _WIN32
#ifndef UMEM_STANDALONE
{ "sbrk_minalloc", "Private", ITEM_SIZE,
"The minimum allocation chunk for the sbrk(2) heap.",
NULL, 0, NULL, &vmem_sbrk_minalloc
},
{ "sbrk_pagesize", "Private", ITEM_SIZE,
"The preferred page size for the sbrk(2) heap.",
NULL, 0, NULL, &vmem_sbrk_pagesize
@ -413,6 +435,49 @@ umem_log_process(const umem_env_item_t *item, const char *item_arg)
return (ARG_SUCCESS);
}
static int
umem_size_process(const umem_env_item_t *item, const char *item_arg)
{
const char *name = item->item_name;
void (*action_func)(size_t);
size_t result;
int ret;
if (strcmp(name, "size_clear") == 0) {
if (item_arg != NULL) {
log_message("%s: %s: does not take a value. ignored\n",
CURRENT, name);
return (ARG_BAD);
}
umem_alloc_sizes_clear();
return (ARG_SUCCESS);
} else if (strcmp(name, "size_add") == 0) {
action_func = umem_alloc_sizes_add;
} else if (strcmp(name, "size_remove") == 0) {
action_func = umem_alloc_sizes_remove;
} else {
log_message("%s: %s: internally unrecognized\n",
CURRENT, name, name, name);
return (ARG_BAD);
}
if (item_arg == NULL) {
log_message("%s: %s: requires a value. ignored\n",
CURRENT, name);
return (ARG_BAD);
}
ret = item_size_process(item, item_arg);
if (ret != ARG_SUCCESS)
return (ret);
result = *item->item_size_target;
action_func(result);
return (ARG_SUCCESS);
}
#ifndef UMEM_STANDALONE
static int
umem_backend_process(const umem_env_item_t *item, const char *item_arg)
@ -649,6 +714,7 @@ umem_setup_envvars(int invalid)
# define dlerror() 0
#endif
state = STATE_DLOPEN;
/* get a handle to the "a.out" object */
if ((h = dlopen(0, RTLD_FIRST | RTLD_LAZY)) != NULL) {
for (cur_env = umem_envvars; cur_env->env_name != NULL;

16
misc.c
View file

@ -20,7 +20,7 @@
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
@ -50,10 +50,6 @@
#include <umem_impl.h>
#include "misc.h"
#ifdef ECELERITY
#include "util.h"
#endif
#define UMEM_ERRFD 2 /* goes to standard error */
#define UMEM_MAX_ERROR_SIZE 4096 /* error messages are truncated to this */
@ -80,15 +76,12 @@ static uint_t umem_error_end = 0;
}
static void
umem_log_enter(const char *error_str, int serious)
umem_log_enter(const char *error_str)
{
int looped;
char c;
looped = 0;
#ifdef ECELERITY
mem_printf(serious ? DCRITICAL : DINFO, "umem: %s", error_str);
#endif
(void) mutex_lock(&umem_error_lock);
@ -121,7 +114,7 @@ umem_error_enter(const char *error_str)
(void) write(UMEM_ERRFD, error_str, strlen(error_str));
#endif
umem_log_enter(error_str, 1);
umem_log_enter(error_str);
}
int
@ -207,7 +200,7 @@ log_message(const char *format, ...)
(void) write(UMEM_ERRFD, buf, strlen(buf));
#endif
umem_log_enter(buf, 0);
umem_log_enter(buf);
}
#ifndef UMEM_STANDALONE
@ -296,6 +289,7 @@ print_sym(void *pointer)
return (1);
}
#else
umem_printf("?? (0x%p)", pointer);
return 0;
#endif
}

View file

@ -205,6 +205,14 @@ static INLINE uint64_t umem_atomic_inc64(uint64_t *val)
#define P2SAMEHIGHBIT(x, y) (((x) ^ (y)) < ((x) & (y)))
#define IS_P2ALIGNED(v, a) ((((uintptr_t)(v)) & ((uintptr_t)(a) - 1)) == 0)
#define ISP2(x) (((x) & ((x) - 1)) == 0)
/*
* return TRUE if adding len to off would cause it to cross an align
* boundary.
* eg, P2BOUNDARY(0x1234, 0xe0, 0x100) == TRUE (0x1234 + 0xe0 == 0x1314)
* eg, P2BOUNDARY(0x1234, 0x50, 0x100) == FALSE (0x1234 + 0x50 == 0x1284)
*/
#define P2BOUNDARY(off, len, align) \
(((off) ^ ((off) + (len) - 1)) > (align) - 1)
/* beware! umem only uses these atomic adds for incrementing by 1 */
#define atomic_add_64(lvalptr, delta) umem_atomic_inc64(lvalptr)

166
umem.c
View file

@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
* Common Development and Distribution License.
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
@ -88,6 +87,7 @@
*
* * KM_SLEEP v.s. UMEM_NOFAIL
*
* * lock ordering
*
* 2. Initialization
* -----------------
@ -362,6 +362,32 @@
* If a constructor callback _does_ do a UMEM_NOFAIL allocation, and
* the nofail callback does a non-local exit, we will leak the
* partially-constructed buffer.
*
*
* 6. Lock Ordering
* ----------------
* umem has a few more locks than kmem does, mostly in the update path. The
* overall lock ordering (earlier locks must be acquired first) is:
*
* umem_init_lock
*
* vmem_list_lock
* vmem_nosleep_lock.vmpl_mutex
* vmem_t's:
* vm_lock
* sbrk_lock
*
* umem_cache_lock
* umem_update_lock
* umem_flags_lock
* umem_cache_t's:
* cache_cpu[*].cc_lock
* cache_depot_lock
* cache_lock
* umem_log_header_t's:
* lh_cpu[*].clh_lock
* lh_lock
*
* \endcode
*/
@ -413,8 +439,12 @@ size_t pagesize;
* bytes, so that it will be 64-byte aligned. For all multiples of 64,
* the next kmem_cache_size greater than or equal to it must be a
* multiple of 64.
*
* This table must be in sorted order, from smallest to highest. The
* highest slot must be UMEM_MAXBUF, and every slot afterwards must be
* zero.
*/
static const int umem_alloc_sizes[] = {
static int umem_alloc_sizes[] = {
#ifdef _LP64
1 * 8,
1 * 16,
@ -433,17 +463,19 @@ static const int umem_alloc_sizes[] = {
P2ALIGN(8192 / 7, 64),
P2ALIGN(8192 / 6, 64),
P2ALIGN(8192 / 5, 64),
P2ALIGN(8192 / 4, 64),
P2ALIGN(8192 / 4, 64), 2304,
P2ALIGN(8192 / 3, 64),
P2ALIGN(8192 / 2, 64),
P2ALIGN(8192 / 1, 64),
P2ALIGN(8192 / 2, 64), 4544,
P2ALIGN(8192 / 1, 64), 9216,
4096 * 3,
8192 * 2,
UMEM_MAXBUF, /* = 8192 * 2 */
/* 24 slots for user expansion */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
};
#define NUM_ALLOC_SIZES (sizeof (umem_alloc_sizes) / sizeof (*umem_alloc_sizes))
#define UMEM_MAXBUF 16384
static umem_magtype_t umem_magtype[] = {
{ 1, 8, 3200, 65536 },
{ 3, 16, 256, 32768 },
@ -757,6 +789,8 @@ umem_remove_updates(umem_cache_t *cp)
* Get it out of the active state
*/
while (cp->cache_uflags & UMU_ACTIVE) {
int cancel_state;
ASSERT(cp->cache_unext == NULL);
cp->cache_uflags |= UMU_NOTIFY;
@ -768,7 +802,10 @@ umem_remove_updates(umem_cache_t *cp)
ASSERT(umem_update_thr != thr_self() &&
umem_st_update_thr != thr_self());
(void) _cond_wait(&umem_update_cv, &umem_update_lock);
(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
&cancel_state);
(void) cond_wait(&umem_update_cv, &umem_update_lock);
(void) pthread_setcancelstate(cancel_state, NULL);
}
/*
* Get it out of the Work Requested state
@ -1097,7 +1134,7 @@ umem_log_enter(umem_log_header_t *lhp, void *data, size_t size)
{
void *logspace;
umem_cpu_log_header_t *clhp =
&(lhp->lh_cpu[CPU(umem_cpu_mask)->cpu_number]);
&lhp->lh_cpu[CPU(umem_cpu_mask)->cpu_number];
if (lhp == NULL || umem_logging == 0)
return (NULL);
@ -2769,6 +2806,88 @@ umem_cache_destroy(umem_cache_t *cp)
vmem_free(umem_cache_arena, cp, UMEM_CACHE_SIZE(umem_max_ncpus));
}
void
umem_alloc_sizes_clear(void)
{
int i;
umem_alloc_sizes[0] = UMEM_MAXBUF;
for (i = 1; i < NUM_ALLOC_SIZES; i++)
umem_alloc_sizes[i] = 0;
}
void
umem_alloc_sizes_add(size_t size_arg)
{
int i, j;
size_t size = size_arg;
if (size == 0) {
log_message("size_add: cannot add zero-sized cache\n",
size, UMEM_MAXBUF);
return;
}
if (size > UMEM_MAXBUF) {
log_message("size_add: %ld > %d, cannot add\n", size,
UMEM_MAXBUF);
return;
}
if (umem_alloc_sizes[NUM_ALLOC_SIZES - 1] != 0) {
log_message("size_add: no space in alloc_table for %d\n",
size);
return;
}
if (P2PHASE(size, UMEM_ALIGN) != 0) {
size = P2ROUNDUP(size, UMEM_ALIGN);
log_message("size_add: rounding %d up to %d\n", size_arg,
size);
}
for (i = 0; i < NUM_ALLOC_SIZES; i++) {
int cur = umem_alloc_sizes[i];
if (cur == size) {
log_message("size_add: %ld already in table\n",
size);
return;
}
if (cur > size)
break;
}
for (j = NUM_ALLOC_SIZES - 1; j > i; j--)
umem_alloc_sizes[j] = umem_alloc_sizes[j-1];
umem_alloc_sizes[i] = size;
}
void
umem_alloc_sizes_remove(size_t size)
{
int i;
if (size == UMEM_MAXBUF) {
log_message("size_remove: cannot remove %ld\n", size);
return;
}
for (i = 0; i < NUM_ALLOC_SIZES; i++) {
int cur = umem_alloc_sizes[i];
if (cur == size)
break;
else if (cur > size || cur == 0) {
log_message("size_remove: %ld not found in table\n",
size);
return;
}
}
for (; i + 1 < NUM_ALLOC_SIZES; i++)
umem_alloc_sizes[i] = umem_alloc_sizes[i+1];
umem_alloc_sizes[i] = 0;
}
static int
umem_cache_init(void)
{
@ -2861,6 +2980,10 @@ umem_cache_init(void)
for (i = 0; i < NUM_ALLOC_SIZES; i++) {
size_t cache_size = umem_alloc_sizes[i];
size_t align = 0;
if (cache_size == 0)
break; /* 0 terminates the list */
/*
* If they allocate a multiple of the coherency granularity,
* they get a coherency-granularity-aligned address.
@ -2888,6 +3011,9 @@ umem_cache_init(void)
for (i = 0; i < NUM_ALLOC_SIZES; i++) {
size_t cache_size = umem_alloc_sizes[i];
if (cache_size == 0)
break; /* 0 terminates the list */
cp = umem_alloc_caches[i];
while (size <= cache_size) {
@ -2895,6 +3021,7 @@ umem_cache_init(void)
size += UMEM_ALIGN;
}
}
ASSERT(size - UMEM_ALIGN == UMEM_MAXBUF);
return (1);
}
@ -2904,7 +3031,7 @@ umem_cache_init(void)
*/
void
umem_startup(caddr_t start, size_t len, size_t pagesize, caddr_t minstack,
caddr_t maxstack)
caddr_t maxstack)
{
#ifdef UMEM_STANDALONE
int idx;
@ -2989,9 +3116,16 @@ umem_init(void)
* someone else beat us to initializing umem. Wait
* for them to complete, then return.
*/
while (umem_ready == UMEM_READY_INITING)
(void) _cond_wait(&umem_init_cv,
while (umem_ready == UMEM_READY_INITING) {
int cancel_state;
(void) pthread_setcancelstate(
PTHREAD_CANCEL_DISABLE, &cancel_state);
(void) cond_wait(&umem_init_cv,
&umem_init_lock);
(void) pthread_setcancelstate(
cancel_state, NULL);
}
ASSERT(umem_ready == UMEM_READY ||
umem_ready == UMEM_READY_INIT_FAILED);
(void) mutex_unlock(&umem_init_lock);

View file

@ -120,6 +120,10 @@ extern void umem_process_updates(void);
extern void umem_cache_applyall(void (*)(umem_cache_t *));
extern void umem_cache_update(umem_cache_t *);
extern void umem_alloc_sizes_add(size_t);
extern void umem_alloc_sizes_clear(void);
extern void umem_alloc_sizes_remove(size_t);
/*
* umem_fork.c: private interfaces
*/

View file

@ -137,12 +137,6 @@ umem_panic(const char *format, ...)
if (format[strlen(format)-1] != '\n')
umem_error_enter("\n");
#ifdef ECELERITY
va_start(va, format);
ec_debug_vprintf(DCRITICAL, DMEM, format, va);
va_end(va);
#endif
print_stacktrace();
umem_do_abort();
@ -171,7 +165,6 @@ __umem_assert_failed(const char *assertion, const char *file, int line)
{
umem_panic("Assertion failed: %s, file %s, line %d\n",
assertion, file, line);
umem_do_abort();
/*NOTREACHED*/
return (0);
}

View file

@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@ -19,18 +18,13 @@
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Portions Copyright 2006-2008 Message Systems, Inc.
*/
/* #pragma ident "@(#)umem_fork.c 1.3 05/06/08 SMI" */
#include "config.h"
/* #include "mtlib.h" */
#include "umem_base.h"
#include "vmem_base.h"
@ -38,7 +32,8 @@
#include <unistd.h>
/*
* The following functions are for pre- and post-fork1(2) handling.
* The following functions are for pre- and post-fork1(2) handling. See
* "Lock Ordering" in lib/libumem/common/umem.c for the lock ordering used.
*/
static void
@ -108,6 +103,10 @@ umem_lockup(void)
(void) umem_init();
(void) mutex_lock(&umem_init_lock);
}
vmem_lockup();
vmem_sbrk_lockup();
(void) mutex_lock(&umem_cache_lock);
(void) mutex_lock(&umem_update_lock);
(void) mutex_lock(&umem_flags_lock);
@ -124,46 +123,30 @@ umem_lockup(void)
(void) cond_broadcast(&umem_update_cv);
vmem_sbrk_lockup();
vmem_lockup();
}
static void
umem_release(void)
{
umem_cache_t *cp;
vmem_release();
vmem_sbrk_release();
umem_release_log_header(umem_slab_log);
umem_release_log_header(umem_failure_log);
umem_release_log_header(umem_content_log);
umem_release_log_header(umem_transaction_log);
for (cp = umem_null_cache.cache_next; cp != &umem_null_cache;
cp = cp->cache_next)
umem_release_cache(cp);
umem_release_cache(&umem_null_cache);
(void) mutex_unlock(&umem_flags_lock);
(void) mutex_unlock(&umem_update_lock);
(void) mutex_unlock(&umem_cache_lock);
(void) mutex_unlock(&umem_init_lock);
}
static void
umem_release_child(void)
umem_do_release(int as_child)
{
umem_cache_t *cp;
int cleanup_update = 0;
/*
* Clean up the update state
* Clean up the update state if we are the child process and
* another thread was processing updates.
*/
umem_update_thr = 0;
if (as_child) {
if (umem_update_thr != thr_self()) {
umem_update_thr = 0;
cleanup_update = 1;
}
if (umem_st_update_thr != thr_self()) {
umem_st_update_thr = 0;
cleanup_update = 1;
}
}
if (umem_st_update_thr != thr_self()) {
umem_st_update_thr = 0;
if (cleanup_update) {
umem_reaping = UMEM_REAP_DONE;
for (cp = umem_null_cache.cache_next; cp != &umem_null_cache;
@ -196,7 +179,36 @@ umem_release_child(void)
}
}
umem_release();
umem_release_log_header(umem_slab_log);
umem_release_log_header(umem_failure_log);
umem_release_log_header(umem_content_log);
umem_release_log_header(umem_transaction_log);
for (cp = umem_null_cache.cache_next; cp != &umem_null_cache;
cp = cp->cache_next)
umem_release_cache(cp);
umem_release_cache(&umem_null_cache);
(void) mutex_unlock(&umem_flags_lock);
(void) mutex_unlock(&umem_update_lock);
(void) mutex_unlock(&umem_cache_lock);
vmem_sbrk_release();
vmem_release();
(void) mutex_unlock(&umem_init_lock);
}
static void
umem_release(void)
{
umem_do_release(0);
}
static void
umem_release_child(void)
{
umem_do_release(1);
}
#endif

View file

@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
@ -35,10 +34,18 @@
#include <signal.h>
/*
* we use the _ version, since we don't want to be cancelled.
*/
extern int _cond_timedwait(cond_t *cv, mutex_t *mutex, const timespec_t *delay);
struct umem_suspend_signal_object {
/* locked by creating thread; unlocked when umem_update_thread
* can proceed */
pthread_mutex_t mtx;
/* lock associated with the condition variable */
pthread_mutex_t cmtx;
/* condition variable is signalled by umem_update_thread when
* it has obtained the mtx; it is then safe for the creating
* thread to clean up its stack (on which this object resides) */
pthread_cond_t cond;
int flag;
};
/*ARGSUSED*/
static THR_RETURN
@ -46,6 +53,12 @@ THR_API umem_update_thread(void *arg)
{
struct timeval now;
int in_update = 0;
struct umem_suspend_signal_object *obj = arg;
pthread_mutex_lock(&obj->mtx);
obj->flag = 1;
pthread_cond_signal(&obj->cond);
obj = NULL;
(void) mutex_lock(&umem_update_lock);
@ -110,12 +123,16 @@ THR_API umem_update_thread(void *arg)
* next update, or someone wakes us.
*/
if (umem_null_cache.cache_unext == &umem_null_cache) {
int cancel_state;
timespec_t abs_time;
abs_time.tv_sec = umem_update_next.tv_sec;
abs_time.tv_nsec = umem_update_next.tv_usec * 1000;
(void) _cond_timedwait(&umem_update_cv,
(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
&cancel_state);
(void) cond_timedwait(&umem_update_cv,
&umem_update_lock, &abs_time);
(void) pthread_setcancelstate(cancel_state, NULL);
}
}
/* LINTED no return statement */
@ -127,6 +144,10 @@ umem_create_update_thread(void)
#ifndef _WIN32
sigset_t sigmask, oldmask;
#endif
pthread_t newthread;
pthread_attr_t attr;
struct umem_suspend_signal_object obj;
int cancel_state;
ASSERT(MUTEX_HELD(&umem_update_lock));
ASSERT(umem_update_thr == 0);
@ -136,18 +157,65 @@ umem_create_update_thread(void)
* The update thread handles no signals
*/
(void) sigfillset(&sigmask);
(void) thr_sigsetmask(SIG_BLOCK, &sigmask, &oldmask);
(void) pthread_sigmask(SIG_BLOCK, &sigmask, &oldmask);
#endif
if (thr_create(NULL, 0, umem_update_thread, NULL,
THR_BOUND | THR_DAEMON | THR_DETACHED, &umem_update_thr) == 0) {
/*
* drop the umem_update_lock; we cannot hold locks acquired in
* pre-fork handler while calling thr_create or thr_continue().
*/
(void) mutex_unlock(&umem_update_lock);
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_mutex_init(&obj.mtx, NULL);
pthread_mutex_init(&obj.cmtx, NULL);
pthread_cond_init(&obj.cond, NULL);
obj.flag = 0;
pthread_mutex_lock(&obj.mtx);
if (pthread_create(&newthread, &attr, umem_update_thread, &obj) == 0) {
#ifndef _WIN32
(void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
(void) pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
#endif
(void) mutex_lock(&umem_update_lock);
/*
* due to the locking in umem_reap(), only one thread can
* ever call umem_create_update_thread() at a time. This
* must be the case for this code to work.
*/
ASSERT(umem_update_thr == 0);
umem_update_thr = newthread;
(void) mutex_unlock(&umem_update_lock);
/* tell the thread to continue */
pthread_mutex_unlock(&obj.mtx);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
/* wait for it to be done with obj */
pthread_mutex_lock(&obj.cmtx);
do {
if (obj.flag) {
break;
}
ASSERT(pthread_cond_wait(&obj.cond, &obj.cmtx) == 0);
} while (1);
pthread_setcancelstate(cancel_state, NULL);
pthread_mutex_destroy(&obj.mtx);
pthread_mutex_destroy(&obj.cmtx);
pthread_cond_destroy(&obj.cond);
(void) mutex_lock(&umem_update_lock);
return (1);
} else { /* thr_create failed */
(void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
(void) mutex_lock(&umem_update_lock);
pthread_mutex_destroy(&obj.mtx);
pthread_mutex_destroy(&obj.cmtx);
pthread_cond_destroy(&obj.cond);
}
umem_update_thr = 0;
#ifndef _WIN32
(void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
#endif
return (0);
}

33
vmem.c
View file

@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@ -37,7 +36,7 @@
* Proceedings of the 2001 Usenix Conference.
* Available as /shared/sac/PSARC/2000/550/materials/vmem.pdf.
*
* For the "Big Theory Statement", see usr/src/common/os/vmem.c
* For the "Big Theory Statement", see usr/src/uts/common/os/vmem.c
*
* 1. Overview of changes
* ------------------------------
@ -230,12 +229,6 @@ vmem_free_t *vmem_heap_free;
uint32_t vmem_mtbf; /* mean time between failures [default: off] */
size_t vmem_seg_size = sizeof (vmem_seg_t);
/*
* we use the _ version, since we don't want to be cancelled.
* Actually, this is automatically taken care of by including "mtlib.h".
*/
extern int _cond_wait(cond_t *cv, mutex_t *mutex);
/*
* Insert/delete from arena list (type 'a') or next-of-kin list (type 'k').
*/
@ -775,6 +768,8 @@ vmem_nextfit_alloc(vmem_t *vmp, size_t size, int vmflag)
break;
vsp = vsp->vs_anext;
if (vsp == rotor) {
int cancel_state;
/*
* We've come full circle. One possibility is that the
* there's actually enough space, but the rotor itself
@ -799,7 +794,10 @@ vmem_nextfit_alloc(vmem_t *vmp, size_t size, int vmflag)
0, 0, NULL, NULL, vmflag & VM_UMFLAGS));
}
vmp->vm_kstat.vk_wait++;
(void) _cond_wait(&vmp->vm_cv, &vmp->vm_lock);
(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
&cancel_state);
(void) cond_wait(&vmp->vm_cv, &vmp->vm_lock);
(void) pthread_setcancelstate(cancel_state, NULL);
vsp = rotor->vs_anext;
}
}
@ -867,6 +865,8 @@ vmem_xalloc(vmem_t *vmp, size_t size, size_t align, size_t phase,
(void) mutex_lock(&vmp->vm_lock);
for (;;) {
int cancel_state;
if (vmp->vm_nsegfree < VMEM_MINFREE &&
!vmem_populate(vmp, vmflag))
break;
@ -930,7 +930,7 @@ vmem_xalloc(vmem_t *vmp, size_t size, size_t align, size_t phase,
start = MAX(vsp->vs_start, (uintptr_t)minaddr);
end = MIN(vsp->vs_end - 1, (uintptr_t)maxaddr - 1) + 1;
taddr = P2PHASEUP(start, align, phase);
if (P2CROSS(taddr, taddr + size - 1, nocross))
if (P2BOUNDARY(taddr, size, nocross))
taddr +=
P2ROUNDUP(P2NPHASE(taddr, nocross), align);
if ((taddr - start) + size > end - start ||
@ -986,7 +986,10 @@ vmem_xalloc(vmem_t *vmp, size_t size, size_t align, size_t phase,
if (vmflag & VM_NOSLEEP)
break;
vmp->vm_kstat.vk_wait++;
(void) _cond_wait(&vmp->vm_cv, &vmp->vm_lock);
(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
&cancel_state);
(void) cond_wait(&vmp->vm_cv, &vmp->vm_lock);
(void) pthread_setcancelstate(cancel_state, NULL);
}
if (vbest != NULL) {
ASSERT(vbest->vs_type == VMEM_FREE);
@ -994,7 +997,7 @@ vmem_xalloc(vmem_t *vmp, size_t size, size_t align, size_t phase,
(void) vmem_seg_alloc(vmp, vbest, addr, size);
(void) mutex_unlock(&vmp->vm_lock);
ASSERT(P2PHASE(addr, align) == phase);
ASSERT(!P2CROSS(addr, addr + size - 1, nocross));
ASSERT(!P2BOUNDARY(addr, size, nocross));
ASSERT(addr >= (uintptr_t)minaddr);
ASSERT(addr + size - 1 <= (uintptr_t)maxaddr - 1);
return ((void *)addr);

View file

@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/

View file

@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
* Common Development and Distribution License, (the "License").
You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@ -60,6 +59,7 @@ extern void vmem_reap(void); /* vmem_populate()-safe reap */
extern size_t pagesize;
extern size_t vmem_sbrk_pagesize;
extern size_t vmem_sbrk_minalloc;
extern uint_t vmem_backend;
#define VMEM_BACKEND_SBRK 0x0000001

View file

@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
* Copyright 2002 Sun Microsystems, Inc. All rights reserved.
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
@ -66,11 +65,9 @@ vmem_mmap_alloc(vmem_t *src, size_t size, int vmflags)
ret = vmem_alloc(src, size, vmflags);
#ifndef _WIN32
if (ret != NULL
&&
if (ret != NULL &&
mmap(ret, size, ALLOC_PROT, ALLOC_FLAGS | MAP_FIXED, -1, 0) ==
MAP_FAILED
) {
MAP_FAILED) {
vmem_free(src, ret, size);
vmem_reap();
@ -116,18 +113,11 @@ vmem_mmap_top_alloc(vmem_t *src, size_t size, int vmflags)
#ifdef _WIN32
buf = VirtualAlloc(NULL, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
if (buf == NULL) buf = MAP_FAILED;
#elif defined(MAP_ALIGN)
buf = mmap((void*)CHUNKSIZE, size, FREE_PROT, FREE_FLAGS | MAP_ALIGN,
-1, 0);
#else
buf = mmap(
#ifdef MAP_ALIGN
(void *)CHUNKSIZE,
#else
0,
#endif
size, FREE_PROT, FREE_FLAGS
#ifdef MAP_ALIGN
| MAP_ALIGN
#endif
, -1, 0);
buf = mmap(0, size, FREE_PROT, FREE_FLAGS, -1, 0);
#endif
if (buf != MAP_FAILED) {
@ -170,8 +160,7 @@ vmem_mmap_arena(vmem_alloc_t **a_out, vmem_free_t **f_out)
#endif
if (mmap_heap == NULL) {
mmap_heap = vmem_init("mmap_top",
CHUNKSIZE,
mmap_heap = vmem_init("mmap_top", CHUNKSIZE,
vmem_mmap_top_alloc, vmem_free,
"mmap_heap", NULL, 0, pagesize,
vmem_mmap_alloc, vmem_mmap_free);

View file

@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
@ -70,7 +69,8 @@
size_t vmem_sbrk_pagesize = 0; /* the preferred page size of the heap */
#define MIN_ALLOC (64*1024)
#define VMEM_SBRK_MINALLOC (64 * 1024)
size_t vmem_sbrk_minalloc = VMEM_SBRK_MINALLOC; /* minimum allocation */
static size_t real_pagesize;
static vmem_t *sbrk_heap;
@ -90,8 +90,9 @@ static sbrk_fail_t sbrk_fails = {
};
static mutex_t sbrk_faillock = DEFAULTMUTEX;
static mutex_t sbrk_lock = DEFAULTMUTEX;
/*
/*
* _sbrk_grow_aligned() aligns the old break to a low_align boundry,
* adds min_size, aligns to a high_align boundry, and calls _brk_unlocked()
* to set the new break. The low_aligned-aligned value is returned, and
@ -100,48 +101,52 @@ static mutex_t sbrk_faillock = DEFAULTMUTEX;
* Unlike sbrk(2), _sbrk_grow_aligned takes an unsigned size, and does
* not allow shrinking the heap.
*/
void *
static void *
_sbrk_grow_aligned(size_t min_size, size_t low_align, size_t high_align,
size_t *actual_size)
{
uintptr_t old_brk;
uintptr_t ret_brk;
uintptr_t high_brk;
uintptr_t new_brk;
int brk_result;
uintptr_t old_brk;
uintptr_t ret_brk;
uintptr_t high_brk;
uintptr_t new_brk;
int brk_result;
#define ALIGNSZ 16
#define BRKALIGN(x) (caddr_t)P2ROUNDUP((uintptr_t)(x), ALIGNSZ)
if ((low_align & (low_align - 1)) != 0 ||
(high_align & (high_align - 1)) != 0) {
errno = EINVAL;
return ((void *)-1);
}
low_align = MAX(low_align, ALIGNSZ);
high_align = MAX(high_align, ALIGNSZ);
old_brk = (uintptr_t)BRKALIGN(sbrk(0));
ret_brk = P2ROUNDUP(old_brk, low_align);
high_brk = ret_brk + min_size;
new_brk = P2ROUNDUP(high_brk, high_align);
if ((low_align & (low_align - 1)) != 0 ||
(high_align & (high_align - 1)) != 0) {
errno = EINVAL;
return ((void *)-1);
}
low_align = MAX(low_align, ALIGNSZ);
high_align = MAX(high_align, ALIGNSZ);
/*
* Check for overflow
*/
if (ret_brk < old_brk || high_brk < ret_brk || new_brk < high_brk) {
errno = ENOMEM;
return ((void *)-1);
}
mutex_lock(&sbrk_lock);
brk_result = brk((void *)new_brk);
old_brk = (uintptr_t)BRKALIGN(sbrk(0));
ret_brk = P2ROUNDUP(old_brk, low_align);
high_brk = ret_brk + min_size;
new_brk = P2ROUNDUP(high_brk, high_align);
if (brk_result != 0)
return ((void *)-1);
/*
* Check for overflow
*/
if (ret_brk < old_brk || high_brk < ret_brk || new_brk < high_brk) {
mutex_unlock(&sbrk_lock);
errno = ENOMEM;
return ((void *)-1);
}
if (actual_size != NULL)
*actual_size = (new_brk - ret_brk);
return ((void *)ret_brk);
brk_result = brk((void *)new_brk);
mutex_unlock(&sbrk_lock);
if (brk_result != 0)
return ((void *)-1);
if (actual_size != NULL)
*actual_size = (new_brk - ret_brk);
return ((void *)ret_brk);
}
/*
@ -210,9 +215,6 @@ vmem_sbrk_tryfail(vmem_t *src, size_t size, int vmflags)
static void *
vmem_sbrk_alloc(vmem_t *src, size_t size, int vmflags)
{
extern void *_sbrk_grow_aligned(size_t min_size, size_t low_align,
size_t high_align, size_t *actual_size);
void *ret;
void *buf;
size_t buf_size;
@ -234,7 +236,7 @@ vmem_sbrk_alloc(vmem_t *src, size_t size, int vmflags)
(ret = vmem_sbrk_tryfail(src, size, vmflags)) != NULL)
return (ret);
buf_size = MAX(size, MIN_ALLOC);
buf_size = MAX(size, vmem_sbrk_minalloc);
/*
* buf_size gets overwritten with the actual allocated size
@ -311,6 +313,11 @@ vmem_sbrk_arena(vmem_alloc_t **a_out, vmem_free_t **f_out)
}
vmem_sbrk_pagesize = heap_size;
/* validate vmem_sbrk_minalloc */
if (vmem_sbrk_minalloc < VMEM_SBRK_MINALLOC)
vmem_sbrk_minalloc = VMEM_SBRK_MINALLOC;
vmem_sbrk_minalloc = P2ROUNDUP(vmem_sbrk_minalloc, heap_size);
sbrk_heap = vmem_init("sbrk_top", real_pagesize,
vmem_sbrk_alloc, vmem_free,
"sbrk_heap", NULL, 0, real_pagesize,