Index: trunk/src/VBox/Additions/os2/VBoxSF/VBoxSF.cpp
===================================================================
--- trunk/src/VBox/Additions/os2/VBoxSF/VBoxSF.cpp	(revision 76144)
+++ trunk/src/VBox/Additions/os2/VBoxSF/VBoxSF.cpp	(revision 76146)
@@ -506,4 +506,6 @@
         {
             pDst->u16Length = (uint16_t)RTUtf16Len(pDst->String.utf16) * (uint16_t)sizeof(RTUTF16);
+            Assert(pDst->u16Length < pDst->u16Size);
+            pDst->u16Size   = pDst->u16Length + (uint16_t)sizeof(RTUTF16); /* (limit how much is copied to the host) */
             *ppStr = pDst;
             return NO_ERROR;
@@ -512,6 +514,6 @@
 
         /*
-         * This shouldn't happen, but just in case we try again with
-         * twice the buffer size.
+         * This shouldn't happen, but just in case we try again with twice
+         * the buffer size.
          */
         if (rc == 0x20412 /*ULS_BUFFERFULL*/)
@@ -524,4 +526,6 @@
                 {
                     pDst->u16Length = (uint16_t)RTUtf16Len(pDst->String.utf16) * (uint16_t)sizeof(RTUTF16);
+                    Assert(pDst->u16Length < pDst->u16Size);
+                    pDst->u16Size   = pDst->u16Length + (uint16_t)sizeof(RTUTF16);
                     *ppStr = pDst;
                     return NO_ERROR;
@@ -572,12 +576,12 @@
     {
         RT_BZERO(pvBuf, offStrInBuf);
-
         PSHFLSTRING pDst = (PSHFLSTRING)((uint8_t *)pvBuf + offStrInBuf);
+
         APIRET rc = KernStrToUcs(NULL, &pDst->String.utf16[0], (char *)pszFolderPath, cchSrc + 4, cchSrc);
         if (rc == NO_ERROR)
         {
             pDst->u16Length = (uint16_t)RTUtf16Len(pDst->String.utf16) * (uint16_t)sizeof(RTUTF16);
-            pDst->u16Size   = (uint16_t)((cchSrc + 4) * sizeof(RTUTF16));
-            Assert(pDst->u16Length < pDst->u16Size);
+            Assert(pDst->u16Length < (cchSrc + 4) * sizeof(RTUTF16));
+            pDst->u16Size   = pDst->u16Length + (uint16_t)sizeof(RTUTF16); /* (limit how much is copied to the host) */
             *ppvBuf = pvBuf;
             return NO_ERROR;
@@ -585,19 +589,23 @@
         VbglR0PhysHeapFree(pvBuf);
 
-#if 0
         /*
-         * This shouldn't happen, but just in case we try again with
-         * twice the buffer size.
+         * This shouldn't happen, but just in case we try again with twice
+         * the buffer size.
          */
         if (rc == 0x20412 /*ULS_BUFFERFULL*/)
         {
-            pDst = vboxSfOs2StrAlloc((cchSrc + 16) * 2);
-            if (pDst)
-            {
+            pvBuf = VbglR0PhysHeapAlloc(offStrInBuf + SHFLSTRING_HEADER_SIZE + (cchSrc + 16) * sizeof(RTUTF16) * 2);
+            if (pvBuf)
+            {
+                RT_BZERO(pvBuf, offStrInBuf);
+                pDst = (PSHFLSTRING)((uint8_t *)pvBuf + offStrInBuf);
+
                 rc = KernStrToUcs(NULL, pDst->String.utf16, (char *)pszFolderPath, (cchSrc + 16) * 2, cchSrc);
                 if (rc == NO_ERROR)
                 {
                     pDst->u16Length = (uint16_t)RTUtf16Len(pDst->String.utf16) * (uint16_t)sizeof(RTUTF16);
-                    *ppStr = pDst;
+                    Assert(pDst->u16Length < (cchSrc + 16) * 2 * sizeof(RTUTF16));
+                    pDst->u16Size   = pDst->u16Length + (uint16_t)sizeof(RTUTF16);
+                    *ppvBuf = pvBuf;
                     return NO_ERROR;
                 }
@@ -607,5 +615,4 @@
         }
         else
-#endif
             LogRel(("vboxSfOs2ConvertPath: KernStrToUcs returns %#x for %.*Rhxs\n", rc, cchSrc, pszFolderPath));
     }
Index: trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFFile.cpp
===================================================================
--- trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFFile.cpp	(revision 76144)
+++ trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFFile.cpp	(revision 76146)
@@ -40,4 +40,85 @@
 #include <iprt/assert.h>
 #include <iprt/mem.h>
+
+
+/*********************************************************************************************************************************
+*   Structures and Typedefs                                                                                                      *
+*********************************************************************************************************************************/
+/** A preallocated buffer. */
+typedef struct
+{
+    RTCCPHYS        PhysAddr;
+    void           *pvBuf;
+    bool volatile   fBusy;
+} VBOXSFOS2BUF;
+
+
+/*********************************************************************************************************************************
+*   Global Variables                                                                                                             *
+*********************************************************************************************************************************/
+/** Buffer spinlock. */
+static SpinLock_t   g_BufferLock;
+/** 64KB buffers. */
+static VBOXSFOS2BUF g_aBigBuffers[4];
+
+
+
+/**
+ * Initialize file buffers.
+ */
+void vboxSfOs2InitFileBuffers(void)
+{
+    KernAllocSpinLock(&g_BufferLock);
+
+    for (uint32_t i = 0; i < RT_ELEMENTS(g_aBigBuffers); i++)
+    {
+        g_aBigBuffers[i].pvBuf = RTMemContAlloc(&g_aBigBuffers[i].PhysAddr, _64K);
+        g_aBigBuffers[i].fBusy = g_aBigBuffers[i].pvBuf == NULL;
+    }
+}
+
+
+/**
+ * Allocates a big buffer.
+ * @returns Pointer to buffer on success, NULL on failure.
+ * @param   pPhysAddr           The physical address of the buffer.
+ */
+DECLINLINE(void *) vboxSfOs2AllocBigBuffer(RTGCPHYS *pPhysAddr)
+{
+    KernAcquireSpinLock(&g_BufferLock);
+    for (uint32_t i = 0; i < RT_ELEMENTS(g_aBigBuffers); i++)
+        if (!g_aBigBuffers[i].fBusy)
+        {
+            g_aBigBuffers[i].fBusy = true;
+            KernReleaseSpinLock(&g_BufferLock);
+
+            *pPhysAddr = g_aBigBuffers[i].PhysAddr;
+            return g_aBigBuffers[i].pvBuf;
+        }
+    KernReleaseSpinLock(&g_BufferLock);
+    *pPhysAddr = NIL_RTGCPHYS;
+    return NULL;
+}
+
+
+/**
+ * Frees a big buffer.
+ * @param   pvBuf               The address of the buffer to be freed.
+ */
+DECLINLINE(void) vboxSfOs2FreeBigBuffer(void *pvBuf)
+{
+    Assert(pvBuf);
+    KernAcquireSpinLock(&g_BufferLock);
+    for (uint32_t i = 0; i < RT_ELEMENTS(g_aBigBuffers); i++)
+        if (g_aBigBuffers[i].pvBuf == pvBuf)
+        {
+            Assert(g_aBigBuffers[i].fBusy);
+            g_aBigBuffers[i].fBusy = false;
+            KernReleaseSpinLock(&g_BufferLock);
+            return;
+        }
+    KernReleaseSpinLock(&g_BufferLock);
+    AssertFailed();
+}
 
 
@@ -986,4 +1067,68 @@
 
 
+/**
+ * Convert KernVMLock page list to HGCM page list.
+ *
+ * The trouble is that it combine pages.
+ */
+static void vboxSfOs2ConvertPageList(KernPageList_t volatile *paSrc, RTGCPHYS64 volatile *paDst, ULONG cSrc, uint32_t cDst)
+{
+    LogFlow(("vboxSfOs2ConvertPageList: %d vs %d\n", cSrc, cDst));
+
+    /* If the list have identical length, the job is easy. */
+    if (cSrc == cDst)
+        for (uint32_t i = 0; i < cSrc; i++)
+            paDst[i] &= ~(uint32_t)PAGE_OFFSET_MASK;
+    else
+    {
+        Assert(cSrc <= cDst);
+        Assert(cSrc > 0);
+
+        /*
+         * We have fewer source entries than destiation pages, so something needs
+         * expanding.  The fact that the first and last pages might be partial ones
+         * makes this more interesting.  We have to do it backwards, of course.
+         */
+
+        /* Deal with the partial page stuff first. */
+        paSrc[0].Size += paSrc[0].Addr & PAGE_OFFSET_MASK;
+        paSrc[0].Addr &= ~(ULONG)PAGE_OFFSET_MASK;
+        paSrc[cSrc - 1].Size = RT_ALIGN_32(paSrc[cSrc - 1].Size, PAGE_SIZE);
+
+        /* The go do work on the conversion. */
+        uint32_t iDst = cDst;
+        uint32_t iSrc = cSrc;
+        while (iSrc-- > 0)
+        {
+            ULONG cbSrc    = paSrc[iSrc].Size;
+            ULONG uAddrSrc = paSrc[iSrc].Addr + cbSrc;
+            Assert(!(cbSrc & PAGE_OFFSET_MASK));
+            Assert(!(uAddrSrc & PAGE_OFFSET_MASK));
+            while (cbSrc > 0)
+            {
+                uAddrSrc     -= PAGE_SIZE;
+                Assert(iDst > 0);
+                paDst[--iDst] = uAddrSrc;
+                cbSrc        -= PAGE_SIZE;
+            }
+        }
+        Assert(iDst == 0);
+    }
+}
+
+
+/**
+ * Helper for FS32_READ.
+ */
+DECLINLINE(uint32_t) vboxSfOs2ReadFinalize(PSFFSI pSfFsi, uint64_t offRead, uint32_t cbActual)
+{
+    pSfFsi->sfi_positionl = offRead + cbActual;
+    if ((uint64_t)pSfFsi->sfi_sizel < offRead + cbActual)
+        pSfFsi->sfi_sizel = offRead + cbActual;
+    pSfFsi->sfi_tstamp   |= ST_SREAD | ST_PREAD;
+    return cbActual;
+}
+
+
 extern "C" APIRET APIENTRY
 FS32_READ(PSFFSI pSfFsi, PVBOXSFSYFI pSfFsd, PVOID pvData, PULONG pcb, ULONG fIoFlags)
@@ -992,5 +1137,5 @@
 
     /*
-     * Validate input.
+     * Validate and extract input.
      */
     AssertReturn(pSfFsd->u32Magic == VBOXSFSYFI_MAGIC, ERROR_SYS_INTERNAL);
@@ -1002,20 +1147,21 @@
     RT_NOREF(pFolder);
 
-    /*
-     * If the read request is small enough, go thru a temporary buffer to
-     * avoid locking/unlocking user memory.
-     */
     uint64_t const offRead  = pSfFsi->sfi_positionl;
     uint32_t const cbToRead = *pcb;
     uint32_t       cbActual = cbToRead;
-#if 0 /** @todo debug some other day. */
-    if (cbToRead <= _8K - ALLOC_HDR_SIZE - RT_UOFFSETOF(VBOXSFREADEMBEDDEDREQ, abData[0]))
-    {
-        size_t                 cbReq = RT_UOFFSETOF(VBOXSFREADEMBEDDEDREQ, abData[0]) + RT_ALIGN_32(cbToRead, 4);
+
+    /*
+     * We'll try embedded buffers for reads a smaller than ~2KB if we get
+     * a heap block that's entirely within one page so the host can lock it
+     * and avoid bouncing it off the heap on completion.
+     */
+    if (cbToRead <= _2K)
+    {
+        size_t                 cbReq = RT_UOFFSETOF(VBOXSFREADEMBEDDEDREQ, abData[0]) + cbToRead;
         VBOXSFREADEMBEDDEDREQ *pReq  = (VBOXSFREADEMBEDDEDREQ *)VbglR0PhysHeapAlloc(cbReq);
-        if (pReq != NULL)
-        {
-            RT_BZERO(pReq, cbReq);
-
+        if (   pReq != NULL
+            && (   PAGE_SIZE - (PAGE_OFFSET_MASK & (uintptr_t)pReq) >= cbReq
+                || cbToRead == 0))
+        {
             APIRET rc;
             int vrc = vboxSfOs2HostReqReadEmbedded(pFolder, pReq, pSfFsd->hHostFile, offRead, cbToRead);
@@ -1027,15 +1173,11 @@
                 if (rc == NO_ERROR)
                 {
-                    *pcb = cbActual;
-                    pSfFsi->sfi_positionl = offRead + cbActual;
-                    if ((uint64_t)pSfFsi->sfi_sizel < offRead + cbActual)
-                        pSfFsi->sfi_sizel = offRead + cbActual;
-                    pSfFsi->sfi_tstamp   |= ST_SREAD | ST_PREAD;
-                    LogFlow(("FS32_READ: returns; cbActual=%#x sfi_positionl=%RI64 [copy]\n", cbActual, pSfFsi->sfi_positionl));
+                    *pcb = vboxSfOs2ReadFinalize(pSfFsi, offRead, cbActual);
+                    LogFlow(("FS32_READ: returns; cbActual=%#x sfi_positionl=%RI64 [embedded]\n", cbActual, pSfFsi->sfi_positionl));
                 }
             }
             else
             {
-                Log(("FS32_READ: vboxSfOs2HostReqReadEmbedded(off=%#RU64,cb=%#x) -> %Rrc [copy]\n", offRead, cbToRead, vrc));
+                Log(("FS32_READ: vboxSfOs2HostReqReadEmbedded(off=%#RU64,cb=%#x) -> %Rrc [embedded]\n", offRead, cbToRead, vrc));
                 rc = ERROR_BAD_NET_RESP;
             }
@@ -1043,27 +1185,123 @@
             return rc;
         }
-    }
-#endif
-
-
-    /*
-     * Do the read directly on the buffer, Vbgl will do the locking for us.
-     */
-    int vrc = VbglR0SfRead(&g_SfClient, &pFolder->hHostFolder, pSfFsd->hHostFile,
-                           offRead, &cbActual, (uint8_t *)pvData, false /*fLocked*/);
-    if (RT_SUCCESS(vrc))
-    {
-        AssertStmt(cbActual <= cbToRead, cbActual = cbToRead);
-        *pcb = cbActual;
-        pSfFsi->sfi_positionl = offRead + cbActual;
-        if ((uint64_t)pSfFsi->sfi_sizel < offRead + cbActual)
-            pSfFsi->sfi_sizel = offRead + cbActual;
-        pSfFsi->sfi_tstamp   |= ST_SREAD | ST_PREAD;
-        LogFlow(("FS32_READ: returns; cbActual=%#x sfi_positionl=%RI64 [direct]\n", cbActual, pSfFsi->sfi_positionl));
-        return NO_ERROR;
-    }
-    Log(("FS32_READ: VbglR0SfRead(off=%#RU64,cb=%#x) -> %Rrc [direct]\n", offRead, cbToRead, vrc));
+        if (pReq)
+            VbglR0PhysHeapFree(pReq);
+    }
+
+    /*
+     * Whatever we do now we're going to use a page list request.
+     * So, allocate one with sufficient space to cover the whole buffer.
+     */
+    uint32_t cPages = ((cbToRead + PAGE_SIZE - 1) >> PAGE_SHIFT) + 1;
+    VBOXSFREADPGLSTREQ *pReq = (VBOXSFREADPGLSTREQ *)VbglR0PhysHeapAlloc(RT_UOFFSETOF_DYN(VBOXSFREADPGLSTREQ, PgLst.aPages[cPages]));
+    if (pReq)
+    { /* likely */ }
+    else
+    {
+        LogRel(("FS32_READ: Out of memory for page list request (%u pages)\n", cPages));
+        return ERROR_NOT_ENOUGH_MEMORY;
+    }
+
+    /*
+     * If the request is less than 16KB or smaller, we try bounce it off the
+     * physical heap (slab size is 64KB).  For requests up to 64KB we try use
+     * one of a handful of preallocated big buffers rather than the phys heap.
+     */
+    if (cbToRead <= _64K)
+    {
+        RTGCPHYS GCPhys;
+        void    *pvBuf = NULL;
+        if (cbToRead <= _16K)
+        {
+            pvBuf = VbglR0PhysHeapAlloc(cbToRead);
+            GCPhys = pvBuf ? VbglR0PhysHeapGetPhysAddr(pvBuf) : NIL_RTGCPHYS;
+        }
+        else
+            pvBuf = vboxSfOs2AllocBigBuffer(&GCPhys);
+        if (pvBuf)
+        {
+            pReq->PgLst.offFirstPage = (uint16_t)GCPhys & (uint16_t)PAGE_OFFSET_MASK;
+            cPages = (cbToRead + ((uint16_t)GCPhys & (uint16_t)PAGE_OFFSET_MASK) + PAGE_SIZE - 1) >> PAGE_SHIFT;
+            GCPhys &= ~(RTGCPHYS)PAGE_OFFSET_MASK;
+            for (uint32_t i = 0; i < cPages; i++, GCPhys += PAGE_SIZE)
+                pReq->PgLst.aPages[i] = GCPhys;
+
+            APIRET rc;
+            int vrc = vboxSfOs2HostReqReadPgLst(pFolder, pReq, pSfFsd->hHostFile, offRead, cbToRead, cPages);
+            if (RT_SUCCESS(vrc))
+            {
+                cbActual = pReq->Parms.cb32Read.u.value32;
+                AssertStmt(cbActual <= cbToRead, cbActual = cbToRead);
+                rc = KernCopyOut(pvData, pvBuf, cbActual);
+                if (rc == NO_ERROR)
+                {
+                    *pcb = vboxSfOs2ReadFinalize(pSfFsi, offRead, cbActual);
+                    LogFlow(("FS32_READ: returns; cbActual=%#x sfi_positionl=%RI64 [bounced]\n", cbActual, pSfFsi->sfi_positionl));
+                }
+            }
+            else
+            {
+                Log(("FS32_READ: vboxSfOs2HostReqReadEmbedded(off=%#RU64,cb=%#x) -> %Rrc [bounced]\n", offRead, cbToRead, vrc));
+                rc = ERROR_BAD_NET_RESP;
+            }
+
+            if (cbToRead <= _16K)
+                VbglR0PhysHeapFree(pvBuf);
+            else
+                vboxSfOs2FreeBigBuffer(pvBuf);
+            VbglR0PhysHeapFree(pReq);
+            return rc;
+        }
+    }
+
+    /*
+     * We couldn't use a bounce buffer for it, so lock the buffer pages.
+     */
+    KernVMLock_t Lock;
+    ULONG cPagesRet;
+    AssertCompile(sizeof(KernPageList_t) == sizeof(pReq->PgLst.aPages[0]));
+    APIRET rc = KernVMLock(VMDHL_LONG | VMDHL_WRITE, (void *)pvData, cbToRead, &Lock,
+                           (KernPageList_t *)&pReq->PgLst.aPages[0], &cPagesRet);
+    if (rc == NO_ERROR)
+    {
+        pReq->PgLst.offFirstPage = (uint16_t)(uintptr_t)pvData & (uint16_t)PAGE_OFFSET_MASK;
+        cPages = (cbToRead + ((uint16_t)(uintptr_t)pvData & (uint16_t)PAGE_OFFSET_MASK) + PAGE_SIZE - 1) >> PAGE_SHIFT;
+        vboxSfOs2ConvertPageList((KernPageList_t volatile *)&pReq->PgLst.aPages[0], &pReq->PgLst.aPages[0], cPagesRet, cPages);
+
+        APIRET rc;
+        int vrc = vboxSfOs2HostReqReadPgLst(pFolder, pReq, pSfFsd->hHostFile, offRead, cbToRead, cPages);
+        if (RT_SUCCESS(vrc))
+        {
+            cbActual = pReq->Parms.cb32Read.u.value32;
+            AssertStmt(cbActual <= cbToRead, cbActual = cbToRead);
+            *pcb = vboxSfOs2ReadFinalize(pSfFsi, offRead, cbActual);
+            LogFlow(("FS32_READ: returns; cbActual=%#x sfi_positionl=%RI64 [locked]\n", cbActual, pSfFsi->sfi_positionl));
+        }
+        else
+        {
+            Log(("FS32_READ: vboxSfOs2HostReqReadEmbedded(off=%#RU64,cb=%#x) -> %Rrc [locked]\n", offRead, cbToRead, vrc));
+            rc = ERROR_BAD_NET_RESP;
+        }
+
+        KernVMUnlock(&Lock);
+    }
+    else
+        Log(("FS32_READ: KernVMLock(,%p,%#x,) failed -> %u\n", pvData, cbToRead, rc));
+    VbglR0PhysHeapFree(pReq);
     RT_NOREF_PV(fIoFlags);
-    return ERROR_BAD_NET_RESP;
+    return rc;
+}
+
+
+/**
+ * Helper for FS32_WRITE.
+ */
+DECLINLINE(uint32_t) vboxSfOs2WriteFinalize(PSFFSI pSfFsi, uint64_t offWrite, uint32_t cbActual)
+{
+    pSfFsi->sfi_positionl = offWrite + cbActual;
+    if ((uint64_t)pSfFsi->sfi_sizel < offWrite + cbActual)
+        pSfFsi->sfi_sizel = offWrite + cbActual;
+    pSfFsi->sfi_tstamp   |= ST_SWRITE | ST_PWRITE;
+    return cbActual;
 }
 
@@ -1073,5 +1311,5 @@
 {
     /*
-     * Validate input.
+     * Validate and extract input.
      */
     AssertReturn(pSfFsd->u32Magic == VBOXSFSYFI_MAGIC, ERROR_SYS_INTERNAL);
@@ -1083,61 +1321,146 @@
     RT_NOREF(pFolder);
 
-    /*
-     * If the write request is small enough, go thru a temporary buffer to
-     * avoid locking/unlocking user memory.
-     */
-    uint64_t offWrite = pSfFsi->sfi_positionl;
-    uint32_t cbWrite  = *pcb;
-    uint32_t cbActual = cbWrite;
-    if (cbWrite <= _8K - ALLOC_HDR_SIZE)
-    {
-        void *pvBuf = VbglR0PhysHeapAlloc(cbWrite);
-        if (pvBuf != NULL)
-        {
-            APIRET rc = KernCopyIn(pvBuf, pvData, cbWrite);
+    uint64_t offWrite  = pSfFsi->sfi_positionl;
+    uint32_t cbToWrite = *pcb;
+    uint32_t cbActual  = cbToWrite;
+
+    /*
+     * We'll try embedded buffers for writes a smaller than ~2KB if we get
+     * a heap block that's entirely within one page so the host can lock it
+     * and avoid bouncing it off the heap on completion.
+     */
+    if (cbToWrite <= _2K)
+    {
+        size_t                  cbReq = RT_UOFFSETOF(VBOXSFWRITEEMBEDDEDREQ, abData[0]) + cbToWrite;
+        VBOXSFWRITEEMBEDDEDREQ *pReq  = (VBOXSFWRITEEMBEDDEDREQ *)VbglR0PhysHeapAlloc(cbReq);
+        if (   pReq != NULL
+            && (   PAGE_SIZE - (PAGE_OFFSET_MASK & (uintptr_t)pReq) >= cbReq
+                || cbToWrite == 0))
+        {
+            APIRET rc = KernCopyIn(&pReq->abData[0], pvData, cbToWrite);
             if (rc == NO_ERROR)
             {
-                int vrc = VbglR0SfWrite(&g_SfClient, &pFolder->hHostFolder, pSfFsd->hHostFile,
-                                        offWrite, &cbActual, (uint8_t *)pvBuf, true /*fLocked*/);
+                int vrc = vboxSfOs2HostReqWriteEmbedded(pFolder, pReq, pSfFsd->hHostFile, offWrite, cbToWrite);
                 if (RT_SUCCESS(vrc))
                 {
-                    AssertStmt(cbActual <= cbWrite, cbActual = cbWrite);
-                    *pcb = cbActual;
-                    pSfFsi->sfi_positionl = offWrite + cbActual;
-                    if ((uint64_t)pSfFsi->sfi_sizel < offWrite + cbActual)
-                        pSfFsi->sfi_sizel = offWrite + cbActual;
-                    pSfFsi->sfi_tstamp   |= ST_SWRITE | ST_PWRITE;
-                    LogFlow(("FS32_READ: returns; cbActual=%#x sfi_positionl=%RI64 [copy]\n", cbActual, pSfFsi->sfi_positionl));
+                    cbActual = pReq->Parms.cb32Write.u.value32;
+                    AssertStmt(cbActual <= cbToWrite, cbActual = cbToWrite);
+                    *pcb = vboxSfOs2WriteFinalize(pSfFsi, offWrite, cbActual);
+                    LogFlow(("FS32_WRITE: returns; cbActual=%#x sfi_positionl=%RI64 [embedded]\n", cbActual, pSfFsi->sfi_positionl));
                 }
                 else
                 {
-                    Log(("FS32_READ: VbglR0SfWrite(off=%#x,cb=%#x) -> %Rrc [copy]\n", offWrite, cbWrite, vrc));
+                    Log(("FS32_WRITE: vboxSfOs2HostReqWriteEmbedded(off=%#RU64,cb=%#x) -> %Rrc [embedded]\n", offWrite, cbToWrite, vrc));
                     rc = ERROR_BAD_NET_RESP;
                 }
             }
-            VbglR0PhysHeapFree(pvBuf);
+            VbglR0PhysHeapFree(pReq);
             return rc;
         }
-    }
-
-    /*
-     * Do the write directly on the buffer, Vbgl will do the locking for us.
-     */
-    int vrc = VbglR0SfWrite(&g_SfClient, &pFolder->hHostFolder, pSfFsd->hHostFile,
-                            offWrite, &cbActual, (uint8_t *)pvData, false /*fLocked*/);
-    if (RT_SUCCESS(vrc))
-    {
-        AssertStmt(cbActual <= cbWrite, cbActual = cbWrite);
-        *pcb = cbActual;
-        pSfFsi->sfi_positionl = offWrite + cbActual;
-        if ((uint64_t)pSfFsi->sfi_sizel < offWrite + cbActual)
-            pSfFsi->sfi_sizel = offWrite + cbActual;
-        pSfFsi->sfi_tstamp   |= ST_SWRITE | ST_PWRITE;
-        LogFlow(("FS32_READ: returns; cbActual=%#x sfi_positionl=%RI64 [direct]\n", cbActual, pSfFsi->sfi_positionl));
-        return NO_ERROR;
-    }
-    Log(("FS32_READ: VbglR0SfWrite(off=%#x,cb=%#x) -> %Rrc [direct]\n", offWrite, cbWrite, vrc));
+        if (pReq)
+            VbglR0PhysHeapFree(pReq);
+    }
+
+    /*
+     * Whatever we do now we're going to use a page list request.
+     * So, allocate one with sufficient space to cover the whole buffer.
+     */
+    uint32_t cPages = ((cbToWrite + PAGE_SIZE - 1) >> PAGE_SHIFT) + 1;
+    VBOXSFWRITEPGLSTREQ *pReq = (VBOXSFWRITEPGLSTREQ *)VbglR0PhysHeapAlloc(RT_UOFFSETOF_DYN(VBOXSFWRITEPGLSTREQ, PgLst.aPages[cPages]));
+    if (pReq)
+    { /* likely */ }
+    else
+    {
+        LogRel(("FS32_WRITE: Out of memory for page list request (%u pages)\n", cPages));
+        return ERROR_NOT_ENOUGH_MEMORY;
+    }
+
+    /*
+     * If the request is less than 16KB or smaller, we try bounce it off the
+     * physical heap (slab size is 64KB).  For requests up to 64KB we try use
+     * one of a handful of preallocated big buffers rather than the phys heap.
+     */
+    if (cbToWrite <= _64K)
+    {
+        RTGCPHYS GCPhys;
+        void    *pvBuf = NULL;
+        if (cbToWrite <= _16K)
+        {
+            pvBuf = VbglR0PhysHeapAlloc(cbToWrite);
+            GCPhys = pvBuf ? VbglR0PhysHeapGetPhysAddr(pvBuf) : NIL_RTGCPHYS;
+        }
+        else
+            pvBuf = vboxSfOs2AllocBigBuffer(&GCPhys);
+        if (pvBuf)
+        {
+            APIRET rc = KernCopyIn(pvBuf, pvData, cbToWrite);
+            if (rc == NO_ERROR)
+            {
+                pReq->PgLst.offFirstPage = (uint16_t)GCPhys & (uint16_t)PAGE_OFFSET_MASK;
+                cPages = (cbToWrite + ((uint16_t)GCPhys & (uint16_t)PAGE_OFFSET_MASK) + PAGE_SIZE - 1) >> PAGE_SHIFT;
+                GCPhys &= ~(RTGCPHYS)PAGE_OFFSET_MASK;
+                for (uint32_t i = 0; i < cPages; i++, GCPhys += PAGE_SIZE)
+                    pReq->PgLst.aPages[i] = GCPhys;
+
+                APIRET rc;
+                int vrc = vboxSfOs2HostReqWritePgLst(pFolder, pReq, pSfFsd->hHostFile, offWrite, cbToWrite, cPages);
+                if (RT_SUCCESS(vrc))
+                {
+                    cbActual = pReq->Parms.cb32Write.u.value32;
+                    AssertStmt(cbActual <= cbToWrite, cbActual = cbToWrite);
+                    *pcb = vboxSfOs2WriteFinalize(pSfFsi, offWrite, cbActual);
+                    LogFlow(("FS32_WRITE: returns; cbActual=%#x sfi_positionl=%RI64 [bounced]\n", cbActual, pSfFsi->sfi_positionl));
+                }
+                else
+                {
+                    Log(("FS32_WRITE: vboxSfOs2HostReqWriteEmbedded(off=%#RU64,cb=%#x) -> %Rrc [bounced]\n", offWrite, cbToWrite, vrc));
+                    rc = ERROR_BAD_NET_RESP;
+                }
+            }
+
+            if (cbToWrite <= _16K)
+                VbglR0PhysHeapFree(pvBuf);
+            else
+                vboxSfOs2FreeBigBuffer(pvBuf);
+            VbglR0PhysHeapFree(pReq);
+            return rc;
+        }
+    }
+
+    /*
+     * We couldn't use a bounce buffer for it, so lock the buffer pages.
+     */
+    KernVMLock_t Lock;
+    ULONG cPagesRet;
+    AssertCompile(sizeof(KernPageList_t) == sizeof(pReq->PgLst.aPages[0]));
+    APIRET rc = KernVMLock(VMDHL_LONG, (void *)pvData, cbToWrite, &Lock, (KernPageList_t *)&pReq->PgLst.aPages[0], &cPagesRet);
+    if (rc == NO_ERROR)
+    {
+        pReq->PgLst.offFirstPage = (uint16_t)(uintptr_t)pvData & (uint16_t)PAGE_OFFSET_MASK;
+        cPages = (cbToWrite + ((uint16_t)(uintptr_t)pvData & (uint16_t)PAGE_OFFSET_MASK) + PAGE_SIZE - 1) >> PAGE_SHIFT;
+        vboxSfOs2ConvertPageList((KernPageList_t volatile *)&pReq->PgLst.aPages[0], &pReq->PgLst.aPages[0], cPagesRet, cPages);
+
+        APIRET rc;
+        int vrc = vboxSfOs2HostReqWritePgLst(pFolder, pReq, pSfFsd->hHostFile, offWrite, cbToWrite, cPages);
+        if (RT_SUCCESS(vrc))
+        {
+            cbActual = pReq->Parms.cb32Write.u.value32;
+            AssertStmt(cbActual <= cbToWrite, cbActual = cbToWrite);
+            *pcb = vboxSfOs2WriteFinalize(pSfFsi, offWrite, cbActual);
+            LogFlow(("FS32_WRITE: returns; cbActual=%#x sfi_positionl=%RI64 [locked]\n", cbActual, pSfFsi->sfi_positionl));
+        }
+        else
+        {
+            Log(("FS32_WRITE: vboxSfOs2HostReqWriteEmbedded(off=%#RU64,cb=%#x) -> %Rrc [locked]\n", offWrite, cbToWrite, vrc));
+            rc = ERROR_BAD_NET_RESP;
+        }
+
+        KernVMUnlock(&Lock);
+    }
+    else
+        Log(("FS32_WRITE: KernVMLock(,%p,%#x,) failed -> %u\n", pvData, cbToWrite, rc));
+    VbglR0PhysHeapFree(pReq);
     RT_NOREF_PV(fIoFlags);
-    return ERROR_BAD_NET_RESP;
+    return rc;
 }
 
Index: trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFInit.cpp
===================================================================
--- trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFInit.cpp	(revision 76144)
+++ trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFInit.cpp	(revision 76146)
@@ -104,4 +104,9 @@
                     RTLogBackdoorPrintf("VBoxSFR0Init: Embedded buffers feature is missing.  Upgrade to latest VirtualBox!\n");
 
+                /*
+                 * Allocate some big buffers for reading and writing.
+                 */
+                vboxSfOs2InitFileBuffers();
+
 #ifndef DONT_LOCK_SEGMENTS
                 /*
Index: trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFInternal.h
===================================================================
--- trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFInternal.h	(revision 76144)
+++ trunk/src/VBox/Additions/os2/VBoxSF/VBoxSFInternal.h	(revision 76146)
@@ -222,4 +222,5 @@
 extern VBGLSFCLIENT g_SfClient;
 
+void        vboxSfOs2InitFileBuffers(void);
 PSHFLSTRING vboxSfOs2StrAlloc(size_t cwcLength);
 PSHFLSTRING vboxSfOs2StrDup(PCSHFLSTRING pSrc);
@@ -798,5 +799,5 @@
 
 
-/** Request structure for vboxSfOs2HostReqRead. */
+/** Request structure for vboxSfOs2HostReqReadEmbedded. */
 typedef struct VBOXSFREADEMBEDDEDREQ
 {
@@ -808,5 +809,5 @@
 
 /**
- * SHFL_FN_INFORMATION[SHFL_INFO_GET | SHFL_INFO_FILE] request.
+ * SHFL_FN_READ request using embedded data buffer.
  */
 DECLINLINE(int) vboxSfOs2HostReqReadEmbedded(PVBOXSFFOLDER pFolder, VBOXSFREADEMBEDDEDREQ *pReq, uint64_t hHostFile,
@@ -840,4 +841,141 @@
 
 
+/** Request structure for vboxSfOs2HostReqRead. */
+typedef struct VBOXSFREADPGLSTREQ
+{
+    VBGLIOCIDCHGCMFASTCALL  Hdr;
+    VMMDevHGCMCall          Call;
+    VBoxSFParmRead          Parms;
+    HGCMPageListInfo        PgLst;
+} VBOXSFREADPGLSTREQ;
+
+/**
+ * SHFL_FN_READ request using page list for data buffer (caller populated).
+ */
+DECLINLINE(int) vboxSfOs2HostReqReadPgLst(PVBOXSFFOLDER pFolder, VBOXSFREADPGLSTREQ *pReq, uint64_t hHostFile,
+                                          uint64_t offRead, uint32_t cbToRead, uint32_t cPages)
+{
+    VBGLIOCIDCHGCMFASTCALL_INIT(&pReq->Hdr, VbglR0PhysHeapGetPhysAddr(pReq), &pReq->Call, g_SfClient.idClient,
+                                SHFL_FN_READ, SHFL_CPARMS_READ,
+                                RT_UOFFSETOF_DYN(VBOXSFREADPGLSTREQ, PgLst.aPages[cPages]));
+
+    pReq->Parms.id32Root.type               = VMMDevHGCMParmType_32bit;
+    pReq->Parms.id32Root.u.value32          = pFolder->hHostFolder.root;
+
+    pReq->Parms.u64Handle.type              = VMMDevHGCMParmType_64bit;
+    pReq->Parms.u64Handle.u.value64         = hHostFile;
+
+    pReq->Parms.off64Read.type              = VMMDevHGCMParmType_64bit;
+    pReq->Parms.off64Read.u.value64         = offRead;
+
+    pReq->Parms.cb32Read.type               = VMMDevHGCMParmType_32bit;
+    pReq->Parms.cb32Read.u.value32          = cbToRead;
+
+    pReq->Parms.pBuf.type                   = VMMDevHGCMParmType_PageList;
+    pReq->Parms.pBuf.u.PageList.size        = cbToRead;
+    pReq->Parms.pBuf.u.PageList.offset      = RT_UOFFSETOF(VBOXSFREADEMBEDDEDREQ, abData[0]) - sizeof(VBGLIOCIDCHGCMFASTCALL);
+    pReq->PgLst.flags                       = VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST;
+    pReq->PgLst.cPages                      = (uint16_t)cPages;
+    AssertReturn(cPages <= UINT16_MAX, VERR_OUT_OF_RANGE);
+    /* caller sets offset */
+
+    int vrc = VbglR0HGCMFastCall(g_SfClient.handle, &pReq->Hdr,
+                                 RT_UOFFSETOF_DYN(VBOXSFREADPGLSTREQ, PgLst.aPages[cPages]));
+    if (RT_SUCCESS(vrc))
+        vrc = pReq->Call.header.result;
+    return vrc;
+}
+
+
+
+/** Request structure for vboxSfOs2HostReqWriteEmbedded. */
+typedef struct VBOXSFWRITEEMBEDDEDREQ
+{
+    VBGLIOCIDCHGCMFASTCALL  Hdr;
+    VMMDevHGCMCall          Call;
+    VBoxSFParmWrite         Parms;
+    uint8_t                 abData[RT_FLEXIBLE_ARRAY];
+} VBOXSFWRITEEMBEDDEDREQ;
+
+/**
+ * SHFL_FN_WRITE request using embedded data buffer.
+ */
+DECLINLINE(int) vboxSfOs2HostReqWriteEmbedded(PVBOXSFFOLDER pFolder, VBOXSFWRITEEMBEDDEDREQ *pReq, uint64_t hHostFile,
+                                              uint64_t offWrite, uint32_t cbToWrite)
+{
+    VBGLIOCIDCHGCMFASTCALL_INIT(&pReq->Hdr, VbglR0PhysHeapGetPhysAddr(pReq), &pReq->Call, g_SfClient.idClient,
+                                SHFL_FN_WRITE, SHFL_CPARMS_WRITE, RT_UOFFSETOF(VBOXSFWRITEEMBEDDEDREQ, abData[0]) + cbToWrite);
+
+    pReq->Parms.id32Root.type               = VMMDevHGCMParmType_32bit;
+    pReq->Parms.id32Root.u.value32          = pFolder->hHostFolder.root;
+
+    pReq->Parms.u64Handle.type              = VMMDevHGCMParmType_64bit;
+    pReq->Parms.u64Handle.u.value64         = hHostFile;
+
+    pReq->Parms.off64Write.type             = VMMDevHGCMParmType_64bit;
+    pReq->Parms.off64Write.u.value64        = offWrite;
+
+    pReq->Parms.cb32Write.type              = VMMDevHGCMParmType_32bit;
+    pReq->Parms.cb32Write.u.value32         = cbToWrite;
+
+    pReq->Parms.pBuf.type                   = VMMDevHGCMParmType_Embedded;
+    pReq->Parms.pBuf.u.Embedded.cbData      = cbToWrite;
+    pReq->Parms.pBuf.u.Embedded.offData     = RT_UOFFSETOF(VBOXSFWRITEEMBEDDEDREQ, abData[0]) - sizeof(VBGLIOCIDCHGCMFASTCALL);
+    pReq->Parms.pBuf.u.Embedded.fFlags      = VBOX_HGCM_F_PARM_DIRECTION_TO_HOST;
+
+    int vrc = VbglR0HGCMFastCall(g_SfClient.handle, &pReq->Hdr, RT_UOFFSETOF(VBOXSFWRITEEMBEDDEDREQ, abData[0]) + cbToWrite);
+    if (RT_SUCCESS(vrc))
+        vrc = pReq->Call.header.result;
+    return vrc;
+}
+
+
+/** Request structure for vboxSfOs2HostReqWrite. */
+typedef struct VBOXSFWRITEPGLSTREQ
+{
+    VBGLIOCIDCHGCMFASTCALL  Hdr;
+    VMMDevHGCMCall          Call;
+    VBoxSFParmWrite         Parms;
+    HGCMPageListInfo        PgLst;
+} VBOXSFWRITEPGLSTREQ;
+
+/**
+ * SHFL_FN_WRITE request using page list for data buffer (caller populated).
+ */
+DECLINLINE(int) vboxSfOs2HostReqWritePgLst(PVBOXSFFOLDER pFolder, VBOXSFWRITEPGLSTREQ *pReq, uint64_t hHostFile,
+                                           uint64_t offWrite, uint32_t cbToWrite, uint32_t cPages)
+{
+    VBGLIOCIDCHGCMFASTCALL_INIT(&pReq->Hdr, VbglR0PhysHeapGetPhysAddr(pReq), &pReq->Call, g_SfClient.idClient,
+                                SHFL_FN_WRITE, SHFL_CPARMS_WRITE,
+                                RT_UOFFSETOF_DYN(VBOXSFWRITEPGLSTREQ, PgLst.aPages[cPages]));
+
+    pReq->Parms.id32Root.type               = VMMDevHGCMParmType_32bit;
+    pReq->Parms.id32Root.u.value32          = pFolder->hHostFolder.root;
+
+    pReq->Parms.u64Handle.type              = VMMDevHGCMParmType_64bit;
+    pReq->Parms.u64Handle.u.value64         = hHostFile;
+
+    pReq->Parms.off64Write.type              = VMMDevHGCMParmType_64bit;
+    pReq->Parms.off64Write.u.value64         = offWrite;
+
+    pReq->Parms.cb32Write.type               = VMMDevHGCMParmType_32bit;
+    pReq->Parms.cb32Write.u.value32          = cbToWrite;
+
+    pReq->Parms.pBuf.type                   = VMMDevHGCMParmType_PageList;
+    pReq->Parms.pBuf.u.PageList.size        = cbToWrite;
+    pReq->Parms.pBuf.u.PageList.offset      = RT_UOFFSETOF(VBOXSFWRITEEMBEDDEDREQ, abData[0]) - sizeof(VBGLIOCIDCHGCMFASTCALL);
+    pReq->PgLst.flags                       = VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST;
+    pReq->PgLst.cPages                      = (uint16_t)cPages;
+    AssertReturn(cPages <= UINT16_MAX, VERR_OUT_OF_RANGE);
+    /* caller sets offset */
+
+    int vrc = VbglR0HGCMFastCall(g_SfClient.handle, &pReq->Hdr,
+                                 RT_UOFFSETOF_DYN(VBOXSFWRITEPGLSTREQ, PgLst.aPages[cPages]));
+    if (RT_SUCCESS(vrc))
+        vrc = pReq->Call.header.result;
+    return vrc;
+}
+
+
 /** @} */
 
