/*static char *SCCSID = "@(#)bufchain.c	6.2 91/01/14";*/
#define SCCSID "@(#)bufchain.c	6.2 91/01/14"
/*
 *	IBM/Microsoft Confidential
 *
 *	Copyright (c) IBM Corporation 1987, 1989
 *	Copyright (c) Microsoft Corporation 1987, 1989
 *
 *	All Rights Reserved
 *
 */

/*  NAME :  BUFCHAIN.C
 *    
 *
 *  DESCRIPTION
 *    
 *      This file contains the functions that deal with chains of BUF
 *      structures.
 *
 *
 *  FUNCTIONS DEFINED
 *
 *      GetBufChain ()  --  Allocate the BUF's to support this multiple
 *                          sector read.
 *
 *      AddBufChain ()  --  Add a chain of buffers to an in use list.
 *  
 * 
 *  REVISION HISTORY (in reverse chronological order)
 *  -----------------------------------------------------------------------
 *  4/9/90      Brian Andrew        Initial version of GetBufChain.  Initial
 *                                  version of AddBufChain.
 *
 *
 *  TO DO
 *  -------------------------------------------------------------------------
 */

#define INCL_DOSERRORS
#define INCL_NOPMAPI

#include <os2.h>
#include <fsh.h>

#include "cdfs.h"
#include "sem.h"
#include "path.h"
#include "vol.h"
#include "heap.h"
#include "mem.h"
#include "buffer.h"

#ifdef STATS_H
#include "stats.h"
#endif 

#include "message.h"
#include "fsdata.h"

#ifdef DEBUG
#include "pr_debug.h"
#endif

#include "pr_memin.h"
#include "pr_cache.h"


/***    GetBufChain ()  --  Allocate the BUF's to support this multiple     
 *                          sector read.
 *      
 *  DESCRIPTION
 *
 *      This function generates a linked list of BUF structures to   
 *      support a multiple sector read.  It receives the linked list
 *      of headers for the read and the range of sectors read.  It will
 *      attempt to allocate a single BUF for each header in the range.
 *
 *      An error is not considered fatal as long as at least one BUF
 *      structure is allocated.
 *      
 *      This routine holds no semaphores for access into the cache list.
 *      
 *      
 *  ENTRY   (Arguements passed to function)
 *      
 *      The following are the fields in the FIND_BUF_LOC structure used here.
 *
 *      pblAddedHdrs    --  Linked list of added (or assumed) headers.
 *      csSectorCount   --  Number of sectors read.
 *      pbBufChain      --  Linked chain of BUF structures.
 *      pbEndBufChain   --  End of linked chain of BUF structures.
 *
 *
 *  GLOBAL  (Global variables accessed by this function)
 *
 *      NONE.
 *
 *
 *  GOTO    (Jump targets)
 *
 *      NONE.
 *
 *
 *  RETURN  (Value returned by this function)
 *
 *      NO_ERROR if at least one allocation performed.
 *      Error code from GetBuf otherwise.
 *
 *
 *  EXIT    (Changes to variables seen outside this function)
 *
 *      The following 'pfblVars' fields may be modified.
 *      
 *      pbBufChain      --  Linked chain of BUF structures.
 *      pbEndBufChain   --  End of linked chain of BUF structures.
 *          
 *      
 *  LOCAL   (Variables declared within this function)    
 *      
 *      pblThisHdr  --  Current header in linked list.
 *      usRet       --  Error code returned.
 *      pbLastBuf   --  Previous BUF structure in chain.
 *      pbThisBuf   --  Current BUF structure in chain.
 *      fMinSuccess --  Flag indicating if at least minimal success.
 *      snLastSect  --  Last possible sector for chain.
 */

USHORT  GetBufChain (pfblVars)
PFIND_BUF_LOC   pfblVars;
{
	/* Local Vars */
	PBUF_LIST   pblThisHdr;
	USHORT      usRet;
	PBUF        pbLastBuf;
	PBUF        pbThisBuf;
	BOOL        fMinSuccess;
	SECT_NUM    snLastSect;

	/* Initialize the local variables */
	/* Compute the last sector */
	snLastSect =    pfblVars->snStartSector +
					pfblVars->csSectorCount;

	/* Recover the starting header */
	pblThisHdr = pfblVars->pblAddedHdrs;

	/* Success is false by default */
	fMinSuccess = FALSE;

	/* Previous BUF is false */
	pbLastBuf = NULL;

	/*
	 *  while loop :       
	 *        
	 *      This look is traversed for each header in the add header list.
	 *      An attempt is made to allocate a BUF structure for each
	 *      header.  An error from an allocation routine is returned only
	 *      if it occurred on the first allocation.  Otherwise a partial
	 *      list is returned and adjustments are made elsewhere.
	 *        
	 *      Exit conditions are (1) BUF allocation fails or (2) have reached   
	 *      the last sector read on the disk or (3) no more hdrs in list.
	 */        
	while ( pblThisHdr &&
			snLastSect > pblThisHdr->snSectNum &&
			!(usRet = GetBuf (&pbThisBuf)))
	{
		/* If this is first allocation */
		if (!fMinSuccess)
		{
			/* Store address of this BUF in the 'pfblVars' structure */
			pfblVars->pbBufChain = pbThisBuf;

			/* Indicate success */
			fMinSuccess = TRUE;

		} /* end if first allocation */

		/* Else link previous BUF with this BUF */
		else
		{
			pbLastBuf->pbNextBUF = pbThisBuf;

		} /* end else subsequent allocation */

		/* Link this BUF with previous BUF */
		pbThisBuf->pbPrevBUF = pbLastBuf;

		/* Move to the next header */
		pblThisHdr = pblThisHdr->pblNextHdr;

		/* Make this BUF the previous BUF */
		pbLastBuf = pbThisBuf;

	} /* end while loop */

	/* Check if operation was successful */
	if (fMinSuccess)
	{
		/* Null terminate and emember the end of the chain */
		(pfblVars->pbEndBufChain = pbLastBuf)->pbNextBUF = NULL;
	}

	/* Return the error code */
	return usRet;

} /* end GetBufChain */


/***    AddBufChain ()  --  Add a chain of buffers to an in use list.   
 *      
 *  DESCRIPTION
 *
 *      This function adds a chain of buffers to the appropriate in use
 *      list.  The pointers to the beginning and end of the list are
 *      passed and are used as links to connect the entire block in
 *      a unit.
 *      
 *      
 *  ENTRY   (Arguements passed to function)
 *      
 *      pbFirstBuf  --  Pointer to top of chain.
 *      pbLastBuf   --  Pointer to end of chain.
 *      fBLOwns     --  Flag indicating if this is a BUF_LIST or PATH_TABLE.
 *      
 *
 *  GLOBAL  (Global variables accessed by this function)
 *
 *      pbFirstSec  --  Pointer to global list of in use data sectors.
 *      pbLastSec   --  Pointer to end of in-use data sectors.
 *      pbFirstPT   --  Pointer to top of in-use path table sectors.
 *      pbLastPT    --  Pointer to end of in-use path table sectors.
 *      sBLSec      --  Semaphore controlling access to BUF_LIST sectors.
 *      sPTSec      --  Semaphore controlling access to PATH_TABLE sectors.
 *      
 *
 *  GOTO    (Jump targets)
 *
 *      NONE.
 *
 *
 *  RETURN  (Value returned by this function)
 *
 *      NONE.
 *
 *
 *  EXIT    (Changes to variables seen outside this function)
 *
 *      Global variables listed above may be modified.
 *
 *      
 *  LOCAL   (Variables declared within this function)    
 *      
 *      NONE.  
 */

VOID AddBufChain (pbFirstBuf, pbLastBuf, fBLOwns)
PBUF    pbFirstBuf;
PBUF    pbLastBuf;
BOOL    fBLOwns;
{
	/* Check for BUF_LIST sectors */
	if (fBLOwns)
	{
		/* Grab the semaphore for BUF_LIST sectors */
		SEM_REQ (sBLSec);

		/* Attach to the top of the existing list */
		pbLastBuf->pbNextBUF = pbFirstSec;
		pbFirstBuf->pbPrevBUF = NULL;

		/* Check if this is only sector cluster on list */
		if (!pbFirstSec)
		{
			pbLastSec = pbLastBuf;

		} /* end if only cluster */

		/* Else hook existing to end of chain */
		else
		{
			pbFirstSec->pbPrevBUF = pbLastBuf;

		} /* end else hook existing list to chain */

		/* Point to top of chain */
		pbFirstSec = pbFirstBuf;

		/* Release the BUF_LIST semaphore */
		SEM_CLEAR (sBLSec);

	} /* end if */


	/* Else Path Table sectors */
	else
	{
		/* Grab the semaphore for BUF_LIST sectors */
		SEM_REQ (sPTSec);

		/* Attach to the top of the existing list */
		pbLastBuf->pbNextBUF = pbFirstPT;
		pbFirstBuf->pbPrevBUF = NULL;

		/* Check if this is only Path Table cluster on list */
		if (!pbFirstPT)
		{
			pbLastPT = pbLastBuf;

		} /* end if only cluster */

		/* Else hook existing to end of chain */
		else
		{
			pbFirstPT->pbPrevBUF = pbLastBuf;

		} /* end else hook existing to end of chain */

		/* Point to top of chain */
		pbFirstPT = pbFirstBuf;

		/* Release the PATH_TABLE semaphore */
		SEM_CLEAR (sPTSec);

	} /* end else Path Table sectors */

} /* end AddBufChain */
