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

#define SCCSID "@(#)diskr.c 6.15 92/03/11"
/********************************************************/
/*                                                      */
/*       IBM/Microsoft Confidential                     */
/*                                                      */
/*       Copyright (c) IBM Corporation 1987, 1989       */
/*       Copyright (c) Microsoft Corporation 1987, 1989 */
/*                                                      */
/*       All Rights Reserved                            */
/********************************************************/
/* 12-15-93 RAC 76699 Make Perfview optional with PERFVIEW compiler Flag */
/* 03-08-94 wdm merged 76699 changes with 74667 changes                  */
#define FNO_NUM FNO_DISKR_C
#define INCL_TYPES                      // for basedef.h typedefs
#include <osconst.h>
#include <ostypes.h>
#include <basemac.h>
#include <basedef.h>
#include <bseerr.h>
#include <fnofs.h>
#include <osmac.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>
#include <strat2.h>
#include <vmowner.h>
#include <fslocal.h>
#include <filesys.h>

#include <dossymc.h>
#include <sigdef.h>                     /* for tkptda.h */             //b722131
#include <except.h>                     /* for tkptda.h */             //b722131
#include <curdir.h>                     /* for tkptda.h */             //b722131
#include <dirent.h>                     /* for tkptda.h */             //b722131
#include <find.h>                       /* for tkptda.h */             //b722131
#include <screen.h>                     /* for tkptda.h */             //b722131
#include <sem.h>                        /* for tkptda.h */             //b722131
#include <tkptda.h>                                                    //b722131
#include <string.h>                                                    //b722131

#ifdef PERFVIEW
#include <pvwxport.h>                                                  //b722131
#include <perfvw.h>                                                    //b722131
#endif

/***LP  SetRBA_Count - Set starting RBA and count
 *
 *      SetRBA_Count determines the starting RBA of a request and the count
 *      of sectors. It rounds the passed in RBA down to be cache block
 *      aligned, however if bad sectors are detected then the request will
 *      not be cache block aligned. It also rounds the count up to account for
 *      the rounding down of the RBA as well as the rounding required to make
 *      the request end on a cache block boundary. Similarly, if bad sectors
 *      are detected on the tail then the request will not be cache block
 *      aligned.
 *
 *      ENTRY   pRBA    - pointer to store rounded rba.
 *              rba     - rba for which need to align.
 *              pCount  - pointer to store rounded count.
 *              hVPB    - handle to volume
 *
 *      EXIT:   NONE
 *
 *
 *
 */
void SetRBA_Count(ulong_t           *pRBA,
                  register ulong_t  rba,
                  ulong_t           *pCount,
                  ulong_t           hVPB)
{
   register ulong_t Rrba;
   register ulong_t Endrba;
   int rc;

      Rrba = ROUNDRBA(rba);

      // If the starting rba is already rounded down and the count is a
      // multiple of SECPERBUF then return.

      if ((Rrba == rba) && (!(*pCount % SECPERBUF))) {
         *pRBA = rba;
         return;
      }

      Endrba = rba + *pCount;
      if (Endrba % SECPERBUF)
         Endrba += SECPERBUF - (Endrba % SECPERBUF);
      rc = TRUE;

      // Process the head.

      while (rc && Rrba != rba){        // while no bad sectors and not at
          rc = IsRBABad(Rrba,hVPB);     // starting rba
          if (rc){                      // current rba bad ?
             BufAssert (rba != Rrba);
             ++Rrba;                    // yes, point to next rba
          }
          else
             *pCount += rba - Rrba;     // no, adjust count
      }

      // Process the Tail.

      rba = Rrba + *pCount;
      while (rba != Endrba){            // while not at the endrba
         rc = IsRBABad(rba,hVPB);
         if (!rc) ++*pCount;            // if current rba not bad, inc count
         else {
            break;                      // else quit
         }
         ++rba;                         // increment loop rba
      }
      *pRBA = Rrba;                     // return new starting rba
      return;
}

/***LP  BufReadError - BufRead Error
 *
 *      BufReadError frees the request list and goes through the buffer
 *      transfer records cleaning up buffers that were allocated.
 *
 *      ENTRY   ReqListSel   - Selector of current request list
 *              ReqListOff   - Offset of current request list
 *              pBufXferRec  - Pointer to first buffer transfer record in array
 *              BufXferIndex - Current buffer transfer index
 *
 *      EXIT:   NONE
 *
 *
 *
 */
void BufReadError(ulong_t    ReqListSel,
                  ulong_t    ReqListOff,
                  BufXferRec (*pBufXferRecs)[1],
                  int        BufXferIndex,
                  int        fDOSBuffer)
{
   int i;
   PBUFHDR pBuf;

   if (!fDOSBuffer && ReqListSel != 0){
      FreeReqList(ReqListSel,ReqListOff);
   }
   for (i = 0; i < BufXferIndex; ++i) {
      if (pBufXferRecs[i]->BufXfer_Type == BUFXFER_BUF) {
         pBuf = pBufXferRecs[i]->BufXfer_pBuf;
         BufUnLock(pBuf);
         RemoveHash(pBuf);
         BufFree(pBuf);
      }
   }
}
/***LP  BufReadComplete - BufRead Complete
 *
 *      BufReadComplete goes through the buffer transfer records transfering
 *      data from the buffers in those records to the user buffers whose
 *      addresses are in the records. It also places new buffers on the LRU
 *      chain and unlocks them. For duplicate buffers we decrement the
 *      reference count.
 *
 *      We process the records in reverse order so that if an error occurs
 *      BufReadError will be able to cleanup only those records that have not
 *      been processed.
 *
 *      ENTRY   pBufXferRec  - Pointer to first buffer transfer record in array
 *              BufXferIndex - Current buffer transfer index
 *              ReqListSel   - Selector of current request list
 *              ReqListOff   - Offset of current request list
 *
 *      EXIT:   rc            - NULL or Error Code.
 *
 *
 *
 */
int  BufReadComplete(BufXferRec (*pBufXferRecs)[1],
                     int        *pBufXferIndex,
                     ulong_t    ReqListSel,
                     ulong_t    ReqListOff,
                     int        fDOSBuffer)
{
   int i,rc;
   ulong_t BufOff;
   PBUFHDR pBuf;

   for (i = *pBufXferIndex; i != 0; --i) {

       BufOff = BUFOFFSET(pBufXferRecs[i-1]->BufXfer_rba);
       pBuf = pBufXferRecs[i-1]->BufXfer_pBuf;

       if (rc = SuBuff386(pBufXferRecs[i-1]->BufXfer_cbCount,
                          pBufXferRecs[i-1]->BufXfer_UserOffset,
                          pBuf->BufLinAddr + BufOff +
                          pBufXferRecs[i-1]->BufXfer_SecBytPos) != 0){
          BufReadError (ReqListSel,
                        ReqListOff,
                        pBufXferRecs,
                        *pBufXferIndex,
                        fDOSBuffer);
          return(rc);
       }
       if (pBufXferRecs[i-1]->BufXfer_Type == BUFXFER_BUF) {
          PlaceBuf(pBuf,PLCBUF_MRU);
          BufUnLock(pBuf);
          pBuf->BufFlag &= ~BUF_READING;
       }
       else {
          BufAssert (pBufXferRecs[i-1]->BufXfer_Type == BUFXFER_DUP);
          BufDecRef(pBuf);
       }

       --*pBufXferIndex;
   }
   return(0);
}

/***LP  BufReadExecute - BufRead Execute
 *
 *      BufReadExecute executes the current request list and calls
 *      BufReadComplete to transfer the data from the buffers into user memory.
 *
 *      ENTRY   pBufXferRec   - pointer to first buffer transfer record.
 *              BufXferIndex  - current buffer transfer array index.
 *              ReqListSel    - selector of current request list.
 *              ReqListOff    - offset of current request list.
 *
 *      EXIT:   rc            - NULL or Error Code.
 *
 *
 *
 */
int  BufReadExecute (BufXferRec (*pBufXferRecs)[1],
                     int        *pBufXferIndex,
                     ulong_t    ReqListSel,
                     ulong_t    ReqListOff,
                     int        fDOSBuffer)
{
   int rc;
   if (rc = ReqListExecute(ReqListSel,ReqListOff) != 0) {
      BufReadError (ReqListSel,
                    ReqListOff,
                    pBufXferRecs,
                    *pBufXferIndex,
                    fDOSBuffer);
      return(rc);
   }
   if (rc = BufReadComplete(pBufXferRecs,
                            pBufXferIndex,
                            ReqListSel,
                            ReqListOff,
                            fDOSBuffer) != 0) {
      return(rc);
   }

}

/***LP  BufAddToReqList - Add Buffer to request list
 *
 *      BufAddToReqList allocates a buffer for the RBA, adds it to the hash
 *      table, and locks. If the request list has not been initialized, it
 *      initializes it adds the buffer to the request list.
 *
 *      ENTRY   ReqListSel   - Selector of current request list
 *              ReqListOff   - Offset of current request list
 *              hVPB         - Handle to volume
 *              rba          - rba of new buffer
 *              pfIONeeded   - Pointer to IONeeded flag
 *              ppBuf        - pointer to pointer of new buffer
 *
 *
 *      EXIT:   rc            - NULL or Error Code.
 *
 */
int BufAddToReqList (register ulong_t  ReqListSel,
                     register ulong_t  ReqListOff,
                     ulong_t           hVPB,
                     ulong_t           rba,
                     int               *pfIONeeded,
                     PBUFHDR           *ppBuf)
{
   ulong_t Rrba,count;
   int rc;
   PBUFHDR pBuf;


   pBuf = BufAlloc(rba,hVPB);           // allocate new buffer
   pBuf->BufFlag = BUF_READING;
   rc = AddHash(pBuf,hVPB);             // add buffer to hash table
   BufAssert (!rc);
   BufLock(pBuf);                       // lock buffer down

   count = 1;
   SetRBA_Count(SSToDS(&Rrba),rba,SSToDS(&count),hVPB);

   // If request list not initialized then initalize it.

   if (!*pfIONeeded) {
      *pfIONeeded = TRUE;
      ReqListInit(ReqListSel,
                  ReqListOff,
                  hVPB,
                  RLCB_KEEP_REQ);
   }

   if ((rc = ReqListAdd_REQ(Rrba,
                            count,
                            (ulong_t)PB_READ_X,
                            ReqListSel,
                            ReqListOff,
                                                                //#83284
//                            (ulong_t)PRIO_FOREGROUND_USER,

                            (ulong_t)PRIO_BACKGROUND_USER,
                            hVPB)) == 0) {
      ReqListAdd_SG((ulong_t)SECSIZE*count,
                    ReqListSel,
                    ReqListOff,
                    pBuf->BufPhysAddr + BUFOFFSET(Rrba));
   }
   *ppBuf = pBuf;                       // return buffer
   return (rc);
}

/***LP  BufReadPartial - perform user partial read through Buffers
 *
 *      BufReadPartial tries to find the rba in the cache and if it does it
 *      transfers the data from the cache to the user's buffer. If it does not
 *      find it in the cache it will call BufAddToReqList to add a buffer to
 *      the request list.
 *
 *      We are very careful not to block since we maybe holding buffers. When
 *      checking to see if a sector is in the cache we initially call HashFind
 *      instead BufFind which may block. If no one else is trying to get a free
 *      buffer and HashFind did not find it in the cache, then we can safely
 *      assume that it is not in the cache. If someone is in trying to get a
 *      free buffer or the buffer is locked or needs to be lazy read then
 *      if we are holding buffers we execute the pending request, otherwise
 *      we call BufFind to do the synchronization or lazy reading for us.
 *
 *      ENTRY   hVPB         - Handle to volume
 *              pTCB         - pointer to current TCB
 *              pxferaddr    - pointer to user transfer address
 *              pcbRun       - pointer to count of bytes remaining in run
 *              pcRun        - pointer to count of sectors remaining in run
 *              prba         - pointer to rba
 *              ReqListSel   - Selector of current request list
 *              ReqListOff   - Offset of current request list
 *              pfIONeeded   - Pointer to IONeeded flag
 *              pBufXferRec  - Pointer to first buffer transfer record in array
 *              BufXferIndex - Current buffer transfer index
 *
 *      EXIT:   rc           - NULL or Error Code.
 *
 *
 *
 */
int BufReadPartial (ulong_t     hVPB,
                    tcb_t       *pTCB,
                    ulong_t     *pxferaddr,
                    ulong_t     *pcbRun,
                    ulong_t     *pcRun,
                    ulong_t     *prba,
                    ulong_t     *pReqListSel,
                    ulong_t     *pReqListOff,
                    int         *pfIONeeded,
                    BufXferRec  (*pBufXferRecs)[1],
                    int         *pBufXferIndex,
                    int         fDOSBuffer,
                    ulong_t     rdCount)
{
    ulong_t BufOff,cbCount,Rrba;
    int rc;
    PBUFHDR pBuf;
    PBUFXFERREC pBufXfer;
    int fReqListAlloc,fMemLocked;

    BufAssert (*pcbRun <= 0x10000);
    BufAssert (*pcRun <= 132l);
    fMemLocked = FALSE;

    // Figure out the count of bytes that are actually part of
    // of the users I/O request that belong to this cache page

    BufOff = BUFOFFSET(*prba);
    if (pTCB->TCBBytSecPos) {
       cbCount = MIN(*pcbRun,(ulong_t)SECSIZE - (ulong_t)pTCB->TCBBytSecPos);
    }
    else {
       cbCount = MIN(*pcbRun,BufOff == 0 ? (ulong_t)BUFPAGESIZE:
                                          ((ulong_t)BUFPAGESIZE - BufOff));
       if (*pcRun) {
          cbCount = MIN(cbCount,*pcRun * SECSIZE);
       }
    }
    Rrba = *prba;
    Rrba = ROUNDRBA(Rrba);
readpart:
    pBuf = HashFind(Rrba,hVPB);
    if (pBuf == NULL){
       if (!fDOSBuffer) {
          if (*pReqListSel == 0){
             ReqListAlloc(pReqListSel,
                          pReqListOff);
          }
          if ((pTCB->TCBSecCnt != 0) && (pTCB->TCBfIOLock == 0) && (!fMemLocked)){
             fMemLocked = TRUE;
             if (rc = ioBeginTransfer((SEL)((pTCB->TCBDMAAdd) >>16),
                                      (ushort_t)(pTCB->TCBDMAAdd),
                                      rdCount,
                                      VML_DIRTY,   // buf will be dirtied
                                      (struct pagelist_s *)VM_NO_ADDR,
                                      (ulong_t *)VM_NO_ADDR) != 0){
                   BufReadError (*pReqListSel,
                                 *pReqListOff,
                                 pBufXferRecs,
                                 *pBufXferIndex,
                                 fDOSBuffer);
                   return (rc);
             }
          }                                                  //gwd74667
          pBuf = HashFind(Rrba,hVPB);                        //gwd74667
       }
    }
    if (fGFBActive || pBuf) {
       if ((fGFBActive || (pBuf->BufFlag & (BUF_LOCK | BUF_LAZYREAD))) &&
           *pfIONeeded){

           // The buffer we want is locked or needs to be
           // read from the disk. We can't hold buffers while
           // waiting on another event other than I/O. So we
           // Execute the current request list and release the
           // the buffers we own and then go back and do
           // whatever is necessary to claim this buffer.


           if (rc = BufReadExecute(pBufXferRecs,
                                   pBufXferIndex,
                                   *pReqListSel,
                                   *pReqListOff,
                                   fDOSBuffer) != 0) {
              return(rc);
           }
           *pfIONeeded = FALSE;
       }
       pBuf = BufFind(*prba,BFND_READ,hVPB);
    }
    if (pBuf){
#ifdef PERFVIEW
                                                                    //b722131
       PvwCnt(fbc,RdHit,0);   // counter for the number of read cache hits
#endif
       BufIncRef(pBuf);
       rc = SuBuff386(cbCount,
                      *pxferaddr,
                      pBuf->BufLinAddr +  BUFOFFSET(*prba) +
                                          (ulong_t)pTCB->TCBBytSecPos);

       BufDecRef(pBuf);
       if (rc) {
          BufReadError (*pReqListSel,
                        *pReqListOff,
                        pBufXferRecs,
                        *pBufXferIndex,
                        fDOSBuffer);
          return(rc);
       }
    }
    else {
       if (!fDOSBuffer) {
          fReqListAlloc = FALSE;
          if (*pReqListSel == 0){
             ReqListAlloc(pReqListSel,
                          pReqListOff);
             fReqListAlloc = TRUE;
          }
          if ((pTCB->TCBSecCnt != 0) && (pTCB->TCBfIOLock == 0) && (!fMemLocked)){
             fMemLocked = TRUE;
             if (rc = ioBeginTransfer((SEL)((pTCB->TCBDMAAdd) >>16),
                                      (ushort_t)(pTCB->TCBDMAAdd),
                                      rdCount,
                                      VML_DIRTY,   // buf will be dirtied
                                      (struct pagelist_s *)VM_NO_ADDR,
                                      (ulong_t *)VM_NO_ADDR) != 0){
                   BufReadError (*pReqListSel,
                                 *pReqListOff,
                                 pBufXferRecs,
                                 *pBufXferIndex,
                                 fDOSBuffer);
                   return (rc);
             }
             goto readpart;
          }
          if (fReqListAlloc) {                                         //gwd74667
             goto readpart;                                            //gwd74667
          }                                                            //gwd74667
       }
#ifdef PERFVIEW
                                                                       //b722131
       PvwCnt(fbc,RdMiss,0);  // counter for the number of read cache hits
#endif

       if (rc = BufAddToReqList(*pReqListSel,
                                *pReqListOff,
                                hVPB,
                                *prba,
                                pfIONeeded,
                                SSToDS(&pBuf)) != 0) {
          BufReadError (*pReqListSel,
                        *pReqListOff,
                        pBufXferRecs,
                        *pBufXferIndex,
                        fDOSBuffer);
          return(rc);
       }

       pBufXfer = (PBUFXFERREC)&pBufXferRecs[(*pBufXferIndex)++];
       BufAssert (*pBufXferIndex <= 6);

       pBufXfer->BufXfer_pBuf = pBuf;
       pBufXfer->BufXfer_UserOffset = *pxferaddr;
       pBufXfer->BufXfer_cbCount = cbCount;
       pBufXfer->BufXfer_rba = *prba;
       pBufXfer->BufXfer_SecBytPos = pTCB->TCBBytSecPos;
       pBufXfer->BufXfer_Type = BUFXFER_BUF;
    }
    *pxferaddr += cbCount;
    *pcbRun -= cbCount;
    *prba += cbCount / SECSIZE;
    *pcRun -= cbCount / SECSIZE;
    if (cbCount % SECSIZE) {
       ++*prba;
       --*pcRun;
    }
    return(0);
}


/***LP  BufCopyUserExtent - Copy User extent into cache buffers
 *
 *      BufCopyUserExtent copies data from user memory into cache buffers. It
 *      assumes that there is at least one cache block worth of data and
 *      that the count is a multiple of SECPERBUF.
 *
 *      ENTRY   rba          - Starting sector for which to copy
 *              UserIOoffset - User Offset address
 *              count        - count of user sectors
 *              hVPB         - handle to volume
 *
 *      EXIT:   rc           - NULL or Error Code.
 *
 *
 *
 */
int BufCopyUserExtent (register ulong_t  rba,
                       register ulong_t  UserIOoffset,
                       ulong_t           count,
                       ulong_t           hVPB)
{
   int rc,cBlocks,i;
   register PBUFHDR pBuf;

   cBlocks = count / SECPERBUF;
   BufAssert (!(count % SECPERBUF));
   BufAssert (!(rba % SECPERBUF));
   for (i = 0; i < cBlocks; ++i) {
     pBuf = BufFind(rba,
                    BFND_WRITE,
                    hVPB);
     if (!pBuf) {

        pBuf = BufAlloc(rba,hVPB);
        pBuf->BufFlag = 0;
        BufLock(pBuf);
        rc = AddHash(pBuf,hVPB);
        BufAssert (!rc);
        if (rc = FuBuff386(BUFPAGESIZE,
                           UserIOoffset,
                           pBuf->BufLinAddr) != 0){
           BufUnLock(pBuf);
           RemoveHash(pBuf);
           BufFree(pBuf);
           return (rc);
        }
        PlaceBuf(pBuf,PLCBUF_MRU);
        BufUnLock(pBuf);
     }
     rba += SECPERBUF;
     UserIOoffset += BUFPAGESIZE;
   }
   return(0);
}


/***LP  BufAddReadExtent - Add User read extent to reqlist
 *
 *      BufAddReadExtent initializes the request list if this is the first
 *      request. If we need to copy the data into user buffers, then we execute
 *      the request list and then call BufCopyUserExtent.
 *
 *      ENTRY   ReqListSel   - Selector of current request list
 *              ReqListOff   - Offset of current request list
 *              count        - Number of sectors in extent
 *              hVPB         - Handle to volume
 *              rba          - rba of new buffer
 *              UserIOoffset - User Offset address
 *              fUseCache    - Flag if copying user data into cache.
 *              pBufXferRec  - Pointer to first buffer transfer record in array
 *              BufXferIndex - Current buffer transfer index
 *              pfIONeeded   - Pointer to IONeeded flag
 *
 *      EXIT:   rc           - NULL or Error Code.
 *
 *
 */
int BufAddReadExtent (register ulong_t ReqListSel,
                      register ulong_t ReqListOff,
                      ulong_t          count,
                      ulong_t          hVPB,
                      ulong_t          rba,
                      ulong_t          UserIOoffset,
                      int              fUseCache,
                      BufXferRec       (*pBufXferRecs)[1],
                      int              *pBufXferIndex,
                      int              *pfIONeeded,
                      int              fDOSBuffer)
{
   int rc;


   if (!*pfIONeeded) {                  // First Request ?
      *pfIONeeded = TRUE;               // Yes, set not First request flag
      ReqListInit(ReqListSel,           // Initialize request list
                  ReqListOff,
                  hVPB,
                  RLCB_KEEP_REQ);
   }
   if (rc = ReqListAdd_Extent(rba,      // Add extent to request list
                              count,
                              PB_READ_X,
                              ReqListSel,
                              ReqListOff,
                              UserIOoffset) != 0){
      BufReadError (ReqListSel,
                    ReqListOff,
                    pBufXferRecs,
                    *pBufXferIndex,
                    fDOSBuffer);
      return (rc);
   }

   // If we need to copy the data into cache, execute the current list and
   // then call BufCopyUserExtent to copy the data.

   if (fUseCache) {
      if (rc = BufReadExecute(pBufXferRecs,
                              pBufXferIndex,
                              ReqListSel,
                              ReqListOff,
                              fDOSBuffer) != 0) {
         return(rc);
      }
      *pfIONeeded = FALSE;
      if (rc = BufCopyUserExtent(rba,
                                 UserIOoffset,
                                 count,
                                 hVPB) != 0) {
         BufReadError (ReqListSel,
                       ReqListOff,
                       pBufXferRecs,
                       *pBufXferIndex,
                       fDOSBuffer);
         return (rc);
      }
   }
   return(0);
}

/***LP  BufRead - perform user read using FAT Cache Buffers
 *
 *      BufRead reads data from disk or retrieves data from cache blocks and
 *      returns with the expected user data and optionally starts a read-ahead
 *      for future I/Os.
 *
 *
 *      ENTRY   rdCount     - Count of bytes to read
 *              _pSFT       - pointer to SFT (maybe NULL)
 *              fDirect     - Direct access flag
 *              hVPB        - handle to volume
 *
 *      EXIT:   rc          - NULL or Error Code.
 *
 *
 */
int  BufRead(rdCount,_pSFT,fDirect,hVPB)
ulong_t rdCount;
sf_entry *_pSFT;
int fDirect;
ulong_t hVPB;
{
    register tcb_t *pTCB = pTCBCur;
    ulong_t rba,sgcount,cul,rbaNextRun,culRemaining;
    ulong_t SecClusPos;
    int EOF = FALSE;
    register PBUFHDR pBuf;
    int rc,fUseCache = FALSE,fFit,fAligned;
    register sf_entry *pSFT = _pSFT;
    ulong_t xferaddr,BufOff;
    sfdFATFS *pSFTFAT;
    int fIO = FALSE,fIONeeded = FALSE;
    ulong_t cbRun,cRun;
    ulong_t cusSectsToRead,nextcluster,TmpRba,TmpRba2,cSec,dummy_xferaddr;
    BufXferRec BufXferRecords[6];
    int BufXferIndex = 0,fPartBegin,fDOSBuffer = FALSE;
    ushort_t oldTCBNextAdd = pTCB->TCBNextAdd;
    ulong_t ReqListSel = 0,ReqListOff = 0,tmpNextRA;
    if (pSFT != NULL)
       pSFTFAT = (sfdFATFS *)&(pSFT->sf_fsd);

    if (IsDriveLocked(hVPB) ||
        pTCB->TCBJfnFlag & JFN_NCACHE) {
       fUseCache = FALSE;
    }
    else {
       if (DiskThresh >= (ulong_t)pTCB->TCBSecCnt)
          fUseCache = TRUE;
    }

    if (!pSFT){
       fDOSBuffer = TRUE;
       ReqListSel = (ushort_t)((pTCB->TCBDMAAdd) >>16);
       ReqListOff = (ushort_t)(pTCB->TCBDMAAdd);
    }
    cbRun = rdCount;

    // Determine total sectors in request

    cSec = (ulong_t)pTCB->TCBSecCnt;
    if (pTCB->TCBBytCnt1) {
       ++cSec;
    }
    if (pTCB->TCBBytCnt2) {
       ++cSec;
    }

    // Initialize variables for SetupPhysIO

    SecClusPos = (ulong_t)pTCB->TCBSecClusPos;
    nextcluster = pTCB->TCBClusNum;
    xferaddr = (ulong_t)pTCB->TCBNextAdd;

    // In the regular FAT File System, they split the I/Os up into head,
    // middle, and tail. Here we treat the head, middle, and tail as one
    // run which maybe split into different clusters. We are careful however,
    // to know if the head does not start on a sector boundary and if we are
    // processing the head or tail such that either one is < 512.

    while (cSec) {
       if (rc = SetupPhysIO(SSToDS(&cRun),
                            SSToDS(&nextcluster),
                            cSec,
                            nextcluster,
                            SSToDS(&rba),
                            SSToDS(&cSec),
                            SSToDS(&dummy_xferaddr),
                            SecClusPos,
                            fDirect,
                            hVPB)){
           BufReadError (ReqListSel,
                         ReqListOff,
                         SSToDS(&BufXferRecords[0]),
                         BufXferIndex,
                         fDOSBuffer);
           return(rc);
       }

       MMPHPreSDR(); /* performance hook (3213) */
       fPartBegin = FALSE;
       if (pTCB->TCBBytSecPos || cbRun < SECSIZE) {
          if (pTCB->TCBBytSecPos)
             fPartBegin = TRUE;

          if (rc = BufReadPartial(hVPB,
                                  pTCB,
                                  SSToDS(&xferaddr),
                                  SSToDS(&cbRun),
                                  SSToDS(&cRun),
                                  SSToDS(&rba),
                                  SSToDS(&ReqListSel),
                                  SSToDS(&ReqListOff),
                                  SSToDS(&fIONeeded),
                                  SSToDS(&BufXferRecords[0]),
                                  SSToDS(&BufXferIndex),
                                  fDOSBuffer,
                                  rdCount) != 0) {
             return(rc);
          }
          pTCB->TCBBytSecPos = 0;
       }
       BufAssert (cbRun <= 0x10000);
       BufAssert (cRun <= 132l);

       if (cRun) {

          if (fPartBegin){
             if (cbRun > SECSIZE) {

                // If there are any remaining sectors in this run see if
                // there are any that were included in the partial
                // transfer.

                if (cRun && rba % SECPERBUF) {
                   BufOff = BUFOFFSET(rba);
                   sgcount = MIN (cbRun,BufOff == 0 ? (ulong_t)BUFPAGESIZE:
                                 ((ulong_t)BUFPAGESIZE - BufOff));
                   sgcount = MIN(sgcount,cRun * SECSIZE);

                   pBuf = HashFind(ROUNDRBA(rba),hVPB);
                   BufAssert (pBuf);
                   if (pBuf->BufFlag & BUF_LOCK) {

                      BufIncRef(pBuf);
                      BufXferRecords[BufXferIndex].BufXfer_pBuf = pBuf;
                      BufXferRecords[BufXferIndex].BufXfer_UserOffset = xferaddr;
                      BufXferRecords[BufXferIndex].BufXfer_cbCount = sgcount;
                      BufXferRecords[BufXferIndex].BufXfer_rba = rba;
                      BufXferRecords[BufXferIndex].BufXfer_SecBytPos = 0;
                      BufXferRecords[BufXferIndex].BufXfer_Type = BUFXFER_DUP;
                      ++BufXferIndex;
                      BufAssert (BufXferIndex <= 6);
                   }
                   else {
                      BufAssert (!(pBuf->BufFlag & BUF_LAZYREAD));
                      BufIncRef(pBuf);
                      rc = SuBuff386(sgcount,
                                     xferaddr,
                                     pBuf->BufLinAddr +  BufOff);
                      BufDecRef(pBuf);
                      if (rc) {
                         BufReadError (ReqListSel,
                                       ReqListOff,
                                       SSToDS(&BufXferRecords[0]),
                                       BufXferIndex,
                                       fDOSBuffer);
                         return(rc);
                      }
                   }
                   xferaddr += sgcount;
                   cbRun -= sgcount;
                   rba += sgcount / SECSIZE;
                   cRun -= sgcount / SECSIZE;
                   if (sgcount % SECSIZE) {
                      ++rba;
                      --cRun;
                   }
                }
             }
             else {

                // If we get here there was a partial sector at the
                // beginning of the run followed immediately by an end
                // partial sector which was not a part of the beginning
                // partial sector's cache block.

                if (rc = BufReadPartial(hVPB,
                                        pTCB,
                                        SSToDS(&xferaddr),
                                        SSToDS(&cbRun),
                                        SSToDS(&cRun),
                                        SSToDS(&rba),
                                        SSToDS(&ReqListSel),
                                        SSToDS(&ReqListOff),
                                        SSToDS(&fIONeeded),
                                        SSToDS(&BufXferRecords[0]),
                                        SSToDS(&BufXferIndex),
                                        fDOSBuffer,
                                        rdCount) != 0) {
                   return(rc);
                }
                BufAssert (cbRun <= 0x10000);
                BufAssert (cRun <= 132l);
             }
          }
       }
       if (cRun) {

          BufAssert (cbRun <= 0x10000);
          BufAssert (cRun <= 132l);

          // At this point there is no partial sector at the beginning of
          // the run. If there is a partial sector at the end of the run
          // then we also know that there are other whole sectors before
          // it in the run. We also know that no whole sectors of this run
          // belong to the cache block of the partial sector at the
          // beginning of the run.

          fAligned = fFit = FALSE;

          // BUGBUG the following sequence is to fix possible compiler bug

          TmpRba = rba + cRun - 1;
          TmpRba = ROUNDRBA(TmpRba);
          TmpRba2 = ROUNDRBA(rba);
          if (TmpRba2 == TmpRba)
             fFit = TRUE;


          if (!(rba % SECPERBUF))
             fAligned = TRUE;

          // if we are not on a cache boundary and the request is below
          // the threshold or the entire request will fit in a cache block
          // then transfer it through the cache.

          if ((fUseCache && (!fAligned || fFit)) ||
              (fFit && !cSec) ||
              (!fAligned && rdCount <= BUFPAGESIZE)){

             if (rc = BufReadPartial(hVPB,
                                     pTCB,
                                     SSToDS(&xferaddr),
                                     SSToDS(&cbRun),
                                     SSToDS(&cRun),
                                     SSToDS(&rba),
                                     SSToDS(&ReqListSel),
                                     SSToDS(&ReqListOff),
                                     SSToDS(&fIONeeded),
                                     SSToDS(&BufXferRecords[0]),
                                     SSToDS(&BufXferIndex),
                                     fDOSBuffer,
                                     rdCount) != 0) {
                return(rc);
             }
          }
       }
       if (cRun){
          BufAssert (cbRun <= 0x10000);
          BufAssert (cRun <= 132l);

          cul = 0;

          if (fUseCache && ((rba + cRun) % SECPERBUF)) {

             /*  rba + cRun is the sector following
              *  the end of the run length.  If it is a multiple
              *  of SECPERBUF, the run length ends at a block boundary.
              *
              */

            cul = MIN(((rba + cRun) % SECPERBUF),cRun);
          }
          else {

             // If we are the at the end of a RUN and there is a partial sector
             // at the end then we must read it in through the cache

             if ((cbRun % SECSIZE) && !cSec) {

                /*  rba + cRun is the sector following
                 *  the end of the run length.  If it is a multiple
                 *  of SECPERBUF, the run length ends at a block boundary.
                 *
                 */
                if ((rba + cRun) % SECPERBUF){

                   cul = MIN(((rba + cRun) % SECPERBUF),cRun);
                }
                else {
                   cul = MIN(cRun,SECPERBUF);
                }
             }
          }

          cRun -= cul;
          BufAssert (cbRun <= 0x10000);
          BufAssert (cRun <= 132l);
          while (cRun) {

             BufAssert (cbRun <= 0x10000);
             BufAssert (cRun <= 132l);
             rbaNextRun = rba - (rba % SECPERBUF);
             culRemaining = cRun + (rba % SECPERBUF);

             do {
                 pBuf = HashFind(rbaNextRun,hVPB);
                 if (fGFBActive || pBuf) {
                    if ((fGFBActive || (pBuf->BufFlag &
                                       (BUF_LOCK | BUF_LAZYREAD))) &&
                        fIONeeded){

                        // The buffer we want is locked or needs to be
                        // read from the disk. We can't hold buffers while
                        // waiting on another event other than I/O. So we
                        // Execute the current request list and release the
                        // the buffers we own and then go back and do
                        // whatever is necessary to claim this buffer.


                        if (rc = BufReadExecute(SSToDS(&BufXferRecords[0]),
                                                SSToDS(&BufXferIndex),
                                                ReqListSel,
                                                ReqListOff,
                                                fDOSBuffer) != 0) {
                           return(rc);
                        }
                        fIONeeded = FALSE;
                    }
                    pBuf = BufFind(rbaNextRun,BFND_READ,hVPB);
                 }
                 if (pBuf == NULL) {
                    if (culRemaining >= SECPERBUF) {
                        rbaNextRun += SECPERBUF;
                        culRemaining -= SECPERBUF;
                    } else {
                         rbaNextRun += culRemaining;
                         culRemaining = 0;
                    }
                 }
              } while ((pBuf == NULL) && (culRemaining));

               /*  Increment the holds on our cache buffer, if we
                *  found one.
                *
                */
               if (pBuf != NULL) {
                   BufIncRef (pBuf);
               }
              /*  We've found the end of a subrun, either because we
               *  reached the end of the run or because there was a
               *  cache hit.
               *
               *  If the read is non-zero length, build a request
               *  block for it.
               *
               */
              if ( cRun > culRemaining ) {
                  cusSectsToRead = cRun - culRemaining;
              }
              else {
                  cusSectsToRead = 0;
              }
              if (cusSectsToRead) {
                 if (pSFT) {
                    if (ReqListSel == 0){
                        ReqListAlloc(SSToDS(&ReqListSel),
                                     SSToDS(&ReqListOff));
                    }
                    if ((pTCB->TCBSecCnt != 0) && (pTCB->TCBfIOLock == 0)){
                       if (rc = ioBeginTransfer((SEL)((pTCB->TCBDMAAdd) >>16),
                                                (ushort_t)(pTCB->TCBDMAAdd),
                                                rdCount,
                                                VML_DIRTY,   // buf will be dirtied
                                                (struct pagelist_s *)VM_NO_ADDR,
                                                (ulong_t *)VM_NO_ADDR) != 0){

                             /*  Decrement the holds on our cache buffer, if we
                              *  found one.
                              *
                              */
                             if (pBuf != NULL) {
                                 BufDecRef (pBuf);
                             }
                             BufReadError (ReqListSel,
                                           ReqListOff,
                                           SSToDS(&BufXferRecords[0]),
                                           BufXferIndex,
                                           fDOSBuffer);
                             return (rc);
                       }
                    }
                 }
                 if (rc = BufAddReadExtent(ReqListSel,
                                           ReqListOff,
                                           cusSectsToRead,
                                           hVPB,
                                           rba,
                                           xferaddr,
                                           fUseCache,
                                           SSToDS(&BufXferRecords[0]),
                                           SSToDS(&BufXferIndex),
                                           SSToDS(&fIONeeded),
                                           fDOSBuffer) != 0){

                    /*  Decrement the holds on our cache buffer, if we
                     *  found one.
                     *
                     */
                    if (pBuf != NULL) {
                        BufDecRef (pBuf);
                    }
                    return(rc);
                 }
                 xferaddr += cusSectsToRead * SECSIZE;
              }
              /*  We've posted our read, if any.  If there are
               *  sectors left in this run, we know that the next
               *  MIN (SECPERBUF, cRun - cusSectsToRead) sectors
               *  are in a cache block.
               *
               *  Note that we may not be at the first sector in
               *  the block (if the beginning of our original
               *  run length is in the cache).
               *
               */

              rba += cusSectsToRead;
              cRun -= cusSectsToRead;
              cbRun -= cusSectsToRead * SECSIZE;
              BufAssert (cbRun <= 0x10000);
              BufAssert (cRun <= 132l);

              if (cRun) {
                 BufOff = BUFOFFSET(rba);
                 sgcount = MIN(((ulong_t)BUFPAGESIZE - BufOff),
                               (cRun * (ulong_t)SECSIZE));
                 if (rc = SuBuff386(sgcount,
                                    xferaddr,
                                    pBuf->BufLinAddr + BufOff) != 0){
                    BufDecRef(pBuf);
                    BufReadError (ReqListSel,
                                  ReqListOff,
                                  SSToDS(&BufXferRecords[0]),
                                  BufXferIndex,
                                  fDOSBuffer);
                    return(rc);
                 }
                 xferaddr += sgcount;
                 rba += sgcount / SECSIZE;
                 cRun -= sgcount / SECSIZE;
                 cbRun -= sgcount;
                 BufAssert (cbRun <= 0x10000);
                 BufAssert (cRun <= 132l);
              }
              /*  Decrement the holds on our cache buffer, if we
               *  found one.
               *
               */
              if (pBuf != NULL) {
                  BufDecRef (pBuf);
              }
          }
          if (cul){
             BufAssert(cul <= SECPERBUF);

             if (rc = BufReadPartial(hVPB,
                                     pTCB,
                                     SSToDS(&xferaddr),
                                     SSToDS(&cbRun),
                                     SSToDS(&cul),
                                     SSToDS(&rba),
                                     SSToDS(&ReqListSel),
                                     SSToDS(&ReqListOff),
                                     SSToDS(&fIONeeded),
                                     SSToDS(&BufXferRecords[0]),
                                     SSToDS(&BufXferIndex),
                                     fDOSBuffer,
                                     rdCount) != 0) {
                return(rc);
             }
          }
       }
       if (cSec){
          if (rc = IsEOF(nextcluster,hVPB)){ // test for EOF on FAT size
             cSec = 0;                       // Done with transfer. No error.
          }
          SecClusPos = 0;              // Start at first sector within cluster
          ++pTCB->TCBLastPos;          //  We'll be using next cluster
       }
       if (fIONeeded){

          if (rc = BufReadExecute(SSToDS(&BufXferRecords[0]),
                                  SSToDS(&BufXferIndex),
                                  ReqListSel,
                                  ReqListOff,
                                  fDOSBuffer) != 0) {
             return(rc);
          }
       }
       fIONeeded = FALSE;
       MMPHPostSDR(); /* performance hook (3293) */
    }
    if (!fDOSBuffer && ReqListSel != 0){
       FreeReqList(ReqListSel,ReqListOff);
    }

    // If read-ahead necessary then initiate it.



    if (!fDirect && fAsyncReadRunning) {
       pSFTFAT->sfdFAT_bBufRun = 64;
       if (rdCount >= MIN_READLEN && rdCount <= MAX_READLEN) {
           if (!(pSFT->sf_flags2 & sff2_RA_ON)) {
               if (++pSFTFAT->sfdFAT_bRAReads >= RA_HI) {
                   pSFT->sf_flags2 |= sff2_RA_ON;
                   pSFTFAT->sfdFAT_ulNextRA = pSFT->sf_fsi.sfi_position + rdCount;
                   pSFTFAT->sfdFAT_bBufRun = 64;
               }
           }

           if (rdCount >= 513) {
               pSFTFAT->sfdFAT_bBufRun = 64;
               if (rdCount >= 514)
                  pSFTFAT->sfdFAT_bBufRun = 128;
               if (!(pSFT->sf_flags2 & sff2_RA_BIG)) {
                   if (++pSFTFAT->sfdFAT_bRABigReads >= RA_HI){
                       pSFT->sf_flags2 |= sff2_RA_BIG;
                   }
               }
           }
           else {
               if (pSFT->sf_flags2 & sff2_RA_BIG){
                   if (--pSFTFAT->sfdFAT_bRABigReads <= RA_LOW){
                       pSFT->sf_flags2 &= ~sff2_RA_BIG;
                       pSFTFAT->sfdFAT_bBufRun = 64;
                   }
               }
               else {
                  if (pSFTFAT->sfdFAT_bRAReads >= RA_LOW) {
                      pSFTFAT->sfdFAT_bBufRun = 64;
                  }
               }
           }
       }
       else {
           if (pSFT->sf_flags2 & sff2_RA_ON) {
               if (--pSFTFAT->sfdFAT_bRAReads <= RA_LOW)
                   pSFT->sf_flags2 &= ~sff2_RA_ON;
           }
           if (pSFT->sf_flags2 & sff2_RA_BIG) {
               if (--pSFTFAT->sfdFAT_bRABigReads <= RA_LOW){
                   pSFT->sf_flags2 &= ~sff2_RA_BIG;
                   pSFTFAT->sfdFAT_bBufRun = 64;
               }
           }
       }
       if (pSFT->sf_flags2 & sff2_RA_ON){
           if ((pSFT->sf_fsi.sfi_position + rdCount + (pSFTFAT->sfdFAT_bBufRun * 512)) >=
                                                      pSFTFAT->sfdFAT_ulNextRA){

              // If the initial read-ahead will go past the size
              /* fix for a previous large read that didn't make the cache */
              if (pSFT->sf_fsi.sfi_position + rdCount > pSFTFAT->sfdFAT_ulNextRA)
                pSFTFAT->sfdFAT_ulNextRA = pSFT->sf_fsi.sfi_position + rdCount;

              tmpNextRA = ROUND_TO_SECSIZE(pSFTFAT->sfdFAT_ulNextRA);
              if ((tmpNextRA + SECSIZE) < pSFT->sf_fsi.sfi_size){

                 // If the read-ahead will end past the end of the file then
                 // limit the read-ahead up to the end of the file

                 if ((tmpNextRA + (pSFTFAT->sfdFAT_bBufRun * SECSIZE)) >
                                   pSFT->sf_fsi.sfi_size) {

                    pSFTFAT->sfdFAT_bBufRun -= ((((tmpNextRA +
                                                (pSFTFAT->sfdFAT_bBufRun * SECSIZE)) -
                                                 pSFT->sf_fsi.sfi_size) / SECSIZE)-1);
                 }
                 StartReadAhead(pSFT,
                                pSFTFAT,
                                tmpNextRA,
                                hVPB);
              }
           }
       }
    }
    pTCB->TCBNextAdd = oldTCBNextAdd + (ushort_t)rdCount;
    return(0);
}
