Windows: EOF for Synchronous Deferred Writes on XP/2003
authorRod Widdowson <rdw@your-file-system.com>
Sun, 20 Oct 2013 18:29:35 +0000 (14:29 -0400)
committerJeffrey Altman <jaltman@your-file-system.com>
Sat, 26 Oct 2013 22:14:22 +0000 (15:14 -0700)
The Windows IO Manager is not supposed to issue multiple
outstanding cached writes to a file system for a synchronous
file object.  To do so would risk out of order application
of writes that extend the end of file and in turn risk data
corruption.  It turns out that on Server 2003 SP2 and more
than likely XP and 2000 as well, if a file system returns
STATUS_PENDING because a write was deferred due to the
Windows Cache Manager failing CcCanIWrite(), the IO Manager
will happily continue issue subsequent write requests.

On OSes older than Vista disable the use of deferred writes
and sit in a spin loop waiting for the Windows cache manager
to make room.  This is much less efficient and increases the
write latency but it is safe.

Change-Id: Ic47d62749bdb4d0475661967fcbfd25834f21a72
Reviewed-on: http://gerrit.openafs.org/10351
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Jeffrey Altman <jaltman@your-file-system.com>

src/WINNT/afsrdr/kernel/lib/AFSWrite.cpp

index 328ca84..05a715d 100644 (file)
@@ -415,39 +415,73 @@ AFSCommonWrite( IN PDEVICE_OBJECT DeviceObject,
                 }
             }
 
-            if (!CcCanIWrite( pFileObject,
-                              ulByteCount,
-                              FALSE,
-                              bRetry))
-            {
+           //
+           // On versions of Microsoft Windows older than Vista the IO Manager
+           // will issue multiple outstanding writes on a synchronous file object
+           // if one of the cached writes completes with STATUS_PENDING.  This can
+           // result in the writes being completed out of order which can corrupt
+           // the end of file marker.  On OS versions older than Vista use a spin
+           // loop instead of deferring the write.
+           //
+
+           if ( bSynchronousFo &&
+                AFSRtlSysVersion.dwMajorVersion < 6)
+           {
 
-                AFSDbgTrace(( AFS_SUBSYSTEM_IO_PROCESSING,
-                              AFS_TRACE_LEVEL_WARNING,
-                              "AFSCommonWrite (FO: %p) CcCanIWrite says No room for Offset %0I64X Length %08lX bytes! Deferring%s\n",
-                              pFileObject,
-                              liStartingByte.QuadPart,
-                              ulByteCount,
-                              bRetry ? " RETRY" : ""));
+               while (!CcCanIWrite( pFileObject,
+                                    ulByteCount,
+                                    FALSE,
+                                    bRetry))
+               {
+                   static const LONGLONG llWriteDelay = (LONGLONG)-100000;
+                   bRetry = TRUE;
 
-                ntStatus = AFSDeferWrite( DeviceObject, pFileObject, hCallingUser, Irp, ulByteCount, bRetry);
+                   AFSDbgLogMsg( AFS_SUBSYSTEM_IO_PROCESSING,
+                                 AFS_TRACE_LEVEL_WARNING,
+                                 "AFSCommonWrite (FO: %p) CcCanIWrite says No room for %u bytes! Retry in 10ms\n",
+                                 pFileObject,
+                                 ulByteCount);
 
-                if ( STATUS_PENDING == ntStatus)
-                {
+                   KeDelayExecutionThread(KernelMode, FALSE, (PLARGE_INTEGER)&llWriteDelay);
+               }
+           }
+           else
+           {
 
-                    bCompleteIrp = FALSE;
-                }
-                else
-                {
+               if (!CcCanIWrite( pFileObject,
+                                 ulByteCount,
+                                 FALSE,
+                                 bRetry))
+               {
 
-                    AFSDbgTrace(( AFS_SUBSYSTEM_IO_PROCESSING,
-                                  AFS_TRACE_LEVEL_ERROR,
-                                  "AFSCommonWrite (FO: %p) AFSDeferWrite failure Status %08lX\n",
-                                  pFileObject,
-                                  ntStatus));
-                }
+                   AFSDbgTrace(( AFS_SUBSYSTEM_IO_PROCESSING,
+                                 AFS_TRACE_LEVEL_WARNING,
+                                 "AFSCommonWrite (FO: %p) CcCanIWrite says No room for Offset %0I64X Length %08lX bytes! Deferring%s\n",
+                                 pFileObject,
+                                 liStartingByte.QuadPart,
+                                 ulByteCount,
+                                 bRetry ? " RETRY" : ""));
 
-                try_return( ntStatus);
-            }
+                   ntStatus = AFSDeferWrite( DeviceObject, pFileObject, hCallingUser, Irp, ulByteCount, bRetry);
+
+                   if ( STATUS_PENDING == ntStatus)
+                   {
+
+                       bCompleteIrp = FALSE;
+                   }
+                   else
+                   {
+
+                       AFSDbgTrace(( AFS_SUBSYSTEM_IO_PROCESSING,
+                                     AFS_TRACE_LEVEL_ERROR,
+                                     "AFSCommonWrite (FO: %p) AFSDeferWrite failure Status %08lX\n",
+                                     pFileObject,
+                                     ntStatus));
+                   }
+
+                   try_return( ntStatus);
+               }
+           }
         }
 
         //
@@ -723,7 +757,7 @@ try_exit:
             if ( !bPagingIo)
             {
 
-                if( bSynchronousFo)
+               if( bSynchronousFo)
                 {
 
                     pFileObject->CurrentByteOffset.QuadPart = liStartingByte.QuadPart + ulByteCount;