diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /nsprpub/pr/src/md/unix/irix.c | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'nsprpub/pr/src/md/unix/irix.c')
-rw-r--r-- | nsprpub/pr/src/md/unix/irix.c | 1648 |
1 files changed, 1648 insertions, 0 deletions
diff --git a/nsprpub/pr/src/md/unix/irix.c b/nsprpub/pr/src/md/unix/irix.c new file mode 100644 index 000000000..c57a07b33 --- /dev/null +++ b/nsprpub/pr/src/md/unix/irix.c @@ -0,0 +1,1648 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <signal.h> + +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/syssgi.h> +#include <sys/time.h> +#include <sys/immu.h> +#include <sys/utsname.h> +#include <sys/sysmp.h> +#include <sys/pda.h> +#include <sys/prctl.h> +#include <sys/wait.h> +#include <sys/resource.h> +#include <sys/procfs.h> +#include <task.h> +#include <dlfcn.h> + +static void _MD_IrixIntervalInit(void); + +#if defined(_PR_PTHREADS) +/* + * for compatibility with classic nspr + */ +void _PR_IRIX_CHILD_PROCESS() +{ +} +#else /* defined(_PR_PTHREADS) */ + +static void irix_detach_sproc(void); +char *_nspr_sproc_private; /* ptr. to private region in every sproc */ + +extern PRUintn _pr_numCPU; + +typedef struct nspr_arena { + PRCList links; + usptr_t *usarena; +} nspr_arena; + +#define ARENA_PTR(qp) \ + ((nspr_arena *) ((char*) (qp) - offsetof(nspr_arena , links))) + +static usptr_t *alloc_new_arena(void); + +PRCList arena_list = PR_INIT_STATIC_CLIST(&arena_list); +ulock_t arena_list_lock; +nspr_arena first_arena; +int _nspr_irix_arena_cnt = 1; + +PRCList sproc_list = PR_INIT_STATIC_CLIST(&sproc_list); +ulock_t sproc_list_lock; + +typedef struct sproc_data { + void (*entry) (void *, size_t); + unsigned inh; + void *arg; + caddr_t sp; + size_t len; + int *pid; + int creator_pid; +} sproc_data; + +typedef struct sproc_params { + PRCList links; + sproc_data sd; +} sproc_params; + +#define SPROC_PARAMS_PTR(qp) \ + ((sproc_params *) ((char*) (qp) - offsetof(sproc_params , links))) + +long _nspr_irix_lock_cnt = 0; +long _nspr_irix_sem_cnt = 0; +long _nspr_irix_pollsem_cnt = 0; + +usptr_t *_pr_usArena; +ulock_t _pr_heapLock; + +usema_t *_pr_irix_exit_sem; +PRInt32 _pr_irix_exit_now = 0; +PRInt32 _pr_irix_process_exit_code = 0; /* exit code for PR_ProcessExit */ +PRInt32 _pr_irix_process_exit = 0; /* process exiting due to call to + PR_ProcessExit */ + +int _pr_irix_primoridal_cpu_fd[2] = { -1, -1 }; +static void (*libc_exit)(int) = NULL; +static void *libc_handle = NULL; + +#define _NSPR_DEF_INITUSERS 100 /* default value of CONF_INITUSERS */ +#define _NSPR_DEF_INITSIZE (4 * 1024 * 1024) /* 4 MB */ + +int _irix_initusers = _NSPR_DEF_INITUSERS; +int _irix_initsize = _NSPR_DEF_INITSIZE; + +PRIntn _pr_io_in_progress, _pr_clock_in_progress; + +PRInt32 _pr_md_irix_sprocs_created, _pr_md_irix_sprocs_failed; +PRInt32 _pr_md_irix_sprocs = 1; +PRCList _pr_md_irix_sproc_list = +PR_INIT_STATIC_CLIST(&_pr_md_irix_sproc_list); + +sigset_t ints_off; +extern sigset_t timer_set; + +#if !defined(PR_SETABORTSIG) +#define PR_SETABORTSIG 18 +#endif +/* + * terminate the entire application if any sproc exits abnormally + */ +PRBool _nspr_terminate_on_error = PR_TRUE; + +/* + * exported interface to set the shared arena parameters + */ +void _PR_Irix_Set_Arena_Params(PRInt32 initusers, PRInt32 initsize) +{ + _irix_initusers = initusers; + _irix_initsize = initsize; +} + +static usptr_t *alloc_new_arena() +{ + return(usinit("/dev/zero")); +} + +static PRStatus new_poll_sem(struct _MDThread *mdthr, int val) +{ +PRIntn _is; +PRStatus rv = PR_SUCCESS; +usema_t *sem = NULL; +PRCList *qp; +nspr_arena *arena; +usptr_t *irix_arena; +PRThread *me = _MD_GET_ATTACHED_THREAD(); + + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(_is); + _PR_LOCK(arena_list_lock); + for (qp = arena_list.next; qp != &arena_list; qp = qp->next) { + arena = ARENA_PTR(qp); + sem = usnewpollsema(arena->usarena, val); + if (sem != NULL) { + mdthr->cvar_pollsem = sem; + mdthr->pollsem_arena = arena->usarena; + break; + } + } + if (sem == NULL) { + /* + * If no space left in the arena allocate a new one. + */ + if (errno == ENOMEM) { + arena = PR_NEWZAP(nspr_arena); + if (arena != NULL) { + irix_arena = alloc_new_arena(); + if (irix_arena) { + PR_APPEND_LINK(&arena->links, &arena_list); + _nspr_irix_arena_cnt++; + arena->usarena = irix_arena; + sem = usnewpollsema(arena->usarena, val); + if (sem != NULL) { + mdthr->cvar_pollsem = sem; + mdthr->pollsem_arena = arena->usarena; + } else + rv = PR_FAILURE; + } else { + PR_DELETE(arena); + rv = PR_FAILURE; + } + + } else + rv = PR_FAILURE; + } else + rv = PR_FAILURE; + } + _PR_UNLOCK(arena_list_lock); + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(_is); + if (rv == PR_SUCCESS) + _MD_ATOMIC_INCREMENT(&_nspr_irix_pollsem_cnt); + return rv; +} + +static void free_poll_sem(struct _MDThread *mdthr) +{ +PRIntn _is; +PRThread *me = _MD_GET_ATTACHED_THREAD(); + + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(_is); + usfreepollsema(mdthr->cvar_pollsem, mdthr->pollsem_arena); + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(_is); + _MD_ATOMIC_DECREMENT(&_nspr_irix_pollsem_cnt); +} + +static PRStatus new_lock(struct _MDLock *lockp) +{ +PRIntn _is; +PRStatus rv = PR_SUCCESS; +ulock_t lock = NULL; +PRCList *qp; +nspr_arena *arena; +usptr_t *irix_arena; +PRThread *me = _MD_GET_ATTACHED_THREAD(); + + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(_is); + _PR_LOCK(arena_list_lock); + for (qp = arena_list.next; qp != &arena_list; qp = qp->next) { + arena = ARENA_PTR(qp); + lock = usnewlock(arena->usarena); + if (lock != NULL) { + lockp->lock = lock; + lockp->arena = arena->usarena; + break; + } + } + if (lock == NULL) { + /* + * If no space left in the arena allocate a new one. + */ + if (errno == ENOMEM) { + arena = PR_NEWZAP(nspr_arena); + if (arena != NULL) { + irix_arena = alloc_new_arena(); + if (irix_arena) { + PR_APPEND_LINK(&arena->links, &arena_list); + _nspr_irix_arena_cnt++; + arena->usarena = irix_arena; + lock = usnewlock(irix_arena); + if (lock != NULL) { + lockp->lock = lock; + lockp->arena = arena->usarena; + } else + rv = PR_FAILURE; + } else { + PR_DELETE(arena); + rv = PR_FAILURE; + } + + } else + rv = PR_FAILURE; + } else + rv = PR_FAILURE; + } + _PR_UNLOCK(arena_list_lock); + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(_is); + if (rv == PR_SUCCESS) + _MD_ATOMIC_INCREMENT(&_nspr_irix_lock_cnt); + return rv; +} + +static void free_lock(struct _MDLock *lockp) +{ +PRIntn _is; +PRThread *me = _MD_GET_ATTACHED_THREAD(); + + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(_is); + usfreelock(lockp->lock, lockp->arena); + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(_is); + _MD_ATOMIC_DECREMENT(&_nspr_irix_lock_cnt); +} + +void _MD_FREE_LOCK(struct _MDLock *lockp) +{ + PRIntn _is; + PRThread *me = _MD_GET_ATTACHED_THREAD(); + + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(_is); + free_lock(lockp); + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(_is); +} + +/* + * _MD_get_attached_thread + * Return the thread pointer of the current thread if it is attached. + * + * This function is needed for Irix because the thread-local-storage is + * implemented by mmapin'g a page with the MAP_LOCAL flag. This causes the + * sproc-private page to inherit contents of the page of the caller of sproc(). + */ +PRThread *_MD_get_attached_thread(void) +{ + + if (_MD_GET_SPROC_PID() == get_pid()) + return _MD_THIS_THREAD(); + else + return 0; +} + +/* + * _MD_get_current_thread + * Return the thread pointer of the current thread (attaching it if + * necessary) + */ +PRThread *_MD_get_current_thread(void) +{ +PRThread *me; + + me = _MD_GET_ATTACHED_THREAD(); + if (NULL == me) { + me = _PRI_AttachThread( + PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL, 0); + } + PR_ASSERT(me != NULL); + return(me); +} + +/* + * irix_detach_sproc + * auto-detach a sproc when it exits + */ +void irix_detach_sproc(void) +{ +PRThread *me; + + me = _MD_GET_ATTACHED_THREAD(); + if ((me != NULL) && (me->flags & _PR_ATTACHED)) { + _PRI_DetachThread(); + } +} + + +PRStatus _MD_NEW_LOCK(struct _MDLock *lockp) +{ + PRStatus rv; + PRIntn is; + PRThread *me = _MD_GET_ATTACHED_THREAD(); + + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + rv = new_lock(lockp); + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); + return rv; +} + +static void +sigchld_handler(int sig) +{ + pid_t pid; + int status; + + /* + * If an sproc exited abnormally send a SIGKILL signal to all the + * sprocs in the process to terminate the application + */ + while ((pid = waitpid(0, &status, WNOHANG)) > 0) { + if (WIFSIGNALED(status) && ((WTERMSIG(status) == SIGSEGV) || + (WTERMSIG(status) == SIGBUS) || + (WTERMSIG(status) == SIGABRT) || + (WTERMSIG(status) == SIGILL))) { + + prctl(PR_SETEXITSIG, SIGKILL); + _exit(status); + } + } +} + +static void save_context_and_block(int sig) +{ +PRThread *me = _PR_MD_CURRENT_THREAD(); +_PRCPU *cpu = _PR_MD_CURRENT_CPU(); + + /* + * save context + */ + (void) setjmp(me->md.jb); + /* + * unblock the suspending thread + */ + if (me->cpu) { + /* + * I am a cpu thread, not a user-created GLOBAL thread + */ + unblockproc(cpu->md.suspending_id); + } else { + unblockproc(me->md.suspending_id); + } + /* + * now, block current thread + */ + blockproc(getpid()); +} + +/* +** The irix kernel has a bug in it which causes async connect's which are +** interrupted by a signal to fail terribly (EADDRINUSE is returned). +** We work around the bug by blocking signals during the async connect +** attempt. +*/ +PRInt32 _MD_irix_connect( + PRInt32 osfd, const PRNetAddr *addr, PRInt32 addrlen, PRIntervalTime timeout) +{ + PRInt32 rv; + sigset_t oldset; + + sigprocmask(SIG_BLOCK, &ints_off, &oldset); + rv = connect(osfd, addr, addrlen); + sigprocmask(SIG_SETMASK, &oldset, 0); + + return(rv); +} + +#include "prprf.h" + +/********************************************************************/ +/********************************************************************/ +/*************** Various thread like things for IRIX ****************/ +/********************************************************************/ +/********************************************************************/ + +void *_MD_GetSP(PRThread *t) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + void *sp; + + if (me == t) + (void) setjmp(t->md.jb); + + sp = (void *)(t->md.jb[JB_SP]); + PR_ASSERT((sp >= (void *) t->stack->stackBottom) && + (sp <= (void *) (t->stack->stackBottom + t->stack->stackSize))); + return(sp); +} + +void _MD_InitLocks() +{ + char buf[200]; + char *init_users, *init_size; + + PR_snprintf(buf, sizeof(buf), "/dev/zero"); + + if (init_users = getenv("_NSPR_IRIX_INITUSERS")) + _irix_initusers = atoi(init_users); + + if (init_size = getenv("_NSPR_IRIX_INITSIZE")) + _irix_initsize = atoi(init_size); + + usconfig(CONF_INITUSERS, _irix_initusers); + usconfig(CONF_INITSIZE, _irix_initsize); + usconfig(CONF_AUTOGROW, 1); + usconfig(CONF_AUTORESV, 1); + if (usconfig(CONF_ARENATYPE, US_SHAREDONLY) < 0) { + perror("PR_Init: unable to config mutex arena"); + exit(-1); + } + + _pr_usArena = usinit(buf); + if (!_pr_usArena) { + fprintf(stderr, + "PR_Init: Error - unable to create lock/monitor arena\n"); + exit(-1); + } + _pr_heapLock = usnewlock(_pr_usArena); + _nspr_irix_lock_cnt++; + + arena_list_lock = usnewlock(_pr_usArena); + _nspr_irix_lock_cnt++; + + sproc_list_lock = usnewlock(_pr_usArena); + _nspr_irix_lock_cnt++; + + _pr_irix_exit_sem = usnewsema(_pr_usArena, 0); + _nspr_irix_sem_cnt = 1; + + first_arena.usarena = _pr_usArena; + PR_INIT_CLIST(&first_arena.links); + PR_APPEND_LINK(&first_arena.links, &arena_list); +} + +/* _PR_IRIX_CHILD_PROCESS is a private API for Server group */ +void _PR_IRIX_CHILD_PROCESS() +{ +extern PRUint32 _pr_global_threads; + + PR_ASSERT(_PR_MD_CURRENT_CPU() == _pr_primordialCPU); + PR_ASSERT(_pr_numCPU == 1); + PR_ASSERT(_pr_global_threads == 0); + /* + * save the new pid + */ + _pr_primordialCPU->md.id = getpid(); + _MD_SET_SPROC_PID(getpid()); +} + +static PRStatus pr_cvar_wait_sem(PRThread *thread, PRIntervalTime timeout) +{ + int rv; + +#ifdef _PR_USE_POLL + struct pollfd pfd; + int msecs; + + if (timeout == PR_INTERVAL_NO_TIMEOUT) + msecs = -1; + else + msecs = PR_IntervalToMilliseconds(timeout); +#else + struct timeval tv, *tvp; + fd_set rd; + + if(timeout == PR_INTERVAL_NO_TIMEOUT) + tvp = NULL; + else { + tv.tv_sec = PR_IntervalToSeconds(timeout); + tv.tv_usec = PR_IntervalToMicroseconds( + timeout - PR_SecondsToInterval(tv.tv_sec)); + tvp = &tv; + } + FD_ZERO(&rd); + FD_SET(thread->md.cvar_pollsemfd, &rd); +#endif + + /* + * call uspsema only if a previous select call on this semaphore + * did not timeout + */ + if (!thread->md.cvar_pollsem_select) { + rv = _PR_WAIT_SEM(thread->md.cvar_pollsem); + PR_ASSERT(rv >= 0); + } else + rv = 0; +again: + if(!rv) { +#ifdef _PR_USE_POLL + pfd.events = POLLIN; + pfd.fd = thread->md.cvar_pollsemfd; + rv = _MD_POLL(&pfd, 1, msecs); +#else + rv = _MD_SELECT(thread->md.cvar_pollsemfd + 1, &rd, NULL,NULL,tvp); +#endif + if ((rv == -1) && (errno == EINTR)) { + rv = 0; + goto again; + } + PR_ASSERT(rv >= 0); + } + + if (rv > 0) { + /* + * acquired the semaphore, call uspsema next time + */ + thread->md.cvar_pollsem_select = 0; + return PR_SUCCESS; + } else { + /* + * select timed out; must call select, not uspsema, when trying + * to acquire the semaphore the next time + */ + thread->md.cvar_pollsem_select = 1; + return PR_FAILURE; + } +} + +PRStatus _MD_wait(PRThread *thread, PRIntervalTime ticks) +{ + if ( thread->flags & _PR_GLOBAL_SCOPE ) { + _MD_CHECK_FOR_EXIT(); + if (pr_cvar_wait_sem(thread, ticks) == PR_FAILURE) { + _MD_CHECK_FOR_EXIT(); + /* + * wait timed out + */ + _PR_THREAD_LOCK(thread); + if (thread->wait.cvar) { + /* + * The thread will remove itself from the waitQ + * of the cvar in _PR_WaitCondVar + */ + thread->wait.cvar = NULL; + thread->state = _PR_RUNNING; + _PR_THREAD_UNLOCK(thread); + } else { + _PR_THREAD_UNLOCK(thread); + /* + * This thread was woken up by a notifying thread + * at the same time as a timeout; so, consume the + * extra post operation on the semaphore + */ + _MD_CHECK_FOR_EXIT(); + pr_cvar_wait_sem(thread, PR_INTERVAL_NO_TIMEOUT); + } + _MD_CHECK_FOR_EXIT(); + } + } else { + _PR_MD_SWITCH_CONTEXT(thread); + } + return PR_SUCCESS; +} + +PRStatus _MD_WakeupWaiter(PRThread *thread) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRIntn is; + + PR_ASSERT(_pr_md_idle_cpus >= 0); + if (thread == NULL) { + if (_pr_md_idle_cpus) + _MD_Wakeup_CPUs(); + } else if (!_PR_IS_NATIVE_THREAD(thread)) { + if (_pr_md_idle_cpus) + _MD_Wakeup_CPUs(); + } else { + PR_ASSERT(_PR_IS_NATIVE_THREAD(thread)); + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + _MD_CVAR_POST_SEM(thread); + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); + } + return PR_SUCCESS; +} + +void create_sproc (void (*entry) (void *, size_t), unsigned inh, + void *arg, caddr_t sp, size_t len, int *pid) +{ +sproc_params sparams; +char data; +int rv; +PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (!_PR_IS_NATIVE_THREAD(me) && (_PR_MD_CURRENT_CPU()->id == 0)) { + *pid = sprocsp(entry, /* startup func */ + inh, /* attribute flags */ + arg, /* thread param */ + sp, /* stack address */ + len); /* stack size */ + } else { + sparams.sd.entry = entry; + sparams.sd.inh = inh; + sparams.sd.arg = arg; + sparams.sd.sp = sp; + sparams.sd.len = len; + sparams.sd.pid = pid; + sparams.sd.creator_pid = getpid(); + _PR_LOCK(sproc_list_lock); + PR_APPEND_LINK(&sparams.links, &sproc_list); + rv = write(_pr_irix_primoridal_cpu_fd[1], &data, 1); + PR_ASSERT(rv == 1); + _PR_UNLOCK(sproc_list_lock); + blockproc(getpid()); + } +} + +/* + * _PR_MD_WAKEUP_PRIMORDIAL_CPU + * + * wakeup cpu 0 + */ + +void _PR_MD_WAKEUP_PRIMORDIAL_CPU() +{ +char data = '0'; +int rv; + + rv = write(_pr_irix_primoridal_cpu_fd[1], &data, 1); + PR_ASSERT(rv == 1); +} + +/* + * _PR_MD_primordial_cpu + * + * process events that need to executed by the primordial cpu on each + * iteration through the idle loop + */ + +void _PR_MD_primordial_cpu() +{ +PRCList *qp; +sproc_params *sp; +int pid; + + _PR_LOCK(sproc_list_lock); + while ((qp = sproc_list.next) != &sproc_list) { + sp = SPROC_PARAMS_PTR(qp); + PR_REMOVE_LINK(&sp->links); + pid = sp->sd.creator_pid; + (*(sp->sd.pid)) = sprocsp(sp->sd.entry, /* startup func */ + sp->sd.inh, /* attribute flags */ + sp->sd.arg, /* thread param */ + sp->sd.sp, /* stack address */ + sp->sd.len); /* stack size */ + unblockproc(pid); + } + _PR_UNLOCK(sproc_list_lock); +} + +PRStatus _MD_CreateThread(PRThread *thread, +void (*start)(void *), +PRThreadPriority priority, +PRThreadScope scope, +PRThreadState state, +PRUint32 stackSize) +{ + typedef void (*SprocEntry) (void *, size_t); + SprocEntry spentry = (SprocEntry)start; + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRInt32 pid; + PRStatus rv; + + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + thread->md.cvar_pollsem_select = 0; + thread->flags |= _PR_GLOBAL_SCOPE; + + thread->md.cvar_pollsemfd = -1; + if (new_poll_sem(&thread->md,0) == PR_FAILURE) { + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); + return PR_FAILURE; + } + thread->md.cvar_pollsemfd = + _PR_OPEN_POLL_SEM(thread->md.cvar_pollsem); + if ((thread->md.cvar_pollsemfd < 0)) { + free_poll_sem(&thread->md); + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); + return PR_FAILURE; + } + + create_sproc(spentry, /* startup func */ + PR_SALL, /* attribute flags */ + (void *)thread, /* thread param */ + NULL, /* stack address */ + stackSize, &pid); /* stack size */ + if (pid > 0) { + _MD_ATOMIC_INCREMENT(&_pr_md_irix_sprocs_created); + _MD_ATOMIC_INCREMENT(&_pr_md_irix_sprocs); + rv = PR_SUCCESS; + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); + return rv; + } else { + close(thread->md.cvar_pollsemfd); + thread->md.cvar_pollsemfd = -1; + free_poll_sem(&thread->md); + thread->md.cvar_pollsem = NULL; + _MD_ATOMIC_INCREMENT(&_pr_md_irix_sprocs_failed); + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); + return PR_FAILURE; + } +} + +void _MD_CleanThread(PRThread *thread) +{ + if (thread->flags & _PR_GLOBAL_SCOPE) { + close(thread->md.cvar_pollsemfd); + thread->md.cvar_pollsemfd = -1; + free_poll_sem(&thread->md); + thread->md.cvar_pollsem = NULL; + } +} + +void _MD_SetPriority(_MDThread *thread, PRThreadPriority newPri) +{ + return; +} + +extern void _MD_unix_terminate_waitpid_daemon(void); + +void +_MD_CleanupBeforeExit(void) +{ + extern PRInt32 _pr_cpus_exit; + + _MD_unix_terminate_waitpid_daemon(); + + _pr_irix_exit_now = 1; + if (_pr_numCPU > 1) { + /* + * Set a global flag, and wakeup all cpus which will notice the flag + * and exit. + */ + _pr_cpus_exit = getpid(); + _MD_Wakeup_CPUs(); + while(_pr_numCPU > 1) { + _PR_WAIT_SEM(_pr_irix_exit_sem); + _pr_numCPU--; + } + } + /* + * cause global threads on the recycle list to exit + */ + _PR_DEADQ_LOCK; + if (_PR_NUM_DEADNATIVE != 0) { + PRThread *thread; + PRCList *ptr; + + ptr = _PR_DEADNATIVEQ.next; + while( ptr != &_PR_DEADNATIVEQ ) { + thread = _PR_THREAD_PTR(ptr); + _MD_CVAR_POST_SEM(thread); + ptr = ptr->next; + } + } + _PR_DEADQ_UNLOCK; + while(_PR_NUM_DEADNATIVE > 1) { + _PR_WAIT_SEM(_pr_irix_exit_sem); + _PR_DEC_DEADNATIVE; + } +} + +#ifdef _PR_HAVE_SGI_PRDA_PROCMASK +extern void __sgi_prda_procmask(int); +#endif + +PRStatus +_MD_InitAttachedThread(PRThread *thread, PRBool wakeup_parent) +{ + PRStatus rv = PR_SUCCESS; + + if (thread->flags & _PR_GLOBAL_SCOPE) { + if (new_poll_sem(&thread->md,0) == PR_FAILURE) { + return PR_FAILURE; + } + thread->md.cvar_pollsemfd = + _PR_OPEN_POLL_SEM(thread->md.cvar_pollsem); + if ((thread->md.cvar_pollsemfd < 0)) { + free_poll_sem(&thread->md); + return PR_FAILURE; + } + if (_MD_InitThread(thread, PR_FALSE) == PR_FAILURE) { + close(thread->md.cvar_pollsemfd); + thread->md.cvar_pollsemfd = -1; + free_poll_sem(&thread->md); + thread->md.cvar_pollsem = NULL; + return PR_FAILURE; + } + } + return rv; +} + +PRStatus +_MD_InitThread(PRThread *thread, PRBool wakeup_parent) +{ + struct sigaction sigact; + PRStatus rv = PR_SUCCESS; + + if (thread->flags & _PR_GLOBAL_SCOPE) { + thread->md.id = getpid(); + setblockproccnt(thread->md.id, 0); + _MD_SET_SPROC_PID(getpid()); +#ifdef _PR_HAVE_SGI_PRDA_PROCMASK + /* + * enable user-level processing of sigprocmask(); this is an + * undocumented feature available in Irix 6.2, 6.3, 6.4 and 6.5 + */ + __sgi_prda_procmask(USER_LEVEL); +#endif + /* + * set up SIGUSR1 handler; this is used to save state + */ + sigact.sa_handler = save_context_and_block; + sigact.sa_flags = SA_RESTART; + /* + * Must mask clock interrupts + */ + sigact.sa_mask = timer_set; + sigaction(SIGUSR1, &sigact, 0); + + + /* + * PR_SETABORTSIG is a new command implemented in a patch to + * Irix 6.2, 6.3 and 6.4. This causes a signal to be sent to all + * sprocs in the process when one of them terminates abnormally + * + */ + if (prctl(PR_SETABORTSIG, SIGKILL) < 0) { + /* + * if (errno == EINVAL) + * + * PR_SETABORTSIG not supported under this OS. + * You may want to get a recent kernel rollup patch that + * supports this feature. + */ + } + /* + * SIGCLD handler for detecting abormally-terminating + * sprocs and for reaping sprocs + */ + sigact.sa_handler = sigchld_handler; + sigact.sa_flags = SA_RESTART; + sigact.sa_mask = ints_off; + sigaction(SIGCLD, &sigact, NULL); + } + return rv; +} + +/* + * PR_Cleanup should be executed on the primordial sproc; migrate the thread + * to the primordial cpu + */ + +void _PR_MD_PRE_CLEANUP(PRThread *me) +{ +PRIntn is; +_PRCPU *cpu = _pr_primordialCPU; + + PR_ASSERT(cpu); + + me->flags |= _PR_BOUND_THREAD; + + if (me->cpu->id != 0) { + _PR_INTSOFF(is); + _PR_RUNQ_LOCK(cpu); + me->cpu = cpu; + me->state = _PR_RUNNABLE; + _PR_ADD_RUNQ(me, cpu, me->priority); + _PR_RUNQ_UNLOCK(cpu); + _MD_Wakeup_CPUs(); + + _PR_MD_SWITCH_CONTEXT(me); + + _PR_FAST_INTSON(is); + PR_ASSERT(me->cpu->id == 0); + } +} + +/* + * process exiting + */ +PR_EXTERN(void ) _MD_exit(PRIntn status) +{ +PRThread *me = _PR_MD_CURRENT_THREAD(); + + /* + * the exit code of the process is the exit code of the primordial + * sproc + */ + if (!_PR_IS_NATIVE_THREAD(me) && (_PR_MD_CURRENT_CPU()->id == 0)) { + /* + * primordial sproc case: call _exit directly + * Cause SIGKILL to be sent to other sprocs + */ + prctl(PR_SETEXITSIG, SIGKILL); + _exit(status); + } else { + int rv; + char data; + sigset_t set; + + /* + * non-primordial sproc case: cause the primordial sproc, cpu 0, + * to wakeup and call _exit + */ + _pr_irix_process_exit = 1; + _pr_irix_process_exit_code = status; + rv = write(_pr_irix_primoridal_cpu_fd[1], &data, 1); + PR_ASSERT(rv == 1); + /* + * block all signals and wait for SIGKILL to terminate this sproc + */ + sigfillset(&set); + sigsuspend(&set); + /* + * this code doesn't (shouldn't) execute + */ + prctl(PR_SETEXITSIG, SIGKILL); + _exit(status); + } +} + +/* + * Override the exit() function in libc to cause the process to exit + * when the primodial/main nspr thread calls exit. Calls to exit by any + * other thread simply result in a call to the exit function in libc. + * The exit code of the process is the exit code of the primordial + * sproc. + */ + +void exit(int status) +{ +PRThread *me, *thr; +PRCList *qp; + + if (!_pr_initialized) { + if (!libc_exit) { + + if (!libc_handle) + libc_handle = dlopen("libc.so",RTLD_NOW); + if (libc_handle) + libc_exit = (void (*)(int)) dlsym(libc_handle, "exit"); + } + if (libc_exit) + (*libc_exit)(status); + else + _exit(status); + } + + me = _PR_MD_CURRENT_THREAD(); + + if (me == NULL) /* detached thread */ + (*libc_exit)(status); + + PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || + (_PR_MD_CURRENT_CPU())->id == me->cpu->id); + + if (me->flags & _PR_PRIMORDIAL) { + + me->flags |= _PR_BOUND_THREAD; + + PR_ASSERT((_PR_MD_CURRENT_CPU())->id == me->cpu->id); + if (me->cpu->id != 0) { + _PRCPU *cpu = _pr_primordialCPU; + PRIntn is; + + _PR_INTSOFF(is); + _PR_RUNQ_LOCK(cpu); + me->cpu = cpu; + me->state = _PR_RUNNABLE; + _PR_ADD_RUNQ(me, cpu, me->priority); + _PR_RUNQ_UNLOCK(cpu); + _MD_Wakeup_CPUs(); + + _PR_MD_SWITCH_CONTEXT(me); + + _PR_FAST_INTSON(is); + } + + PR_ASSERT((_PR_MD_CURRENT_CPU())->id == 0); + + if (prctl(PR_GETNSHARE) > 1) { +#define SPROC_EXIT_WAIT_TIME 5 + int sleep_cnt = SPROC_EXIT_WAIT_TIME; + + /* + * sprocs still running; caue cpus and recycled global threads + * to exit + */ + _pr_irix_exit_now = 1; + if (_pr_numCPU > 1) { + _MD_Wakeup_CPUs(); + } + _PR_DEADQ_LOCK; + if (_PR_NUM_DEADNATIVE != 0) { + PRThread *thread; + PRCList *ptr; + + ptr = _PR_DEADNATIVEQ.next; + while( ptr != &_PR_DEADNATIVEQ ) { + thread = _PR_THREAD_PTR(ptr); + _MD_CVAR_POST_SEM(thread); + ptr = ptr->next; + } + } + + while (sleep_cnt-- > 0) { + if (waitpid(0, NULL, WNOHANG) >= 0) + sleep(1); + else + break; + } + prctl(PR_SETEXITSIG, SIGKILL); + } + (*libc_exit)(status); + } else { + /* + * non-primordial thread; simply call exit in libc. + */ + (*libc_exit)(status); + } +} + + +void +_MD_InitRunningCPU(_PRCPU *cpu) +{ + extern int _pr_md_pipefd[2]; + + _MD_unix_init_running_cpu(cpu); + cpu->md.id = getpid(); + _MD_SET_SPROC_PID(getpid()); + if (_pr_md_pipefd[0] >= 0) { + _PR_IOQ_MAX_OSFD(cpu) = _pr_md_pipefd[0]; +#ifndef _PR_USE_POLL + FD_SET(_pr_md_pipefd[0], &_PR_FD_READ_SET(cpu)); +#endif + } +} + +void +_MD_ExitThread(PRThread *thread) +{ + if (thread->flags & _PR_GLOBAL_SCOPE) { + _MD_ATOMIC_DECREMENT(&_pr_md_irix_sprocs); + _MD_CLEAN_THREAD(thread); + _MD_SET_CURRENT_THREAD(NULL); + } +} + +void +_MD_SuspendCPU(_PRCPU *cpu) +{ + PRInt32 rv; + + cpu->md.suspending_id = getpid(); + rv = kill(cpu->md.id, SIGUSR1); + PR_ASSERT(rv == 0); + /* + * now, block the current thread/cpu until woken up by the suspended + * thread from it's SIGUSR1 signal handler + */ + blockproc(getpid()); + +} + +void +_MD_ResumeCPU(_PRCPU *cpu) +{ + unblockproc(cpu->md.id); +} + +#if 0 +/* + * save the register context of a suspended sproc + */ +void get_context(PRThread *thr) +{ + int len, fd; + char pidstr[24]; + char path[24]; + + /* + * open the file corresponding to this process in procfs + */ + sprintf(path,"/proc/%s","00000"); + len = strlen(path); + sprintf(pidstr,"%d",thr->md.id); + len -= strlen(pidstr); + sprintf(path + len,"%s",pidstr); + fd = open(path,O_RDONLY); + if (fd >= 0) { + (void) ioctl(fd, PIOCGREG, thr->md.gregs); + close(fd); + } + return; +} +#endif /* 0 */ + +void +_MD_SuspendThread(PRThread *thread) +{ + PRInt32 rv; + + PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) && + _PR_IS_GCABLE_THREAD(thread)); + + thread->md.suspending_id = getpid(); + rv = kill(thread->md.id, SIGUSR1); + PR_ASSERT(rv == 0); + /* + * now, block the current thread/cpu until woken up by the suspended + * thread from it's SIGUSR1 signal handler + */ + blockproc(getpid()); +} + +void +_MD_ResumeThread(PRThread *thread) +{ + PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) && + _PR_IS_GCABLE_THREAD(thread)); + (void)unblockproc(thread->md.id); +} + +/* + * return the set of processors available for scheduling procs in the + * "mask" argument + */ +PRInt32 _MD_GetThreadAffinityMask(PRThread *unused, PRUint32 *mask) +{ + PRInt32 nprocs, rv; + struct pda_stat *pstat; +#define MAX_PROCESSORS 32 + + nprocs = sysmp(MP_NPROCS); + if (nprocs < 0) + return(-1); + pstat = (struct pda_stat*)PR_MALLOC(sizeof(struct pda_stat) * nprocs); + if (pstat == NULL) + return(-1); + rv = sysmp(MP_STAT, pstat); + if (rv < 0) { + PR_DELETE(pstat); + return(-1); + } + /* + * look at the first 32 cpus + */ + nprocs = (nprocs > MAX_PROCESSORS) ? MAX_PROCESSORS : nprocs; + *mask = 0; + while (nprocs) { + if ((pstat->p_flags & PDAF_ENABLED) && + !(pstat->p_flags & PDAF_ISOLATED)) { + *mask |= (1 << pstat->p_cpuid); + } + nprocs--; + pstat++; + } + return 0; +} + +static char *_thr_state[] = { + "UNBORN", + "RUNNABLE", + "RUNNING", + "LOCK_WAIT", + "COND_WAIT", + "JOIN_WAIT", + "IO_WAIT", + "SUSPENDED", + "DEAD" +}; + +void _PR_List_Threads() +{ + PRThread *thr; + void *handle; + struct _PRCPU *cpu; + PRCList *qp; + int len, fd; + char pidstr[24]; + char path[24]; + prpsinfo_t pinfo; + + + printf("\n%s %-s\n"," ","LOCAL Threads"); + printf("%s %-s\n"," ","----- -------"); + printf("%s %-14s %-10s %-12s %-3s %-10s %-10s %-12s\n\n"," ", + "Thread", "State", "Wait-Handle", + "Cpu","Stk-Base","Stk-Sz","SP"); + for (qp = _PR_ACTIVE_LOCAL_THREADQ().next; + qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) { + thr = _PR_ACTIVE_THREAD_PTR(qp); + printf("%s 0x%-12x %-10s "," ",thr,_thr_state[thr->state]); + if (thr->state == _PR_LOCK_WAIT) + handle = thr->wait.lock; + else if (thr->state == _PR_COND_WAIT) + handle = thr->wait.cvar; + else + handle = NULL; + if (handle) + printf("0x%-10x ",handle); + else + printf("%-12s "," "); + printf("%-3d ",thr->cpu->id); + printf("0x%-8x ",thr->stack->stackBottom); + printf("0x%-8x ",thr->stack->stackSize); + printf("0x%-10x\n",thr->md.jb[JB_SP]); + } + + printf("\n%s %-s\n"," ","GLOBAL Threads"); + printf("%s %-s\n"," ","------ -------"); + printf("%s %-14s %-6s %-12s %-12s %-12s %-12s\n\n"," ","Thread", + "Pid","State","Wait-Handle", + "Stk-Base","Stk-Sz"); + + for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next; + qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) { + thr = _PR_ACTIVE_THREAD_PTR(qp); + if (thr->cpu != NULL) + continue; /* it is a cpu thread */ + printf("%s 0x%-12x %-6d "," ",thr,thr->md.id); + /* + * check if the sproc is still running + * first call prctl(PR_GETSHMASK,pid) to check if + * the process is part of the share group (the pid + * could have been recycled by the OS) + */ + if (prctl(PR_GETSHMASK,thr->md.id) < 0) { + printf("%-12s\n","TERMINATED"); + continue; + } + /* + * Now, check if the sproc terminated and is in zombie + * state + */ + sprintf(path,"/proc/pinfo/%s","00000"); + len = strlen(path); + sprintf(pidstr,"%d",thr->md.id); + len -= strlen(pidstr); + sprintf(path + len,"%s",pidstr); + fd = open(path,O_RDONLY); + if (fd >= 0) { + if (ioctl(fd, PIOCPSINFO, &pinfo) < 0) + printf("%-12s ","TERMINATED"); + else if (pinfo.pr_zomb) + printf("%-12s ","TERMINATED"); + else + printf("%-12s ",_thr_state[thr->state]); + close(fd); + } else { + printf("%-12s ","TERMINATED"); + } + + if (thr->state == _PR_LOCK_WAIT) + handle = thr->wait.lock; + else if (thr->state == _PR_COND_WAIT) + handle = thr->wait.cvar; + else + handle = NULL; + if (handle) + printf("%-12x ",handle); + else + printf("%-12s "," "); + printf("0x%-10x ",thr->stack->stackBottom); + printf("0x%-10x\n",thr->stack->stackSize); + } + + printf("\n%s %-s\n"," ","CPUs"); + printf("%s %-s\n"," ","----"); + printf("%s %-14s %-6s %-12s \n\n"," ","Id","Pid","State"); + + + for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) { + cpu = _PR_CPU_PTR(qp); + printf("%s %-14d %-6d "," ",cpu->id,cpu->md.id); + /* + * check if the sproc is still running + * first call prctl(PR_GETSHMASK,pid) to check if + * the process is part of the share group (the pid + * could have been recycled by the OS) + */ + if (prctl(PR_GETSHMASK,cpu->md.id) < 0) { + printf("%-12s\n","TERMINATED"); + continue; + } + /* + * Now, check if the sproc terminated and is in zombie + * state + */ + sprintf(path,"/proc/pinfo/%s","00000"); + len = strlen(path); + sprintf(pidstr,"%d",cpu->md.id); + len -= strlen(pidstr); + sprintf(path + len,"%s",pidstr); + fd = open(path,O_RDONLY); + if (fd >= 0) { + if (ioctl(fd, PIOCPSINFO, &pinfo) < 0) + printf("%-12s\n","TERMINATED"); + else if (pinfo.pr_zomb) + printf("%-12s\n","TERMINATED"); + else + printf("%-12s\n","RUNNING"); + close(fd); + } else { + printf("%-12s\n","TERMINATED"); + } + + } + fflush(stdout); +} +#endif /* defined(_PR_PTHREADS) */ + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#if !defined(_PR_PTHREADS) + if (isCurrent) { + (void) setjmp(t->md.jb); + } + *np = sizeof(t->md.jb) / sizeof(PRWord); + return (PRWord *) (t->md.jb); +#else + *np = 0; + return NULL; +#endif +} + +void _MD_EarlyInit(void) +{ +#if !defined(_PR_PTHREADS) + char *eval; + int fd; + extern int __ateachexit(void (*func)(void)); + + sigemptyset(&ints_off); + sigaddset(&ints_off, SIGALRM); + sigaddset(&ints_off, SIGIO); + sigaddset(&ints_off, SIGCLD); + + if (eval = getenv("_NSPR_TERMINATE_ON_ERROR")) + _nspr_terminate_on_error = (0 == atoi(eval) == 0) ? PR_FALSE : PR_TRUE; + + fd = open("/dev/zero",O_RDWR , 0); + if (fd < 0) { + perror("open /dev/zero failed"); + exit(1); + } + /* + * Set up the sproc private data area. + * This region exists at the same address, _nspr_sproc_private, for + * every sproc, but each sproc gets a private copy of the region. + */ + _nspr_sproc_private = (char*)mmap(0, _pr_pageSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE| MAP_LOCAL, fd, 0); + if (_nspr_sproc_private == (void*)-1) { + perror("mmap /dev/zero failed"); + exit(1); + } + _MD_SET_SPROC_PID(getpid()); + close(fd); + __ateachexit(irix_detach_sproc); +#endif + _MD_IrixIntervalInit(); +} /* _MD_EarlyInit */ + +void _MD_IrixInit(void) +{ +#if !defined(_PR_PTHREADS) + struct sigaction sigact; + PRThread *me = _PR_MD_CURRENT_THREAD(); + int rv; + +#ifdef _PR_HAVE_SGI_PRDA_PROCMASK + /* + * enable user-level processing of sigprocmask(); this is an undocumented + * feature available in Irix 6.2, 6.3, 6.4 and 6.5 + */ + __sgi_prda_procmask(USER_LEVEL); +#endif + + /* + * set up SIGUSR1 handler; this is used to save state + * during PR_SuspendAll + */ + sigact.sa_handler = save_context_and_block; + sigact.sa_flags = SA_RESTART; + sigact.sa_mask = ints_off; + sigaction(SIGUSR1, &sigact, 0); + + /* + * Change the name of the core file from core to core.pid, + * This is inherited by the sprocs created by this process + */ +#ifdef PR_COREPID + prctl(PR_COREPID, 0, 1); +#endif + /* + * Irix-specific terminate on error processing + */ + /* + * PR_SETABORTSIG is a new command implemented in a patch to + * Irix 6.2, 6.3 and 6.4. This causes a signal to be sent to all + * sprocs in the process when one of them terminates abnormally + * + */ + if (prctl(PR_SETABORTSIG, SIGKILL) < 0) { + /* + * if (errno == EINVAL) + * + * PR_SETABORTSIG not supported under this OS. + * You may want to get a recent kernel rollup patch that + * supports this feature. + * + */ + } + /* + * PR_SETEXITSIG - send the SIGCLD signal to the parent + * sproc when any sproc terminates + * + * This is used to cause the entire application to + * terminate when any sproc terminates abnormally by + * receipt of a SIGSEGV, SIGBUS or SIGABRT signal. + * If this is not done, the application may seem + * "hung" to the user because the other sprocs may be + * waiting for resources held by the + * abnormally-terminating sproc. + */ + prctl(PR_SETEXITSIG, 0); + + sigact.sa_handler = sigchld_handler; + sigact.sa_flags = SA_RESTART; + sigact.sa_mask = ints_off; + sigaction(SIGCLD, &sigact, NULL); + + /* + * setup stack fields for the primordial thread + */ + me->stack->stackSize = prctl(PR_GETSTACKSIZE); + me->stack->stackBottom = me->stack->stackTop - me->stack->stackSize; + + rv = pipe(_pr_irix_primoridal_cpu_fd); + PR_ASSERT(rv == 0); +#ifndef _PR_USE_POLL + _PR_IOQ_MAX_OSFD(me->cpu) = _pr_irix_primoridal_cpu_fd[0]; + FD_SET(_pr_irix_primoridal_cpu_fd[0], &_PR_FD_READ_SET(me->cpu)); +#endif + + libc_handle = dlopen("libc.so",RTLD_NOW); + PR_ASSERT(libc_handle != NULL); + libc_exit = (void (*)(int)) dlsym(libc_handle, "exit"); + PR_ASSERT(libc_exit != NULL); + /* dlclose(libc_handle); */ + +#endif /* _PR_PTHREADS */ + + _PR_UnixInit(); +} + +/**************************************************************************/ +/************** code and such for NSPR 2.0's interval times ***************/ +/**************************************************************************/ + +#define PR_PSEC_PER_SEC 1000000000000ULL /* 10^12 */ + +#ifndef SGI_CYCLECNTR_SIZE +#define SGI_CYCLECNTR_SIZE 165 /* Size user needs to use to read CC */ +#endif + +static PRIntn mmem_fd = -1; +static PRIntn clock_width = 0; +static void *iotimer_addr = NULL; +static PRUint32 pr_clock_mask = 0; +static PRUint32 pr_clock_shift = 0; +static PRIntervalTime pr_ticks = 0; +static PRUint32 pr_clock_granularity = 1; +static PRUint32 pr_previous = 0, pr_residual = 0; +static PRUint32 pr_ticks_per_second = 0; + +extern PRIntervalTime _PR_UNIX_GetInterval(void); +extern PRIntervalTime _PR_UNIX_TicksPerSecond(void); + +static void _MD_IrixIntervalInit(void) +{ + /* + * As much as I would like, the service available through this + * interface on R3000's (aka, IP12) just isn't going to make it. + * The register is only 24 bits wide, and rolls over at a verocious + * rate. + */ + PRUint32 one_tick = 0; + struct utsname utsinfo; + uname(&utsinfo); + if ((strncmp("IP12", utsinfo.machine, 4) != 0) + && ((mmem_fd = open("/dev/mmem", O_RDONLY)) != -1)) + { + int poffmask = getpagesize() - 1; + __psunsigned_t phys_addr, raddr, cycleval; + + phys_addr = syssgi(SGI_QUERY_CYCLECNTR, &cycleval); + raddr = phys_addr & ~poffmask; + iotimer_addr = mmap( + 0, poffmask, PROT_READ, MAP_PRIVATE, mmem_fd, (__psint_t)raddr); + + clock_width = syssgi(SGI_CYCLECNTR_SIZE); + if (clock_width < 0) + { + /* + * We must be executing on a 6.0 or earlier system, since the + * SGI_CYCLECNTR_SIZE call is not supported. + * + * The only pre-6.1 platforms with 64-bit counters are + * IP19 and IP21 (Challenge, PowerChallenge, Onyx). + */ + if (!strncmp(utsinfo.machine, "IP19", 4) || + !strncmp(utsinfo.machine, "IP21", 4)) + clock_width = 64; + else + clock_width = 32; + } + + /* + * 'cycleval' is picoseconds / increment of the counter. + * I'm pushing for a tick to be 100 microseconds, 10^(-4). + * That leaves 10^(-8) left over, or 10^8 / cycleval. + * Did I do that right? + */ + + one_tick = 100000000UL / cycleval ; /* 100 microseconds */ + + while (0 != one_tick) + { + pr_clock_shift += 1; + one_tick = one_tick >> 1; + pr_clock_granularity = pr_clock_granularity << 1; + } + pr_clock_mask = pr_clock_granularity - 1; /* to make a mask out of it */ + pr_ticks_per_second = PR_PSEC_PER_SEC + / ((PRUint64)pr_clock_granularity * (PRUint64)cycleval); + + iotimer_addr = (void*) + ((__psunsigned_t)iotimer_addr + (phys_addr & poffmask)); + } + else + { + pr_ticks_per_second = _PR_UNIX_TicksPerSecond(); + } +} /* _MD_IrixIntervalInit */ + +PRIntervalTime _MD_IrixIntervalPerSec(void) +{ + return pr_ticks_per_second; +} + +PRIntervalTime _MD_IrixGetInterval(void) +{ + if (mmem_fd != -1) + { + if (64 == clock_width) + { + PRUint64 temp = *(PRUint64*)iotimer_addr; + pr_ticks = (PRIntervalTime)(temp >> pr_clock_shift); + } + else + { + PRIntervalTime ticks = pr_ticks; + PRUint32 now = *(PRUint32*)iotimer_addr, temp; + PRUint32 residual = pr_residual, previous = pr_previous; + + temp = now - previous + residual; + residual = temp & pr_clock_mask; + ticks += temp >> pr_clock_shift; + + pr_previous = now; + pr_residual = residual; + pr_ticks = ticks; + } + } + else + { + /* + * No fast access. Use the time of day clock. This isn't the + * right answer since this clock can get set back, tick at odd + * rates, and it's expensive to acqurie. + */ + pr_ticks = _PR_UNIX_GetInterval(); + } + return pr_ticks; +} /* _MD_IrixGetInterval */ + |