1
0
Files
irix-657m-src/irix/kern/io/usync.c
2022-09-29 17:59:04 +03:00

1179 lines
27 KiB
C

/**************************************************************************
* *
* Copyright (C) 1990-1993 Silicon Graphics, Inc. *
* *
* These coded instructions, statements, and computer programs contain *
* unpublished proprietary information of Silicon Graphics, Inc., and *
* are protected by Federal copyright law. They may not be disclosed *
* to third parties or copied or duplicated in any form, in whole or *
* in part, without the prior written consent of Silicon Graphics, Inc. *
* *
**************************************************************************/
#ident "$Revision: 1.56 $"
#include "sys/types.h"
#include "sys/systm.h"
#include "sys/sysmacros.h"
#include "sys/param.h"
#include "sys/errno.h"
#include "sys/poll.h"
#include "ksys/vproc.h"
#include "sys/proc.h"
#include <os/as/region.h> /* XXX */
#include "ksys/vfile.h"
#include "sys/sema.h"
#include "sys/vnode.h"
#include "sys/debug.h"
#include "sys/kmem.h"
#include "sys/cmn_err.h"
#include "sys/usync.h"
#include <sys/conf.h>
#include <sys/runq.h>
#include <sys/ddi.h>
#include <sys/idbgentry.h>
#include <sys/xlate.h>
#include <sys/sema_private.h>
#include <sys/kabi.h>
#include <sys/atomic_ops.h>
#include <sys/idbg.h>
#ifdef CKPT
#include <sys/ckpt.h>
#endif
#include "os/proc/pproc_private.h" /* XXX bogus */
#include <os/as/as_private.h> /* XXX bogus */
#include <sys/kthread.h>
/*
* Usync
*
* Control module for the user-level synchronization of processes or threads
*
*/
/*
* CPR IRIX 6.4/6.5 restart support:
* XXX The following data structure may be removed for all major
* releases of IRIX following (but not including) 6.5.
*
* Version 1 usync data structure format
*/
typedef struct usync_v1_arg {
int ua_version;
__uint64_t ua_addr;
ushort_t ua_policy;
ushort_t ua_flags;
usync_sigevent_t ua_sigev;
__uint64_t ua_reserved[2];
} usync_v1_arg_t;
/*
* Usync object
*/
typedef struct usync_obj {
sv_t uo_wait; /* wait object */
int uo_count; /* number of blocked procs */
int uo_prepost; /* prepost value */
caddr_t uo_handle; /* vnode or region pointer */
off_t uo_off; /* vnode or region offset */
int uo_flag; /* debug identification flags*/
pid_t uo_notify_pid; /* notification owner */
long uo_notify_stime; /* detects proc pid reuse */
uint uo_notify_exec; /* detects proc execs */
int uo_handoff; /* handoff state */
struct usync_obj *uo_prev; /* hash list pointer */
struct usync_obj *uo_next; /* hash list pointer */
mutex_t uo_lock; /* protects usync_obj */
int uo_refs; /* usync_obj reference count */
} usync_obj_t;
/*
* Usync object flags
*/
#define UO_FLAG_PRIO 0x01
#define UO_FLAG_DEAD 0x02
#define UO_FLAG_MQ_NOTIFY 0x10 /* Debug hint */
#define UO_FLAG_MUTEX 0x20 /* Debug hint */
#define UO_FLAG_CV 0x40 /* Debug hint */
/*
* Usync object macros
*/
#define UO_LOCK(uo) mutex_lock(&uo->uo_lock, PZERO)
#define UO_UNLOCK(uo) mutex_unlock(&uo->uo_lock)
#define UO_INC_REF(uo) atomicAddInt(&uo->uo_refs, 1)
#define UO_DEC_REF(uo) atomicAddInt(&uo->uo_refs, -1)
#define UO_TRYDEC_REF(uo) compare_and_dec_int_gt(&uo->uo_refs, 1)
static struct zone *usync_zone;
#define US_DBG_GET_UO 0x1
#define US_DBG_UO_ALLOC 0x2
#define US_DBG_UO_FREE 0x4
#define US_DBG_BLOCK 0x8
#define US_DBG_UNBLOCK 0x10
#define US_DBG_CLEANUP 0x20
#define US_DBG_CTRL 0x40
#define US_DBG_FREE_HNDL 0x80
#define US_DBG_REM_UO 0x100
#ifdef DEBUG
static int usyncdebug; /* runtime debug control */
static int usync_cleanup_cnt;
static int usync_block_cnt;
static int usync_intr_block_cnt;
static int usync_handoff_cnt;
static int usync_timed_block_cnt;
static int usync_unblock_cnt;
static int usync_unblock_all_cnt;
static int usync_get_state_cnt;
static int usync_notify_register_cnt;
static int usync_notify_cnt;
static int usync_notify_delete_cnt;
static int usync_notify_clear_cnt;
static int usync_interrupt_cnt;
#define UDPRINT(debug_lvl,arg) if (usyncdebug & debug_lvl) qprintf arg
#define UDINCR(debug_var) atomicAddInt(&debug_var, 1)
#else /* DEBUG */
#define UDPRINT(debug_lvl,arg)
#define UDINCR(debug_var)
#endif /* DEBUG */
/*
* Hash Table for usync objects
*/
static struct uo_hash {
mutex_t uoh_lock;
usync_obj_t *uoh_next;
usync_obj_t *uoh_cache_list;
int uoh_cache_count;
} *uo_hash;
u_int usync_cache_max = 4;
u_int uo_hash_mask;
#define UO_HASH_SHIFT 4
#define UO_HASH(uo_addr, uo_off) \
(((((__psint_t)uo_addr) >> UO_HASH_SHIFT) + uo_off) & uo_hash_mask)
/*
* mutex/cv to synchronize waiting for mem/freeing up memory
*/
extern int usync_max_objs;
lock_t usync_mem_lock; /* protects usync_objs */
sv_t usync_mem_wait;
int max_usync_objs;
int usync_objs;
extern void qprintf(char *, ...);
/* Local functions */
static int usync_object_get(caddr_t, usync_obj_t **, short);
static void usync_object_remove(usync_obj_t *);
static void usync_print_uo(usync_obj_t *);
static void usync_print_table(void);
static int usync_get_handle(uthread_t *, caddr_t, caddr_t *, off_t *);
/*
* usync_cntl:
*
* System call entry point
*
* Errno values: EINVAL - invalid user address
* EINTR - process interrupted
*/
struct usync_cntla {
sysarg_t cmd;
usync_arg_t *ua;
};
int
usync_cntl(struct usync_cntla *uap, rval_t *rvp)
{
usync_arg_t sa;
caddr_t addr;
usync_obj_t *uo;
usync_obj_t *handoff_uo;
proc_t *p = NULL;
sigval_t sval;
int ret = 0;
vproc_t *vpr;
int nprocs;
int resid;
struct timespec now, timeout, *tsp;
int ver;
#if _K64U64
UDPRINT(US_DBG_CTRL,
("usync_cntl: cmd = 0x%x arg = 0x%llx \n", uap->cmd, uap->ua));
#else
UDPRINT(US_DBG_CTRL,
("usync_cntl: cmd = 0x%x arg = 0x%x \n", uap->cmd, uap->ua));
#endif /* _K64U64 */
if (copyin((caddr_t)uap->ua, (caddr_t)&sa, sizeof(usync_arg_t))) {
/*
* CPR IRIX 6.4/6.5 restart support:
* XXX This version 1 copyin may be removed for all major
* releases of IRIX following (but not including) 6.5
*/
if (copyin((caddr_t)uap->ua,
(caddr_t)&sa,
sizeof(usync_v1_arg_t)))
return EFAULT;
}
ver = sa.ua_version;
if (ver != USYNC_VERSION_2 && ver != USYNC_VERSION_1)
return EINVAL;
addr = (caddr_t ) sa.ua_addr;
if (ret = usync_object_get(addr, &uo, sa.ua_policy))
return (ret);
/*
* uo == locked usync object
*/
ASSERT(MUTEX_OWNER(uo->uo_lock.m_bits) == curthreadp);
switch (uap->cmd) {
case USYNC_HANDOFF:
UDINCR(usync_handoff_cnt);
/*
* Protect against deadlock
*/
if (sa.ua_addr == sa.ua_handoff) {
ret = EDEADLK;
goto error;
}
/*
* Get secondary object to handoff
*/
ret = usync_object_get((caddr_t) sa.ua_handoff,
&handoff_uo,
sa.ua_policy);
if (ret)
goto error;
/*
* Set up for handoff of secondary object
*/
handoff_uo->uo_flag |= UO_FLAG_MUTEX;
handoff_uo->uo_handoff++;
if (sv_signal(&handoff_uo->uo_wait) == 0)
handoff_uo->uo_prepost++;
usync_object_remove(handoff_uo);
/*
* Set up primary object timeout, if specified.
* The specified timeout is an absolute timeout.
*/
if (sa.ua_flags & USYNC_FLAGS_TIMEOUT) {
tsp = &timeout;
tsp->tv_sec = (time_t)sa.ua_sec;
tsp->tv_nsec = (long)sa.ua_nsec;
nanotime(&now);
timespec_sub(tsp, &now);
if (tsp->tv_sec < 0
|| tsp->tv_sec == 0 && tsp->tv_nsec <= 0) {
/* timeout already expired */
ret = ETIMEDOUT;
goto error;
}
} else {
tsp = NULL;
}
/*
* Now wait on primary object
*/
if (uo->uo_flag & UO_FLAG_PRIO) {
kthread_t *kt = private.p_curkthread;
if (sa.ua_flags & USYNC_FLAGS_USERPRI)
kt->k_qkey = sa.ua_userpri;
else
kt->k_qkey = kt->k_pri;
}
uo->uo_flag |= UO_FLAG_CV;
uo->uo_count++;
ret = sv_timedwait_sig_notify(&uo->uo_wait, PZERO, &uo->uo_lock,
0, 0, tsp, NULL);
UO_LOCK(uo);
uo->uo_count--;
if (ret) {
UDINCR(usync_interrupt_cnt);
if (ret == -2)
ret = ETIMEDOUT;
else
ret = EINTR;
}
break;
case USYNC_INTR_BLOCK:
UDINCR(usync_intr_block_cnt);
if (uo->uo_prepost == 0) {
/*
* Block
*/
if (uo->uo_flag & UO_FLAG_PRIO) {
kthread_t *kt = private.p_curkthread;
if (sa.ua_flags & USYNC_FLAGS_USERPRI)
kt->k_qkey = sa.ua_userpri;
else
kt->k_qkey = kt->k_pri;
}
uo->uo_count++;
ret = sv_wait_sig(&uo->uo_wait, PZERO, &uo->uo_lock, 0);
UO_LOCK(uo);
uo->uo_count--;
if (ret) {
UDINCR(usync_interrupt_cnt);
ret = EINTR;
}
} else {
/*
* No need to block, since a prepost is pending.
*/
ASSERT(uo->uo_prepost > 0);
uo->uo_prepost--;
}
if (ret == 0 && uo->uo_handoff) {
uo->uo_handoff--;
rvp->r_val1 = 1;
}
break;
case USYNC_BLOCK:
UDINCR(usync_block_cnt);
if (uo->uo_prepost == 0) {
/*
* Block
*/
if (uo->uo_flag & UO_FLAG_PRIO) {
kthread_t *kt = private.p_curkthread;
if (sa.ua_flags & USYNC_FLAGS_USERPRI)
kt->k_qkey = sa.ua_userpri;
else
kt->k_qkey = kt->k_pri;
}
uo->uo_count++;
sv_wait(&uo->uo_wait, PZERO, &uo->uo_lock, 0);
UO_LOCK(uo);
uo->uo_count--;
} else {
/*
* No need to block, since a prepost is pending.
*/
ASSERT(uo->uo_prepost > 0);
uo->uo_prepost--;
}
break;
case USYNC_UNBLOCK:
UDINCR(usync_unblock_cnt);
/*
* CPR IRIX 6.4/6.5 restart support: old semantic
* XXX The following version check may be removed for all
* major releases of IRIX following (but not including) 6.5
*/
if (ver == USYNC_VERSION_1)
sa.ua_flags = 0;
/*
* Unblock one process
*/
if (sv_signal(&uo->uo_wait) == 0) {
if (!(sa.ua_flags & USYNC_FLAGS_PREPOST_NONE)) {
/*
* Prepost!
*
* No one to wake-up! Either processes blocked
* earlier have been signalled, or the unblock
* operation beat the block operation into the
* kernel.
*/
uo->uo_prepost++;
}
}
break;
case USYNC_UNBLOCK_ALL:
UDINCR(usync_unblock_all_cnt);
/*
* Unblock all blocked processes
*/
nprocs = rvp->r_val1 = sv_broadcast(&uo->uo_wait);
if (!(sa.ua_flags & USYNC_FLAGS_PREPOST_NONE)
&& (resid = sa.ua_count - nprocs) > 0)
uo->uo_prepost += resid;
break;
case USYNC_GET_STATE:
UDINCR(usync_get_state_cnt);
/*
* Get the count/prepost state of a usync object
*/
if (uo->uo_prepost) {
rvp->r_val1 = uo->uo_prepost;
if (sa.ua_flags & USYNC_FLAGS_PREPOST_CONSUME)
uo->uo_prepost--;
} else {
int val = 0;
if (sa.ua_flags & USYNC_FLAGS_PREPOST_CONSUME) {
/* inaccuracy ok */
val = uo->uo_count;
} else {
/* accuracy required */
if (uo->uo_count)
val = sv_waitq(&uo->uo_wait);
}
rvp->r_val1 = 0 - val;
}
break;
case USYNC_NOTIFY_REGISTER:
UDINCR(usync_notify_register_cnt);
uo->uo_flag |= UO_FLAG_MQ_NOTIFY;
/*
* check if some other proc is already registered
*/
if ((uo->uo_notify_pid != 0) &&
(vpr = VPROC_LOOKUP(uo->uo_notify_pid)) != NULL) {
VPROC_GET_PROC(vpr, &p);
}
if (p != NULL) {
/*
* another proc registered
*/
if (p->p_start == uo->uo_notify_stime &&
p->p_exec_cnt == uo->uo_notify_exec) {
VPROC_RELE(vpr);
/*
* return EBUSY to indicate that the
* previously registered process still exists
*/
ret = EBUSY;
goto error;
}
VPROC_RELE(vpr);
}
uo->uo_notify_pid = curprocp->p_pid;
uo->uo_notify_stime = curprocp->p_start;
uo->uo_notify_exec = curprocp->p_exec_cnt;
/*
* We increment the prepost here to prevent the uo from
* being removed until the notification is either sent
* or deleted
*/
uo->uo_prepost = 1;
break;
case USYNC_NOTIFY:
UDINCR(usync_notify_cnt);
/*
* send signal to the process that registered for async
* notification
*/
if ((uo->uo_notify_pid != 0) &&
(vpr = VPROC_LOOKUP(uo->uo_notify_pid)) != NULL) {
VPROC_GET_PROC(vpr, &p);
}
if (p != NULL) {
if (p->p_start == uo->uo_notify_stime &&
p->p_exec_cnt == uo->uo_notify_exec) {
sval.sival_ptr =
(void *) sa.ua_sigev.ss_value;
ksigqueue(vpr, sa.ua_sigev.ss_signo,
sa.ua_sigev.ss_si_code, sval, 0);
} else {
/*
* the proc that registered for async
* notification doesn't exist; it's
* pid has been recycled
*/
ret = ENOENT;
}
VPROC_RELE(vpr);
} else
ret = EINVAL;
/*
* remove the notification
*/
uo->uo_notify_pid = 0;
uo->uo_prepost = 0;
break;
case USYNC_NOTIFY_DELETE:
UDINCR(usync_notify_delete_cnt);
/*
* delete the notification registration, if it belongs
* to this process
*/
p = curprocp;
if (p->p_pid == uo->uo_notify_pid &&
p->p_start == uo->uo_notify_stime &&
p->p_exec_cnt == uo->uo_notify_exec) {
uo->uo_notify_pid = 0;
uo->uo_prepost = 0;
} else
ret = EBUSY;
break;
case USYNC_NOTIFY_CLEAR:
UDINCR(usync_notify_clear_cnt);
/*
* delete the notification registration
*/
if (uo->uo_notify_pid)
uo->uo_prepost = 0;
uo->uo_notify_pid = 0;
break;
default:
ret = EINVAL;
}
error:
usync_object_remove(uo);
return (ret);
}
/*
* usync_object_get:
*
* Lookup the usync object associated with addr.
*
* Returns: locked usync object
*/
static int
usync_object_get(caddr_t user_addr, usync_obj_t **uo, short policy)
{
usync_obj_t *tmp_uo;
struct uo_hash *uoh;
caddr_t addr;
off_t off;
int hindex;
int s;
if (usync_get_handle(NULL, user_addr, &addr, &off))
return EINVAL;
#if _K64U64
UDPRINT(US_DBG_GET_UO,
("usync_object_get: user_addr = 0x%llx, addr = 0x%llx off = 0x%llx\n",
user_addr, addr, off));
#else
UDPRINT(US_DBG_GET_UO,
("usync_object_get: user_addr = 0x%x, addr = 0x%x off = 0x%llx\n",
user_addr, addr, off));
#endif
/*
* Hash chain lookup
*/
hindex = UO_HASH(addr, off);
ASSERT((hindex >= 0) && (hindex <= uo_hash_mask));
uoh = &uo_hash[hindex];
start_over:
mutex_lock(&uoh->uoh_lock, PZERO);
for (tmp_uo = uoh->uoh_next; tmp_uo; tmp_uo = tmp_uo->uo_next) {
if ((tmp_uo->uo_handle == addr) && (tmp_uo->uo_off == off))
break;
}
if (tmp_uo) {
/*
* A usync object exists
*/
UO_INC_REF(tmp_uo);
mutex_unlock(&uoh->uoh_lock);
UO_LOCK(tmp_uo);
/*
* Make sure the uo is not marked for dead
*/
if (tmp_uo->uo_flag & UO_FLAG_DEAD) {
usync_object_remove(tmp_uo);
return EINVAL;
}
} else {
/*
* A usync object was not found in the hash table.
* Allocate and initialize a new one.
*/
/* Check hash chain cache first */
if (uoh->uoh_cache_count) {
tmp_uo = uoh->uoh_cache_list;
ASSERT(tmp_uo);
uoh->uoh_cache_list = tmp_uo->uo_next;
uoh->uoh_cache_count--;
} else {
s = mutex_spinlock(&usync_mem_lock);
if (usync_objs >= max_usync_objs) {
mutex_unlock(&uoh->uoh_lock);
if (sv_wait_sig(&usync_mem_wait, PZERO,
&usync_mem_lock, s) == -1) {
return EINTR;
}
goto start_over;
}
usync_objs++;
mutex_spinunlock(&usync_mem_lock, s);
tmp_uo = kmem_zone_alloc(usync_zone, KM_SLEEP);
}
/* insert in hash chain */
tmp_uo->uo_handle = addr;
tmp_uo->uo_off = off;
tmp_uo->uo_next = uoh->uoh_next;
tmp_uo->uo_prev = NULL;
if (uoh->uoh_next)
uoh->uoh_next->uo_prev = tmp_uo;
uoh->uoh_next = tmp_uo;
/* set first reference */
tmp_uo->uo_refs = 1;
/* grab uo lock */
mutex_init(&tmp_uo->uo_lock, MUTEX_DEFAULT, "uo_lock");
UO_LOCK(tmp_uo);
/* release hash lock */
mutex_unlock(&uoh->uoh_lock);
/*
* The new object is now on the hash chain, and we own it.
* Continue with initialization.
*/
tmp_uo->uo_count = 0;
tmp_uo->uo_prepost = 0;
tmp_uo->uo_notify_pid = 0;
tmp_uo->uo_handoff = 0;
if (policy == USYNC_POLICY_PRIORITY) {
sv_init(&tmp_uo->uo_wait, SV_KEYED, "uo_wait:p");
tmp_uo->uo_flag = UO_FLAG_PRIO;
} else {
sv_init(&tmp_uo->uo_wait, SV_FIFO, "uo_wait:f");
tmp_uo->uo_flag = 0;
}
}
*uo = tmp_uo;
return 0;
}
/*
* usync_object_remove:
*
* Remove the usync object if it is no longer needed.
*/
static void
usync_object_remove(usync_obj_t *uo)
{
struct uo_hash *uoh;
int preposts;
int s;
int refs;
/*
* Usync object locking & reference counting
*
* Warning: this isn't as simple as one might think.
*
* A usync object reference count can only be atomically incremented
* when the hash chain lock is held.
*
* A usync object reference count can be atomically decremented
* whenever the decrementor knows it already has a reference
* (no locks are necessary).
*
* A usync object can only be removed when: the hash chain lock is
* held, references are zero, and preposts are zero. The usync object
* lock doesn't need to be acquired when removing the uo, since we
* implicitly have exclusive access to the uo when the hash chain lock
* is acquired AND references are zero.
*
* Preposts can only be modified when the usync object lock is held,
* and the usync object lock can only be acquired if at least one
* reference is outstanding. Therefore, if there are no references,
* the uo lock cannot be acquired and preposts cannot be modified.
*/
ASSERT(uo->uo_refs);
if (preposts = uo->uo_prepost)
UO_DEC_REF(uo);
UO_UNLOCK(uo);
if (preposts || UO_TRYDEC_REF(uo))
return;
/*
* At this point we still hold our reference, and we believe
* we hold the last one, but we won't know for sure until we
* acquire the hash chain lock.
*/
#if _K64U64
UDPRINT(US_DBG_REM_UO, ("usync_object_remove: uo = 0x%llx\n", uo));
#else
UDPRINT(US_DBG_REM_UO, ("usync_object_remove: uo = 0x%x\n", uo));
#endif
/*
* Grab hash chain lock
*/
uoh = &uo_hash[UO_HASH((uo->uo_handle),(uo->uo_off))];
mutex_lock(&uoh->uoh_lock, PZERO);
refs = UO_DEC_REF(uo);
/*
* Check if the usync object can be removed
*/
if (refs == 0 && uo->uo_prepost == 0) {
ASSERT(!(MUTEX_ISLOCKED(&uo->uo_lock)));
ASSERT(!(SV_WAITER(&uo->uo_wait)));
/*
* Remove from the hash chain
*/
if (uo->uo_next)
uo->uo_next->uo_prev = uo->uo_prev;
if (uo->uo_prev)
uo->uo_prev->uo_next = uo->uo_next;
else
uoh->uoh_next = uo->uo_next;
mutex_destroy(&uo->uo_lock);
sv_destroy(&uo->uo_wait);
if (uoh->uoh_cache_count < usync_cache_max) {
uo->uo_next = uoh->uoh_cache_list;
uoh->uoh_cache_list = uo;
uoh->uoh_cache_count++;
mutex_unlock(&uoh->uoh_lock);
} else {
mutex_unlock(&uoh->uoh_lock);
kmem_zone_free(usync_zone, (void *) uo);
s = mutex_spinlock(&usync_mem_lock);
if (usync_objs-- >= max_usync_objs)
sv_broadcast(&usync_mem_wait);
mutex_spinunlock(&usync_mem_lock, s);
}
} else
mutex_unlock(&uoh->uoh_lock);
return;
}
/*
* usync_get_handle:
*
* Find unique handle for a given user address.
*
* The computed handle is either a vnode/offset pair for vnode-backed
* memory, or a region/offset pair for anonymous memory.
*
* The handle is used by the usync module to allocate unique usync
* objects for process synchronization.
*
* A "callback" flag is used for calling the usync module when the
* memory object is unmapped by all processes: RG_USYNC or VUSYNC.
*
* Returns: 0 on success, with addr and off set to the unique handle.
* -1, on failure (in case the user address is invalid)
*/
static int
usync_get_handle(uthread_t *ut, caddr_t user_addr, caddr_t *addr, off_t *off)
{
register preg_t *prp;
register reg_t *rp;
pgno_t rpn;
vasid_t vasid;
pas_t *pas;
ppas_t *ppas;
if (ut == NULL)
as_lookup_current(&vasid);
else
as_lookup(ut->ut_asid, &vasid);
pas = VASID_TO_PAS(vasid);
ppas = (ppas_t *)vasid.vas_pasid;
mraccess(&pas->pas_aspacelock);
if ((prp = findpreg(pas, ppas, user_addr)) == NULL) {
mrunlock(&pas->pas_aspacelock);
if (ut)
as_rele(vasid);
return -1;
}
rp = prp->p_reg;
/*
* vnode pointer is the handle for vnode-backed objects
* and region pointer is the handle for the anon-backed objects
*/
if (rp->r_anon == NULL) {
ASSERT(rp->r_vnode);
VN_FLAGSET(rp->r_vnode, VUSYNC);
*addr = (caddr_t) rp->r_vnode;
rpn = vtorpn(prp, user_addr);
*off = (off_t) (ctob(rpn) + rp->r_fileoff);
} else {
rp->r_flags |= RG_USYNC;
*addr = (caddr_t) rp;
rpn = vtoapn(prp, user_addr);
*off = (off_t) ctob(rpn);
}
mrunlock(&pas->pas_aspacelock);
*off += poff(user_addr); /* add the offset part */
if (ut)
as_rele(vasid);
return 0;
}
/*
* usyncinit
*/
void
usyncinit(void)
{
int i;
max_usync_objs = usync_max_objs;
/*
* Allocate locks, synch variables, hash table
*/
usync_zone = kmem_zone_init(sizeof(usync_obj_t),"usync_objects");
spinlock_init(&usync_mem_lock, "usync_mem_lock");
sv_init(&usync_mem_wait, SV_PRIO, "usync_mem_wait");
for (i = 1; i < max_usync_objs; i <<= 1)
;
if (i >= 10)
i /= 10;
uo_hash_mask = i - 1;
uo_hash = kmem_zalloc(i * sizeof (*uo_hash), KM_SLEEP);
while (i-- > 0) {
mutex_init(&(uo_hash[i].uoh_lock), MUTEX_DEFAULT, "uoh_lock");
}
idbg_addfunc("usync_table", usync_print_table);
idbg_addfunc("usync", usync_print_uo);
}
/*
* usync_cleanup:
*
* Free all usync objects allocated for this handle, i.e. for all offsets
* within this mem object, because the object is unmapped by all processes.
*/
void
usync_cleanup(caddr_t handle)
{
usync_obj_t *uo, *uo_tmp;
int i, s, freed = 0;
UDINCR(usync_cleanup_cnt);
for (i = 0; i <= uo_hash_mask; i++) {
mutex_lock(&(uo_hash[i].uoh_lock), PZERO);
uo = uo_hash[i].uoh_next;
while (uo) {
if (uo->uo_handle == handle) {
/*
* We don't bother taking a reference
* here, since we are not going to drop
* the hash chain lock.
*/
UO_LOCK(uo);
/*
* If stragglers exist, wake them up and
* let them handle the tear-down.
*
* Note: some may be blocked waiting for,
* the uo lock. To prevent further operations,
* we mark the uo for dead.
*/
if (uo->uo_refs) {
(void) sv_broadcast(&uo->uo_wait);
uo->uo_flag |= UO_FLAG_DEAD;
uo->uo_prepost = 0;
UO_UNLOCK(uo);
uo = uo->uo_next;
continue;
}
/*
* Otherwise the usync object is no longer
* in use.
*/
ASSERT(!(SV_WAITER(&uo->uo_wait)));
/*
* Remove from the hash list, no caching
*/
if (uo->uo_next)
uo->uo_next->uo_prev = uo->uo_prev;
if (uo->uo_prev)
uo->uo_prev->uo_next = uo->uo_next;
else
uo_hash[i].uoh_next = uo->uo_next;
uo_tmp = uo->uo_next;
/*
* Tear-down synchronizers
*/
UO_UNLOCK(uo);
mutex_destroy(&uo->uo_lock);
sv_destroy(&uo->uo_wait);
kmem_zone_free(usync_zone, (void *) uo);
freed++;
uo = uo_tmp;
} else {
uo = uo->uo_next;
}
}
mutex_unlock(&(uo_hash[i].uoh_lock));
}
s = mutex_spinlock(&usync_mem_lock);
if (usync_objs >= max_usync_objs)
sv_broadcast(&usync_mem_wait);
usync_objs -= freed;
mutex_spinunlock(&usync_mem_lock, s);
return;
}
#ifdef CKPT
/*
* usync_object_ckpt:
*
* Find all usync objects in the region for process p identified by user_addr
*/
int
usync_object_ckpt(uthread_t *ut, caddr_t user_addr, int *count, caddr_t bufaddr)
{
struct uo_hash *uoh;
usync_obj_t *tmp_uo;
caddr_t addr;
off_t off;
int i, j = 0;
usync_ckpt_t ckpt;
if (usync_get_handle(ut, user_addr, &addr, &off))
return EINVAL;
#if _K64U64
UDPRINT(US_DBG_GET_UO,
("usync_object_get: user_addr = 0x%llx, addr = 0x%llx off = 0x%llx\n",
user_addr, addr, off));
#else
UDPRINT(US_DBG_GET_UO,
("usync_object_get: user_addr = 0x%x, addr = 0x%x off = 0x%llx\n",
user_addr, addr, off));
#endif
for (i = 0; i <= uo_hash_mask; i++) {
mutex_lock(&(uo_hash[i].uoh_lock), PZERO);
uoh = &uo_hash[i];
for (tmp_uo = uoh->uoh_next; tmp_uo; tmp_uo = tmp_uo->uo_next) {
if ((tmp_uo->uo_handle == addr)&&(tmp_uo->uo_off >= off)) {
if (j < *count) {
ckpt.off = tmp_uo->uo_off;
ckpt.voff = tmp_uo->uo_off - off;
ckpt.notify = tmp_uo->uo_notify_pid;
ckpt.value = tmp_uo->uo_prepost;
ckpt.handoff = tmp_uo->uo_handoff;
if (tmp_uo->uo_flag & UO_FLAG_PRIO)
ckpt.policy = USYNC_POLICY_PRIORITY;
else
ckpt.policy = USYNC_POLICY_FIFO;
if (copyout((caddr_t)&ckpt, bufaddr, sizeof(ckpt)) < 0)
return EFAULT;
bufaddr += sizeof(ckpt);
}
j++;
}
}
mutex_unlock(&uoh->uoh_lock);
}
*count = j;
return (0);
}
#endif
/*
* print usync_obj struct
*/
static void
usync_print_uo(usync_obj_t *uo)
{
#if _K64U64
qprintf("uo 0x%llx handle 0x%llx offset 0x%llx\n",
uo, uo->uo_handle, uo->uo_off);
qprintf(" lock 0x%llx wait 0x%llx\n", &uo->uo_lock, &uo->uo_wait);
#else
qprintf("uo 0x%x handle 0x%x offset 0x%x\n",
uo, uo->uo_handle, uo->uo_off);
qprintf(" lock 0x%x wait 0x%x\n", &uo->uo_lock, &uo->uo_wait);
#endif
qprintf(" count %d prepost %d refs %d handoff %d\n",
uo->uo_count, uo->uo_prepost, uo->uo_refs, uo->uo_handoff);
qprintf(" flags:");
if (uo->uo_flag & UO_FLAG_DEAD)
qprintf(" DEAD");
if (uo->uo_flag & UO_FLAG_PRIO)
qprintf(" PRIO");
else
qprintf(" FIFO");
if ((uo->uo_flag & ~(UO_FLAG_PRIO|UO_FLAG_DEAD)) == 0)
qprintf(" SEM MQ");
else if (uo->uo_flag & UO_FLAG_MQ_NOTIFY)
qprintf(" MQ_NOTIFY");
else if (uo->uo_flag & UO_FLAG_MUTEX)
qprintf(" MUTEX");
else if (uo->uo_flag & UO_FLAG_CV)
qprintf(" CV");
qprintf("\n");
}
/*
* dump list of block structures
*/
static void
usync_print_table(void)
{
int i;
usync_obj_t *uo;
qprintf("usync_objs = %d\n", usync_objs);
#ifdef DEBUG
qprintf("usync_cleanup_cnt = %d\n", usync_cleanup_cnt);
qprintf("usync_block_cnt = %d\n", usync_block_cnt);
qprintf("usync_intr_block_cnt = %d\n", usync_intr_block_cnt);
qprintf("usync_handoff_cnt = %d\n", usync_handoff_cnt);
qprintf("usync_timed_block_cnt = %d\n", usync_timed_block_cnt);
qprintf("usync_unblock_cnt = %d\n", usync_unblock_cnt);
qprintf("usync_unblock_all_cnt = %d\n", usync_unblock_all_cnt);
qprintf("usync_interrupt_cnt = %d\n", usync_interrupt_cnt);
qprintf("usync_get_state_cnt = %d\n", usync_get_state_cnt);
qprintf("usync_notify_register_cnt = %d\n", usync_notify_register_cnt);
qprintf("usync_notify_cnt = %d\n", usync_notify_cnt);
qprintf("usync_notify_delete_cnt = %d\n", usync_notify_delete_cnt);
qprintf("usync_notify_clear_cnt = %d\n", usync_notify_clear_cnt);
usync_cleanup_cnt = 0;
usync_block_cnt = 0;
usync_intr_block_cnt = 0;
usync_handoff_cnt = 0;
usync_timed_block_cnt = 0;
usync_unblock_cnt = 0;
usync_unblock_all_cnt = 0;
usync_interrupt_cnt = 0;
usync_get_state_cnt = 0;
usync_notify_register_cnt = 0;
usync_notify_cnt = 0;
usync_notify_delete_cnt = 0;
usync_notify_clear_cnt = 0;
#endif /* DEBUG */
qprintf("hash_table_size = %d\n", uo_hash_mask + 1);
for (i=0; i <= uo_hash_mask; i++) {
uo = uo_hash[i].uoh_next;
if (uo || uo_hash[i].uoh_cache_count) {
#if _K64U64
qprintf("hash index [%d] cache %d hlock 0x%llx\n",
i, uo_hash[i].uoh_cache_count,
&uo_hash[i].uoh_lock);
#else
qprintf("hash index [%d] cache %d hlock 0x%x\n",
i, uo_hash[i].uoh_cache_count,
&uo_hash[i].uoh_lock);
#endif
while (uo) {
usync_print_uo(uo);
uo = uo->uo_next;
}
}
}
}