1
0
Files
2022-09-29 17:59:04 +03:00

917 lines
22 KiB
C

#ident "$Id: bla.c,v 1.8 1997/06/23 20:27:29 sp Exp $"
/*
* This file contains code to resolve potential deadlocks that may occur if a
* thread goes into a long term interruptible sleep while holding a behavior
* lock.
*
* If you compile with -DBLALOG, additional code is enabled that will
* log all behavior locking chains & check the locking hierarchy for
* potential deadlocks. Use the idbg "blalog" command to print the log.
*/
#include <sys/types.h>
#include <sys/kthread.h>
#include <ksys/behavior.h>
/* ------ BEGIN DEBUG STUFF ZZZ -----------*/
int zzzranfail=0;
/* ------ END DEBUG STUFF ZZZ -----------*/
#define MAX_STARVE_LOCKS 16 /* number of locks for
starvation prevention */
/*
* The following macros and structures are used for the starvation prevention
* algorithms. (If we make behavior locks barrier locks, this can go
* away (maybe).
*
* A thread that is about to sleep trying to acquire a behavior lock head for
* update mode will:
* - search a small array of the bla_starve_t structures and acquire one
* of the locks for update mode.
* - if all locks in the array are in use, no lock (obviously)
* can be acquired. The threads proceed as described below but
* interrupted threads will not sleep on any lock. The array
* should be large enough that this seldom happens.
* - wake up all threads that are in interruptible sleeps that hold the
* lock being waited on. A handle to the bla_starve_t lock & it's current
* <count> is passed to the thread being woken.
* - when the thread finally gets the lock for update, it will increment
* the bla_starve_t <count> and release lock.
* - threads that were awaken from interruptible sleeps will unwind from
* the sleep & release the behavior head locks. After all locks are
* released, the interrupted thread will sleep til the <count> value
* in the bla_starve_t structure changes. (sleeps with mraccess on the
* bla_starve_t lock).
*/
typedef struct {
mrlock_t lock; /* lock set while slot is in use */
uint_t count; /* count of number of times slot has
been used */
} bla_starve_t;
#define STARVENULL 0
#define STARVEISNULL(arg) ((arg) == STARVENULL)
#define STARVEARG(i) (((i) >= MAX_STARVE_LOCKS) ? STARVENULL : \
(((uint_t)(i)+128)<<24) | \
(bla_starve[(i)].count&0xffffff))
#define STARVEINDEX(arg) (((arg)>>24)&127)
#define STARVEISBUSY(arg) ((bla_starve[STARVEINDEX(arg)].count&0xffffff) \
== ((arg)&0xffffff))
/*
* Static variables - private to this file & not exported anywhere else
*/
STATIC lock_t bla_gbla_mutex; /* protects the GBLA list */
STATIC struct kthread *bla_gbla_head; /* head of BLA list */
STATIC bla_starve_t bla_starve[MAX_STARVE_LOCKS];
/*
* if BLALOG is enabled, the following structure is used to keep statistics.
*/
#ifdef BLALOG
struct {
uint_t push;
uint_t push_intr;
uint_t pop;
uint_t pop_unseq;
uint_t isleep;
uint_t isleep_locks_held;
uint_t isleep_lock_waiters;
uint_t isleep_fake_fail;
uint_t gbla_queue;
uint_t gbla_dequeue;
uint_t mrwait;
uint_t mrwait_wakeup;
uint_t mrwait_wakeup_skipped;
uint_t mrgot;
uint_t mrgotnull;
uint_t mrnostarve;
uint_t mrnostarve_no_backup_pc;
uint_t mrnostarvenull;
uint_t mrnostarve_sleep;
} bla_statistics;
#define STAT(x) bla_statistics.x++
#define RWMASK 1 /* mask for read/write in rwi */
#define INTRMASK 2 /* mask for interruptin rwi */
#else /* BLALOG */
#define STAT(x)
#endif /* BLALOG */
/*
* Initialize the bla_starve structure. Called once early in boot.
*/
void
bla_init(void)
{
int i;
private.p_blaptr = curthreadp->k_bla.kb_lockp;
for (i=0; i<MAX_STARVE_LOCKS; i++)
mrinit(&bla_starve[i].lock, "blastarve");
}
/*
* INTERNAL - Add my thread's BLA to the GBLA.
*/
STATIC void
bla_gba_queue(kthread_t *kt)
{
int rv;
ASSERT(!kt->k_bla.kb_in_gbla);
rv = mutex_spinlock(&bla_gbla_mutex);
kt->k_bla.kb_in_gbla = 1;
STAT(gbla_queue);
kt->k_bla.kb_blink = NULL;
kt->k_bla.kb_flink = bla_gbla_head;
if (kt->k_bla.kb_flink)
kt->k_bla.kb_flink->k_bla.kb_blink = kt;
bla_gbla_head = kt;
mutex_spinunlock(&bla_gbla_mutex, rv);
}
/*
* INTERNAL - Remove my thread's BLA from the GBLA.
*/
STATIC void
bla_gba_dequeue(kthread_t *kt)
{
int rv;
ASSERT(kt->k_bla.kb_in_gbla);
rv = mutex_spinlock(&bla_gbla_mutex);
kt->k_bla.kb_in_gbla = 0;
STAT(gbla_dequeue);
if (kt->k_bla.kb_blink)
kt->k_bla.kb_blink->k_bla.kb_flink = kt->k_bla.kb_flink;
else
bla_gbla_head = kt->k_bla.kb_flink;
if (kt->k_bla.kb_flink)
kt->k_bla.kb_flink->k_bla.kb_blink = kt->k_bla.kb_blink;
mutex_spinunlock(&bla_gbla_mutex, rv);
kt->k_bla.kb_flink = NULL;
kt->k_bla.kb_blink = NULL;
}
/*
* Called when a thread is about to go into an interruptible sleep.
* If any behavior read locks are held, the thread's BLA is added to the
* GBLA.
*/
void
bla_isleep(int *spl)
{
kthread_t *kt = curthreadp;
mrlock_t *mrp;
int i, nheld;
STAT(isleep);
ASSERT(!kt->k_bla.kb_in_gbla);
/*
* If no behavior locks are held OR if there is an interrupt already
* pending, nothing needs to be done.
*/
nheld = bla_curlocksheld();
ASSERT(nheld >= 0 && nheld <= KB_MAX_LOCKS);
if (nheld == 0 || thread_interrupted(kt))
return;
STAT(isleep_locks_held);
#ifdef ZZZRANFAIL
{
uint_t ts;
ts = get_timestamp();
if ( (ts%100) < zzzranfail) {
kt->k_flags |= KT_BHVINTR;
thread_interrupt(kt, spl);
STAT(isleep_fake_fail);
return;
}
}
#endif
/*
* add my thread to the GBLA.
*/
bla_gba_queue(kt);
/*
* Now scan the list of behavior locks held by this thread. If any locks
* are already being waited for, we must interrupt the current sleep.
* This check must be done after inserting the BLA into the GBLA to
* prevent race conditions.
*/
for (i=0; i < nheld; i++) {
mrp = kt->k_bla.kb_lockp[i];
#ifdef BLALOG
ASSERT(kt->k_bla.kb_rwi[i] == 0); /* dont sleep holding WRITE locks */
#endif
if (mrgetnwaiters(mrp)) {
STAT(isleep_lock_waiters);
kt->k_flags |= KT_BHVINTR;
thread_interrupt(kt, spl);
return;
}
}
}
/*
* Called when a thread comes out of an interruptible sleep.
* If the thread's BLA is in the GBLA, it is removed from the GBLA.
*/
void
bla_iunsleep()
{
kthread_t *kt = curthreadp;
ASSERT(bla_curlocksheld()>=0 && bla_curlocksheld() <= KB_MAX_LOCKS);
ASSERT(kt->k_bla.kb_in_gbla || bla_curlocksheld() == 0 ||
thread_interrupted(kt));
if (kt->k_bla.kb_in_gbla)
bla_gba_dequeue(curthreadp);
}
/*
* Called by mrpsema just before a thread waits for a mrlock in a
* behavior head. Called ONLY if the lock is being acquired for UPDATE.
*
* Searches the GBLA to see if any thread that is in a long term interruptible
* sleep holds the lock. Any thread found holding the lock will be interrupted.
*
*/
uint_t
bla_wait_for_mrlock(mrlock_t *mrp)
{
kthread_t *kt;
mrlock_t *mrp2;
int i, rv, s;
uint_t starvearg;
int nheld;
STAT(mrwait);
/*
* Try to acquire a bla_starve_t lock. If all are busy, (should not
* happen much), a NULL starevearg is generated. No wait will happen
* in interrupted threads but everything should work ok. If the
* interrupt happens again, maybe some locks will be available then.
*/
for (i=0; i<MAX_STARVE_LOCKS; i++)
if (mrtryupdate(&bla_starve[i].lock))
break;
starvearg = STARVEARG(i);
/*
* Now interrupt any threads that hold the lock we are about to wait
* for. Note that interrupted threads are in long-term interruptible
* sleeps holding the lock for ACCESS & we are trying to get it
* for UPDATE.
*/
rv = mutex_spinlock(&bla_gbla_mutex);
for (kt = bla_gbla_head; kt; kt=kt->k_bla.kb_flink) {
nheld = bla_klocksheld(kt);
ASSERT(nheld >= 0 && nheld <= KB_MAX_LOCKS);
for (i=0; i < nheld; i++) {
mrp2 = kt->k_bla.kb_lockp[i];
if (mrp2 == mrp) {
s = kt_lock(kt);
kt->k_bla.kb_starvearg = starvearg;
if (thread_interruptible(kt)) {
kt->k_flags |= KT_BHVINTR;
thread_interrupt(kt, &s);
kt_unlock(kt, s);
STAT(mrwait_wakeup);
} else {
kt_unlock(kt, s);
STAT(mrwait_wakeup_skipped);
}
}
}
}
mutex_spinunlock(&bla_gbla_mutex, rv);
/*
* Return starvearg to the caller. It will be passed back when the
* behavior lock is finally obtained.
*/
return(starvearg);
}
/*
* Called by mrpsema after it acquires a behavior lock that it had
* to wait for.
*/
void
bla_got_mrlock(uint_t arg)
{
int i;
/*
* If a bla_starve_t lock was obtained, advance its count & release
* the lock. This allows any interrupted threads to proceed from the
* signal handler & reacquire the behavior locks they held in the
* long-term sleep.
*/
STAT(mrgot);
if (! STARVEISNULL(arg)) {
i = STARVEINDEX(arg);
ASSERT(i <= MAX_STARVE_LOCKS);
bla_starve[i].count++;
mrunlock(&bla_starve[i].lock);
} else {
STAT(mrgotnull);
}
}
/*
* Called by psig when it detects that the current thread was woken from
* an interruptible sleep because it held a BHV lock that someone
* wanted for update mode.
* This function will delay the current thread until the thread attempting
* to acquire the behavior lock for update actually gets it.
*/
void
bla_prevent_starvation(int backup_pc) /* backup pc for debug only */
{
kthread_t *kt = curthreadp;
int i;
uint_t arg;
STAT(mrnostarve);
if (!backup_pc)
STAT(mrnostarve_no_backup_pc);
/*
* If a bla_starve_t lock was assigned, acquire the bla_starve_t lock
* for ACCESS. The thread that woke up up acquired the bla_starve_t for
* UPDATE before waking us up. When it gets the behavior lock that we
* use to hold, it will release the bla_starve_t lock & allow us to
* proceed. If the bla_starve_t lock has already been released &
* is being used for an unrelated behavior lock, the STARVEISBUSY
* check will usually prevent us from waiting on the wrong event.
*/
arg = kt->k_bla.kb_starvearg;
if (! STARVEISNULL(arg)) {
i = STARVEINDEX(arg);
ASSERT(i <= MAX_STARVE_LOCKS);
while (STARVEISBUSY(arg)) {
STAT(mrnostarve_sleep);
mraccess(&bla_starve[i].lock);
mrunlock(&bla_starve[i].lock);
}
} else {
STAT(mrnostarvenull);
}
kt->k_bla.kb_starvearg = STARVENULL;
}
#ifdef BLALOG
/*
* The following is used if BLALOG is enabled to collect statistics about
* behavior lock usage & to provide additional debugging help.
*
* The code also creates a global log of unique behavior lock chains.
* This log can be printed using idbg commands. There are also debugging
* routines to scan the log for potential deadlocks in the order in which
* behavior locks are acquired.
*/
#include <sys/debug.h>
#include <sys/idbgentry.h>
#define BHV_NAME_SIZE 16 /* max size of BHV head name */
#define BHV_MAX_NAMES 20 /* max unique BHV names */
#define MAX_LOG_ENTRIES 400 /* max log entries to keep */
#define MAX_RECURSION 100 /* max recursion in deadlock
detection algorithm */
#define MATCH(idp1,idp2) ((idp1)->lid_name == (idp2)->lid_name && \
(idp1)->lid_rwi == (idp2)->lid_rwi && \
(idp1)->lid_recursive == (idp2)->lid_recursive)
#define CONFLICT(idp1,idp2) ((idp1)->lid_name == (idp2)->lid_name && \
(((idp1)->lid_rwi|(idp2)->lid_rwi)&RWMASK))
#define LOWTOUP(c) ((char) ((int)(c) - (int)'a' + (int)'A'))
#define ISLOWER(c) ((c) >= 'a' && (c) <= 'z')
typedef char bla_name_t[BHV_NAME_SIZE];
typedef struct {
uchar_t lid_rwi;
uchar_t lid_recursive;
uchar_t lid_name;
void *lid_caller;
} bla_lock_id;
typedef struct {
uint_t lchecksum;
uint_t lcount;
uchar_t lsubset;
bla_lock_id lockid[KB_MAX_LOCKS+1];
} bla_log_entry_t;
/*
* Forward/external procedure declarations
*/
extern void _prsymoff(void *, char *, char *);
STATIC void bla_log_entry(kthread_t *kt);
STATIC int bla_check_for_entry(bla_log_entry_t *lp);
STATIC int bla_check_for_subset(int, bla_log_entry_t *lp);
STATIC int bla_check_deadlock (int index);
void bla_deadlock(void);
STATIC int bla_check_deadlock2(int nesting, bla_lock_id *starting_idp[],
bla_lock_id *id);
STATIC void bla_print_log_entry (int index, int syms,
bla_lock_id *highlite1, bla_lock_id *highlite2);
int bla_debugnow=0; /* set to 1 to breakpoint on
deadlocks when they occur */
lock_t bla_log_mutex; /* Mutex to protect log*/
int bla_log_next_index=0; /* Number of entries in log */
bla_name_t bla_name[BHV_MAX_NAMES];/* Array of BHV lock names */
int bla_namex=1; /* index to next entry in bla_name
(entry 0 is used for NULL */
bla_log_entry_t bla_log[MAX_LOG_ENTRIES];/* Log of behavior log
locking chains */
extern mrlock_t *dummy_bla[];
/*
* Called by BHV_READ_LOCK, BHV_WRITE_LOCK & BHV_WRITE_TO_READ after obtaining
* a behavior lock. Additional info about the lock is saved. Also provides a
* lot of ASSERTs to make sure everything is working ok.
*
* If the lock was obtained for update, add an entry to the list of behavior
* locks currently held by the current thread. (lock routines only log
* read-locks).
*/
void
bla_push(mrlock_t *mrp, int rwi)
{
kthread_t *kt = curthreadp;
mrlock_t **lockpp;
int nheld;
/*
* skip ASSERTs if idbg running - they'll fail
*/
if (private.p_blaptr >= dummy_bla &&
private.p_blaptr < &dummy_bla[KB_MAX_LOCKS])
return;
STAT(push);
if (rwi) {
lockpp = private.p_blaptr++;
*lockpp = mrp;
}
nheld = bla_curlocksheld();
ASSERT(nheld == 0 || kt->k_bla.kb_lockp[0] != NULL);
ASSERT(nheld > 0 && nheld <= KB_MAX_LOCKS);
ASSERT(kt->k_bla.kb_lockp[nheld-1] == mrp);
kt->k_bla.kb_growing = 1;
if (private.p_kstackflag == PDA_CURINTSTK) {
rwi |= INTRMASK;
STAT(push_intr);
}
kt->k_bla.kb_rwi[nheld-1] = rwi;
kt->k_bla.kb_pc[nheld-1] = __return_address;
}
/*
* Called by BHV_READ_UNLOCK & BHV_WRITE_UNLOCK before they release a behavior
* lock.
*
* If the lock was held for update, remove the entry from the list of behavior
* locks held by the current thread.
*
* If the chain of locks held is starting to shrink (ie., at a local max size),
* log the behavior lock chain if its not already in the log.
*/
void
bla_pop(mrlock_t *mrp, int rwi)
{
kthread_t *kt = curthreadp;
int i, nheld;
/*
* skip ASSERTs if idbg running - they'll fail
*/
if (private.p_blaptr >= dummy_bla &&
private.p_blaptr < &dummy_bla[KB_MAX_LOCKS])
return;
STAT(pop);
if (private.p_kstackflag == PDA_CURINTSTK)
rwi |= INTRMASK;
nheld = bla_curlocksheld();
ASSERT(nheld >= 1 && nheld <= KB_MAX_LOCKS);
ASSERT(nheld == 0 || kt->k_bla.kb_lockp[0] != NULL);
for (i=nheld-1; i>=0; i--)
if (kt->k_bla.kb_lockp[i] == mrp)
break;
if (i != nheld-1)
STAT(pop_unseq);
ASSERT(i>=0);
ASSERT(kt->k_bla.kb_rwi[i] == rwi);
if (kt->k_bla.kb_growing)
bla_log_entry(kt);
kt->k_bla.kb_growing = 0;
for (; i<nheld-1; i++) {
if (rwi&RWMASK)
kt->k_bla.kb_lockp[i] = kt->k_bla.kb_lockp[i+1];
kt->k_bla.kb_rwi[i] = kt->k_bla.kb_rwi[i+1];
kt->k_bla.kb_pc[i] = kt->k_bla.kb_pc[i+1];
}
if (rwi&RWMASK)
private.p_blaptr--;
ASSERT(nheld == 0 || kt->k_bla.kb_lockp[0] != NULL);
}
/*
* Convert a mrlock name into an index into the bla_name array.
*/
STATIC uchar_t
bla_findname (char *name)
{
int i;
for (i=1; i< bla_namex; i++)
if (strcmp(name, bla_name[i]) == 0)
return(i);
ASSERT(bla_namex < BHV_MAX_NAMES);
strcpy(bla_name[bla_namex++], name);
return(bla_namex);
}
/*
* Add an entry to the global log of unique behavior lock chains if
* not already there.
*
* THIS IS FOR DEBUGGING ONLY
*/
STATIC void
bla_log_entry(kthread_t *kt)
{
bla_log_entry_t ent;
int entries, index, new_index=-1, i, j, rv;
uint64_t checksum=0;
mrlock_t *mrp;
if (bla_log_next_index >= MAX_LOG_ENTRIES)
return;
entries = bla_curlocksheld();
if (entries < 2) /* ignore chains of length 1 */
return;
for (i=0; i < entries; i++) {
mrp = kt->k_bla.kb_lockp[i];
ent.lockid[i].lid_rwi = kt->k_bla.kb_rwi[i];
ent.lockid[i].lid_caller = kt->k_bla.kb_pc[i];
for (j=0; j<i; j++)
if (kt->k_bla.kb_lockp[j] ==
kt->k_bla.kb_lockp[i])
break;
ent.lockid[i].lid_recursive = (i && i != j);
ent.lockid[i].lid_name = bla_findname(wchanname(mrp, KT_WMRLOCK));
checksum = (checksum<<4) ^ ent.lockid[i].lid_name ^ ent.lockid[i].lid_rwi;
}
ent.lockid[i].lid_name = 0;
ent.lchecksum = checksum;
ent.lcount = 1;
ent.lsubset = 0;
if ( (index=bla_check_for_entry(&ent)) >= 0) {
bla_log[index].lcount++;
return;
}
rv = mutex_spinlock(&bla_log_mutex);
if (bla_log_next_index < MAX_LOG_ENTRIES && bla_check_for_entry(&ent) < 0){
bla_log[bla_log_next_index] = ent;
new_index = bla_log_next_index++;
}
mutex_spinunlock(&bla_log_mutex, rv);
if (bla_debugnow) {
if (new_index >= 0 && bla_check_deadlock (new_index))
bla_deadlock();
}
}
/*
* Check to see if an entry is in the global log of unique behavior lock chains
* Returns:
* -1 not in log
* >=0 index of log entry in log
*/
STATIC int
bla_check_for_entry(bla_log_entry_t *new_entry_p)
{
bla_log_entry_t *entry;
bla_lock_id *idp1, *idp2;
int i;
for (i=0, entry=&bla_log[0]; i<bla_log_next_index; entry++, i++) {
if (entry->lchecksum != new_entry_p->lchecksum)
continue;
for (idp1=entry->lockid, idp2=new_entry_p->lockid;
idp1->lid_name && idp2->lid_name;
idp1++, idp2++)
if (!MATCH(idp1, idp2))
break;
if (idp1->lid_name == 0 && idp2->lid_name == 0)
return(i);
}
return(-1);
}
/*
* Check to see if an entry is a subset of another entry in the
* log. No need to check subset-entries to see if the are part
* of a deadlock chain.
* Returns:
* 0 not a subset
* 1 subset of another log entry
*/
STATIC int
bla_check_for_subset(int xlogi, bla_log_entry_t *xep)
{
bla_log_entry_t *ep;
bla_lock_id *xidp, *idp;
int logi, ret=0;
for (logi=0, ep=bla_log; logi<bla_log_next_index; ep++, logi++) {
if (xep == ep)
continue;
for (xidp=xep->lockid, idp=ep->lockid;
xidp->lid_name && idp->lid_name;
idp++)
if (MATCH(xidp, idp))
xidp++;
if (xidp->lid_name == 0) {
if (!ret) {
ret = 1;
qprintf(" %d subset of ", xlogi);
}
qprintf(" %d", logi);
}
}
if (ret)
qprintf("\n");
return(ret);
}
/*
* Check a log entry to see if it is part of a potential deadlock chain.
* Returns:
* 0 - no potential deadlocks detected
* >0 - one or more potential deadlocks detected
*/
STATIC int
bla_check_deadlock (int index)
{
bla_lock_id *idp;
bla_lock_id *term_idp[MAX_RECURSION+2];
int rv=0;
for (idp=bla_log[index].lockid; idp->lid_name; idp++) {
term_idp[0] = idp;
term_idp[1] = NULL;
if (!idp->lid_recursive)
rv += bla_check_deadlock2 (0, term_idp, idp);
}
return (rv);
}
STATIC int
bla_check_deadlock2 (int nesting, bla_lock_id *term_idp[], bla_lock_id *sidp)
{
int logi;
bla_lock_id *bidp, *idp, *idp2, **idpp;
for (logi=0; logi<bla_log_next_index; logi++) {
if (bla_log[logi].lsubset)
continue;
for (idp=bla_log[logi].lockid; idp->lid_name; idp++) {
if (!idp->lid_recursive && idp != sidp &&
CONFLICT(sidp, idp)) {
idp2 = idp;
bidp = bla_log[logi].lockid;
while (idp != bidp) {
idp--;
if (idp->lid_recursive)
continue;
if (idp == term_idp[0]) {
qprintf ("\nPotential behavior lock deadlock detected.......\n");
bla_print_log_entry(logi, 0, idp, idp2);
return(1);
}
for (idpp=&term_idp[1]; *idpp; idpp++)
if (idp == *idpp)
return(0);
ASSERT(nesting < MAX_RECURSION);
*idpp = idp;
*(idpp+1) = NULL;
if (bla_check_deadlock2 (nesting+1, term_idp, idp)) {
bla_print_log_entry(logi, 0, idp, idp2);
return(1);
}
*idpp = NULL;
}
break;
}
}
}
return(0);
}
/*
* Print the entire behavior lock log. Called from idbg command (blalog).
*/
void
bla_print_log ()
{
int logi;
for (logi=0; logi<bla_log_next_index; logi++)
bla_print_log_entry(logi, 0, NULL, NULL);
qprintf("\n");
for (logi=0; logi<bla_log_next_index; logi++)
bla_print_log_entry(logi, 1, NULL, NULL);
qprintf("\n");
for (logi=0; logi<bla_log_next_index; logi++)
if (bla_check_for_subset(logi, &bla_log[logi]))
bla_log[logi].lsubset = 1;
qprintf("\n");
for (logi=0; logi<bla_log_next_index; logi++)
if (!bla_log[logi].lsubset)
bla_check_deadlock (logi);
qprintf("\nBLA Statistics....\n");
qprintf ("%10d push lock\n", bla_statistics.push);
qprintf ("%10d push intr\n", bla_statistics.push_intr);
qprintf ("%10d pop lock\n", bla_statistics.pop);
qprintf ("%10d pop not LIFO\n", bla_statistics.pop_unseq);
qprintf ("%10d interruptible sleep\n", bla_statistics.isleep);
qprintf ("%10d held BHV locks\n", bla_statistics.isleep_locks_held);
qprintf ("%10d waiters for BHV locks\n", bla_statistics.isleep_lock_waiters);
qprintf ("%10d faked waiters for BHV locks\n", bla_statistics.isleep_fake_fail);
qprintf ("%10d mrwait called\n", bla_statistics.mrwait);
qprintf ("%10d woke up thread\n", bla_statistics.mrwait_wakeup);
qprintf ("%10d woke skipped - lost race\n", bla_statistics.mrwait_wakeup_skipped);
qprintf ("%10d mrgot called\n", bla_statistics.mrgot);
qprintf ("%10d null arg passed\n", bla_statistics.mrgotnull);
qprintf ("%10d mrnostarve called\n", bla_statistics.mrnostarve);
qprintf ("%10d no backup pc\n", bla_statistics.mrnostarve_no_backup_pc);
qprintf ("%10d null arg passed \n", bla_statistics.mrnostarvenull);
qprintf ("%10d mrnostarve slept\n", bla_statistics.mrnostarve_sleep);
qprintf ("%10d GBLA current size\n", bla_statistics.gbla_queue-
bla_statistics.gbla_dequeue);
}
/*
* Print one entry in the behavior lock log.
*/
STATIC void
bla_print_log_entry (int logi, int sym, bla_lock_id *highlite1,
bla_lock_id *highlite2)
{
bla_lock_id *idp;
char *p, name[BHV_NAME_SIZE];
int i;
qprintf ("%3d: %7d |", logi, bla_log[logi].lcount);
for (i=0, idp = bla_log[logi].lockid; idp->lid_name; i++, idp++) {
if (i)
qprintf ("->");
if ( (idp->lid_rwi&INTRMASK) &&
(i==0 || !((idp-1)->lid_rwi&INTRMASK)))
qprintf ("INTERRUPT->");
if (idp->lid_recursive)
qprintf ("(R)");
if (idp == highlite1 || idp == highlite2)
qprintf ("[");
strcpy (name, bla_name[idp->lid_name]);
if (idp->lid_rwi&RWMASK)
for(p=name; *p; p++)
*p = ISLOWER(*p) ? LOWTOUP(*p) : *p;
if (sym)
_prsymoff((void *)idp->lid_caller, NULL, NULL);
else
qprintf("%s", name);
if (idp == highlite1 || idp == highlite2)
qprintf ("]");
}
qprintf("\n");
}
/*
* Set breakpoint here to see deadlocks as detected
* must set bla_debugnow=1
*/
void
bla_deadlock() {
}
#endif /* BLALOG */