/*static char *SCCSID = "@(#)verifydb.c	6.3 92/01/31";*/
#define INCL_DOSERRORS
#include <os2def.h>
#include <bse.h>

#include <fsh.h>

#include "api.h"
#include "const.h"
#include "misc.h"
#include "chain.h"
#include "buf.h"
#include "dir.h"
#include "volume.h"
#include "storage.h"
#include "fnode.h"
#include "tables.h"
#include "superb.h"
#include "lw.h"
#include "frame.h"
#include "fsdata.h"
#include "pbmacros.h"
#include "log.h"

#include "pr_ALLOC.h"
#include "pr_BITMP.h"
#include "pr_CACHE.h"
#include "pr_DEBUG.h"
#include "pr_MISC.h"


/*
 ***	VerifyDB - Verify Sufficient DIRBLKs Exist
 *
 *	Some actions, such as the insertion of a name in a directory,
 *	can require a directory tree split.  We may run out of DIRBLKs
 *	in the middle of this - it's hard to guarantee that they exist
 *	because they have to be on quad sector boundarys so our total
 *	free space number isn't useful.  Running out of DIRBLKs in the
 *	middle of a split is a real mess, with the tree being inconsistent,
 *	so VerifyDB is called before any directory changes are made to
 *	guarantee that sufficient DIRBLKs exist.
 *
 *	VerifyDB maintains a count of the # of folks who may be splitting
 *	directorys.  RelVerDB decrements the count when done.  If there
 *	are guaranteed enough free DIRBLKs for the new caller then he goes
 *	though, else he blocks.  This means that the # of parallel folks
 *	doing directory splits is controlled by the amount of guaranteed
 *	DIRBLK space.  The superblock emergency list contains enough
 *	for two threads doing splits; any unused space in the DIRBLK
 *	allocation band is also counted.
 *
 *	Note that we keep the number of spare DB blocks in the VOLTAB
 *	iff the list is full.  This allows us to compute how many parallel
 *	threads we can allow based upon our spare size.	   If we've used
 *	a spare we'll have to read the SpareB anyway, so we keep 0 in
 *	VOLTAB to show that we're cutting into our emergency supply.  This
 *	allows us to have a per-volume number of spares without having
 *	to store both a max and a current value in the VOLTAB.
 *
 *	ENTRY	none
 *	RETURNS TRUE if clear to go ahead
 *		FALSE if we can't guarantee the space, refuse request.
 *
 *		** Switches Context **
 */
PVOID pVerifyDBWaitChain = NULL;

BOOL
VerifyDB(PFRAME pfrm)
{

    LSN lsn;
    USHORT cDirBlksNeeded;
    PSPAREB psb;		/* pointer to the spare block */
    BOOL bSave;		/* save result from AllocDB */
    PVOLTAB pvol = pfrm->pvol;

    for (;;) {

	/* See if we've used up any of our emergency space.  If
	 * so, try to restore it.
	 *
	 */
	while (pvol->cSpareDirblks == 0) {
	    bSave = AllocDB (0L, &lsn, pfrm);

	    /* this is a critical section (only one reserved buffer) */
	    SemRequest (&semHotFix,pfrm);

	    /* we always get one (but it may be one of the emergency ones */

	    psb = RdHF(SEC_SPAREB | pvol->ulVolBits,pfrm);

	    if( psb == NULL ) {

		/*
		 *  Can't read the spares block--this is really
		 *  bad.  Give up right away.
		 */
		SemClear (&semHotFix);
		FreeDB(lsn,pfrm);
		return FALSE;
	    }

	    psb->alsnSpareDirBlks [psb->cdbSpares] = lsn;

	    if (++psb->cdbSpares == psb->cdbMaxSpare)
   		pvol->cSpareDirblks = (UINT) psb->cdbSpares;

	    /* vdb5: */

	    /*
	     *	Since we have the spares block handy, let's
	     *	go ahead and set the dirty bit and update the
	     *	volume table's copy of the spares block flags.
	     *	This will prevent DoVolIO from calling
	     *	WriteSparesFlag (which also uses the hotfix
	     *	buffer).  Or course, if the disk is already
	     *	dirty, this works out to be a no-op.
	     */
	    psb->bFlag |= SPF_DIRT;
	    pvol->bSpareFlags = psb->bFlag;


	    WtHF(SEC_SPAREB | pvol->ulVolBits,pfrm);

	    /* end of critical section */
	    SemClear (&semHotFix);

	    if (!bSave) {
		/*
		 * This DIRBLK was taken from the emergency store--
		 * we can't replenish the emergency space.
		 * Refuse the request--there's no DIRBLKS to be had.
		 */
		return (FALSE);
	    }
	}

	/*	Our reserve is now full.  Compute the reserve needed for the
	 *	number of threads presently doing allocations.
	 *
	 *	vdb10:
	 */
	cDirBlksNeeded = MAX_DIR_NEED * (pvol->cVDBcalls + 1);
	if ((INT)(cDirBlksNeeded -= pvol->cSpareDirblks) > 0 ) {

	    if ((pvol->bndDirBand.csecFree > 0) &&
		(pvol->cVDBcalls > 0)) {

		/*  The number of DIRBLKS we can guarantee is not enough
		 *  to cover this thread as well as all those presently
		 *  allocating, so it'll just have to wait and try again.
		 *
		 */
		BlockMisc( &pVerifyDBWaitChain, LOB_VDB,pfrm );

	    } else {

		/*
		 *  There are not enough free DirBlks left to
		 *  reasonably cover this request (or there
		 *  is no other thread with a reservation);
		 *  return failure.
		 */
		return( FALSE );
	    }

	} else {

	    /*	There are enough DIRBLKS available to satisfy this
	     *	thread as well as all threads presently allocating,
	     *	so we accept it as a client.
	     *
	     */
	    pvol->cVDBcalls++;
	    return (TRUE);
	}
    }
}



/*
 ***	RelVerDB - Release VerifyDB Reservation
 *
 *	RelVerDB releases the space reservation made by VerifyDB.
 *
 *	ENTRY	none
 *	EXIT	none
 *
 */
void
RelVerDB(PFRAME pfrm)
{

#ifdef DEBUG
    if (pfrm->pvol->cVDBcalls == 0)
	INTERR ("RelVerDB:  Count of clients about to go negative.");
#endif

    pfrm->pvol->cVDBcalls--;

    if (pVerifyDBWaitChain != 0) {
	WakeupMisc (&pVerifyDBWaitChain, LOB_VDB);
    }
}
