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

#ifndef lint
static char *rcsid = "$Header:afs_daemons.c 12.3$";
#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/buf.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"

/* background request queue size */
struct lock afs_xbrs;		/* lock for brs */
int brsInit = 0;
short afs_brsDaemons = 0;	/* number of daemons waiting for brs requests */
short afs_brsWaiters = 0;	/* number of users waiting for brs buffers */
struct brequest	afs_brs[NBRS];	/* request structures */

extern char afs_rootVolumeName[];
extern struct VenusFid afs_rootFid;
extern struct osi_dev cacheDev;
extern char *afs_indexFlags;
extern struct lock afs_xvcache;
extern struct vcache *afs_NewVCache();
extern int afs_running;

afs_daemons_cleanup() {
    bzero(&afs_xbrs, sizeof(afs_xbrs));
    brsInit = 0;
    afs_brsDaemons = 0;
    afs_brsWaiters = 0;
    bzero(afs_brs, sizeof(afs_brs));
}

afs_Daemon() {
    register long code;
    long now;
    long last3MinCheck, last10MinCheck, last60MinCheck;
    last3MinCheck = last10MinCheck = last60MinCheck = 0;
    afs_rootFid.Fid.Volume = 0;
    while (afs_initState < 101) osi_Sleep(&afs_initState);

    /* start off with afs_initState >= 101 (basic init done) */
    while(afs_running) {
	now = osi_Time();

	/* things to do every minute */
	afs_CheckSize(0);		/* make sure we're under disk quota */
	DFlush();			/* write out dir buffers */
	afs_WriteThroughDSlots();	/* write through cacheinfo entries */
	afs_KeepFlocksAlive();		/* keep flocks held */

	if (last3MinCheck + 180 < now) {
	    afs_dp("checking down servers periodically\n");
	    afs_CheckServers(1);	/* only check down servers */
	    last3MinCheck = now;
	}
	if (last10MinCheck + 600 < now) {
	    afs_dp("checking working servers periodically\n");
	    afs_CheckServers(0);
	    afs_GCUserData();	    /* gc old conns */
	    last10MinCheck = now;
	}
	if (last60MinCheck + 3600 < now) {
	    afs_dp("checking volume names periodically\n");
	    afs_CheckRootVolume();
	    afs_CheckVolumeNames();
	    last60MinCheck = now;
	}
	if (afs_initState < 300) {	/* while things ain't rosy */
	    code = afs_CheckRootVolume();
	    if (code ==	0) afs_initState = 300;		    /* succeeded */
	    if (afs_initState <	200) afs_initState = 200;   /* tried once */
	    osi_Wakeup(&afs_initState);
	}

	/* finally sleep for a minute and see what's up */
	code = osi_NetWait((struct osi_socket *) 0, 60*1000, (struct osi_NetHandle *) 0);
	if (code) afs_dp("afs_daemon: netwait code %d\n", code);
    };
}

afs_CheckRootVolume () {
    register struct conn *tconn;
    register struct cell *tcell;
    struct vrequest treq;
    struct BBS rootVolData;
    char rootVolName[32];
    register struct volume *tvp;
    register long code;

    afs_dp("searching for root volume\n");
    afs_InitReq(&treq, &osi_cred);
    if (*afs_rootVolumeName == 0) {
	rootVolData.MaxSeqLen = 32;
	rootVolData.SeqLen = 0;
	rootVolData.SeqBody = rootVolName;
	tcell = afs_GetCell(LOCALCELL);	/* better be here by now */
	if (!tcell) panic("afs_daemon: no primary cell");
	do {
	    tconn = afs_ConnByMHosts(tcell->cellHosts, tcell->cell, &treq);
#ifdef	NINTERFACE
	    if (tconn) code = AFS_GetRootVolume(tconn->id, &rootVolData);
#else
	    if (tconn) code = RViceGetRootVolume(tconn->id, &rootVolData);
#endif
	    else code = -1;
	} while (afs_Analyze(tconn, code, (struct ViceFid *) 0, &treq));
    }
    else {
	strcpy(rootVolName, afs_rootVolumeName);
	code = 0;
    }
    if (code) afs_dp("failed to get root volname, code %d\n", code);
    else {
	tvp = afs_GetVolumeByName(rootVolName, LOCALCELL, 1, (struct vrequest *) 0);
	if (tvp) {
	    afs_rootFid.Cell = LOCALCELL;
	    afs_rootFid.Fid.Volume = tvp->volume;
	    afs_rootFid.Fid.Vnode = 1;
	    afs_rootFid.Fid.Unique = 1;
	    afs_initState = 300;    /* won */
	    osi_Wakeup(&afs_initState);
	    afs_PutVolume(tvp);
	}
    }
    if (afs_rootFid.Fid.Volume) return 0;
    else return ENOENT;
}

/* parm 0 to the fetch is the chunk number; parm 1 is null or a dcache entry to wakeup */
BPrefetch(ab)
    register struct brequest *ab; {
    register struct dcache *tdc;
    register struct vcache *tvc;
    long offset, len;
    struct vrequest treq;

    afs_InitReq(&treq, ab->cred);   /* probably should keep credentials */
    tvc = ab->vnode;
    tdc = afs_GetDCache(tvc, ab->parm[0], &treq, &offset, &len, 1);
    if (tdc) {
	afs_PutDCache(tdc);
    }
    /* now, dude may be waiting for us to clear DFetchReq bit; do so */
    tdc = (struct dcache *) (ab->parm[1]);
    if (tdc) {
	/* can't use tdc from GetDCache since afs_GetDCache may fail, but someone
	    may be waiting for our wakeup anyway */
	tdc->f.states &= ~DFetchReq;
	osi_Wakeup(&tdc->validPos);
    }
}

/* already write-locked vcache */
BStore(ab)
    register struct brequest *ab; {
    register struct dcache *tdc;
    register struct vcache *tvc;
    register long code;
    struct vrequest treq;

    afs_InitReq(&treq, ab->cred);
    code = 0;
    tvc = ab->vnode;
    UpgradeSToWLock(&tvc->lock);
    tvc->execsOrWriters--;
    if (tvc->execsOrWriters == 0) {
	/* put the file back */
	tdc = afs_FindDCache(tvc, 0);
	if (tdc) {
	    ConvertWToSLock(&tvc->lock);
	    code = afs_StoreDCache(tvc, tdc, &treq);
	    UpgradeSToWLock(&tvc->lock);
	    afs_PutDCache(tdc);
	    tdc->f.states &= ~DWriting;
	    tdc->f.states |= DEntryMod;
            if (code) {
                /* failed to store, invalidate bad cache info */
                tvc->states &= ~CStatd;
                afs_indexFlags[tdc->index] &= ~IFDataMod;
                tdc->f.versionNo = -1;
            }
	}
	else afs_dp("warning, no file on close\n");
    }
    ReleaseWriteLock(&tvc->lock);
    /* now set final return code, and wakeup anyone waiting */
    if ((ab->flags & BUVALID) == 0) {
	ab->code = code;	    /* set final code */
	ab->flags |= BUVALID;
	if (ab->flags & BUWAIT) {
	    ab->flags &= ~BUWAIT;
	    osi_Wakeup(ab);
	}
    }
}

/* release a held request buffer */
afs_BRelease(ab)
    register struct brequest *ab; {

    ObtainWriteLock(&afs_xbrs);
    if (--ab->refCount <= 0) {
	ab->flags = 0;
    }
    if (afs_brsWaiters) osi_Wakeup(&afs_brsWaiters);
    ReleaseWriteLock(&afs_xbrs);
}

/* return true if bkg fetch daemons are all busy */
int afs_BBusy() {
    if (afs_brsDaemons > 0) return 0;
    return 1;
}

struct brequest *afs_BQueue(aopcode, avc, ause, aparm0, aparm1, aparm2, aparm3, aparm4)
    register short aopcode;
    long ause;
    register struct vcache *avc;
    long aparm1, aparm2, aparm3, aparm0; {
    register int i;
    register struct brequest *tb;

    ObtainWriteLock(&afs_xbrs);
    while (1) {
	tb = afs_brs;
	for(i=0;i<NBRS;i++,tb++) {
	    if (tb->refCount == 0) break;
	}
	if (i < NBRS) {
	    /* found a buffer */
	    tb->opcode = aopcode;
	    tb->vnode = avc;
	    tb->cred = u.u_cred;    /* really should get this as a parm */
	    crhold(tb->cred);
	    avc->vrefCount++;
	    tb->refCount = ause+1;
	    tb->parm[0] = aparm0;
	    tb->parm[1] = aparm1;
	    tb->parm[2] = aparm2;
	    tb->parm[3] = aparm3;
	    tb->flags = 0;
	    tb->code = 0;
	    /* if daemons are waiting for work, wake them up */
	    if (afs_brsDaemons > 0) {
		osi_Wakeup(&afs_brsDaemons);
	    }
	    ReleaseWriteLock(&afs_xbrs);
	    return tb;
	}
	/* no free buffers, sleep a while */
	afs_brsWaiters++;
	ReleaseWriteLock(&afs_xbrs);
	osi_Sleep(&afs_brsWaiters);
	ObtainWriteLock(&afs_xbrs);
	afs_brsWaiters--;
    }
}

afs_BackgroundDaemon() {
    register struct brequest *tb;
    register int i;
    int foundAny;
    long code;

    /* initialize subsystem */
    if (brsInit == 0) {
	Lock_Init(&afs_xbrs);
	bzero(afs_brs, sizeof(afs_brs));
	brsInit = 1;
    }
    ObtainWriteLock(&afs_xbrs);
    while (afs_running) {
	/* find a request */
	tb = afs_brs;
	foundAny = 0;
	for(i=0;i<NBRS;i++,tb++) {
	    /* look for request */
	    if ((tb->refCount > 0) && !(tb->flags & BSTARTED)) {
		/* new request, not yet picked up */
		tb->flags |= BSTARTED;
		ReleaseWriteLock(&afs_xbrs);
		foundAny = 1;
		afs_dp("bkg found request %d\n", tb->opcode);
		if (tb->opcode == BOP_FETCH) 
		    code = BPrefetch(tb); 
		else if (tb->opcode == BOP_STORE) 
		    code = BStore(tb); 
		else panic("background bop");
		if (tb->vnode) {
		    tb->vnode->vrefCount--;	    /* fix up reference count */
		    tb->vnode = (struct vcache *) 0;
		}
		if (tb->cred) {
		    tb->cred->cr_ref--;
		    tb->cred = (struct ucred *) 0;
		}
		afs_BRelease(tb);   /* this grabs and releases afs_xbrs lock */
		ObtainWriteLock(&afs_xbrs);
	    }
	}
	if (!foundAny) {
	    /* wait for new request */
	    afs_brsDaemons++;
	    afs_dp("bkg about to wait\n");
	    ReleaseWriteLock(&afs_xbrs);
	    osi_Sleep(&afs_brsDaemons);
	    if (afs_running) ObtainWriteLock(&afs_xbrs);
	    afs_dp("bkg woke up\n");
	    afs_brsDaemons--;
	}
    }
}
