489 lines
14 KiB
C
489 lines
14 KiB
C
static char sccsid[] = "@(#)83 1.15.1.3 src/bos/kernel/proc/lock_alloc.c, sysproc, bos412, 9447B 11/21/94 09:44:40";
|
|
/*
|
|
* COMPONENT_NAME: SYSPROC
|
|
*
|
|
* FUNCTIONS: lock_alloc
|
|
* lock_alloc_instr
|
|
* lock_free
|
|
* lock_free_instr
|
|
* lockd_timer_post
|
|
*
|
|
* ORIGINS: 27 83
|
|
*
|
|
* This module contains IBM CONFIDENTIAL code. -- (IBM
|
|
* Confidential Restricted when combined with the aggregated
|
|
* modules for this product)
|
|
* SOURCE MATERIALS
|
|
*
|
|
* (C) COPYRIGHT International Business Machines Corp. 1993, 1994
|
|
* All Rights Reserved
|
|
* US Government Users Restricted Rights - Use, duplication or
|
|
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
|
|
*/
|
|
|
|
/*
|
|
* LEVEL 1, 5 Years Bull Confidential Information
|
|
*/
|
|
|
|
/*
|
|
* NOTES:
|
|
*/
|
|
|
|
#include <sys/intr.h>
|
|
#include <sys/param.h>
|
|
#include <sys/lock_def.h>
|
|
#include <sys/lock_alloc.h>
|
|
#include <sys/atomic_op.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/trchkid.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/sleep.h>
|
|
#include <sys/syspest.h>
|
|
|
|
#ifdef _INSTRUMENTATION
|
|
|
|
/* to be defined: with this value all locks are spinning unless the lock's
|
|
owner is not running */
|
|
unsigned int maxspin = MAXSPIN_UP;
|
|
|
|
/* tunable variables */
|
|
unsigned int lock_highwatermark = LOCK_HIGHWATERMARK;
|
|
|
|
/* pinned and pageable pools control block */
|
|
struct lock_pool pinned_cb;
|
|
struct lock_pool pageable_cb;
|
|
|
|
/* lock on family lock statistics table */
|
|
Simple_lock family_table_lock;
|
|
|
|
#ifdef DEBUG
|
|
/*
|
|
* in this file, under DEBUG, we use the
|
|
* link_register for more than just trace hooks
|
|
*/
|
|
#ifdef GET_RETURN_ADDRESS
|
|
#undef GET_RETURN_ADDRESS
|
|
#endif
|
|
#define GET_RETURN_ADDRESS(x) \
|
|
{ \
|
|
extern int *get_stkp(); \
|
|
struct stack_frame { \
|
|
struct stack_frame *next; \
|
|
int unused; \
|
|
int link_register; \
|
|
} *sf; \
|
|
sf = (struct stack_frame *)get_stkp(); \
|
|
sf = sf->next; \
|
|
x = sf->link_register; \
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* NAME: lock_alloc_instr
|
|
*
|
|
* FUNCTION: allocate a lock_data_instrumented structure from a lock pool
|
|
*
|
|
*
|
|
* EXECUTION ENVIRONMENT: called by process environment; can be invoked by
|
|
* interrupt handler but this should be ready to get
|
|
* an error return code if no more structure are
|
|
* available.
|
|
*
|
|
*
|
|
* RETURNS: nothing
|
|
*
|
|
*
|
|
*/
|
|
|
|
void
|
|
lock_alloc_instr(void *lockaddr, int flags, short class, short occurrence)
|
|
|
|
{
|
|
struct lock_data_instrumented *l;
|
|
int old_pri;
|
|
register int link_register;
|
|
register int pin_rc = -1;
|
|
register struct lock_data_instrumented *address_pinned = 0;
|
|
#ifdef DEBUG
|
|
register struct thread *myself = curthread;
|
|
#endif
|
|
|
|
/* Get caller of this routine */
|
|
GET_RETURN_ADDRESS(link_register);
|
|
|
|
/* trace lock allocation */
|
|
TRCHKL4T(HKWD_KERN_LOCKALLOC,lockaddr,class,occurrence,link_register);
|
|
|
|
/*
|
|
* validate flags:
|
|
* 1. either LOCK_ALLOC_PIN or LOCK_ALLOC_PAGED should be on,
|
|
* but not both
|
|
* 2. if LOCK_ALLOC_REINIT is on, either *lockaddr is zero,
|
|
* or it points to a valid lock structure.
|
|
*/
|
|
assert(flags & (LOCK_ALLOC_PIN | LOCK_ALLOC_PAGED));
|
|
ASSERT((flags & (LOCK_ALLOC_PIN | LOCK_ALLOC_PAGED)) != (LOCK_ALLOC_PIN | LOCK_ALLOC_PAGED));
|
|
|
|
if ( !(flags & LOCK_ALLOC_REINIT)) {
|
|
/*
|
|
* Initialize lock in case allocation fails.
|
|
* Not necessary in re-init case, since it should have worked
|
|
* the first time.
|
|
*/
|
|
((simple_lock_t)lockaddr)->_slock = 0;
|
|
}
|
|
|
|
/*
|
|
* Allocate a secondary structure to hold lock instrumentation data,
|
|
* if one is not already present. In the re-init case, where it might
|
|
* be present, we depend on the caller to set *lockaddr to zero (as
|
|
* opposed to leaving it uninitialized) to allow us to tell if this
|
|
* needs to be done.
|
|
*/
|
|
if (!(flags & LOCK_ALLOC_REINIT) || 0 == ((simple_lock_t)lockaddr)->_slock)
|
|
/* allocate from the pinned pool */
|
|
if (flags & LOCK_ALLOC_PIN){
|
|
/* unsafely check if new page is needed */
|
|
if (pinned_cb.free < lock_highwatermark) {
|
|
if ((pinned_cb.allocated < (MAX_LOCK - LOCK_PER_PAGE)) && (csa->intpri == INTBASE)) {
|
|
/* pin page outside of lock */
|
|
address_pinned = pinned_cb.start;
|
|
pin_rc = ltpin(address_pinned, PAGESIZE);
|
|
}
|
|
}
|
|
|
|
old_pri = disable_lock(INTMAX, &pinned_cb.pool_lock);
|
|
|
|
/* if the highwater mark has been crossed, a new page of lock
|
|
* instrumentation structure needs to be allocated.
|
|
* The allocation (pinning of the page) is not performed if
|
|
* the last page of structures has already been allocated or
|
|
* if the requestor is disabled and the free list is empty or
|
|
* no more pinnable pages are available
|
|
*/
|
|
|
|
if ((pin_rc == 0) && (pinned_cb.start == address_pinned)) {
|
|
/* page was pinned prior to taking the lock by caller */
|
|
if (pinned_cb.free) {
|
|
/* end_list points to unallocated lock */
|
|
ASSERT(pinned_cb.free_list != NULL);
|
|
pinned_cb.end_list->lo_next = pinned_cb.start;
|
|
} else {
|
|
ASSERT(pinned_cb.free_list == NULL);
|
|
pinned_cb.free_list = pinned_cb.start;
|
|
}
|
|
for( l = &(pinned_cb.start[0]); l < &(pinned_cb.start[LOCK_PER_PAGE]); l++) {
|
|
l->lo_next = l + 1;
|
|
l->lockname = NULL;
|
|
#ifdef DEBUG
|
|
l->dbg_zero = 0; /* matches 'lockname' field for non-DEBUG locks */
|
|
l->dbg_flags = 0; /* clear debug flags */
|
|
#endif
|
|
}
|
|
pinned_cb.start = l;
|
|
(--l)->lo_next = NULL;
|
|
pinned_cb.end_list = l;
|
|
pinned_cb.allocated += LOCK_PER_PAGE;
|
|
pinned_cb.free += LOCK_PER_PAGE;
|
|
}
|
|
|
|
/* need to check if free count is non-zero */
|
|
if (pinned_cb.free == 0) {
|
|
unlock_enable(old_pri, &pinned_cb.pool_lock);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* At least one free entry has been found--use it
|
|
* Note the assumption that the structures for simple and
|
|
* complex locks are sufficiently alike so that the one for
|
|
* simple locks can be used for both.
|
|
*/
|
|
((simple_lock_t)lockaddr)->_slockp = pinned_cb.free_list;
|
|
pinned_cb.free_list = (((simple_lock_t)lockaddr)->_slockp)->lo_next;
|
|
pinned_cb.free--;
|
|
#ifdef DEBUG
|
|
assert(!((((simple_lock_t)lockaddr)->_slockp)->dbg_flags & LOCK_IS_ALLOCATED));
|
|
#endif
|
|
bzero(((simple_lock_t)lockaddr)->_slockp,sizeof(struct lock_data_instrumented));
|
|
(((simple_lock_t)lockaddr)->_slockp)->lock_control_word.s_lock = SIMPLE_LOCK_AVAIL;
|
|
#ifdef DEBUG
|
|
(((simple_lock_t)lockaddr)->_slockp)->dbg_flags |= LOCK_IS_ALLOCATED;
|
|
(((simple_lock_t)lockaddr)->_slockp)->lock_lr = link_register;
|
|
(((simple_lock_t)lockaddr)->_slockp)->lock_caller = myself->t_tid;
|
|
(((simple_lock_t)lockaddr)->_slockp)->lock_cpuid = get_processor_id();
|
|
#endif
|
|
unlock_enable(old_pri, &pinned_cb.pool_lock);
|
|
}
|
|
|
|
/* allocate from the pageable pool */
|
|
else {
|
|
assert(csa->intpri == INTBASE);
|
|
simple_lock (&pageable_cb.pool_lock);
|
|
|
|
/* if the highwater mark has been crossed, a new page of lock
|
|
* instrumentation structure needs to be allocated.
|
|
* The allocation is not performed if the last page of structures
|
|
* has already been allocated; in this case if the free list is empty
|
|
* lock_alloc returns
|
|
*/
|
|
|
|
if (pageable_cb.free < lock_highwatermark) {
|
|
if (pageable_cb.allocated < (MAX_LOCK - LOCK_PER_PAGE)) {
|
|
if (pageable_cb.free) {
|
|
/* end_list points to unallocated lock */
|
|
ASSERT(pageable_cb.free_list != NULL);
|
|
pageable_cb.end_list->lo_next = pageable_cb.start;
|
|
} else {
|
|
ASSERT(pageable_cb.free_list == NULL);
|
|
pageable_cb.free_list = pageable_cb.start;
|
|
}
|
|
for( l = &(pageable_cb.start[0]); l < &(pageable_cb.start[LOCK_PER_PAGE]); l++) {
|
|
l->lo_next = l + 1;
|
|
l->lockname = NULL;
|
|
#ifdef DEBUG
|
|
l->dbg_zero = 0; /* matches 'lockname' field for non-DEBUG locks */
|
|
l->dbg_flags = 0; /* clear debug flags */
|
|
#endif
|
|
}
|
|
pageable_cb.start = l;
|
|
(--l)->lo_next = NULL;
|
|
pageable_cb.end_list = l;
|
|
pageable_cb.allocated += LOCK_PER_PAGE;
|
|
pageable_cb.free += LOCK_PER_PAGE;
|
|
}
|
|
else if (pageable_cb.free == 0) {
|
|
simple_unlock(&pageable_cb.pool_lock);
|
|
return;
|
|
}
|
|
}
|
|
/*
|
|
* At least one free entry has been found--use it
|
|
* Note the assumption that the structures for simple and
|
|
* complex locks are sufficiently alike so that the one for
|
|
* simple locks can be used for both.
|
|
*/
|
|
((simple_lock_t)lockaddr)->_slockp = pageable_cb.free_list;
|
|
pageable_cb.free_list = (((simple_lock_t)lockaddr)->_slockp)->lo_next;
|
|
pageable_cb.free--;
|
|
#ifdef DEBUG
|
|
assert(!((((simple_lock_t)lockaddr)->_slockp)->dbg_flags & LOCK_IS_ALLOCATED));
|
|
#endif
|
|
bzero(((simple_lock_t)lockaddr)->_slockp,sizeof(struct lock_data_instrumented));
|
|
(((simple_lock_t)lockaddr)->_slockp)->lock_control_word.s_lock = SIMPLE_LOCK_AVAIL;
|
|
#ifdef DEBUG
|
|
(((simple_lock_t)lockaddr)->_slockp)->dbg_flags |= LOCK_IS_ALLOCATED;
|
|
(((simple_lock_t)lockaddr)->_slockp)->lock_lr = link_register;
|
|
(((simple_lock_t)lockaddr)->_slockp)->lock_caller = myself->t_tid;
|
|
(((simple_lock_t)lockaddr)->_slockp)->lock_cpuid = get_processor_id();
|
|
#endif
|
|
simple_unlock(&pageable_cb.pool_lock);
|
|
}
|
|
|
|
/* set lock class identifier and the occurrence index inside the class */
|
|
|
|
(((simple_lock_t)lockaddr)->_slockp)->lock_id = class;
|
|
(((simple_lock_t)lockaddr)->_slockp)->_occurrence = occurrence;
|
|
|
|
/* define this family of locks */
|
|
family_lock_statistics[class].lock_id = class;
|
|
|
|
if (flags & LOCK_ALLOC_REINIT) {
|
|
/*
|
|
* If this lock is being re-initialized, accumulate and clear its
|
|
* statistics. This allows the lock to be logically re-used, without
|
|
* having to be released and freed.
|
|
*/
|
|
l=((simple_lock_t)lockaddr)->_slockp;
|
|
if (l->acquisitions) {
|
|
fetch_and_add((atomic_p)&family_lock_statistics[l->lock_id].acquisitions,
|
|
l->acquisitions);
|
|
l->acquisitions = 0;
|
|
}
|
|
if (l->misses) {
|
|
fetch_and_add((atomic_p)&family_lock_statistics[l->lock_id].misses,
|
|
l->misses);
|
|
l->misses = 0;
|
|
}
|
|
if (l->sleeps) {
|
|
fetch_and_add((atomic_p)&family_lock_statistics[l->lock_id].sleeps,
|
|
l->sleeps);
|
|
l->sleeps = 0;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* NAME: lock_free_instr
|
|
*
|
|
* FUNCTION: return a lock_data_instrumented structure to the corresponding
|
|
* pool
|
|
*
|
|
*/
|
|
|
|
|
|
void
|
|
lock_free_instr(void *l)
|
|
|
|
{
|
|
struct lock_data_instrumented *lockp;
|
|
int old_pri;
|
|
#ifdef DEBUG
|
|
register int link_register;
|
|
register struct thread *myself = curthread;
|
|
register int lockword;
|
|
|
|
GET_RETURN_ADDRESS(link_register);
|
|
if ( *((int *)l) & INSTR_ON) {
|
|
lockp = (((simple_lock_t)l)->_slockp);
|
|
lockword = *((int *)lockp);
|
|
} else {
|
|
lockword = *((int *)l);
|
|
}
|
|
assert(lockword == SIMPLE_LOCK_AVAIL); /* okay for complex locks, too */
|
|
#endif
|
|
|
|
if ( *((int *)l) & INSTR_ON) {
|
|
lockp = (((simple_lock_t)l)->_slockp);
|
|
|
|
fetch_and_add((atomic_p)&family_lock_statistics[lockp->lock_id].acquisitions, lockp->acquisitions);
|
|
fetch_and_add((atomic_p)&family_lock_statistics[lockp->lock_id].misses, lockp->misses);
|
|
fetch_and_add((atomic_p)&family_lock_statistics[lockp->lock_id].sleeps, lockp->sleeps);
|
|
|
|
if (lockp < lock_pageable) {
|
|
old_pri = disable_lock (INTMAX, &pinned_cb.pool_lock);
|
|
#ifdef DEBUG
|
|
assert(lockp->dbg_flags & LOCK_IS_ALLOCATED);
|
|
lockp->unlock_lr = link_register;
|
|
lockp->unlock_caller = myself->t_tid;
|
|
lockp->unlock_cpuid = get_processor_id();
|
|
#endif
|
|
lockp->lo_next = pinned_cb.free_list;
|
|
if (pinned_cb.free_list == NULL) {
|
|
ASSERT(pinned_cb.free == 0);
|
|
pinned_cb.end_list = lockp;
|
|
}
|
|
lockp->lockname = NULL;
|
|
pinned_cb.free_list = lockp;
|
|
pinned_cb.free++;
|
|
ASSERT(pinned_cb.free_list != NULL);
|
|
#ifdef DEBUG
|
|
lockp->dbg_flags &= ~LOCK_IS_ALLOCATED;
|
|
#else
|
|
/*
|
|
* by zeroing out the lock word, we effectively un-instrument
|
|
* the lock--allowing multiple lock frees to be done without
|
|
* clobbering our control blocks. This should not be done
|
|
* with DEBUG defined so as to catch the culprits
|
|
*/
|
|
*(int *)l = 0;
|
|
#endif
|
|
unlock_enable(old_pri, &pinned_cb.pool_lock);
|
|
}
|
|
else {
|
|
assert(csa->intpri == INTBASE);
|
|
simple_lock (&pageable_cb.pool_lock);
|
|
#ifdef DEBUG
|
|
assert(lockp->dbg_flags & LOCK_IS_ALLOCATED);
|
|
lockp->unlock_lr = link_register;
|
|
lockp->unlock_caller = myself->t_tid;
|
|
lockp->unlock_cpuid = get_processor_id();
|
|
#endif
|
|
lockp->lo_next = pageable_cb.free_list;
|
|
if (pageable_cb.free_list == NULL) {
|
|
ASSERT(pageable_cb.free == 0);
|
|
pageable_cb.end_list = lockp;
|
|
}
|
|
lockp->lockname = NULL;
|
|
pageable_cb.free_list = lockp;
|
|
pageable_cb.free++;
|
|
ASSERT(pageable_cb.free_list != NULL);
|
|
#ifdef DEBUG
|
|
lockp->dbg_flags &= ~LOCK_IS_ALLOCATED;
|
|
#endif
|
|
simple_unlock(&pageable_cb.pool_lock);
|
|
#ifndef DEBUG
|
|
/*
|
|
* by zeroing out the lock word, we effectively un-instrument
|
|
* the lock--allowing multiple lock frees to be done without
|
|
* clobbering our control blocks. This should not be done
|
|
* with DEBUG defined so as to catch the culprits
|
|
*/
|
|
*(int *)l = 0;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#else /* _INSTRUMENTATION */
|
|
|
|
#if defined(_KERNSYS)
|
|
#ifdef lock_alloc
|
|
/* need to undo MACRO definition--if this function changes, so must MACRO */
|
|
#undef lock_alloc
|
|
#endif
|
|
|
|
#ifdef lock_free
|
|
/* need to undo MACRO definition--if this function changes, so must MACRO */
|
|
#undef lock_free
|
|
#endif
|
|
#endif
|
|
|
|
/*
|
|
* NAME: lock_alloc
|
|
*
|
|
* FUNCTION: not instrumented version; lock allocation is not needed.
|
|
* just returns
|
|
*
|
|
*
|
|
*/
|
|
|
|
void
|
|
lock_alloc(void *lockaddr, int flags, short class, short occurrence)
|
|
{
|
|
/* validate flags enforced when INSTRUMENTATION is enabled */
|
|
assert(flags & (LOCK_ALLOC_PIN | LOCK_ALLOC_PAGED));
|
|
ASSERT((flags & (LOCK_ALLOC_PIN | LOCK_ALLOC_PAGED)) != (LOCK_ALLOC_PIN | LOCK_ALLOC_PAGED));
|
|
|
|
/* this is just to enforce the necessary priority level when INSTRUMENTATION is enabled */
|
|
if (flags & LOCK_ALLOC_PAGED)
|
|
assert(csa->intpri == INTBASE);
|
|
|
|
#ifdef DEBUG
|
|
if ( !(flags & LOCK_ALLOC_REINIT)) {
|
|
/* make sure assert in lock_free doesn't fire unnecessarily */
|
|
((simple_lock_t)lockaddr)->_slock = 0;
|
|
}
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* NAME: lock_free
|
|
*
|
|
* FUNCTION: not instrumented version; lock allocation is not needed.
|
|
* just returns
|
|
*
|
|
*/
|
|
|
|
void
|
|
lock_free(void *l)
|
|
{
|
|
#ifdef DEBUG
|
|
register int lockword;
|
|
|
|
lockword = *((int *)l);
|
|
assert(lockword == SIMPLE_LOCK_AVAIL); /* okay for complex locks, too */
|
|
#endif
|
|
|
|
/* we cannot enforce priority levels here, unfortunately */
|
|
return;
|
|
}
|
|
|
|
#endif
|