/*static char *SCCSID = "@(#)rahead.c   6.6 92/02/21";*/


#define SCCSID "@(#)rahead.c    6.6 92/02/21"
/********************************************************/
/*                                                      */
/*       IBM/Microsoft Confidential                     */
/*                                                      */
/*       Copyright (c) IBM Corporation 1987, 1989       */
/*       Copyright (c) Microsoft Corporation 1987, 1989 */
/*                                                      */
/*       All Rights Reserved                            */
/********************************************************/

#define FNO_NUM FNO_RAHEAD_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 <sched.h>

#define MAXRDREQS 8
/***    StartReadAhead - Initiate ReadAhead for file
 *
 *      StartReadAhead queues up an asynchronous readahead for this file.
 *
 *      Entry   pSFT       - pointer to SFT
 *              pSFTFAT    - pointer FAT file system part of SFT
 *              ulposition - position to start readahead
 *              hVPB       - handle to volume
 *
 *      Exit    NONE
 *
 */
void StartReadAhead (sf_entry *pSFT,
                     sfdFATFS *pSFTFAT,
                     ulong_t ulposition,
                     ulong_t hVPB)
{
    char TCBBuf[TCBIOCopySize];
    ulong_t RAcluster,RASectors,RASecClusPos,RAxferaddr,cbCount,BufOff;
    ulong_t cExtent,cCacheSecs,RAReqListSel = 0,RAReqListOff = 0;
    ushort_t oldlstclus,oldcluspos;
    register tcb_t *pTCB = pTCBCur;
    int rc,fFirstBlock,fIONeeded;
    ulong_t RArba,RARrba,tmprba,oldposition,ulsecoff;
    PBUFHDR pBuf;
    ulong_t RDreqs = 0;

    SaveFatTCBInfo(SSToDS(&TCBBuf));

    oldposition = pSFT->sf_fsi.sfi_position;
    oldlstclus = pSFTFAT->sfdFAT_lstclus;
    oldcluspos = pSFTFAT->sfdFAT_cluspos;
    pSFT->sf_fsi.sfi_position = ulsecoff = ROUND_TO_SECSIZE(ulposition);
    pTCB->TCBDMAAdd = 0;
    cbCount = pSFTFAT->sfdFAT_bBufRun * (USHORT)SECSIZE;


    rc = SetupForIO(cbCount);
    if (rc) return;

    rc = FATReadInit(hVPB);
    if (rc) return;

    pSFT->sf_fsi.sfi_position = oldposition;
    pSFTFAT->sfdFAT_lstclus = oldlstclus;
    pSFTFAT->sfdFAT_cluspos = oldcluspos;

    RASecClusPos = (ulong_t)pTCB->TCBSecClusPos;
    RAcluster = (ulong_t)pTCB->TCBClusNum;

    rc = SetupPhysIO(SSToDS(&cExtent),
                     SSToDS(&RAcluster),
                     (ulong_t)pSFTFAT->sfdFAT_bBufRun,
                     RAcluster,
                     SSToDS(&RArba),
                     SSToDS(&RASectors),
                     SSToDS(&RAxferaddr),
                     RASecClusPos,
                     FALSE,
                     hVPB);
    if (rc) return;

    pSFTFAT->sfdFAT_bBufRun = (ushort_t)SECPERBUF - (((USHORT) RArba) & (ushort_t)SPBMASK);

    SetRBA_Count(SSToDS(&RARrba),RArba,SSToDS(&cExtent),hVPB);
    ReqListAlloc(SSToDS(&RAReqListSel),
                 SSToDS(&RAReqListOff));
    ReqListInit(RAReqListSel,
                RAReqListOff,
                hVPB,
                RLCB_KEEP_REQ);
    RArba = RARrba;
    fFirstBlock = TRUE;
    fIONeeded = FALSE;
    while (cExtent) {
       tmprba = ROUNDRBA(RArba);
       pBuf = CCH(tmprba,hVPB);

       // Figure out the total sectors that are part of the overall
       // rounded request. Rounding occurs in two parts, head and
       // tail. See SetRBA_Count for details.

       BufOff = BUFOFFSET(RArba);
       cCacheSecs = MIN (cExtent,BufOff == 0 ?(ulong_t)SECPERBUF:
                        ((ulong_t)BUFPAGESIZE - BufOff)/SECSIZE);
       if (!fFirstBlock){
          pSFTFAT->sfdFAT_bBufRun += SECPERBUF;
       }
       fFirstBlock = FALSE;
       if (!pBuf){
          pBuf = BufAlloc(RArba,hVPB);
          if (!pBuf)
             break;
          pBuf->BufFlag = BUF_READING;
          rc = AddHash(pBuf,hVPB);
          BufAssert (!rc);
          BufLock(pBuf);
          fIONeeded = TRUE;
          rc = ReqListAdd_REQ(RArba,
                              cCacheSecs,
                              PB_READ_X,
                              RAReqListSel,
                              RAReqListOff,
                              PRIO_PREFETCH,
                              hVPB);
          BufAssert (!rc);
          ReqListAdd_SG(cCacheSecs * SECSIZE,
                        RAReqListSel,
                        RAReqListOff,
                        pBuf->BufPhysAddr + BufOff);
          ReqListLnkPtr(pBuf,
                        &pBuf->BufReadLink,
                        RAReqListSel,
                        RAReqListOff);
          ++RDreqs;
          if (RDreqs >= MAXRDREQS)
               {
               /* List full, arrange to do I/O we've already set up. */
               QueueAsyncRead(RAReqListSel,RAReqListOff);
               ReqListAlloc(SSToDS(&RAReqListSel),
                            SSToDS(&RAReqListOff));
               ReqListInit(RAReqListSel,
                           RAReqListOff,
                           hVPB,
                           RLCB_KEEP_REQ);
               RDreqs  = 0 ;
               fIONeeded = FALSE;
               }
       }
       RArba += cCacheSecs;
       cExtent -= cCacheSecs;
    }
    /* Next read ahead will start at the end this one            */

    pSFTFAT->sfdFAT_ulNextRA = ulsecoff +
                               pSFTFAT->sfdFAT_bBufRun * (USHORT)SECSIZE -
                               MIN_READLEN;
    if (fIONeeded){
       QueueAsyncRead(RAReqListSel,RAReqListOff);
    }
    else {
       FreeReqList(RAReqListSel,RAReqListOff);
    }
    RestoreFatTCBInfo(SSToDS(&TCBBuf));
}

/***    AsyncRead - Execute Queued Read Request Lists
 *
 *      AsyncRead executes the queued read request lists.
 *
 *
 *      Entry   none
 *
 *      Exit    never returns
 *
 */

void AsyncRead ()
{
   ulong_t NextAsyncReadSel;
   ulong_t NextAsyncReadOff;
   PBUFHDR pFirstBuf;
   int rc;

    fAsyncReadRunning = TRUE;
    SCHSetPriority(pTCBCur, (CLASS_TIME_CRITICAL << 8) + LEVEL_MIN);

    for (;;){

        while (AsyncReadHeadSel != 0) {

           ReqList_GetReadAheadData (AsyncReadHeadSel,
                                     AsyncReadHeadOff,
                                     SSToDS(&pFirstBuf));
           rc = ReqListExecute(AsyncReadHeadSel,AsyncReadHeadOff);

           ReqList_GetNextRead(AsyncReadHeadSel,
                               AsyncReadHeadOff,
                               SSToDS (&NextAsyncReadSel),
                               SSToDS (&NextAsyncReadOff));

           while (pFirstBuf){
              if (pFirstBuf->BufFlag & BUF_READING){
                 BufUnLock (pFirstBuf);
                 if (rc) {
                    RemoveHash(pFirstBuf);
                    BufFree(pFirstBuf);
                 }
                 else {
                    PlaceBuf(pFirstBuf,PLCBUF_MRU);
                    pFirstBuf->BufFlag &= ~BUF_READING;
                 }
              }
              else {
                 if (rc){
                    BufUnLock(pFirstBuf);
                    if (pFirstBuf->BufDirtyLru.pbufFwd != NULL) {
                       DBCREM (pFirstBuf);
                    }
                    BufUnlink(pFirstBuf);
                    BufFree(pFirstBuf);
                 }
                 else {
                    if (pFirstBuf->BufFlag & BUF_LAZYREAD){
                       LazyReadIODone(pFirstBuf,(PBYTE)pLinLRBuf);
                    }
                    else {
                       panic ("AsyncRead: LazyRead flag turned off");
                    }
                 }
              }
              pFirstBuf = pFirstBuf->BufReadLink;
           }
           FreeReqList(AsyncReadHeadSel,AsyncReadHeadOff);

           AsyncReadHeadSel = NextAsyncReadSel;
           AsyncReadHeadOff = NextAsyncReadOff;
        }
        AsyncReadTailSel = 0;
        AsyncReadTailOff = 0;

        TKSleep ((void *)&(AsyncReadSem),-1l,0,0);
    }
}
/***    QueueAsyncRead - Queued Read Request List
 *
 *      QueueAsyncRead queues read request list.
 *
 *
 *      Entry   ReqListSel   = Selector of Request List
 *              ReqListOff   = Offset of Request List
 *
 *      Exit
 *
 *      HISTORY
 */

void QueueAsyncRead (ulong_t ReqListSel,ulong_t ReqListOff)
{

      if (AsyncReadHeadSel == 0) {
         AsyncReadHeadSel = AsyncReadTailSel = ReqListSel;
         AsyncReadHeadOff = AsyncReadTailOff = ReqListOff;
         TKWakeup ((void *)&AsyncReadSem,0,NULL);
      }
      else {
         ReqList_SetNextRead(AsyncReadTailSel,
                             AsyncReadTailOff,
                             ReqListSel,
                             ReqListOff);

         AsyncReadTailSel = ReqListSel;
         AsyncReadTailOff = ReqListOff;
      }
}
