Changeset View
Changeset View
Standalone View
Standalone View
libexec/rtld-elf/rtld_lock.c
Show All 39 Lines | |||||
* In this algorithm the lock is a single word. Its low-order bit is | * In this algorithm the lock is a single word. Its low-order bit is | ||||
* set when a writer holds the lock. The remaining high-order bits | * set when a writer holds the lock. The remaining high-order bits | ||||
* contain a count of readers desiring the lock. The algorithm requires | * contain a count of readers desiring the lock. The algorithm requires | ||||
* atomic "compare_and_store" and "add" operations, which we take | * atomic "compare_and_store" and "add" operations, which we take | ||||
* from machine/atomic.h. | * from machine/atomic.h. | ||||
*/ | */ | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/signalvar.h> | |||||
#include <signal.h> | #include <signal.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <time.h> | #include <time.h> | ||||
#include "debug.h" | #include "debug.h" | ||||
#include "rtld.h" | #include "rtld.h" | ||||
#include "rtld_machdep.h" | #include "rtld_machdep.h" | ||||
#include "rtld_libc.h" | #include "rtld_libc.h" | ||||
void _rtld_thread_init(struct RtldLockInfo *) __exported; | void _rtld_thread_init(struct RtldLockInfo *) __exported; | ||||
void _rtld_atfork_pre(int *) __exported; | void _rtld_atfork_pre(int *) __exported; | ||||
void _rtld_atfork_post(int *) __exported; | void _rtld_atfork_post(int *) __exported; | ||||
#define WAFLAG 0x1 /* A writer holds the lock */ | #define WAFLAG 0x1 /* A writer holds the lock */ | ||||
#define RC_INCR 0x2 /* Adjusts count of readers desiring lock */ | #define RC_INCR 0x2 /* Adjusts count of readers desiring lock */ | ||||
typedef struct Struct_Lock { | typedef struct Struct_Lock { | ||||
volatile u_int lock; | volatile u_int lock; | ||||
void *base; | void *base; | ||||
} Lock; | } Lock; | ||||
static sigset_t fullsigmask, oldsigmask; | static sigset_t fullsigmask, oldsigmask; | ||||
static int thread_flag, wnested; | static int thread_flag, wnested; | ||||
static uint32_t fsigblock; | |||||
static void * | static void * | ||||
def_lock_create(void) | def_lock_create(void) | ||||
{ | { | ||||
void *base; | void *base; | ||||
char *p; | char *p; | ||||
uintptr_t r; | uintptr_t r; | ||||
Lock *l; | Lock *l; | ||||
Show All 34 Lines | def_rlock_acquire(void *lock) | ||||
Lock *l = (Lock *)lock; | Lock *l = (Lock *)lock; | ||||
atomic_add_acq_int(&l->lock, RC_INCR); | atomic_add_acq_int(&l->lock, RC_INCR); | ||||
while (l->lock & WAFLAG) | while (l->lock & WAFLAG) | ||||
; /* Spin */ | ; /* Spin */ | ||||
} | } | ||||
static void | static void | ||||
sig_fastunblock(void) | |||||
{ | |||||
uint32_t oldval; | |||||
assert((fsigblock & ~FAST_SIGBLOCK_FLAGS) >= FAST_SIGBLOCK_INC); | |||||
oldval = atomic_fetchadd_32(&fsigblock, -FAST_SIGBLOCK_INC); | |||||
if (oldval == (FAST_SIGBLOCK_PEND | FAST_SIGBLOCK_INC)) | |||||
jilles: Perhaps assert that `oldval` is not too low. | |||||
__sys_fast_sigblock(FAST_SIGBLOCK_UNBLOCK, NULL); | |||||
} | |||||
static void | |||||
def_wlock_acquire(void *lock) | def_wlock_acquire(void *lock) | ||||
{ | { | ||||
Lock *l; | Lock *l; | ||||
sigset_t tmp_oldsigmask; | sigset_t tmp_oldsigmask; | ||||
l = (Lock *)lock; | l = (Lock *)lock; | ||||
if (ld_fast_sigblock) { | |||||
for (;;) { | for (;;) { | ||||
atomic_add_32(&fsigblock, FAST_SIGBLOCK_INC); | |||||
if (atomic_cmpset_acq_int(&l->lock, 0, WAFLAG)) | |||||
break; | |||||
sig_fastunblock(); | |||||
} | |||||
} else { | |||||
for (;;) { | |||||
sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask); | sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask); | ||||
if (atomic_cmpset_acq_int(&l->lock, 0, WAFLAG)) | if (atomic_cmpset_acq_int(&l->lock, 0, WAFLAG)) | ||||
break; | break; | ||||
sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL); | sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL); | ||||
} | } | ||||
if (atomic_fetchadd_int(&wnested, 1) == 0) | if (atomic_fetchadd_int(&wnested, 1) == 0) | ||||
oldsigmask = tmp_oldsigmask; | oldsigmask = tmp_oldsigmask; | ||||
} | } | ||||
} | |||||
static void | static void | ||||
def_lock_release(void *lock) | def_lock_release(void *lock) | ||||
{ | { | ||||
Lock *l; | Lock *l; | ||||
l = (Lock *)lock; | l = (Lock *)lock; | ||||
if ((l->lock & WAFLAG) == 0) | if ((l->lock & WAFLAG) == 0) | ||||
atomic_add_rel_int(&l->lock, -RC_INCR); | atomic_add_rel_int(&l->lock, -RC_INCR); | ||||
else { | else { | ||||
assert(wnested > 0); | |||||
atomic_add_rel_int(&l->lock, -WAFLAG); | atomic_add_rel_int(&l->lock, -WAFLAG); | ||||
if (atomic_fetchadd_int(&wnested, -1) == 1) | if (ld_fast_sigblock) | ||||
sig_fastunblock(); | |||||
else if (atomic_fetchadd_int(&wnested, -1) == 1) | |||||
sigprocmask(SIG_SETMASK, &oldsigmask, NULL); | sigprocmask(SIG_SETMASK, &oldsigmask, NULL); | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
def_thread_set_flag(int mask) | def_thread_set_flag(int mask) | ||||
{ | { | ||||
int old_val = thread_flag; | int old_val = thread_flag; | ||||
▲ Show 20 Lines • Show All 117 Lines • ▼ Show 20 Lines | lock_restart_for_upgrade(RtldLockState *lockstate) | ||||
default: | default: | ||||
assert(0); | assert(0); | ||||
} | } | ||||
} | } | ||||
void | void | ||||
lockdflt_init(void) | lockdflt_init(void) | ||||
{ | { | ||||
int i; | int i; | ||||
deflockinfo.rtli_version = RTLI_VERSION; | deflockinfo.rtli_version = RTLI_VERSION; | ||||
deflockinfo.lock_create = def_lock_create; | deflockinfo.lock_create = def_lock_create; | ||||
deflockinfo.lock_destroy = def_lock_destroy; | deflockinfo.lock_destroy = def_lock_destroy; | ||||
deflockinfo.rlock_acquire = def_rlock_acquire; | deflockinfo.rlock_acquire = def_rlock_acquire; | ||||
deflockinfo.wlock_acquire = def_wlock_acquire; | deflockinfo.wlock_acquire = def_wlock_acquire; | ||||
deflockinfo.lock_release = def_lock_release; | deflockinfo.lock_release = def_lock_release; | ||||
deflockinfo.thread_set_flag = def_thread_set_flag; | deflockinfo.thread_set_flag = def_thread_set_flag; | ||||
deflockinfo.thread_clr_flag = def_thread_clr_flag; | deflockinfo.thread_clr_flag = def_thread_clr_flag; | ||||
deflockinfo.at_fork = NULL; | deflockinfo.at_fork = NULL; | ||||
Not Done Inline Actionsshall we commit the whitespace fix first? emaste: shall we commit the whitespace fix first? | |||||
Done Inline ActionsThe whole diff will be split for commit. As usual, there will be the kernel part, then rtld/libc/libthr parts, then utils. Trivial style changes should be also committed separately where possible. kib: The whole diff will be split for commit. As usual, there will be the kernel part, then… | |||||
for (i = 0; i < RTLD_LOCK_CNT; i++) { | for (i = 0; i < RTLD_LOCK_CNT; i++) { | ||||
rtld_locks[i].mask = (1 << i); | rtld_locks[i].mask = (1 << i); | ||||
rtld_locks[i].handle = NULL; | rtld_locks[i].handle = NULL; | ||||
} | } | ||||
memcpy(&lockinfo, &deflockinfo, sizeof(lockinfo)); | memcpy(&lockinfo, &deflockinfo, sizeof(lockinfo)); | ||||
_rtld_thread_init(NULL); | _rtld_thread_init(NULL); | ||||
if (ld_fast_sigblock) { | |||||
__sys_fast_sigblock(FAST_SIGBLOCK_SETPTR, &fsigblock); | |||||
} else { | |||||
/* | /* | ||||
* Construct a mask to block all signals except traps which might | * Construct a mask to block all signals. Note that | ||||
* conceivably be generated within the dynamic linker itself. | * blocked traps mean that the process is terminated | ||||
* if trap occurs while we are in locked section, with | |||||
* the default settings for kern.forcesigexit. | |||||
*/ | */ | ||||
sigfillset(&fullsigmask); | sigfillset(&fullsigmask); | ||||
sigdelset(&fullsigmask, SIGILL); | |||||
sigdelset(&fullsigmask, SIGTRAP); | |||||
sigdelset(&fullsigmask, SIGABRT); | |||||
sigdelset(&fullsigmask, SIGEMT); | |||||
sigdelset(&fullsigmask, SIGFPE); | |||||
sigdelset(&fullsigmask, SIGBUS); | |||||
sigdelset(&fullsigmask, SIGSEGV); | |||||
sigdelset(&fullsigmask, SIGSYS); | |||||
} | } | ||||
} | |||||
/* | /* | ||||
* Callback function to allow threads implementation to | * Callback function to allow threads implementation to | ||||
* register their own locking primitives if the default | * register their own locking primitives if the default | ||||
* one is not suitable. | * one is not suitable. | ||||
* The current context should be the only context | * The current context should be the only context | ||||
* executing at the invocation time. | * executing at the invocation time. | ||||
*/ | */ | ||||
void | void | ||||
_rtld_thread_init(struct RtldLockInfo *pli) | _rtld_thread_init(struct RtldLockInfo *pli) | ||||
{ | { | ||||
int flags, i; | int flags, i; | ||||
void *locks[RTLD_LOCK_CNT]; | void *locks[RTLD_LOCK_CNT]; | ||||
/* disable all locking while this function is running */ | /* disable all locking while this function is running */ | ||||
flags = thread_mask_set(~0); | flags = thread_mask_set(~0); | ||||
if (pli == NULL) | if (pli == NULL) | ||||
pli = &deflockinfo; | pli = &deflockinfo; | ||||
else if (ld_fast_sigblock) { | |||||
fsigblock = 0; | |||||
__sys_fast_sigblock(FAST_SIGBLOCK_UNSETPTR, NULL); | |||||
} | |||||
for (i = 0; i < RTLD_LOCK_CNT; i++) | for (i = 0; i < RTLD_LOCK_CNT; i++) | ||||
if ((locks[i] = pli->lock_create()) == NULL) | if ((locks[i] = pli->lock_create()) == NULL) | ||||
break; | break; | ||||
if (i < RTLD_LOCK_CNT) { | if (i < RTLD_LOCK_CNT) { | ||||
while (--i >= 0) | while (--i >= 0) | ||||
pli->lock_destroy(locks[i]); | pli->lock_destroy(locks[i]); | ||||
▲ Show 20 Lines • Show All 70 Lines • Show Last 20 Lines |
Perhaps assert that oldval is not too low.