/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:callback.c 12.4$ */
/* $ACIS:callback.c 12.4$ */
/* $Source: /ibm/acis/usr/sys/afs/RCS/callback.c,v $ */

#ifndef lint
static char *rcsid = "$Header:callback.c 12.4$";
#endif

#include "../h/types.h"
#include "../h/param.h"
#include "../h/time.h"
#include "../h/kernel.h"
#include "../h/socket.h"
#include "../h/socketvar.h"
#include "../h/protosw.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/file.h"
#include "../h/uio.h"
#include "../h/vfs.h"
#include "../h/vnode.h"
#include "../ufs/inode.h"
#include "../netinet/in.h"
#include "../h/mbuf.h"
#include "../rpc/types.h"
#include "../rpc/xdr.h"

#include "../afs/osi.h"
#define RFTP_INTERNALS 1
#include "../afs/r.h"
#include "../afs/rftp.h"

#include "../afs/lock.h"
#include "../afs/volerrors.h"
#include "../afsint/rvice.h"
#include "../afsint/rvaux.h"
#include "../afs/afs.h"
#include "../afs/prs_fs.h"
#include "../afs/dir.h"

extern struct r_server *afs_server;
extern struct vcache *afs_FindVCache();
extern struct server *afs_FindServer();
extern int afs_running;
long afs_missedCBs = 0;
long afs_connectBacks = 0;

callback_cleanup() {
    afs_missedCBs = 0;
    afs_connectBacks = 0;
}

static ClearCallBack(aconn, afid)
    register struct r_connection *aconn;
    register struct ViceFid *afid; {
    register struct vcache *tvc;
    register int i;
    register long otherHost;
    register struct server *tserve;
    int didAny;
    struct VenusFid localFid;

    otherHost = r_PeerHost(aconn);		/* hosts are always in network order */
    tserve = afs_FindServer(otherHost);	/* cell is don't care value */
    /* normally cell field is valid, but this could be call from previous boot */
    localFid.Cell = ((tserve && tserve->cell)? tserve->cell->cell : 0);
    localFid.Fid.Volume = afid->Volume;
    localFid.Fid.Vnode = afid->Vnode;
    localFid.Fid.Unique = afid->Unique;
    if (tserve == 0) afs_missedCBs++;	/* may have missed accidentally */
    if (afid->Volume == 0) {
#ifdef notdef
	/* clear all callbacks from this host -- this is not really used yet, as it is still used as volume == 0 is still used as a probe from the file server */
	afs_missedCBs++;    /* including any missed via create race */
        for (i=0;i<VCSIZE;i++)
            for (tvc = afs_vhashTable[i]; tvc; tvc = tvc->hnext) {
                if (tvc->callback == otherHost) tvc->callback = 0;
	    }
#endif
    }
    else if (afid->Vnode == 0) {
        /* Clear callback for the whole volume. */
	afs_missedCBs++;	/* in case we were doing a create */
        for (i=0;i<VCSIZE;i++)
            for (tvc = afs_vhashTable[i]; tvc; tvc = tvc->hnext) {
                if (tvc->fid.Fid.Volume == afid->Volume
		    && (!tserve || tvc->fid.Cell == localFid.Cell)) tvc->callback = 0;
	    }
    }
    else {
	/* a particular fid was specified */
	i = VCHash(&localFid);
	didAny = 0;
	for(tvc = afs_vhashTable[i]; tvc; tvc = tvc->hnext) {
	    if (tvc->fid.Fid.Vnode == afid->Vnode && tvc->fid.Fid.Volume == afid->Volume
		&& tvc->fid.Fid.Unique == afid->Unique
		&& (!tserve || tvc->fid.Cell ==localFid.Cell)) {
		    tvc->callback = 0;
		    didAny = 1;
		}
	}
        if (!didAny) afs_missedCBs++;	/* missed it in the cache */
    }
}

RCallBackBulk(aconn, afids, anumFids)
    register struct r_connection *aconn;
    register struct ViceFid *afids;
    register long anumFids; {
    register int i;

    afs_dp("received callback bulk\n");
    for(i=0;i<anumFids;i++) ClearCallBack(aconn, &afids[i]);
    return 0;
}

RCallBack(aconn, afid)
    register struct r_connection *aconn;
    register struct ViceFid *afid; {
    afs_dp("received callback\n");
    if (afid->Volume != 0) ClearCallBack(aconn, afid);
    return 0;
}

/* store has started; currently not used */
RCallBackReceivedStore(aconn, afid)
    register struct r_connection *aconn;
    register struct ViceFid *afid; {
    long otherHost;
    register struct server *ts;
    struct VenusFid localFid;
    struct vcache *tvc;
    register struct brequest *tb;
    register int i;

    otherHost = r_PeerHost(aconn);		/* hosts are always in network order */
    ts = afs_FindServer(otherHost);	/* cell is don't care value */
    /* normally cell field is valid, but this could be call from previous boot */
    if (!ts || !ts->cell) return 0;
    localFid.Cell = ts->cell->cell;
    localFid.Fid.Volume = afid->Volume;
    localFid.Fid.Vnode = afid->Vnode;
    localFid.Fid.Unique = afid->Unique;
    afs_dp("received callback store\n");
    tvc = afs_FindVCache(&localFid);
    if (tvc) {
	tb = afs_brs;
	for (i=0;i<NBRS;i++, tb++) {
	    /* if request is valid and for this file, we've found it */
	    if (tb->refCount > 0 && tvc == tb->vnode) {
		afs_dp("bkg store found %x\n", tvc);
		/* if CSafeStore is on, then we don't awaken the
		    guy waiting for the store until the whole store has finished.
		    Otherwise, we do it now.  Note that if CSafeStore is on, the
		    BStore routine actually wakes up the user, instead of us.
		*/
		if ((tvc->states & CSafeStore) == 0) {
		    tb->code = 0;
		    tb->flags |= BUVALID;
		    if (tb->flags & BUWAIT) {
			tb->flags &= ~BUWAIT;
			osi_Wakeup(tb);
		    }
		}
		else afs_dp("safestore ignoring %x\n");
		break;
	    }
	}
	tvc->vrefCount--;	/* don't obtain any locks in callback code */
    }
    return 0;
}

RCallBackConnectBack(aconn)
    register struct r_connection *aconn; {
    register struct vcache *tvc;
    register int i;
    register long otherHost;

    otherHost = r_PeerHost(aconn);		/* hosts are always in network order */
    /* clear all callbacks from this host */
    afs_missedCBs++;    /* including any missed via create race */
    afs_connectBacks++;
    for (i=0;i<VCSIZE;i++)
	for (tvc = afs_vhashTable[i]; tvc; tvc = tvc->hnext) {
	    if (tvc->callback == otherHost) tvc->callback = 0;
	}
    return 0;
}

afs_RCallBackServer() {
    struct r_connection *tconn;
    struct r_packet *tp;
    register struct r_server *tserver;
    while (1) {
	if (afs_server) break;
	osi_Sleep(&afs_server);
    }
    tserver = afs_server;
    while (afs_running) {
	r_GetRequest(tserver, &tconn, &tp);
	if (afs_running) RCallBack_ExecuteRequest(tconn, tp);
    }
}
