NFS Stability
Matthew Dillon
dillon at apollo.backplane.com
Tue Apr 5 22:35:40 PDT 2005
:sdrhodus wrote @ Wed, 30 Mar 2005 13:17:46 +0000:
:
:> I know that Matt and myself almost regularly do various NFS testing,
:> and lots of the time the testing does involve using fsx. Though any
:> outside validity testing is always welcome.
:
:This happens on my NFS over tcp mount,
:the server is an via epia with 256MB RAM.
:
:$ /usr/src/test/stress/fsx/dotest
:Chance of close/open is 1 in 100000
:truncating to largest ever: 0x2abbd1
:truncating to largest ever: 0x3abe49
:truncating to largest ever: 0x3b1956
:truncating to largest ever: 0x3e2816
:nfs server morex:/data: not responding
:nfs server morex:/data: is alive again
:truncating to largest ever: 0x3e8ae7
:nfs server morex:/data: not responding
:nfs server morex:/data: is alive again
:truncating to largest ever: 0x3f959f
:nfs server morex:/data: not responding
:Segmentation fault
:
:
:Andy
Ok, Andrew, see if FSX still fails with this patch. I think some of
the recent work I did broke the ftruncate() code. I was hoping that
the recent TCP issue was the cause but I can get fsx to seg-fault
occassionally still and it happens on a UDP mount so there's definitely
a bug still in there.
The issue here is that ftruncate() does some buffer flushing before
it issues the setattr RPC. The buffer flushing can feed back (because
RPC responses from the server often include attribute information) and
confuse the NFS client's notion of the file's size.
I give this about a 70% chance of fixing the problem. The code
interactions are very complex so I don't know for sure.
-Matt
Matthew Dillon
<dillon at xxxxxxxxxxxxx>
Index: nfs_vnops.c
===================================================================
RCS file: /cvs/src/sys/vfs/nfs/nfs_vnops.c,v
retrieving revision 1.38
diff -u -r1.38 nfs_vnops.c
--- nfs_vnops.c 17 Mar 2005 17:28:46 -0000 1.38
+++ nfs_vnops.c 6 Apr 2005 04:41:57 -0000
@@ -736,13 +736,23 @@
return (EROFS);
/*
- * We run vnode_pager_setsize() early (why?),
- * we must set np->n_size now to avoid vinvalbuf
- * V_SAVE races that might setsize a lower
- * value.
+ * This is nasty. The RPCs we send to flush pending
+ * data often return attribute information which is
+ * cached via a callback to nfs_loadattrcache(), which
+ * has the effect of changing our notion of the file
+ * size. Due to flushed appends and other operations
+ * the file size can be set to virtually anything,
+ * including values that do not match either the old
+ * or intended file size.
+ *
+ * When this condition is detected we must loop to
+ * try the operation again. Hopefully no more
+ * flushing is required on the loop so it works the
+ * second time around. THIS CASE ALMOST ALWAYS
+ * HAPPENS!
*/
-
tsize = np->n_size;
+again:
error = nfs_meta_setsize(vp, ap->a_td, vap->va_size);
if (np->n_flag & NLMODIFIED) {
@@ -750,30 +760,28 @@
error = nfs_vinvalbuf(vp, 0, ap->a_td, 1);
else
error = nfs_vinvalbuf(vp, V_SAVE, ap->a_td, 1);
- if (error) {
- np->n_size = tsize;
- vnode_pager_setsize(vp, np->n_size);
- return (error);
- }
}
- /*
- * np->n_size has already been set to vap->va_size
- * in nfs_meta_setsize(). We must set it again since
- * nfs_loadattrcache() could be called through
- * nfs_meta_setsize() and could modify np->n_size.
- *
- * (note that nfs_loadattrcache() will have called
- * vnode_pager_setsize() for us in that case).
+ /*
+ * note: this loop case almost always happens at
+ * least once per truncation.
*/
- np->n_vattr.va_size = np->n_size = vap->va_size;
+ if (error == 0 && np->n_size != vap->va_size)
+ goto again;
+ np->n_vattr.va_size = vap->va_size;
break;
}
} else if ((vap->va_mtime.tv_sec != VNOVAL ||
vap->va_atime.tv_sec != VNOVAL) && (np->n_flag & NLMODIFIED) &&
vp->v_type == VREG &&
- (error = nfs_vinvalbuf(vp, V_SAVE, ap->a_td, 1)) == EINTR)
+ (error = nfs_vinvalbuf(vp, V_SAVE, ap->a_td, 1)) == EINTR
+ ) {
return (error);
+ }
error = nfs_setattrrpc(vp, vap, ap->a_cred, ap->a_td);
+ if (error == 0 && np->n_size != vap->va_size) {
+ printf("NFS ftruncate: server disagrees on the file size: %lld/%lld/%lld\n", tsize, vap->va_size, np->n_size);
+ goto again;
+ }
if (error && vap->va_size != VNOVAL) {
np->n_size = np->n_vattr.va_size = tsize;
vnode_pager_setsize(vp, np->n_size);
More information about the Users
mailing list