patch for 2.1.72 nfs client

Bill Hawes (whawes@star.net)
Fri, 12 Dec 1997 12:02:01 -0500


This is a multi-part message in MIME format.
--------------FD17E2CB820444DBA4E7117F
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

The attached patch for nfs client adds some error recovery code for
mkdir and create operations. If a create operation fails with EEXIST,
the client code performs a new lookup on the name. If this is successful
and the mode of the object is correct, the error is cleared and the
dentry instantiated as it would have been originally.

This should provide more robust operation in the event of return-path
failures. I've tested the error recovery code by making knfsd always
return failure after a successful mkdir, and it appears to be working
correctly.

The patch also removes or downgrades some error messages that are no
longer of great importance, and makes the test for busy inodes smarter.

Regards,
Bill
--------------FD17E2CB820444DBA4E7117F
Content-Type: text/plain; charset=us-ascii; name="nfs_client72-patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="nfs_client72-patch"

--- linux-2.1.72/include/linux/nfs_fs.h.old Wed Dec 10 00:20:02 1997
+++ linux-2.1.72/include/linux/nfs_fs.h Wed Dec 10 12:22:25 1997
@@ -168,6 +168,7 @@
* linux/fs/nfs/write.c
*/
extern int nfs_writepage(struct inode *, struct page *);
+extern int nfs_check_failed_request(struct inode *);
extern int nfs_check_error(struct inode *);
extern int nfs_flush_dirty_pages(struct inode *, pid_t, off_t, off_t);
extern int nfs_truncate_dirty_pages(struct inode *, unsigned long);
--- linux-2.1.72/fs/nfs/dir.c.old Wed Dec 10 00:12:14 1997
+++ linux-2.1.72/fs/nfs/dir.c Fri Dec 12 12:19:04 1997
@@ -471,11 +471,11 @@

static int nfs_lookup(struct inode *dir, struct dentry * dentry)
{
+ int len = dentry->d_name.len;
struct inode *inode;
+ int error;
struct nfs_fh fhandle;
struct nfs_fattr fattr;
- int len = dentry->d_name.len;
- int error;

dfprintk(VFS, "NFS: lookup(%x/%ld, %.*s)\n",
dir->i_dev, dir->i_ino, len, dentry->d_name.name);
@@ -516,10 +516,38 @@
}

/*
+ * Attempt to patch up certain errors following a create or
+ * mkdir operation. We clear the original error if the new
+ * lookup succeeds and has the correct mode.
+ */
+static int nfs_fixup(struct inode *dir, struct dentry *dentry, int mode,
+ struct nfs_fh *fhandle, struct nfs_fattr *fattr, int error)
+{
+ int newerr;
+
+#ifdef NFS_PARANOIA
+printk("nfs_fixup: %s/%s, error=%d, mode=%x\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, error, mode);
+#endif
+ if (error == -EEXIST) {
+ newerr = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir),
+ dentry->d_name.name, fhandle, fattr);
+ if (!newerr) {
+#ifdef NFS_PARANOIA
+printk("nfs_fixup: lookup OK, got mode=%x, want mode=%x\n", fattr->mode, mode);
+#endif
+ if ((fattr->mode & S_IFMT) == (mode & S_IFMT))
+ error = 0;
+ }
+ }
+ return error;
+}
+
+/*
* Code common to create, mkdir, and mknod.
*/
-static int nfs_instantiate(struct dentry *dentry, struct nfs_fattr *fattr,
- struct nfs_fh *fhandle)
+static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr)
{
struct inode *inode;
int error = -EACCES;
@@ -545,12 +573,12 @@
* that the operation succeeded on the server, but an error in the
* reply path made it appear to have failed.
*/
-static int nfs_create(struct inode *dir, struct dentry * dentry, int mode)
+static int nfs_create(struct inode *dir, struct dentry *dentry, int mode)
{
+ int error;
struct nfs_sattr sattr;
struct nfs_fattr fattr;
struct nfs_fh fhandle;
- int error;

dfprintk(VFS, "NFS: create(%x/%ld, %s\n",
dir->i_dev, dir->i_ino, dentry->d_name.name);
@@ -574,9 +602,11 @@
nfs_invalidate_dircache(dir);
error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
dentry->d_name.name, &sattr, &fhandle, &fattr);
+ if (error)
+ error = nfs_fixup(dir, dentry, mode, &fhandle, &fattr, error);
if (!error)
- error = nfs_instantiate(dentry, &fattr, &fhandle);
- else
+ error = nfs_instantiate(dentry, &fhandle, &fattr);
+ if (error)
d_drop(dentry);
out:
return error;
@@ -587,10 +617,10 @@
*/
static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
{
+ int error;
struct nfs_sattr sattr;
struct nfs_fattr fattr;
struct nfs_fh fhandle;
- int error;

dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n",
dir->i_dev, dir->i_ino, dentry->d_name.name);
@@ -612,9 +642,11 @@
nfs_invalidate_dircache(dir);
error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
dentry->d_name.name, &sattr, &fhandle, &fattr);
+ if (error)
+ error = nfs_fixup(dir, dentry, mode, &fhandle, &fattr, error);
if (!error)
- error = nfs_instantiate(dentry, &fattr, &fhandle);
- else
+ error = nfs_instantiate(dentry, &fhandle, &fattr);
+ if (error)
d_drop(dentry);
return error;
}
@@ -624,10 +656,10 @@
*/
static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
+ int error;
struct nfs_sattr sattr;
struct nfs_fattr fattr;
struct nfs_fh fhandle;
- int error;

dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n",
dir->i_dev, dir->i_ino, dentry->d_name.name);
@@ -640,6 +672,8 @@
if (dentry->d_name.len > NFS_MAXNAMLEN)
return -ENAMETOOLONG;

+ /* For some reason mode doesn't have the S_IFDIR flag ... */
+ mode |= S_IFDIR;
sattr.mode = mode;
sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
@@ -647,6 +681,8 @@
nfs_invalidate_dircache(dir);
error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir),
dentry->d_name.name, &sattr, &fhandle, &fattr);
+ if (error)
+ error = nfs_fixup(dir, dentry, mode, &fhandle, &fattr, error);
if (!error) {
/*
* Some AIX servers reportedly fail to fill out the fattr.
@@ -660,8 +696,9 @@
fattr.mode);
goto drop;
}
- error = nfs_instantiate(dentry, &fattr, &fhandle);
- } else {
+ error = nfs_instantiate(dentry, &fhandle, &fattr);
+ }
+ if (error) {
drop:
d_drop(dentry);
}
@@ -858,11 +895,8 @@
* Remove a file after making sure there are no pending writes,
* and after checking that the file has only one user.
*
- * Updating inode->i_nlink here rather than waiting for the next
- * nfs_refresh_inode() is not merely cosmetic; once an object has
- * been deleted, we want to get rid of the inode locally. The NFS
- * server may reuse the fileid for a new inode, and we don't want
- * that to be confused with this inode.
+ * We update inode->i_nlink and free the inode prior to the operation
+ * to avoid possible races if the server reuses the inode.
*/
static int nfs_safe_remove(struct dentry *dentry)
{
@@ -870,6 +904,7 @@
struct inode *inode = dentry->d_inode;
int error, rehash = 0;

+ /* N.B. not needed now that d_delete is done in advance? */
error = -EBUSY;
if (inode) {
if (NFS_WRITEBACK(inode)) {
@@ -897,7 +932,7 @@
goto out;
}
#ifdef NFS_PARANOIA
-if (inode && inode->i_count > 1)
+if (inode && inode->i_count > inode->i_nlink)
printk("nfs_safe_remove: %s/%s inode busy?? i_count=%d, i_nlink=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
inode->i_count, inode->i_nlink);
--- linux-2.1.72/fs/nfs/inode.c.old Sun Nov 30 11:26:14 1997
+++ linux-2.1.72/fs/nfs/inode.c Wed Dec 10 12:24:56 1997
@@ -35,9 +35,6 @@
#define NFSDBG_FACILITY NFSDBG_VFS
#define NFS_PARANOIA 1

-extern void nfs_invalidate_dircache_sb(struct super_block *);
-extern int check_failed_request(struct inode *);
-
static void nfs_read_inode(struct inode *);
static void nfs_put_inode(struct inode *);
static void nfs_delete_inode(struct inode *);
@@ -99,8 +96,9 @@
*/
if (NFS_WRITEBACK(inode) != NULL) {
unsigned long timeout = jiffies + 5*HZ;
- printk("NFS: inode %ld, invalidating pending RPC requests\n",
- inode->i_ino);
+#ifdef NFS_DEBUG_VERBOSE
+printk("nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino);
+#endif
nfs_invalidate_pages(inode);
while (NFS_WRITEBACK(inode) != NULL && jiffies < timeout) {
current->state = TASK_INTERRUPTIBLE;
@@ -112,7 +110,7 @@
printk("NFS: Arghhh, stuck RPC requests!\n");
}

- failed = check_failed_request(inode);
+ failed = nfs_check_failed_request(inode);
if (failed)
printk("NFS: inode %ld had %d failed requests\n",
inode->i_ino, failed);
@@ -355,8 +353,6 @@
* instead of inode number. We use this technique instead of using
* the vfs read_inode function because there is no way to pass the
* file handle or current attributes into the read_inode function.
- * We just have to be careful not to subvert iget's special handling
- * of mount points.
*/
struct inode *
nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle,
@@ -422,8 +418,10 @@
inode->i_size = fattr->size;
inode->i_mtime = fattr->mtime.seconds;
NFS_OLDMTIME(inode) = fattr->mtime.seconds;
+ *NFS_FH(inode) = *fhandle;
}
- *NFS_FH(inode) = *fhandle;
+ if (memcmp(NFS_FH(inode), fhandle, sizeof(struct nfs_fh)))
+ printk("nfs_fhget: fhandle changed!\n");
nfs_refresh_inode(inode, fattr);
dprintk("NFS: fhget(%x/%ld ct=%d)\n",
inode->i_dev, inode->i_ino,
--- linux-2.1.72/fs/nfs/write.c.old Sun Nov 30 11:25:27 1997
+++ linux-2.1.72/fs/nfs/write.c Wed Dec 10 12:24:35 1997
@@ -68,8 +68,6 @@

#define NFSDBG_FACILITY NFSDBG_PAGECACHE

-int check_failed_request(struct inode *);
-
static void nfs_wback_lock(struct rpc_task *task);
static void nfs_wback_result(struct rpc_task *task);

@@ -331,7 +329,7 @@
* Find and release all failed requests for this inode.
*/
int
-check_failed_request(struct inode * inode)
+nfs_check_failed_request(struct inode * inode)
{
struct nfs_wreq * req;
int found = 0;
@@ -496,8 +494,7 @@
}
remove_wait_queue(&page->wait, &wait);
current->state = TASK_RUNNING;
- if (atomic_read(&page->count) == 1)
- printk("NFS: page unused while waiting\n");
+ /* N.B. page may have been unused, so we must use free_page() */
free_page(page_address(page));
return retval;
}

--------------FD17E2CB820444DBA4E7117F--