static char rcsid[] = "$Header: dfs_remote.c,v 820.1 86/12/04 19:44:57 root Exp $";
static char sccsid[]="%W% %Y% %Q% %G%";

/************************************************************************
*									*
*				Copyright 1984				*
*			VALID LOGIC SYSTEMS INCORPORATED		*
*									*
*	This listing contains confidential proprietary information	*
*	which is not to be disclosed to unauthorized persons without	*
*	written consent of an officer of Valid Logic Systems 		*
*	Incorporated.							*
*									*
*	The copyright notice appearing above is included to provide	*
*	statutory protection in the event of unauthorized or 		*
*	unintentional public disclosure.				*
*									*
************************************************************************/

/*
 * Remote reference to local inodes are managed by these routines
 *
 *	Bakul Shah
 *
 * bvs 840417 -- original version
 * jht 841115,16,26-29 -- complete conversion to use prpc.
 * jht 850412 -- Inappropriate panic(dfs_rmt_unlink: rp not in i_rmt_node chain)
 */

#include "../h/param.h"
#include "../h/ioctl.h"
#include "../h/dir.h"
#include "../h/user.h"

#include "../vnet/vnet.h"
#include "../conn/conn.h"
#include "../rpc/rpc.h"
#include "../dfs/dfs.h"

#include "../h/inode.h"
#include "../h/socket.h"
#include "../h/kernel.h"
#include "../net/if.h"
#include "../h/mbuf.h"


#define DFS_SLOTS	32

#if ((DFS_SLOTS & (DFS_SLOTS - 1)) == 0)
#define DFS_HASH(node, ip)	(((int)(node)->host.low + (int)(ip)) \
				  & (DFS_SLOTS-1))
#else
#define DFS_HASH(node, ip)	(((int)(node)->host.low+(int)(ip)) % DFS_SLOTS)
#endif

dfs_remote_t *   dfs_rmt_free = NULL;

dfs_remote_t *   dfs_remotes[DFS_SLOTS];
dfs_remote_t * * dfs_remotesNSLOTS = &dfs_remotes[DFS_SLOTS];

/*
 * Add an entry for a reference to ip from node.
 * If the entry already exists, decrement ref. count of ip.
 * as we need only one remote ref. count per node.
 */
dfs_remote_t *
dfs_rmt_add(node, ip)
	register node_t		* node;
	register struct inode	* ip;
{
	register dfs_remote_t * * rpp = &dfs_remotes[DFS_HASH(node, ip)];
	register dfs_remote_t	* rp;

	if (!ip || ip == DFS_ILLEGAL_INODE) {
#if 1
		DFS_DEBUG(1,("dfs_rmt_add/E-0: ip=0x%X, node=0x%X\n", ip, node));
		panic("dfs_rmt_add: bad ip");
#else
		u.u_error = DFS_EINVAL;
		return NULL;
#endif
	}
	for ( ; rp = *rpp; rpp = &rp->nexthash) {
		/*
		 * if the entry is already in the table
		 * then increment the  remote ref count.
		 */
		if (nodecmp(rp->node, *node) && rp->ip == ip) {
			rp->count++;
			goto ret;
		}
	}
	/*
	 * Get a free entry and link it in the hash chain.
	 */
	if (dfs_rmt_free) {
		rp = dfs_rmt_free;
		dfs_rmt_free = rp->nexthash;
	} else
		rp = (dfs_remote_t *)calloc(sizeof(*rp));
	if (!rp) {
		u.u_error = DFS_ERMTFULL;
		return NULL;
	}
	rp->node	= *node;
	rp->ip		= ip;
	rp->count	= 1;
	rp->wcount	= 0;
	rp->shlockc	= 0;
	rp->exlockc	= 0;
	/*
	 * Link into hash chain
	 */
	rp->nexthash = NULL;
	*rpp = rp;
	/*
	 * link in ip->i_rmt_node chain
	 */
	rp->next	= ip->i_rmt_node;
	ip->i_rmt_node	= rp;
ret:
	/*
	 * CHECK: ip should always be locked
	 * when we call dfs_rmt_add
	 */
	if (ip->i_flag & ILOCKED) {
		rp->flags = DFS_RMT_LOCKED;
#ifdef	DFS_LOCK_FIX
		ip->i_rmt_lock = rp;
#endif	DFS_LOCK_FIX
	}
	return rp;
}

/*
 * Delete an entry from the remote table.
 */
dfs_remote_t *
dfs_rmt_remove(node, ip)
	register node_t		* node;
	register struct inode	* ip;
{
	register dfs_remote_t * * rpp = &dfs_remotes[DFS_HASH(node, ip)];
	register dfs_remote_t	* rp;

	if (!ip || ip == DFS_ILLEGAL_INODE) {
#if 1
		DFS_DEBUG(1,("dfs_rmt_remove/E-0: ip=0x%X node=0x%X\n", ip, node));
		panic("dfs_rmt_remove: bad ip");
#else
		u.u_error = DFS_EINVAL;
		return NULL;
#endif
	}
	for (; rp = *rpp; rpp = &rp->nexthash) {
		if (nodecmp(rp->node, *node) && rp->ip == ip) {
			if (rp->count == 1)
				dfs_rmt_unlink(rpp);
			else {
				rp->count--;
			}
			break;
		}
	}
	return rp;
}

/*
 * Return an existing entry or NULL.
 */
dfs_remote_t *
dfs_rmt_find(node, ip)
	register node_t		* node;
	register struct inode	* ip;
{
	register dfs_remote_t	* rp = dfs_remotes[DFS_HASH(node, ip)];

	if (!ip || ip == DFS_ILLEGAL_INODE) {
#if 1
		DFS_DEBUG(1,("dfs_rmt_find/E-0: ip=0x%X, node=0x%X\n", ip, node));
		panic("dfs_rmt_find: bad ip");
#else
		u.u_error = DFS_EINVAL;
		return NULL;
#endif
	}
	for (; rp; rp = rp->nexthash)
		if (nodecmp(rp->node, *node) && rp->ip == ip)
			return rp;
	u.u_error = DFS_ENOHOST;
	return rp;
}

/*
 * Unlink a remote structure
 * from the chain of remotes referencing the related inode.
 * Put that remote structure
 * on the front of the dfs_rmt_free freelist.
 */
dfs_rmt_unlink(rppOrig)
	register dfs_remote_t * * rppOrig;
{
	register dfs_remote_t * * rpp =  rppOrig;
	register dfs_remote_t	* rp  = *rpp;
	register struct inode	* ip  =  rp->ip;

	/*
	 * Paranoia
	 */
	if (!rppOrig)
		panic("dfs_rmt_unlink(rpp==0)");
	if (!*rppOrig)
		panic("dfs_rmt_unlink(*rpp==0)");

	if (!ip || ip==DFS_ILLEGAL_INODE) {
		DFS_DEBUG(1,("dfs_rmt_unlink/E-0: ip=0x%X\n", ip));
		panic("dfs_rmt_unlink: ip  bogus)");
	}
	/*
	 * Unlink from the hash chain
	 */
	*rpp		= rp->nexthash;
	rp->nexthash	= dfs_rmt_free;
	rp->flags	= 0;
	rp->count	= 0;
	rp->wcount	= 0;
	rp->shlockc	= 0;
	rp->exlockc	= 0;
	dfs_rmt_free	= rp;
	/*
	 * Unlink from the ip->i_rmt_node chain
	 */
	for (rpp = &ip->i_rmt_node;
	     *rpp && rp != *rpp;
	     rpp = &(*rpp)->next)
		continue;
	if (*rpp) {
		*rpp = rp->next;
	} else {
		DFS_DEBUG(4,("dfsServer_exception/E-11: rp=0x%X not found from ip=0x%X\n", rp, ip));
#if	1
		/*
		 * This is too severe, especially since
		 * we are abandoning the data structures.
		 * BUG: Why is it missing from the i_rmt_node chain?!
		 */
		panic("dfs_rmt_unlink: rp not in i_rmt_node chain");
#endif
	}
	rp->next = NULL;
}

/*
 * Free up all dfs_remote structures.
 * Assumes that rp->next links have been
 * broken.
 */
dfs_rmt_freeAll()
{
	register dfs_remote_t * * rpp;
	register dfs_remote_t * * bin;
	register dfs_remote_t *   rp;

	for (bin = dfs_remotes; bin < dfs_remotesNSLOTS; bin++) if (*bin) {
		/*
		 * Find the tail of current list.
		 */
		for (rpp = bin; rp = *rpp; rpp = &rp->nexthash)
			{}
		/*
		 * Attach the free list to the end of current list.
		 * Make the current list new free list.
		 * Clear bin.
		 */
		*rpp = dfs_rmt_free;
		dfs_rmt_free = *bin;
		*bin = NULL;
	}
}

/*
 * Go through the remote inode ref table and
 * return a list of dfs_remotes opened by that node.
 * Remove the list from dfs_remotes.
 */
dfs_remote_t *
dfs_rmt_findByNode(node)
	register node_t		* node;
{
	register dfs_remote_t * * bin;
	register dfs_remote_t * * rpp;
	dfs_remote_t		* list = NULL;
	register dfs_remote_t * * last = &list;
	register dfs_remote_t	* rp;

	for (bin = dfs_remotes; bin < dfs_remotesNSLOTS; bin++)
		for (rpp = bin; rp = *rpp; rpp = &rp->nexthash) {
			if (!nodecmp(rp->node, *node))
				continue;
			*rpp  = rp->nexthash;
			*last = rp;
			last  = &rp->nexthash;
		}
	/*
	 * Terminate the list nicely
	 */
	*last = NULL;
	return list;
}
