/*
 * Copyright (c) 2002 Red Hat, Inc. All rights reserved.
 *
 * This software may be freely redistributed under the terms of the
 * GNU General Public License.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Authors: David Woodhouse <dwmw2@cambridge.redhat.com>
 *          David Howells <dhowells@redhat.com>
 *
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include "server.h"
#include "vnode.h"
#include "internal.h"
#include "cmservice.h"

/*****************************************************************************/
/*
 * allow the fileserver to request callback state (re-)initialisation
 */
int SRXAFSCM_InitCallBackState(struct afs_server *server)
{
	struct list_head callbacks;

	_enter("%p", server);

	INIT_LIST_HEAD(&callbacks);

	/* transfer the callback list from the server to a temp holding area */
	spin_lock(&server->cb_lock);

	list_add(&callbacks, &server->cb_promises);
	list_del_init(&server->cb_promises);

	/* munch our way through the list, grabbing the inode, dropping all the
	 * locks and regetting them in the right order
	 */
	while (!list_empty(&callbacks)) {
		struct afs_vnode *vnode;
		struct inode *inode;

		vnode = list_entry(callbacks.next, struct afs_vnode, cb_link);
		list_del_init(&vnode->cb_link);

		/* try and grab the inode - may fail */
		inode = igrab(AFS_VNODE_TO_I(vnode));
		if (inode) {
			int release = 0;

			spin_unlock(&server->cb_lock);
			spin_lock(&vnode->lock);

			if (vnode->cb_server == server) {
				vnode->cb_server = NULL;
				afs_kafstimod_del_timer(&vnode->cb_timeout);
				spin_lock(&afs_cb_hash_lock);
				list_del_init(&vnode->cb_hash_link);
				spin_unlock(&afs_cb_hash_lock);
				release = 1;
			}

			spin_unlock(&vnode->lock);

			iput(inode);
			afs_put_server(server);

			spin_lock(&server->cb_lock);
		}
	}

	spin_unlock(&server->cb_lock);

	_leave(" = 0");
	return 0;
} /* end SRXAFSCM_InitCallBackState() */

/*****************************************************************************/
/*
 * allow the fileserver to break callback promises
 */
int SRXAFSCM_CallBack(struct afs_server *server, size_t count,
		      struct afs_callback callbacks[])
{
	_enter("%p,%u,", server, count);

	for (; count > 0; callbacks++, count--) {
		struct afs_vnode *vnode = NULL;
		struct inode *inode = NULL;
		int valid = 0;

		_debug("- Fid { vl=%08x n=%u u=%u }  CB { v=%u x=%u t=%u }",
		       callbacks->fid.vid,
		       callbacks->fid.vnode,
		       callbacks->fid.unique,
		       callbacks->version,
		       callbacks->expiry,
		       callbacks->type
		       );

		/* find the inode for this fid */
		spin_lock(&afs_cb_hash_lock);

		list_for_each_entry(vnode,
				    &afs_cb_hash(server, &callbacks->fid),
				    cb_hash_link) {
			if (memcmp(&vnode->fid, &callbacks->fid,
				   sizeof(struct afs_fid)) != 0)
				continue;

			/* right vnode, but is it same server? */
			if (vnode->cb_server != server)
				break; /* no */

			/* try and nail the inode down */
			inode = igrab(AFS_VNODE_TO_I(vnode));
			break;
		}

		spin_unlock(&afs_cb_hash_lock);

		if (inode) {
			/* we've found the record for this vnode */
			spin_lock(&vnode->lock);
			if (vnode->cb_server == server) {
				/* the callback _is_ on the calling server */
				vnode->cb_server = NULL;
				valid = 1;

				afs_kafstimod_del_timer(&vnode->cb_timeout);
				vnode->flags |= AFS_VNODE_CHANGED;

				spin_lock(&server->cb_lock);
				list_del_init(&vnode->cb_link);
				spin_unlock(&server->cb_lock);

				spin_lock(&afs_cb_hash_lock);
				list_del_init(&vnode->cb_hash_link);
				spin_unlock(&afs_cb_hash_lock);
			}
			spin_unlock(&vnode->lock);

			if (valid) {
				invalidate_remote_inode(inode);
				afs_put_server(server);
			}
			iput(inode);
		}
	}

	_leave(" = 0");
	return 0;
} /* end SRXAFSCM_CallBack() */

/*****************************************************************************/
/*
 * allow the fileserver to see if the cache manager is still alive
 */
int SRXAFSCM_Probe(struct afs_server *server)
{
	_debug("SRXAFSCM_Probe(%p)\n", server);
	return 0;
} /* end SRXAFSCM_Probe() */