Print this page
NFS4 data corruption (#3508)
If async calls are disabled, nfs4_async_putapage is supposed to do its
work synchronously. Due to a bug, it sometimes just does nothing, leaving
the page for later.
Unfortunately the caller has already reset the R4DIRTY flag.
Without R4DIRTY, nfs4_attrcache_va can't see that there are still
outstanding writes and accepts the file size from the server, which is
too low.
When the dirty page finally gets written back, the page size is truncated
to the file size, leaving some bytes unwritten.
Reviewed by: Marcel Telka <>
Reviewed by: Robert Gordon <>

@@ -1781,12 +1781,11 @@
         return (0);
-        if (curproc == proc_pageout || curproc == proc_fsflush ||
-            nfs_zone() == mi->mi_zone) {
+        if (curproc == proc_pageout || curproc == proc_fsflush) {
                  * If we get here in the context of the pageout/fsflush,
                  * or we have run out of memory or we're attempting to
                  * unmount we refuse to do a sync write, because this may
                  * hang pageout/fsflush and the machine. In this case,

@@ -1802,22 +1801,24 @@
                         flags &= ~(B_INVAL | B_FORCE);
                 pvn_write_done(pp, flags | B_ERROR);
                 return (0);
+        if (nfs_zone() != mi->mi_zone) {
-         * We'll get here only if (nfs_zone() != mi->mi_zone)
-         * which means that this was a cross-zone sync putpage.
+                 * So this was a cross-zone sync putpage.
          * We pass in B_ERROR to pvn_write_done() to re-mark the pages
          * as dirty and unlock them.
          * We don't want to clear B_FORCE here as the caller presumably
          * knows what they're doing if they set it.
         pvn_write_done(pp, flags | B_ERROR);
         return (EPERM);
+        }
+        return ((*putapage)(vp, pp, off, len, flags, cr));
 nfs4_async_pageio(vnode_t *vp, page_t *pp, u_offset_t io_off, size_t io_len,
     int flags, cred_t *cr, int (*pageio)(vnode_t *, page_t *, u_offset_t,