/*static char *SCCSID = "@(#)qdiskop.c  6.25 92/03/01";*/

#define INCL_DOSERRORS
#include <os2def.h>
#include <bse.h>
#include <fsh.h>
#include <..\..\dos\dosinc\osconst.h>
#include <strat2.h>

#define VML_WRITE    0x08
#define VML_TRY_CONTIG   0x8000
#define VML_CONTIG   0x02
#define ForeGround   0xffff
#define PERCENTDIRTYCBUFS_NUMERATOR    1
#define PERCENTDIRTYCBUFS_DENOMINATOR  1

#include "const.h"
#include "misc.h"
#include "buf.h"
#include "chain.h"
#include "dir.h"
#include "volume.h"
#include "frame.h"
#include "asyncio.h"
#include "lprintf.h"
#include "heap.h"
#include "lw.h"
#include "fsdata.h"
#include "pbmacros.h"
#include "superb.h"        // @65377

#include "pr_heap.h"
#include "storage.h"
#include "tables.h"
#include "pr_misc.h"
//@76193  #ifdef DEBUG
#include "pr_debug.h"
//@76193  #endif
extern BOOL fTrapLowWrite;  /* @63577 Trap on any write to LOW sectors  */

VOID ReWorkSGList(PLISTENT preqLstEnt,USHORT csgMax);

/*
 * Put a disk operation into the request queue.
 *
 *
 * Parameters:
 *     pioh        Pointer to an I/O handle as returned by GetIOHand()
 *     pri         Priority of operation queued
 *     usOp        Disk operation requested from
 *                     QDO_READ        read data from disk
 *                     QDO_WRITE       write from memory to disk
 *                     QDO_WRITEV      write with verify
 *
 *                  the following bits can be ORed into the operation
 *                     QDO_RET_HOTFIX  return a QDERR_HOTFIX if deref'ed sector
 *                     QDO_NO_HOTFIX   do not derefence hotfixed sectors
 *
 *     lsnStart    First sector in run to read/write (vol bits ignored)
 *     cSects      Number of sectors in run
 *     pbData      Pointer to data buffer to read from/write to.  This is
 *                 a 16:16 pointer unless GIH_PHYSADDR was set in the
 *                 GetIOHand call.
 *
 * Returns: an unsigned integer with the following bits set
 *             NO_ERROR (no bits set) if the packet fit and no autoflush occured
 *
 *      Bit
 *       1     QDERR_LISTFULL  if autoflush is not set and the packet will not
 *                             fit in the request list.
 *       2     QDERR_AUTOFLUSH if the packet caused an autoflush
 *       3     QDERR_READ      if a read error occured during autoflush
 *       4     QDERR_WRITE     if a write error occured during autoflush
 *       5     QDERR_GPFAULT   if the buffer pointed to by pbData is
 *                             not accessable.
 *       6     QDERR_INTERRUPT
 *       7     QDERR_HOTFIX    turned on iff usOp had QDO_RET_HOTFIX OR'ed into
 *                             it and QDiskOp had to dereference a hot-fixed
 *                             sector.
 *
 *             Note that if QDERR_READ or QDERR_WRITE is set then
 *             QDERR_AUTOFLUSH will also be set
 *
 * QDiskOp builds a disk request command and adds it to the chain described
 * by *pioh.  If the user buffer is not contiguous, a scatter gather list
 * will be built.  If the request is in the same run and is for the same
 * type of operation as the preceeding request in the chain, then it will
 * be appended to the previous command request instead being built as a
 * new command packette.  If the buffer described by the pioh can not hold
 * this request and the chain was created with the GIH_AUTOFLUSH bit set,
 * then the command chain in the buffer will be posted to the disk drive
 * and QDiskOp will block until they have been serviced (return from
 * QDiskOp will have the QDERR_AUTOFLUSH bit set).  If the request will not
 * fit into the command chain buffer and the GIH_AUTOFLUSH was NOT set,
 * then no action will be taken and the return code will be QDERR_LISTFULL.
 * The normal case in which the command chain buffer is not full and the
 * operation is appended to the end of that buffer will return a value of
 * NO_ERROR.
 *     If an error occurs during the disk access that was caused by an
 * autoflush, then any read or write errors will be reflected in the return
 * code by the QDERR_READ and QDERR_WRITE bits respectivly.  To get the
 * individual I/O request that caused the error, see GetIOListHead,
 * FirstIOReq, and NextIOReq.  If the buffer pointed to by pbData is not
 * writable, or not big enough to hold the reqested number of sectors, then
 * QDiskOp will return immediatly without doing any work and with a
 * return code of QDERR_GPFAULT.  Note that this buffer checking will only
 * occur if the GIH_PHYSADDR bit was NOT set when the IO Handle was
 * created.  Note also that QDiskOp can block if the IO Handle used was not
 * created with the GIH_PHYSADDR bit set and the data buffer pointed to by
 * pbData is not currently in memory.  QDiskOp will only handle
 * dereferencing of hotfix patches on read or writes, it will NOT
 * automatically hot-fix any read or write Hard errors that were detected
 * during an AUTOFLUSH.  All buffers passed into QDiskOp will be locked
 * down into memory and should be unlocked by calling UnlockIOHPages
 * after a call to ExecIOH.  Pages that are filled by an AUTOFLUSH
 * will be automatically unlocked during the flush operation.
 *
 *      HISTORY
 * @65377   30-Nov-1993 by D.Azzarito - IPE when writing to lsn's < SparesBlock
 * @76193   23-Nov-93 by D. Azzarito  - modify logging support for debug
 *          22-Feb-1990 by JR (John Rogers, JohnRo@Microsoft)
 *              - PTR B788262: avoid GP fault in debug code checking pPageList.
 *          13-Feb-90 by JR (John Rogers, JohnRo@Microsoft)
 *              - Added debug output (define DEBUG_ASYNC to get it).  Added
 *                check of byte counts into and out of VMLock().
 */

#if (QDO_READ | QDO_WRITE | QDO_WRITEV ) & (QDO_RET_HOTFIX | QDO_NO_HOTFIX)
#error QDO_RET_HOTFIX or QDO_NO_HOTFIX must have their values changed
#endif

UINT QDiskOp( PIOH pioh, BYTE pri, USHORT usOp,
              LSN lsnStart, USHORT cSects,
              PBYTE pbData, PBYTE pbEventFlags, PFRAME pfrm ) {

    UINT IO_Priority;
    UINT erc = NO_ERROR;
    PBYTE ignore_flags = 0;

    /* variables needed for HotFix code */
    BOOL fFlags = usOp & (QDO_RET_HOTFIX | QDO_NO_HOTFIX);
    PVOLTAB pvol = pioh->pvol;

//@76193 Don't print when logging
#if defined (DEBUG) || defined (LOG_PRINT)
    lprintf( LMOD_PROC,
             __FILE__": entered, ioh=%p lsnStrt=%lu csec=%u pbData=%p",
             pioh, lsnStart, cSects, pbData );
#endif

    usOp &= ~(QDO_RET_HOTFIX | QDO_NO_HOTFIX);

    /* see if we have to derefrence hot fixed sectors */
    if( pvol->cHotFixInUse != 0 && !(fFlags & QDO_NO_HOTFIX) ) {
        /*
         * HotFix handling code:
         *
         * Scan the hot fix list and find the lowest hot fixed sector
         * that is in the range of our request.
         * When we find it, Queue up the good run, plus the 1 bad sector,
         * Repeat until there are no more sectors in our request.
         */

        LSN lsnLimit = lsnStart + cSects;
        BOOL fHotFixed = FALSE;
        PIOHEXL pihlSave;

        usOp |= QDO_NO_HOTFIX;

        /*
         * Save info that could be changed incase we have to
         * back out our recursive QDiskOp's
         *
         * BUGBUG - maybe we should just have QDiskOp return the number
         * of sectors successfully enqueued and not worry about
         * making it atomic.
         *
         */
        if( (pihlSave  = GHS(sizeof(IOHEXL),pfrm)) == NULL )
            return QDERR_NO_MEMORY;

        memcpy( pihlSave, pioh, sizeof( IOHEXL ) - sizeof( LISTENT ) );
        if (pioh->preqCurr) {
            /* there is a previous request to save */
            pihlSave->exl.IOReq.RWReq = pioh->preqCurr->RWReq;
        }


        do {
            LSN lsnMax, lsnFix;

            lsnMax = lsnLimit;

            {
                PLSN plsnHotFix;
                PLSN plsnMaxHF = &(pvol->pHotFixArray[pvol->cHotFixInUse]);

                for( plsnHotFix = pvol->pHotFixArray;
                        plsnHotFix != plsnMaxHF; plsnHotFix++ ) {

                    if( *plsnHotFix >= lsnStart && *plsnHotFix < lsnMax ) {
                        lsnMax = *plsnHotFix;
                        lsnFix =
                            pvol->pHotFixNew[plsnHotFix - pvol->pHotFixArray];
                    }
                }
            }

            if( lsnStart < lsnMax ) {
                erc = QDiskOp( pioh,
                                pri,
                                usOp,
                                lsnStart,
                                (USHORT)(lsnMax - lsnStart),
                                pbData,
                                ignore_flags, pfrm);

                if( erc ) {
                    goto QDOError;
                }

                pbData += (lsnMax - lsnStart) * SECSIZE;
            }

            if( lsnMax < lsnLimit ) {

                fHotFixed = TRUE;

                erc = QDiskOp( pioh,
                                pri,
                                usOp,
                                lsnFix,
                                1,
                                pbData,
                                ignore_flags, pfrm);

                if( erc ) {
                    PLCKHAND plh;
QDOError:
                    plh = pioh->plhChain;
                    /*
                     * Restore saved state of pioh and return error code
                     */
                    memcpy( pioh, pihlSave, sizeof(IOHEXL) - sizeof(LISTENT) );
                    if (pioh->preqCurr) {
                        pioh->preqCurr->RWReq = pihlSave->exl.IOReq.RWReq;
                    }

                    while( plh != pioh->plhChain ) {
                        PLCKHAND plhTmp;

                        MAYBLOCK ( VMUnlock( plh->abLockHand ) )

                        plhTmp = plh;
                        plh = plh->plhNext;
                        RetHeap( plhTmp );
                    }


                    if( (fFlags & QDO_RET_HOTFIX) && fHotFixed )
                        erc |= QDERR_HOTFIX;

                    RetHeap( pihlSave );
                    return erc;
                }

                pbData += SECSIZE;

            }

            lsnStart = lsnMax + 1;

        }while( lsnStart < lsnLimit );

        RetHeap( pihlSave );
        return ((fFlags & QDO_RET_HOTFIX) && fHotFixed) ?
                                         QDERR_HOTFIX : NO_ERROR;

    } else {
        UINT cPages = cSects * SECSIZE; /* Init to cbytes */
        PLISTENT preqCurr = pioh->preqCurr;
        PLISTENT preqApnd = preqCurr;
        PSGENTRY pPageList = NULL;

        /*
         * Normal case for QDiskOp (No hot fixing)
         */

        LSN lsn_start = lsnStart;  /* preserve volume bits for SCB-capable check */


        if (pbEventFlags) {   /* initialize event flags */
           *pbEventFlags &= ~IO_DONE_EVENT;  /* flag = NO_IO_DONE */
        }

#ifdef DEBUG
        /*
         * Make sure the vol bits are 0 or else match the ones in the
         * pioh
         */
        {
            ULONG ulVolBits = lsnStart & ~SECMASK;
            if( (ulVolBits) &&
                    (VolPtr[ IVOLFROMLSN( ulVolBits ) ] != pvol) ) {
                INTERR( __FILE__": vol bits not the same" );
            }
        }
#endif

        /*
         * Strip out the vol bits from the possible vlsn
         */
        lsnStart &= SECMASK;

// @65377 starts
        if ( fTrapLowWrite &&                /* are we trapping low writes? */
             (lsnStart < SEC_SPAREB) &&        /* is this op on low sectors?*/
             ((usOp == QDO_WRITE) ||           /* Is it a write op, */
             (usOp == QDO_WRITEV)))            /* or write/verify? */
        {
           INTERR("QDiskOp: tried to write low sectors.");
        }
// @65377 ends

        /*
         * Lock down the pages if the IOH was created without GIH_PHYSADDR
         */
        if( (pioh->usFlags & GIH_PHYSADDR) == 0 ) {
            UINT fiFlags = 0;
            PLCKHAND pLockHand = NULL;

            /*
             * Convert the Virt address to a list of pages and
             * lock the pages down. Add 1 additional SGENTRY
             * in case ReworkSGList is required.
             */
            if((pPageList =
                GHS(sizeof(SGENTRY)*(SAFE_ROUND_UP_DIV(cPages,PAGESIZE) + 1 + 1),
                    pfrm)) == NULL ||
                (pLockHand = GHS(sizeof(LCKHAND),pfrm)) == NULL) {
                /*
                 * at least one of the GHS's failed, release the mem we did get
                 * and return the error
                 */
                if( pPageList != NULL ) {
                    RetHeap( pPageList );
                }

                if( pLockHand != NULL ) {
                    RetHeap( pLockHand );
                }
                return QDERR_NO_MEMORY;
            }

            /*
             * If we are going to Read data into memory, probe the memory
             * to make sure it is writeable
             */

            pfrm->fRecheckAddr = TRUE;

            if (usOp == QDO_READ)
             erc = ProbeAddress (PB_OPWRITE, pbData, cPages, pfrm);
            else
             erc = ProbeAddress (PB_OPREAD, pbData, cPages, pfrm);

            if (erc) {                         //#114986
              RetHeap( pPageList );
              RetHeap( pLockHand );
              return erc;
            }

            if (!IsSCBcapable (lsn_start)) {
               /* make non-critical request for    */
               /* contiguous memory                */

               fiFlags |= VML_TRY_CONTIG;
            }

            /*
             * cPages goes in to VMLock as cBytes and comes out
             * as count of Page descriptors
             */
#ifdef DEBUG
            {
                UINT uiSavedCBytes = cPages;
                UINT iPageEnt;
#endif

                MAYBLOCK ( erc = VMLock( (ULONG)fiFlags,
                                          pbData, (ULONG)cPages,
                                          pPageList,
                                          &(pLockHand->abLockHand[0]),
                                          &cPages ) )

                if ( erc != NO_ERROR ) {
                    /*
                     * VMLock choked for some reason,
                     * clean up and return the error
                     */
                    RetHeap( pPageList );
                    RetHeap( pLockHand );

                    if( erc == ERROR_INTERRUPT ) {
                        erc = QDERR_INTERRUPT;
                    } else {
#ifdef DEBUG
                        dprintf (__FILE__": VMLock error (%d)\n", erc);
                        Int3 ();
#endif
                        erc = QDERR_GPFAULT;
                    }

                    return erc;
                }

#ifdef DEBUG
                /*
                 * Make sure VMLock returned a total byte count matching
                 * what we started with.
                 */
                for (iPageEnt = 0; iPageEnt < cPages; iPageEnt++) {
                    uiSavedCBytes -= (USHORT) pPageList [iPageEnt].BufferSize;
                }

                if (uiSavedCBytes) {
                    dprintf(__FILE__
                        ": cSects (%d) != SUM (pPageList (%p)); cPages=%d; pbData=%p\n",
                            cSects * SECSIZE, pPageList, cPages, pbData);
                    Int3();
                }
       }
#endif

            /*
             * We've got the pages locked down, add the handle to
             * the list in the ioh
             */
            pLockHand->plhNext = pioh->plhChain;
            pioh->plhChain = pLockHand;

        } else {
          /*
           * Since only FS_PageIO, RAHead, and LWAsync can call with
           * the GIH_PHYSADDR flag in the IOH, WE NOW do need to round
           * up before divide so if the request is less than 4k, we
           * still say that we have at least one SG entry to fill
           */

        if (pbEventFlags) {
         if (*pbEventFlags & RA_CACHE_BLOCKS) { /* convert cPages to cache blocks */
           cPages = SAFE_ROUND_UP_DIV(cPages,(SPB*SECSIZE));
         } else
          cPages = SAFE_ROUND_UP_DIV(cPages,PAGESIZE);
        } else
         cPages = SAFE_ROUND_UP_DIV(cPages,PAGESIZE);

        }

        /*
         * Try to append this run to the last run in list.
         *
         * For this to be possible, 4 criteria must be met:
         *
         *      1.  The request must not be a pager I/O request
         *      2.  The list must not be empty
         *      3.  The new command must match the last one
         *      4.  The starting lsn must be congruent with the
         *          previous request.
         *
         */
        if ( preqCurr != NULL &&
             preqCurr->RWReq.RqHdr.Command_Code == (BYTE)usOp &&
               ( preqCurr->RWReq.Start_Block +
                 preqCurr->RWReq.Block_Count == lsnStart ) ) {
            /*
             * This request can be appended to the last request in the list
             *
             * Make sure there is enough space left in the list
             */

            INT cb = cPages * sizeof( SG_Descriptor );

            if( pioh->cbLeft >= cb ) {
                UINT i;

                /* we've got enough room */
                pioh->cbLeft -= cb;

                /* tack this req on the end as a scatter/gather req */

                /* BUGBUG will the following line work? */
                /* preqCurr->RWReq.RqHdr.Priority |= pri; */

                  preqCurr->RWReq.RqHdr.Priority = pri;

     if (pri < PRIO_FOREGROUND_USER) {
      if (pri == PRIO_LAZY_WRITE) {
        if ((cbufDirty >
           ((LWData.cbufDirtyMax * PERCENTDIRTYCBUFS_NUMERATOR) /
             PERCENTDIRTYCBUFS_DENOMINATOR)) || (fForceLWIO == TRUE))
           preqCurr->RWReq.RqHdr.Priority = PRIO_FOREGROUND_USER;
        else                                                     //#83284
           preqCurr->RWReq.RqHdr.Priority = PRIO_BACKGROUND_USER;

      } else { /* not lazy write */
       if (pbEventFlags) {
         if (!(*pbEventFlags & RA_CACHE_BLOCKS)) { /* not readahead or cachefile I/O */
           if (IO_Priority = FSH_GETPRIORITY ()) {
             preqCurr->RWReq.RqHdr.Priority = PRIO_FOREGROUND_USER;
           }
         }
       } else {
        if (IO_Priority = FSH_GETPRIORITY ()) {
          preqCurr->RWReq.RqHdr.Priority = PRIO_FOREGROUND_USER;
        }
       }
      }
     } /* end pri < FOREGROUND_USER */

     i = preqCurr->RWReq.SG_Desc_Count;
     preqCurr->RWReq.SG_Desc_Count += cPages;
     preqCurr->RWReq.Block_Count += cSects;

     if( pioh->usFlags & GIH_PHYSADDR ) {
         /* just put physical address in list */
         preqCurr->SGList[i].BufferPtr = pbData;
         preqCurr->SGList[i].BufferSize = cSects * SECSIZE;
     } else {
         memcpy( &(preqCurr->SGList[i]),pPageList,cb);

         if ((pvol->csgMax < RWSGLIST-1) &&
             (preqCurr->RWReq.SG_Desc_Count == pvol->csgMax+1)) {

            ReWorkSGList(preqCurr,pvol->csgMax);

         } /* endif */
#ifdef DEBUG
         {   UINT j;
             for (j = 0; j < cPages; j++) {
                 if (pPageList [j].BufferSize == 0) {
                     dprintf (__FILE__": BufferSize == 0; pioh %p; pPageList %p\n",
                              pioh, pPageList);
                     Int3 ();
                 }
             }
         }
#endif
     }

            } else {
                /* aw shucks, it didn't fit! */
                erc = QDERR_LISTFULL;
            }

        } else {

            /*
             * Try to create a new IO Request for this run
             */
            if ( pioh->cbLeft >=
                 (sizeof( LISTENT ) + (cPages - 1) * sizeof(SGENTRY)) ) {

                /* we've got enough room */
                pioh->cbLeft -=
                    ( sizeof( LISTENT ) + (cPages - 1) * sizeof(SGENTRY));

                GetIOListHead(pioh)->ELHdr.Count++;

                /* set up a new scatter gather list */
                if( preqCurr != NULL ) {

                    preqCurr->RWReq.RqHdr.Length =
                        sizeof( PB_Read_Write ) +
                        preqCurr->RWReq.SG_Desc_Count * sizeof( SG_Descriptor );

                    preqApnd = (PLISTENT)BUMP_PTR( preqCurr,
                                                   preqCurr->RWReq.RqHdr.Length );

                } else {
                    /* New list */
                    preqApnd = &(GetIOListHead(pioh)->IOReq);
                }

                /* Zero fill the header */
                memset( &(preqApnd->RWReq.RqHdr), 0, sizeof( Req_Header ) );

                /* fill in the important fields */
                preqApnd->RWReq.RqHdr.Length = RH_LAST_REQ;
                preqApnd->RWReq.RqHdr.Old_Command = PB_REQ_LIST;

                preqApnd->RWReq.RqHdr.Command_Code  = (BYTE)usOp;
                preqApnd->RWReq.RqHdr.Head_Offset   =
                                        PTR_SUB( preqApnd, GetIOListHead(pioh) );

                preqApnd->RWReq.RqHdr.Req_Control   = RH_PB_REQUEST;
                preqApnd->RWReq.RqHdr.Priority = pri;

     if (pri < PRIO_FOREGROUND_USER) {
      if (pri == PRIO_LAZY_WRITE) {
        if ((cbufDirty >
           ((LWData.cbufDirtyMax * PERCENTDIRTYCBUFS_NUMERATOR) /
             PERCENTDIRTYCBUFS_DENOMINATOR)) || (fForceLWIO == TRUE))
          preqApnd->RWReq.RqHdr.Priority = PRIO_FOREGROUND_USER;
        else                                                     //#83284
          preqApnd->RWReq.RqHdr.Priority = PRIO_BACKGROUND_USER;

      } else { /* not lazy write */
       if (pbEventFlags) {
         if (!(*pbEventFlags & RA_CACHE_BLOCKS)) { /* not readahead or cachefile I/O */
           if (IO_Priority = FSH_GETPRIORITY ()) {
              preqApnd->RWReq.RqHdr.Priority = PRIO_FOREGROUND_USER;
           }
         }
       } else {
        if (IO_Priority = FSH_GETPRIORITY ()) {
          preqApnd->RWReq.RqHdr.Priority = PRIO_FOREGROUND_USER;
        }
       }
      }
     } /* end pri < FOREGROUND_USER */

     preqApnd->RWReq.Start_Block = lsnStart;
     preqApnd->RWReq.Block_Count = cSects;
     preqApnd->RWReq.Blocks_Xferred = 0;
     preqApnd->RWReq.RW_Flags = (usOp & (QDO_WRITE|QDO_WRITEV)) ?
                                            RW_Cache_WriteThru : 0;
     preqApnd->RWReq.SG_Desc_Count = cPages;
     preqApnd->RWReq.SG_Desc_Count2 = 0;
     preqApnd->RWReq.reserved = 0;

     if( pioh->usFlags & GIH_PHYSADDR ) {

         /* just put physical address in list */
         preqApnd->SGList[0].BufferPtr = pbData;
         preqApnd->SGList[0].BufferSize = cSects * SECSIZE;

     } else {

         /* copy the whole page list over */
         memcpy( &(preqApnd->SGList[0]), pPageList,
                 cPages * sizeof( SG_Descriptor ) );

         if ((pvol->csgMax < RWSGLIST-1) &&
             (preqApnd->RWReq.SG_Desc_Count == pvol->csgMax+1)) {

            ReWorkSGList(preqApnd,pvol->csgMax);

         } /* endif */
#ifdef DEBUG
         {   UINT j;
          for (j = 0; j < cPages; j++) {
           if (pPageList [j].BufferSize == 0) {
             dprintf (__FILE__": BufferSize == 0; pioh %p; pPageList %p\n",
             pioh, pPageList);
             Int3 ();
           }
          }
         }
#endif

     }

            } else {
                /* aw shucks, it didn't fit! */
                erc = QDERR_LISTFULL;
            }
        }

#ifdef DO_AUTOFLUSH
            if( cant fit it and AUTOFLUSH ) {

                erc |= ExecIOH(pioh,pfrm)

                if (pbEventFlags) {
                   *pbEventFlags |= IO_DONE_EVENT;  /* flag = IO_DONE */
                }

                if( IOH was created without GIH_PHYSADDR ) {
                    UnlockIOHPages( pioh,pfrm )
                }

                ResetIOH( pioh )
            }

            loop back to top and try again

#endif

        if( (erc & QDERR_LISTFULL) == 0 ) {
            /*
             * Don't forget to fix up preqCurr and preqApnd
             * also fix the single request flag in the header
             */

            pioh->preqCurr = preqApnd;

            if( GetIOListHead(pioh)->ELHdr.Count == 1 ) {
                GetIOListHead(pioh)->ELHdr.Request_Control |= RLH_Single_Req;
            } else {
                GetIOListHead(pioh)->ELHdr.Request_Control &= ~RLH_Single_Req;
            }

        } else {
            /* We are returning an error, unlock the pages if we locked them    */
          if( (pPageList != NULL) && ((pioh->usFlags & GIH_PHYSADDR) == 0) ) {

                PLCKHAND plh;

                plh = pioh->plhChain;

                pioh->plhChain = plh->plhNext;

                MAYBLOCK (VMUnlock( plh->abLockHand))

                RetHeap( plh );
          }
        }


        /* clean up */
        if( pPageList != NULL ) {
            RetHeap( pPageList );
        }

#ifdef DEBUG_ASYNC
    {
        int i;
        dprintf(__FILE__": for pxlh=%p, first sector count=%d\n",
                GetIOListHead(pioh),
                GetIOListHead(pioh) -> IOReq.RWReq.Block_Count);
        dprintf(__FILE__": SG list= ");
        for (i=0; i< GetIOListHead(pioh)->IOReq.RWReq.SG_Desc_Count; ++i) {
            dprintf("%p ", GetIOListHead(pioh)->IOReq.SGList[i].BufferPtr);
            dprintf("%lx, ", GetIOListHead(pioh)->IOReq.SGList[i].BufferSize);
        }
        dprintf("\n");
    }
#endif
    return erc;

    }
}


VOID ReWorkSGList(PLISTENT preqLstEnt,USHORT csgMax)
   {

   ULONG ulTmpSize = preqLstEnt->SGList[csgMax].BufferSize;

   /* Remember that SGList is 0-based, thus if we have */
   /* csgMax entries, then the last entry is csgMax - 1 */

   preqLstEnt->SGList[csgMax+1].BufferPtr =
      preqLstEnt->SGList[csgMax].BufferPtr;

   preqLstEnt->SGList[csgMax+1].BufferSize = ulTmpSize;

   preqLstEnt->SGList[csgMax].BufferPtr =
      (PVOID)((ULONG)preqLstEnt->SGList[csgMax-1].BufferPtr + ulTmpSize);

   preqLstEnt->SGList[csgMax].BufferSize =
      preqLstEnt->SGList[csgMax-1].BufferSize - ulTmpSize;

   preqLstEnt->SGList[csgMax-1].BufferSize = ulTmpSize;

   preqLstEnt->RWReq.SG_Desc_Count += 1;

   }
