/*static char *SCCSID = "@(#)cachflus.c	6.12 92/03/11";*/

#define SCCSID "@(#)cachflus.c	6.12 92/03/11"
/********************************************************/
/*                                                      */
/*       IBM/Microsoft Confidential                     */
/*                                                      */
/*       Copyright (c) IBM Corporation 1987, 1989       */
/*       Copyright (c) Microsoft Corporation 1987, 1989 */
/*                                                      */
/*       All Rights Reserved                            */
/********************************************************/

#define FNO_NUM FNO_CACHFLUS_C
#define INCL_TYPES                      // for basedef.h typedefs
#include <osconst.h>
#include <ostypes.h>
#include <fnofs.h>
#include <osmac.h>
#include <basemac.h>
#include <basedef.h>
#include <error.h>
#include <mi.h>
#include <vmexport.h>
#include <selxport.h>
#include <pgexport.h>
#include <tkexport.h>
#include <smexport.h>
#include <sft.h>
#include <cache.h>

/*
 ** BufPreWrt - PreProcess Buffer for Writing and Write Buffer
 *
 *  BufPreWrt checks all the necessary conditons and waits on the conditons
 *  to complete before writing a buffer. If the buffer is locked or being
 *  lazy written we wait until they are done and return that we did not write
 *  the buffer. If the buffer needs to be lazy read we read it and return
 *  that we did not write the buffer. If none of these conditions exists we
 *  we write the buffer and return that we wrote the buffer.
 *
 *  There are two clients of this routine. The first clients want to flush all
 *  dirty buffers for a specific volume or for all volumes. So, they start
 *  there scan always from the top of the dirty chain and call this routine.
 *  If the buffer was locked by someone and we wait on it and it disappears
 *  then, there is no problem since these routines loop back to the beginning
 *  of the dirty chain.
 *
 *  The second clients have incremented the reference count so the buffer
 *  will not disappear. They have also guranteed that the buffer is indeed
 *  dirty. Their loop is to continue to call this routine while the buffer
 *  has not been written and the dirty mask is still not clear.
 *
 *      ENTRY   pBuf    = pointer to buffer to write
 *              pfWrite = pointer to flag to indicate whether we wrote buffer.
 *
 *      EXIT    pfWrite updated
 */
int BufPreWrt (PBUFHDR pBuf,int *pfWrite)
{
   int rc,fMustRead,fWrite = FALSE,rc2 = 0;

   if (pBuf->BufFlag & BUF_LOCK) {
      BufWaitUnLock(pBuf);
   }
   else {
      _disable();
      if (pBuf->BufFlag & BUF_LWLOCK) {
          pBuf->BufFlag |= BUF_LWWANTED;
          TKSleep ((void *)(&pBuf->BufFlag),-1,0,0);
          _enable();
      }
      else {
         _enable();
         fMustRead = fBufWritable(pBuf,
                                  BWRITABLE_GAPS | BWRITABLE_PARTIALSEC);
         if (fMustRead) {
            rc = LazyRead(pBuf,
                          NULL,
                          SSToDS(&fMustRead));
         }
         else {
            /* write this guy */
            rc2 = BufWrt(pBuf,NULL);
            fWrite = TRUE;
         }
      }
   }
   *pfWrite = fWrite;
   return (rc2);
}


/*
 ** BufPreWrt2 - Write Buffer for hybernation only if no wait conditions exist.
 *
 *  BufPreWrt2 checks all the necessary conditons and but does not wait on the
 *  conditons to complete before writing a buffer. If the buffer is locked or
 *  being lazy written we return that we did not write the buffer.
 *  If none of these conditions exists we
 *  we write the buffer and return that we wrote the buffer.
 *
 *  There are two clients of this routine. The first clients want to flush all
 *  dirty buffers for a specific volume or for all volumes. So, they start
 *  there scan always from the top of the dirty chain and call this routine.
 *  If the buffer was locked by someone and we wait on it and it disappears
 *  then, there is no problem since these routines loop back to the beginning
 *  of the dirty chain.
 *
 *  The second clients have incremented the reference count so the buffer
 *  will not disappear. They have also guaranteed that the buffer is indeed
 *  dirty. Their loop is to continue to call this routine while the buffer
 *  has not been written and the dirty mask is still not clear.
 *
 *      ENTRY   pBuf    = pointer to buffer to write
 *              pfWrite = pointer to flag to indicate whether we wrote buffer.
 *
 *      EXIT    pfWrite updated
 */
int BufPreWrt2 (PBUFHDR pBuf,int *pfWrite)
{
   int rc,fMustRead,fWrite = FALSE,rc2 = 0;

   if (pBuf->BufFlag & BUF_LOCK) {
//      BufWaitUnLock(pBuf);
     return (ERROR_WRITE_FAULT);
   }
   else {
      _disable();
      if (pBuf->BufFlag & BUF_LWLOCK) {
//          pBuf->BufFlag |= BUF_LWWANTED;
//          TKSleep ((void *)(&pBuf->BufFlag),-1,0,0);
          _enable();
        return (ERROR_WRITE_FAULT);
      }
      else {
         _enable();
         fMustRead = fBufWritable(pBuf,
                                  BWRITABLE_GAPS | BWRITABLE_PARTIALSEC);

         if (fMustRead) {

           return (ERROR_WRITE_FAULT);

//            rc = LazyRead(pBuf,
//                          NULL,
//                          SSToDS(&fMustRead));

         }
         else {
            /* write this guy */
            rc2 = BufWrt(pBuf,NULL);
            fWrite = TRUE;
         }
      }
   }
   *pfWrite = fWrite;
   return (rc2);
}

/*
 **     BufFlush - Do Selective Flush
 *
 *  BufFlush is called when we're to flush the dirty sectors
 *  belonging to a particular file and the FAT and Directory sectors for that
 *  volume. We go down through the dirty chain writing these guys out.
 *
 *  Note that an optimial algorithm would be to give a list
 *  of these guys to the asynchronous disk driver, but we believe
 *  that we're rare so we'll just skip that for now and call
 *  BufWrt.
 *
 *  BufFlush doesn't interlock things, so if some other thread
 *  is writing to the file we're flushing when we're called then
 *  some stuff might be missed.
 *
 *  Our quick-and-dirty algorithm looks N-squared, since we start
 *  over at the top of the dirty chain each time we write a buffer.
 *  It's not quite so bad.  If there are few eligible guys then
 *  we don't make many passes.  If there are a lot, then each pass
 *  doesn't go very far until we find a guy to write.  Also, the
 *  CPU time is spread out over time because we write a sector between
 *  each pass.
 *
 *      ENTRY       flMask = mask code for pbuf->flDMask
 *
 *      HISTORY
 */

void BufFlush( ulong_t flMask,ulong_t hVPB )
{
    register PBUFHDR pbuf;
    int rc,fWrite;

    for (;;) {

        pbuf = &BufDirtyHead;

        /*
         *   flMask (eax) = mask code
         *   pbuf   (ebx) = address of guy we're to consider flushing
         */

        do {
            if( (pbuf = pbuf->BufDirtyLru.pbufFwd) == &BufDirtyHead ) {
                /* done */
                return;
            }
            if (pbuf->BufhVPB == hVPB && pbuf->BufFileDirtyMsk == -1)
               break;

        /* get next if this buf not eligible */
        } while ((pbuf->BufFileDirtyMsk & flMask) == 0);

        rc = BufPreWrt(pbuf,SSToDS(&fWrite));
    }
}


/*
 ** BufFlushAll - Flush all dirty buffers.
 *
 *  BufFlushAll is called from DOS buffer management when it has encountered
 *  the same function. We flush all dirty buffers for all volumes and do not
 *  return until the dirty chain is empty.
 *
 *      ENTRY   hVPB    - handle to volume or null for all volumes
 *
 *      EXIT    NONE
 */
void BufFlushAll(ulong_t hVPB)
{
    register PBUFHDR pbuf;
    int rc,fWrite;

    for (;;) {

        pbuf = &BufDirtyHead;

        /*
         *   pbuf   (ebx) = address of guy we're to consider flushing
         */

        do {
            if( (pbuf = pbuf->BufDirtyLru.pbufFwd) == &BufDirtyHead ) {
                /* done */
                return;
            }
            if (!hVPB) break;

        } while (pbuf->BufhVPB != hVPB);
        rc = BufPreWrt(pbuf,SSToDS(&fWrite));
    }
}

/*
 ** BufFlushAll2 - Flush all dirty buffers for hybernation.
 *
 *  BufFlushAll2 is called from DOS buffer management when it has encountered
 *  the same function. We flush all dirty buffers for all volumes and do not
 *  return until the dirty chain is empty.
 *
 *      ENTRY   hVPB    - handle to volume or null for all volumes
 *
 *      EXIT    NONE
 */
int BufFlushAll2(ulong_t hVPB)
{
    register PBUFHDR pbuf;
    int rc,fWrite;

    for (;;) {

        pbuf = &BufDirtyHead;

        /*
         *   pbuf   (ebx) = address of guy we're to consider flushing
         */

        do {
            if( (pbuf = pbuf->BufDirtyLru.pbufFwd) == &BufDirtyHead ) {
                /* done */
                return;
            }
            if (!hVPB) break;

        } while (pbuf->BufhVPB != hVPB);
        rc = BufPreWrt2(pbuf,SSToDS(&fWrite));
        if (rc) {
          return(rc);
        }
    }
}

/*
 ** BufFlushFATDir - Flush FAT and Directory buffers to disk.
 *
 *  BufFlushFATDir is called by Extended Attributes to flush FAT and Directory
 *  buffers to disk immediately. Since the EA DATA .SF is so critial to the
 *  system all operations on this file must handle synchronously.
 *
 *  We have a special mask in the buffer header that distingushes FAT and
 *  Directory buffers from others. So, we loop through dirty chain writing out
 *  each of these buffers.
 *
 *  Note: we can't tell if these buffers were used exclusively by the extented
 *  so we just flush all FAT and Directory buffers for this volume.
 *
 *      ENTRY   hVPB    = handle to volume
 *
 *      EXIT    NONE
 */
void BufFlushFATDir(ulong_t hVPB)
{
    register PBUFHDR pbuf;
    int rc,fWrite;

    for (;;) {
        /*
         * BUGBUG - can we move this out of the loop? or
         * will that cause late arrival dirty buffers to be skipped?
         */
        pbuf = &BufDirtyHead;

        do {
            if( (pbuf = pbuf->BufDirtyLru.pbufFwd) == &BufDirtyHead ) {
                /* done */
                return;
            }
        /* get next if this buf not eligible */
        } while (pbuf->BufhVPB != hVPB || pbuf->BufFileDirtyMsk != -1);

        rc = BufPreWrt(pbuf,SSToDS(&fWrite));
    }
}

/*
 ** BufFlushFAT - Flush FAT buffers to disk.
 *
 *  BufFlushFAT flushes only FAT buffers to disk.
 *
 *  We have a special mask in the buffer header that distingushes FAT and
 *  Directory buffers from others.
 *
 *      ENTRY   hVPB    = handle to volume
 *
 *      EXIT    NONE
 */
void BufFlushFAT(ulong_t hVPB)
{
    register PBUFHDR pbuf;
    int rc,fWrite;

    for (;;) {
        /*
         * BUGBUG - can we move this out of the loop? or
         * will that cause late arrival dirty buffers to be skipped?
         */
        pbuf = &BufDirtyHead;

        do {
            if( (pbuf = pbuf->BufDirtyLru.pbufFwd) == &BufDirtyHead ) {
                /* done */
                return;
            }
        /* get next if this buf not eligible */
        } while (pbuf->BufhVPB != hVPB ||
                 pbuf->BufFileDirtyMsk != -1 ||
                 (!(IsRBAForFAT(pbuf->BufRBA,hVPB))) );

        rc = BufPreWrt(pbuf,SSToDS(&fWrite));
    }
}
