/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1991, 1990 MIPS Computer Systems, Inc.      |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 252.227-7013.  |
 * |         MIPS Computer Systems, Inc.                       |
 * |         950 DeGuigne Avenue                               |
 * |         Sunnyvale, California 94088-3650, USA             |
 * |-----------------------------------------------------------|
 */
#ident "$Header: libmutexc.c,v 2.1.1.3 92/08/25 09:43:40 marker Exp $"

#include "sys/abi_mutex.h"
#include "sys/libmutex.h"

/*
	Forward definitions required for public interfaces
*/

private mutex_test_set_type	init_test_and_set();

private mutex_test_set_type	init_test_then_and();
private mutex_test_set_type	init_test_then_nand();
private mutex_test_set_type	init_test_then_not();
private mutex_test_set_type	init_test_then_nor();
private mutex_test_set_type	init_test_then_xor();
private mutex_test_set_type	init_test_then_or();

private mutex_test_set_type	init_test_then_add();
private mutex_test_set_type	init_add_then_test();

private void			init_mutex_wait();

/*
	Public Interfaces
*/

mutex_test_set_type	(*_test_and_set) ()	= init_test_and_set;

mutex_test_set_type	(*_test_then_and) ()	= init_test_then_and;
mutex_test_set_type	(*_test_then_nand) ()	= init_test_then_and;
mutex_test_set_type	(*_test_then_not) ()	= init_test_then_not;
mutex_test_set_type	(*_test_then_nor) ()	= init_test_then_nor;
mutex_test_set_type	(*_test_then_xor) ()	= init_test_then_xor;
mutex_test_set_type	(*_test_then_or) ()	= init_test_then_or;

mutex_test_set_type	(*_test_then_add) ()	= init_test_then_add;
mutex_test_set_type	(*_add_then_test) ()	= init_add_then_test;

mutex_test_set_type	atomic_op();

void	(*mutex_wait) ()	= init_mutex_wait;
void 	set_mutex_wait_action ();

/*
	Public Configuration Interfaces
*/

mutex_pid_type 	set_mutex_pid ();
mutex_pid_type	get_mutex_pid ();
mutex_pid_type	allocate_mutex_pid ();
void		deallocate_mutex_pid ();

/*
	Assembly language routines - in libmutexs.s
*/

#ifdef MIPS2

extern int	is_mips2();		/* detect Mips2 instruction set	*/

extern int	mips2_test_and_set();	/* test and set using ll/sc	*/
extern int	mips2_test_then_and();	/* test then and using ll/sc	*/
extern int	mips2_test_then_nand();	/* test then nand using ll/sc	*/
extern int	mips2_test_then_not();	/* test then not using ll/sc	*/
extern int	mips2_test_then_nor();	/* test then nor using ll/sc	*/
extern int	mips2_test_then_xor();	/* test then xor using ll/sc	*/
extern int	mips2_test_then_or();	/* test then or using ll/sc	*/
extern int	mips2_test_then_add();	/* test then add using ll/sc	*/
extern int 	mips2_add_then_test();	/* add then test using ll/sc	*/

#endif MIPS2

extern void	_flush_writebuf();	/* flush write buffer		*/
extern void	_read_delay();		/* delay reads 			*/

/*
	Private Variables
*/

private mutex_region_type *mutex_region =  NO_REGION;	/*pointer to mutex region*/

private int	mutex_locked = 0;	/* mutex lock counter			*/
private int	mutex_shmid = -1;	/* shmid of mutex shared memory region	*/
private int	mutex_semid = -1;	/* semid of guarding SysV semaphore	*/
private int	mutex_perm = 0;		/* shared memory region permission	*/
private int	unix_pid = 0;		/* unix pid of this process		*/
private char	*mutex_id = (char *)0;	/* id of mutex region			*/
private mutex_key_type	mutex_key = 0;	/* ipc key for above			*/
private mutex_pid_type	mutex_pid = 0;	/* pid for use by Lamport algorithm	*/
private mutex_proc_type	*mutex_myproc;	/* pointer to this process' proc	*/

private int	mutex_monitors		= MUTEX_MONITORS;
private int	mutex_processes		= DEFAULT_MUTEX_PROCESSES;
private int	mutex_delay		= DEFAULT_MUTEX_DELAY;
private int	mutex_delay_multiplier	= DEFAULT_DELAY_MULTIPLIER;
private int	mutex_maxdelay		= MUTEX_MAXDELAY;


mutex_pid_type 
get_mutex_pid ()
{
	return mutex_pid;
}

mutex_pid_type 
set_mutex_pid (pid)
register mutex_pid_type pid;
{
	mutex_pid_type temp = mutex_pid;

	mutex_pid = pid;
	mutex_myproc = &mutex_region->proc[pid];

	return temp;
}

#ifdef SGI
private boolean
is_sgi ()
{
#ifdef EPIX
	if (syscdc(SYSCDC_IS_SGI) == 1)
		return TRUE;
	else
#endif EPIX
		return FALSE;
}
#endif SGI

#ifdef MP
private boolean 
is_MP ()
{
#ifdef EPIX
	if ((syscdc(SYSCDC_TOTAL_PROCESSORS) > 1))
		return TRUE;
	else
		return FALSE;
#else
	if ((sysmips(MIPS_TOTAL_PROCESSORS) > 1))
		return TRUE;
	else
		return FALSE;
#endif EPIX
}
#endif MP

/* mutex_wait functions */

private void 
UP_mutex_wait (delay)
mutex_delay_type delay;
{
	timeval_type to;
	to.tv_usec = (delay  * 1000) % 1000000;
	to.tv_sec = delay / 1000;
	select (0, 0, 0, 0, &to);
}

#ifdef MP
private void
MP_mutex_wait (delay)
mutex_delay_type delay;
{
	/* do nothing */
}
#endif MP

private void 
init_mutex_wait (delay)
mutex_delay_type delay;
{
	private boolean called = FALSE;
	
	if (!called) {
#ifdef MP
		if (is_MP())
			mutex_wait = MP_mutex_wait;
		else
#endif MP
			mutex_wait = UP_mutex_wait;
	called = TRUE;
	}
	
	mutex_wait (delay);
}

void 
set_mutex_wait_action (mutex_wait_action)
void (*mutex_wait_action)();
{
	mutex_wait = mutex_wait_action;
}

mutex_region_type *
attach_region (key, perm)
mutex_perm_type perm;
mutex_key_type key; 
{
	if (key != 0)
		mutex_key = key;
	else {
		char *s = getenv ("MUTEX_KEY");
		if ((s != (char *)0) && (*s != '\0'))
			mutex_key = atoi (s);
		else
			mutex_key = DEFAULT_MUTEX_KEY;
	}

	if (perm != 0) 
		mutex_perm = perm;
	else {
		char *s = getenv ("MUTEX_PERMISSION");
		if ((s != (char *)0) && (*s != '\0'))
			mutex_perm = atoi (s);
		else
			mutex_perm = DEFAULT_MUTEX_PERMISSION;
	}

	mutex_shmid = shmget (mutex_key, 0, mutex_perm);

	if (mutex_shmid != -1)
		mutex_region = (mutex_region_type *) shmat (mutex_shmid, 0, 0);

	return mutex_region;
}

int
create_region (key, procs, perm)
mutex_perm_type perm;
mutex_key_type key;
int procs;
{
	int size;
	char *s;

	if (key != 0)
		mutex_key = key;
	else {
		char *s = getenv ("MUTEX_KEY");
		if ((s != (char *)0) && (*s != '\0'))
			mutex_key = atoi (s);
		else
			mutex_key = DEFAULT_MUTEX_KEY;
	}

	if (procs > 1)
		mutex_processes = procs;
	else
		mutex_processes =
			getval (s, "MUTEX_PROCESSES", MUTEX_MAXPROCESSES, MUTEX_MINPROCESSES, DEFAULT_MUTEX_PROCESSES);

	size = sizeof (mutex_region_type) + sizeof (mutex_proc_type) * (mutex_processes + 1);

	if (perm != 0) 
		mutex_perm = perm;
	else {
		char *s = getenv ("MUTEX_PERMISSION");
		if ((s != (char *)0) && (*s != '\0'))
			mutex_perm = atoi (s);
		else
			mutex_perm = DEFAULT_MUTEX_PERMISSION;
	}

	mutex_shmid = shmget (mutex_key, size, mutex_perm | IPC_CREAT | IPC_EXCL);

	if (mutex_shmid == -1) perror ("shmget");

	return mutex_shmid;
}

int
lock_mutex ()
{
	static sembuf_type P = {0, -1, 0};
	private boolean called = FALSE;
	int ret = 0;

	if (!called) {
		called = TRUE;
		if ((mutex_semid == -1) && (mutex_key != 0))
			mutex_semid = semget (mutex_key, 0, mutex_perm);
		if (mutex_semid == -1)
			qerror ("libmutex: invalid semid", 1);
	}

	if (mutex_locked)
		++mutex_locked;
	else {
		ret = semop (mutex_semid, &P, 1);
		if (ret == -1) 
			perror ("semop");
		else
			++mutex_locked;
	}

	return ret != -1;
}

int
unlock_mutex ()
{
	static sembuf_type V = {0, 1, 0};
	private boolean called = FALSE;
	int ret = 0;

	if (!called) {
		called = TRUE;
		if ((mutex_semid == -1) && (mutex_key != 0))
			mutex_semid = semget (mutex_key, 0, mutex_perm);
		if (mutex_semid == -1)
			qerror ("libmutex: invalid semid", 2);
	}

	if (mutex_locked) {
		if (--mutex_locked == 0) {
			ret = semop (mutex_semid, &V, 1);
			if (ret == -1) perror ("semop");
		}
	}

	return ret != -1;
}

int
start_mutexd ()
{
	int ret = fork ();
	
	if (ret) 
		if (ret == -1) perror ("fork");
		else mutex_region->mutex_mutexd_pid = ret;
	else {
		char *d = getenv ("MUTEX_MUTEXD");
		if (d == NULL || *d == '\0')
			d = DEFAULT_MUTEXD;
		if (execlp (d, d, mutex_id, 0) == -1)
			perror ("execlp"), qerror ("libmutex: mutexd quits", 3);
	}

	return ret;
}

void 
create_mutex (key, procs, perm)
mutex_perm_type perm;
mutex_key_type key;
int procs;
{
	struct timezone tz;
	mutex_monitor_type *m;
	mutex_region_type *r;
	mutex_proc_type *pp;
	char *d, *s;
	int i;

	if ((mutex_shmid = create_region (mutex_key, procs, perm)) == -1)
		qerror ("libmutex: create_region failed", 4);
	else {
		if ((r = attach_region (key, perm)) == NO_REGION)
			qerror ("libmutex: attach_region failed", 5);
		mutex_region = r;
		gettimeofday (&r->create_time, &tz);
		r->mutex_monitors = mutex_monitors; 
		r->mutex_maxpids = mutex_processes; 
		r->mutex_semid = mutex_semid;
		r->mutex_shmid = mutex_shmid;
		r->mutex_procs = 0;
		m = r->monitor;
#ifdef STATS
		r->mutex_stats = TRUE;
#endif STATS
#ifdef ASSERT
		r->mutex_assert = TRUE;
#endif ASSERT
		for (i = 0; i < mutex_monitors; m++,i++) {
			volatile mutex_parms_type *p = &m->parms;
#ifdef STATS
			volatile mutex_stats_type *s = &m->stats;
#endif STATS
			p->delay_multiplier = mutex_delay_multiplier;
			p->max_delay = mutex_maxdelay;
			p->delay = mutex_delay;
#ifdef STATS
			s->most_retries = 0;
#endif STATS
#ifdef ASSERT
			m->hold = 0;
#endif ASSERT
			m->y = 0;
			m->x = 0;
		}
		pp = &r->proc[1];
		for (i = 0; i <= r->mutex_maxpids; pp++,i++)
			pp->mutex_wanted = NO_MUTEX;
#ifdef SGI
#ifdef EPIX
{
		int i, j, *p, *pp;
		int lockpages = mutex_monitors / HW_LOCKS_PER_PAGE;
		int size = lockpages * MUTEX_PAGESIZE;
		if ((r->hw_locks_shmid = shmget (0, size, mutex_perm)) == -1) {
			destroy_mutex();
			qerror ("libmutex: cannot get hw lock region", 18);
		}
		if ((p = shmat (r->hw_locks_shmid, 0, 0)) == -1) {
			destroy_mutex();
			qerror ("libmutex: cannot attach hw lock region", 19);
		}
		m = r->monitor;
		r->hw_locks_addr = p;
		for (i = lockpages; i > 0; p += MUTEX_PAGESIZE, i -= HW_LOCKS_PER_PAGE)
			if (syscdc(SYSCDC_ATTACH_SEMA_PAGE, p) == -1) {
				destroy_mutex();
				shmdt (r->hw_locks_addr);
				qerror ("libmutex: cannot map hw lock page", 20);
			}
			for (pp = p, j = HW_LOCKS_PER_PAGE; j > 0; j--,pp++,m++)
				m->y = (int) p;
}
#endif EPIX
#endif SGI
		r->mutex_magic = MUTEX_MAGIC;
		r->mutex_status = TRUE;
		if ((r->mutex_mutexd_pid = start_mutexd ()) == -1)
			qerror ("libmutex: cannot exec mutexd()", 6);
		mutex_locked = 1;
		if (!unlock_mutex())
			qerror ("libmutex: unlock_mutex failed", 7);
	}
}

int
destroy_mutex ()
{
	int semid = mutex_region->mutex_semid;
	int shmid = mutex_region->mutex_shmid;
	mutex_region->mutex_status = FALSE;
	mutex_region->mutex_magic = 0;
	shmdt (mutex_region);
	shmctl (shmid, IPC_RMID, NULL);
	semctl (semid, 0, IPC_RMID, 0);
}

void deallocate_mutex_pid (pid)
mutex_pid_type pid;
{
	volatile mutex_region_type *r = mutex_region;
	volatile mutex_monitor_type *m = r->monitor;
	int mm = r->mutex_monitors;
	mutex_mutex_type i;

	if (!lock_mutex())
		qerror ("libmutex: lock_mutex() failed", 8);

	for (i = 0; i <= mm; i++,m++) {
#ifdef ASSERT
		if (m->hold == pid) m->hold = 0;
#endif ASSERT
		if (m->y == pid) m->y = 0;
	}

	r->proc[pid].mutex_wanted = NO_MUTEX;
	r->proc[pid].unix_pid = 0;
	r->mutex_procs--;

	if (!unlock_mutex())
		qerror ("libmutex: unlock_mutex() failed", 9);
}

mutex_pid_type
allocate_mutex_pid ()
{
	volatile mutex_region_type *r = mutex_region;
	volatile mutex_proc_type *p = &r->proc[1];
	mutex_pid_type i, pid = 0;
	int mp = r->mutex_maxpids;

	if (!lock_mutex())
		qerror ("libmutex: lock_mutex() failed", 8);

	for (i = 1; i <= mp && p->unix_pid != 0; i++,p++);
	if (i > mp) 
		for (i = 1, p = &r->proc[1]; i <= mp; i++,p++) {
			if ((p->unix_pid != 0) && is_dead (p->unix_pid)) 
				deallocate_mutex_pid (i);
			if (p->unix_pid == 0) break;
		}
	if (i > mp) return 0;
	p->unix_pid = getpid();
	r->mutex_procs++;

	if (is_dead(r->mutex_mutexd_pid)) start_mutexd ();

	if (!unlock_mutex())
		qerror ("libmutex: unlock_mutex() failed", 9);

	return i;
}

int
attach_mutex (id, perm)
mutex_perm_type perm;
char *id; 
{
	mutex_region_type *r;
	mutex_pid_type pid;

	if ((id == (char *)0) || (*id == '\0'))
		mutex_id = getenv ("MUTEX_ID");
	else 
		mutex_id = id;

	if ((mutex_id != (char *)0) && (mutex_id != '\0'))
		mutex_key = mutex_signature (mutex_id);
	else {
		char *s = getenv ("MUTEX_KEY");
		if ((s != (char *)0) && (*s != '\0'))
			mutex_key = atoi (s);
		else
			mutex_key = DEFAULT_MUTEX_KEY;
	}


	if (perm != 0) 
		mutex_perm = perm;
	else {
		char *s = getenv ("MUTEX_PERMISSION");
		if ((s != (char *)0) && (*s != '\0'))
			mutex_perm = atoi (s);
		else
			mutex_perm = DEFAULT_MUTEX_PERMISSION;
	}

	mutex_semid = semget (mutex_key, 1, mutex_perm|IPC_CREAT| IPC_EXCL);

	if (mutex_semid != -1)
		create_mutex (mutex_key, 0, mutex_perm);
	else if (errno != EEXIST) 
		qerror ("libmutex: attach_mutex: semget", 10);

	mutex_semid = semget (mutex_key, 0, mutex_perm);

	if (mutex_semid == -1) 
		qerror ("libmutex: attach_mutex: semget", 11);

	if ((r = attach_region (mutex_key, perm)) == NO_REGION)
		qerror ("libmutex: attach_region failed", 12);

	mutex_region = r;
	while (r->mutex_magic != MUTEX_MAGIC)
		mutex_wait (DEFAULT_MUTEX_DELAY);

	unix_pid = getpid();
	mutex_processes = r->mutex_maxpids;

#ifdef SGI
#ifdef EPIX
	if (shmat (r->hw_locks_shmid, r->hw_locks_addr, 0) == -1)
		qerror ("libmutex: attach mutex: hw lock attach failed", 17);
#endif EPIX
#endif SGI

	if (pid = allocate_mutex_pid())
		set_mutex_pid (pid);
	else 
		qerror ("libmutex: attach_mutex: no more mutex pids", 13);

	return pid;
}

/*
	Real Code ...
*/

#ifdef FLUSH_WRITEBUF
#define WFLUSH if (r->mutex_flush_writebuf)			\
		 _flush_writebuf (r->mutex_flush_writebuf);
#else
#define WFLUSH
#endif FLUSH_WRITEBUF

#ifdef READ_DELAY
#define RDELAY if (r->mutex_read_delay)				\
		 _read_delay (r->mutex_read_delay);
#else
#define RDELAY
#endif READ_DELAY

/*
	Entry protocols - acquire mutex
*/

private void init_acquire_mutex ();

private void
lamport_entry (addr)
volatile mutex_test_set_type *addr;
{
	static mutex_delay_type delay;
	volatile mutex_test_set_type dummy = *addr;	/* check address */
	mutex_pid_type pid = mutex_pid;

	volatile mutex_region_type *r = mutex_region;
	mutex_mutex_type mutex = PAGE_OFFSET(addr)/sizeof(mutex_test_set_type);
	volatile mutex_monitor_type *m = &r->monitor[mutex];
	volatile mutex_proc_type *p = &r->proc[pid];
	volatile mutex_parms_type *mp = &m->parms;
#ifdef STATS
	volatile mutex_stats_type *s = &m->stats;
	mutex_count_type retries = 0;
#endif STATS

	delay = mp->delay;

#ifdef ASSERT
	assert (pid > 0);
	assert (pid <= r->mutex_maxpids);
#endif ASSERT

start:
	p->mutex_wanted = mutex;
	m->x = pid;
	WFLUSH
	RDELAY
	if (m->y != 0) {
		p->mutex_wanted = NO_MUTEX;
		WFLUSH
		RDELAY
		while (m->y != 0) {
			mutex_wait (delay = MIN (delay * mp->delay_multiplier, mp->max_delay));
#ifdef STATS
			++retries;
#endif STATS
		}
		goto start;
        }
	m->y = pid;
	WFLUSH
	RDELAY
	if (m->x != pid) {
		short i;
		p->mutex_wanted = NO_MUTEX;
		WFLUSH
		for (i = 1; i <= r->mutex_maxpids; i++) {
			while (r->proc[i].mutex_wanted == mutex) {
				mutex_wait (delay = MIN (delay * mp->delay_multiplier, mp->max_delay));
#ifdef STATS
				++retries;
#endif STATS
			}
		}
		RDELAY
		if (m->y != pid) {
			while (m->y != 0) { 
				mutex_wait (delay = MIN (delay * mp->delay_multiplier, mp->max_delay));
#ifdef STATS
				++retries;
#endif STATS
			}
			goto start;
		}
	}

#ifdef ASSERT
	if (r->mutex_assert) {
		assert (m->hold == 0);
		m->hold = pid;
		WFLUSH
	}
#endif ASSERT

#ifdef STATS
	if (r->mutex_stats) {
		if (retries > s->most_retries) 
			s->most_retries = retries;
		s->retries += retries; 
		s->gets++; 
		WFLUSH
	}
#endif STATS

}

#ifdef SGI
private void
sgi_entry (addr)
volatile mutex_test_set_type *addr;
{
	static mutex_delay_type delay;
	volatile mutex_test_set_type dummy = *addr;	/* check address */
	mutex_pid_type pid = mutex_pid;

	volatile mutex_region_type *r = mutex_region;
	mutex_mutex_type mutex = PAGE_OFFSET(addr)/sizeof(mutex_test_set_type);
	volatile mutex_monitor_type *m = &r->monitor[mutex];
	volatile mutex_parms_type *mp = &m->parms;
#ifdef STATS
	volatile mutex_stats_type *s = &m->stats;
	mutex_count_type retries = 0;
#endif STATS

	delay = mp->delay;

#ifdef ASSERT
	assert (pid > 0);
	assert (pid <= r->mutex_maxpids);
#endif ASSERT

	while (*(int *)(m->y)) {
		mutex_wait (delay = MIN (delay * mp->delay_multiplier, mp->max_delay));
#ifdef STATS
		++retries;
#endif STATS
	}

#ifdef ASSERT
	if (r->mutex_assert) {
		assert (m->hold == 0);
		m->hold = pid;
		WFLUSH
	}
#endif ASSERT

#ifdef STATS
	if (r->mutex_stats) {
		if (retries > s->most_retries) 
			s->most_retries = retries;
		s->retries += retries; 
		s->gets++; 
		WFLUSH
	}
#endif STATS

}
#endif SGI

private void (*acquire_mutex)() = init_acquire_mutex;

private void
init_acquire_mutex(addr)
volatile mutex_test_set_type *addr;
{
	private boolean called = FALSE;

	if (!called) {

		mutex_pid_type pid = attach_mutex (0, 0);

		if (pid == 0) 
			qerror ("libmutex: attach_mutex: no more pids", 14);

#ifdef SGI
		if (is_sgi())
			acquire_mutex = sgi_entry;
		else
#endif SGI
			acquire_mutex = lamport_entry;

		called = TRUE;
	}
	acquire_mutex (addr);
}


/*
	Exit protocols - release mutex
*/

private void init_release_mutex();

private void
lamport_exit (addr)
volatile mutex_test_set_type *addr;
{
	mutex_test_set_type dummy = *addr;		/* force BUS or SEGV if bad addr */
	mutex_pid_type pid = mutex_pid;

	volatile mutex_region_type *r = mutex_region;
	volatile mutex_proc_type *p = &r->proc[pid];
	mutex_mutex_type mutex = PAGE_OFFSET(addr)/sizeof(mutex_test_set_type);
	volatile mutex_monitor_type *m = &r->monitor[mutex];

#ifdef ASSERT
	if (r->mutex_assert) {
		assert (pid > 0);
		assert (pid <= r->mutex_maxpids);
		assert (m->hold == pid);
		m->hold = 0;
	}
#endif ASSERT

	m->y = 0;
	p->mutex_wanted = NO_MUTEX;
	WFLUSH
}


#ifdef SGI
private void
sgi_exit (addr)
volatile mutex_test_set_type *addr;
{
	mutex_test_set_type dummy = *addr;		/* force BUS or SEGV if bad addr */
	mutex_pid_type pid = mutex_pid;

	volatile mutex_region_type *r = mutex_region;
	mutex_mutex_type mutex = PAGE_OFFSET(addr)/sizeof(mutex_test_set_type);
	volatile mutex_monitor_type *m = &r->monitor[mutex];

#ifdef ASSERT
	if (r->mutex_assert) {
		assert (pid > 0);
		assert (pid <= r->mutex_maxpids);
		assert (m->hold == pid);
		m->hold = 0;
	}
#endif ASSERT

	*(int *)(m->y) = 0;
}
#endif SGI

private void (*release_mutex)() = init_release_mutex;

private void 
init_release_mutex (addr)
volatile mutex_test_set_type *addr;
{
	private boolean called = FALSE;

	if (!called) {
#ifdef SGI
		if (is_sgi())
			release_mutex = sgi_exit;
		else
#endif SGI
			release_mutex = lamport_exit;
	}
	release_mutex (addr);
}


/*
	Function Implementations - mips1
*/

private mutex_test_set_type
mips1_test_and_set (addr, val)
volatile mutex_test_set_type *addr;
register mutex_test_set_type val;
{
	register mutex_test_set_type temp;

	acquire_mutex (addr);
		temp = *addr;
		*addr = val;
	release_mutex (addr);

	return temp;
}
	
private mutex_test_set_type
mips1_test_then_and (addr, val)
volatile mutex_test_set_type *addr;
register mutex_test_set_type val;
{
	register mutex_test_set_type temp;

	acquire_mutex (addr);
		temp = *addr;
		*addr = temp & val;
	release_mutex (addr);

	return temp;
}
	
private mutex_test_set_type
mips1_test_then_nand (addr, val)
volatile mutex_test_set_type *addr;
register mutex_test_set_type val;
{
	register mutex_test_set_type temp;

	acquire_mutex (addr);
		temp = *addr;
		*addr = ~(temp & val);
	release_mutex (addr);

	return temp;
}
	
private mutex_test_set_type
mips1_test_then_not (addr)
volatile mutex_test_set_type *addr;
{
	register mutex_test_set_type temp;

	acquire_mutex (addr);
		temp = *addr;
		*addr = ~temp;
	release_mutex (addr);

	return temp;
}
	
private mutex_test_set_type
mips1_test_then_nor (addr, val)
volatile mutex_test_set_type *addr;
register mutex_test_set_type val;
{
	register mutex_test_set_type temp;

	acquire_mutex (addr);
		temp = *addr;
		*addr = ~(temp | val);
	release_mutex (addr);

	return temp;
}
	
private mutex_test_set_type
mips1_test_then_xor (addr, val)
volatile mutex_test_set_type *addr;
register mutex_test_set_type val;
{
	register mutex_test_set_type temp;

	acquire_mutex (addr);
		temp = *addr;
		*addr = temp ^ val;
	release_mutex (addr);

	return temp;
}
	
private mutex_test_set_type
mips1_test_then_or (addr, val)
volatile mutex_test_set_type *addr;
register mutex_test_set_type val;
{
	register mutex_test_set_type temp;

	acquire_mutex (addr);
		temp = *addr;
		*addr = temp | val;
	release_mutex (addr);

	return temp;
}
	
private mutex_test_set_type
mips1_test_then_add (addr, val)
volatile mutex_test_set_type *addr;
register mutex_test_set_type val;
{
	register mutex_test_set_type temp;

	acquire_mutex (addr);
		temp = *addr;
		*addr = temp + val;
	release_mutex (addr);

	return temp;
}
	
private mutex_test_set_type
mips1_add_then_test (addr, val)
volatile mutex_test_set_type *addr;
register mutex_test_set_type val;
{
	register mutex_test_set_type temp;
	
	acquire_mutex (addr);
		temp = *addr += val;
	release_mutex (addr);

	return temp;
}

private mutex_test_set_type
init_test_and_set (addr, val)
volatile mutex_test_set_type *addr;
register mutex_test_set_type val;
{
	private boolean called = FALSE;

	if (!called) {
#ifdef MIPS2
		if (is_mips2())
			_test_and_set = mips2_test_and_set;
		else 
#endif MIPS2
			_test_and_set = mips1_test_and_set;
		called = TRUE;
	}

	return _test_and_set (addr, val);
}

private mutex_test_set_type
init_test_then_and (addr, val)
volatile mutex_test_set_type *addr;
register mutex_test_set_type val;
{
	private boolean called = FALSE;

	if (!called) {
#ifdef MIPS2
		if (is_mips2())
			_test_then_and = mips2_test_then_and;
		else 
#endif MIPS2
			_test_then_and = mips1_test_then_and;
		called = TRUE;
	}

	return _test_then_and (addr, val);
}

private mutex_test_set_type
init_test_then_nand (addr, val)
volatile mutex_test_set_type *addr;
register mutex_test_set_type val;
{
	private boolean called = FALSE;

	if (!called) {
#ifdef MIPS2
		if (is_mips2())
			_test_then_nand = mips2_test_then_nand;
		else 
#endif MIPS2
			_test_then_nand = mips1_test_then_nand;
		called = TRUE;
	}

	return _test_then_nand (addr, val);
}

private mutex_test_set_type
init_test_then_not (addr, val)
volatile mutex_test_set_type *addr;
register mutex_test_set_type val;
{
	private boolean called = FALSE;

	if (!called) {
#ifdef MIPS2
		if (is_mips2())
			_test_then_not = mips2_test_then_not;
		else 
#endif MIPS2
			_test_then_not = mips1_test_then_not;
		called = TRUE;
	}

	return _test_then_not (addr, val);
}

private mutex_test_set_type
init_test_then_nor (addr, val)
volatile mutex_test_set_type *addr;
register mutex_test_set_type val;
{
	private boolean called = FALSE;

	if (!called) {
#ifdef MIPS2
		if (is_mips2())
			_test_then_nor = mips2_test_then_nor;
		else 
#endif MIPS2
			_test_then_nor = mips1_test_then_nor;
		called = TRUE;
	}

	return _test_then_nor (addr, val);
}

private mutex_test_set_type
init_test_then_xor (addr, val)
volatile mutex_test_set_type *addr;
register mutex_test_set_type val;
{
	private boolean called = FALSE;

	if (!called) {
#ifdef MIPS2
		if (is_mips2())
			_test_then_xor = mips2_test_then_xor;
		else 
#endif MIPS2
			_test_then_xor = mips1_test_then_xor;
		called = TRUE;
	}

	return _test_then_xor (addr, val);
}

private mutex_test_set_type
init_test_then_or (addr, val)
volatile mutex_test_set_type *addr;
register mutex_test_set_type val;
{
	private boolean called = FALSE;

	if (!called) {
#ifdef MIPS2
		if (is_mips2())
			_test_then_or = mips2_test_then_or;
		else 
#endif MIPS2
			_test_then_or = mips1_test_then_or;
		called = TRUE;
	}

	return _test_then_or (addr, val);
}

private mutex_test_set_type
init_test_then_add (addr, val)
volatile mutex_test_set_type *addr;
register mutex_test_set_type val;
{
	private boolean called = FALSE;

	if (!called) {
#ifdef MIPS2
		if (is_mips2())
			_test_then_add = mips2_test_then_add;
		else 
#endif MIPS2
			_test_then_add = mips1_test_then_add;
		called = TRUE;
	}

	return _test_then_add (addr, val);
}

private mutex_test_set_type
init_add_then_test (addr, val)
volatile mutex_test_set_type *addr;
register mutex_test_set_type val;
{
	private boolean called = FALSE;

	if (!called) {
#ifdef MIPS2
		if (is_mips2())
			_add_then_test = mips2_add_then_test;
		else 
#endif MIPS2
			_add_then_test = mips1_add_then_test;
		called = TRUE;
	}

	return _add_then_test (addr, val);
}

mutex_test_set_type
atomic_op (func, addr, val)
volatile mutex_test_set_type *addr;
register mutex_test_set_type val;
mutex_test_set_type (*func)();
{
	register mutex_test_set_type temp;

	acquire_mutex (addr);
		temp = (*func) (addr, val);
	release_mutex (addr);

	return temp;
}

/*

        ABI mips1 functions

*/

int
init_lock(addr)
volatile abilock_t *addr;
{

        acquire_mutex(&addr->abi_lock);
        addr->abi_lock=UNLOCKED;
        release_mutex(&addr->abi_lock);
	return 0;
}

int
acquire_lock(addr)
volatile abilock_t *addr;
{

        acquire_mutex(&addr->abi_lock);
        if (addr->abi_lock == LOCKED) {
                release_mutex(&addr->abi_lock);
                return(LOCKED);
        }
        addr->abi_lock=LOCKED;
        return(0);
}

int
release_lock(addr)
volatile abilock_t *addr;
{
        addr->abi_lock=UNLOCKED;
        release_mutex(&addr->abi_lock);
	return 0;
}

int
stat_lock(addr)
volatile abilock_t *addr;
{
        int retval;

        acquire_mutex(&addr->abi_lock);
        if (addr->abi_lock == UNLOCKED)
                retval=UNLOCKED;
        else
                retval=LOCKED;
        release_mutex(&addr->abi_lock);

        return(retval);
}
	
/* Mutex Parameter Tuning */

mutex_delay_type
get_mutex_delay (addr)
volatile mutex_test_set_type *addr;
{
	volatile mutex_region_type *r = mutex_region;
	mutex_mutex_type mutex = PAGE_OFFSET(addr)/sizeof(mutex_test_set_type);
	volatile mutex_monitor_type *m = &r->monitor[mutex];
	volatile mutex_parms_type *mp = &m->parms;

	return mp->delay;
}

mutex_delay_type
set_mutex_delay (addr, delay)
volatile mutex_test_set_type *addr;
register mutex_delay_type delay;
{
	volatile mutex_region_type *r = mutex_region;
	mutex_mutex_type mutex = PAGE_OFFSET(addr)/sizeof(mutex_test_set_type);
	volatile mutex_monitor_type *m = &r->monitor[mutex];
	volatile mutex_parms_type *mp = &m->parms;

	mutex_delay_type temp = mp->delay;
	mp->delay = delay;

	return temp;
}

mutex_delay_type
get_mutex_delay_multiplier (addr)
volatile mutex_test_set_type *addr;
{
	volatile mutex_region_type *r = mutex_region;
	mutex_mutex_type mutex = PAGE_OFFSET(addr)/sizeof(mutex_test_set_type);
	volatile mutex_monitor_type *m = &r->monitor[mutex];
	volatile mutex_parms_type *mp = &m->parms;

	return mp->delay_multiplier;
}

mutex_delay_type
set_mutex_delay_multiplier (addr, delay_multiplier)
register mutex_delay_type delay_multiplier;
volatile mutex_test_set_type *addr;
{
	volatile mutex_region_type *r = mutex_region;
	mutex_mutex_type mutex = PAGE_OFFSET(addr)/sizeof(mutex_test_set_type);
	volatile mutex_monitor_type *m = &r->monitor[mutex];
	volatile mutex_parms_type *mp = &m->parms;

	mutex_delay_type temp = mp->delay_multiplier;
	mp->delay_multiplier = delay_multiplier;

	return temp;
	
}

get_mutex_parameters (p, addr)
register mutex_parms_type *p;
volatile mutex_test_set_type *addr;
{
	volatile mutex_region_type *r = mutex_region;
	mutex_mutex_type mutex = PAGE_OFFSET(addr)/sizeof(mutex_test_set_type);
	volatile mutex_monitor_type *m = &r->monitor[mutex];
	volatile mutex_parms_type *mp = &m->parms;

	p->delay_multiplier = mp->delay_multiplier;
	p->max_delay = mp->max_delay;
	p->delay = mp->delay;
}

void 
set_mutex_parameters (p, addr)
register mutex_parms_type *p;
volatile mutex_test_set_type *addr;
{
	volatile mutex_region_type *r = mutex_region;
	mutex_mutex_type mutex = PAGE_OFFSET(addr)/sizeof(mutex_test_set_type);
	volatile mutex_monitor_type *m = &r->monitor[mutex];
	volatile mutex_parms_type *mp = &m->parms;

	mp->delay_multiplier = p->delay_multiplier;
	mp->max_delay = p->max_delay;
	mp->delay = p->delay;
}

int
mutex_signature (s)
char *s;
{

#define		MSB(a)		(((a) & 0xff000000) >> 24)

	static	int	ab [256] = {
	
	/* 0 */		         1,          2,          4,          8, 
			        16,         32,         64,        128,
			       256,        512,       1024,       2048, 
			      4096,       8192,      16384,      32768,
			
	/* 16 */	     65536,     131072,     262144,     524288, 
			   1048576,    2097152,    4194304,    8388608,
			  16777216,   33554432,   67108864,  134217728, 
			 268435456,  536870912, 1073741824, 2147483648,
			
	/* 32 */	2217906877, 2354558919, 2627599667, 3171510491,
			4263527179, 2020742315, 4041484630, 1710931985,
			3421863970,  333026041,  666052082, 1332104164, 
			2664208328, 3115040045, 4150651111, 1794720627,
			
	/* 48 */	3589441254,  802660209, 1605320418, 3210640836,
			4203637045, 1898856663, 3797713326, 1082888673,
			2165777346, 2250036537, 2282436815, 2485445411,
			2893763835, 3703847755, 1035658283, 2071316566,
			
	/* 64 */	4142633132, 1776661477, 3553322954,  597974313,
			1195948626, 2391897252, 2567994357, 3054658903,
			3893778451, 1411261083, 2822522166, 3561625809,
			 749118239, 1498236478, 2996472956, 3775367749,
			
	/* 80 */	1178437175, 2356874350, 2630126177, 3183183487,
			4282752579, 2058989115, 4117978230, 1859398225,
			3718796450, 1063402489, 2126804978, 4253609956,
			2133020021, 4266040042, 2025826153, 4051652306,
			
	/* 96 */	1724649241, 3449298482,  520269529, 1040539058,
			2081078116, 4162156232, 1947762477, 3895524954,
			1414753801, 2829507602, 3581880985,  785172367,
			1570344734, 3140689468, 4065636037, 1620823863,
			
	/* 112 */	3241647726,  106802785,  213605570,  427211140,
			 854422280, 1708844560, 3417689120,  324602621,
			 649205242, 1298410484, 2596820968, 2980199789,
			3883329639, 1256145523, 2512291046, 2941229937,
			
	/* 128 */	3668812895,  829020675, 1658041350, 3316082700,
			 259801765,  519603530, 1039207060, 2078414120,
			4156828240, 1807402525, 3614805050,  718899913,
			1437799826, 2875599652, 3539911925,  567221079,
			
	/* 144 */	1134442158, 2268884316, 2320123909, 2426798775,
			2776537043, 3469720859,  430827659,  861655318,
			1723310636, 3446621272,  518789645, 1037579290,
			2075158580, 4150317160, 1794323053, 3588646106,
			
	/* 160 */	 805198601, 1610397202, 3220794404, 4223683317,
			1941047127, 3882094254, 1257934817, 2515869634,
			2950212921, 3682846927,  859112227, 1718224454,
			3436448908,  496667557,  993335114, 1986670228,
			
	/* 176 */	3973340456, 1570384109, 3140768218, 4065997065,
			1619456175, 3238912350,  103370753,  206741506,
			 413483012,  826966024, 1653932048, 3307864096,
			 241603325,  483206650,  966413300, 1932826600,
			
	/* 192 */	3865653200, 1222692125, 2445384250, 2813437641,
			3411140399,  311504099,  623008198, 1246016396,
			2492032792, 2904579213, 3732024231,  953599475,
			1907198950, 3814397900, 1115995429, 2231990858,
			
	/* 208 */	2384487977, 2557632239, 3033935715, 3986607227,
			1594821195, 3189642390, 4161715089, 1949296031,
			3898592062, 1418527937, 2837055874, 3590750649,
			 675254735, 1350509470, 2701018940, 3318619333,
			
	/* 224 */	 263105335,  526210670, 1052421340, 2104842680,
			4209685360, 1911026781, 3822053562, 1135428553,
			2270857106, 2324143513, 2436737423, 2789851555,
			3365999099,  359706955,  719413910, 1438827820,
			
	/* 240 */	2877655640, 3543762445,  574725799, 1149451598,
			2298903196, 2520672133, 2823708087, 3568183763,
			 762233115, 1524466230, 3048932460, 4014250597,
			1522247287, 3044494574, 4007398241, 1502250111
	
	};

	int i, reg = 0, sig = -1;

	if (s == (char *)0 || *s == '\0') return sig;

	while (*s) {
		sig ^= ab [MSB (reg)];
		reg = (reg << 8) | *s++;
	}

	for (i = 0; i < 4; i++) {
		sig ^= ab [MSB (reg)];
		reg <<= 8;
	}

	return sig;
}
