lkml.org 
[lkml]   [1997]   [Aug]   [13]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subjectpatch for 2.0.31 NFS -- need testers!
I've attached a patch for various NFS problems, some of which might be
significant. The patch includes the previously-posted nfsiod fix.

I'm not using NFS and so haven't tested this, but hopefully everything
will work properly :-)

Change summary:
In nfs_put_inode, call clear_inode if i_nlink is 0 or we're doing a
silly rename cleanup, and defer the silly cleanup until the inode has
been cleared. The i_nlink == 0 case is important -- if the NFS server
sets i_nlink to 0, the current code would fail to decrement i_count,
resulting in a stuck inode and tying up the page cache.

In sillyrename_cleanup, unconditionally unlink the file. This routine
is called only if this is the last local reference, so we definitely
want to do the cleanup.

In nfs_refresh_inode, check whether the mode changed the i_op pointer
and complain if it did. Also, don't let FIFOs be initialized more than
once -- if i_mode gets reset to 0 and back to FIFO, a page of memory
will be lost each time.

in nfs_read_super, make sure the error exits decrement the filp count
and unlock the super block.

In nfsiod, check whether the request is still in the free list to avoid
list corruption in the event of a signal or early wakeup.

Happy testing, and let me know if any of the diagnostic messages are
triggered.

Regards,
Bill--- linux-2.0.31/fs/nfs/inode.c.old Sat Nov 30 05:21:21 1996
+++ linux-2.0.31/fs/nfs/inode.c Wed Aug 13 09:02:16 1997
@@ -73,12 +73,34 @@
NFS_CACHEINV(inode);
}

+/*
+ * Defer silly rename cleanup until after clearing inode to avoid races.
+ */
static void nfs_put_inode(struct inode * inode)
{
- if (NFS_RENAMED_DIR(inode))
- nfs_sillyrename_cleanup(inode);
- if (inode->i_pipe)
+ struct inode * dir = NFS_RENAMED_DIR(inode);
+ long ino = inode->i_ino;
+
+ if (inode->i_count > 1) {
+ printk("nfs_put_inode: still in use, count=%d\n",
+ inode->i_count);
+ }
+
+ /*
+ * Always clear FIFOs, deleted files, and silly renamed files.
+ */
+ if (inode->i_pipe || !inode->i_nlink || dir)
clear_inode(inode);
+
+ /*
+ * Defer cleanup until the inode has been cleared to avoid races,
+ * but bump the count temporarily so the inode can't be reused.
+ */
+ if (dir) {
+ inode->i_count++;
+ nfs_sillyrename_cleanup(dir, ino);
+ inode->i_count--;
+ }
}

void nfs_put_super(struct super_block *sb)
@@ -112,9 +134,7 @@
MOD_INC_USE_COUNT;
if (!data) {
printk("nfs_read_super: missing data argument\n");
- sb->s_dev = 0;
- MOD_DEC_USE_COUNT;
- return NULL;
+ goto exit_fail;
}
fd = data->fd;
if (data->version != NFS_MOUNT_VERSION) {
@@ -123,15 +143,11 @@
}
if (fd >= NR_OPEN || !(filp = current->files->fd[fd])) {
printk("nfs_read_super: invalid file descriptor\n");
- sb->s_dev = 0;
- MOD_DEC_USE_COUNT;
- return NULL;
+ goto exit_fail;
}
if (!S_ISSOCK(filp->f_inode->i_mode)) {
printk("nfs_read_super: not a socket\n");
- sb->s_dev = 0;
- MOD_DEC_USE_COUNT;
- return NULL;
+ goto exit_fail;
}
filp->f_count++;
lock_super(sb);
@@ -169,8 +185,7 @@
if (data->addr.sin_addr.s_addr == INADDR_ANY) { /* No address passed */
if (((struct sockaddr_in *)(&server->toaddr))->sin_addr.s_addr == INADDR_ANY) {
printk("NFS: Error passed unconnected socket and no address\n") ;
- MOD_DEC_USE_COUNT;
- return NULL ;
+ goto exit_unlock;
} else {
/* Need access to socket internals JSP */
struct socket *sock;
@@ -191,19 +206,25 @@

if ((server->rsock = rpc_makesock(filp)) == NULL) {
printk("NFS: cannot create RPC socket.\n");
- MOD_DEC_USE_COUNT;
- return NULL;
+ goto exit_unlock;
}

sb->u.nfs_sb.s_root = data->root;
unlock_super(sb);
if (!(sb->s_mounted = nfs_fhget(sb, &data->root, NULL))) {
- sb->s_dev = 0;
printk("nfs_read_super: get root inode failed\n");
- MOD_DEC_USE_COUNT;
- return NULL;
+ goto exit_filp;
}
return sb;
+
+exit_unlock:
+ unlock_super(sb);
+exit_filp:
+ filp->f_count--;
+exit_fail:
+ sb->s_dev = 0;
+ MOD_DEC_USE_COUNT;
+ return NULL;
}

void nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
--- linux-2.0.31/fs/nfs/dir.c.old Sun Apr 27 15:54:43 1997
+++ linux-2.0.31/fs/nfs/dir.c Wed Aug 13 08:45:56 1997
@@ -516,7 +516,7 @@
static int nfs_sillyrename(struct inode *dir, const char *name, int len)
{
struct inode *inode;
- char silly[16];
+ char silly[30];
int slen, ret;

dir->i_count++;
@@ -544,18 +544,21 @@
return ret;
}

-void nfs_sillyrename_cleanup(struct inode *inode)
+/*
+ * When this routine is called, we know for certain the inode was
+ * given a silly name ... remove the file with no further checks.
+ */
+void nfs_sillyrename_cleanup(struct inode*dir, long ino)
{
- struct inode *dir = NFS_RENAMED_DIR(inode);
- char silly[14];
- int error, slen;
+ char silly[30];
+ int error;

- slen = sprintf(silly, ".nfs%ld", inode->i_ino);
- if ((error = nfs_unlink(dir, silly, slen)) < 0) {
- printk("NFS silly_rename cleanup failed (err = %d)\n",
- -error);
- }
- NFS_RENAMED_DIR(inode) = NULL;
+ sprintf(silly, ".nfs%ld", ino);
+ nfs_lookup_cache_remove(dir, NULL, silly);
+ error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), silly);
+ if (error < 0)
+ printk("NFS silly_rename cleanup failed (err = %d)\n", -error);
+ iput(dir);
}

static int nfs_unlink(struct inode *dir, const char *name, int len)
@@ -682,7 +685,8 @@

void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
{
- int was_empty;
+ struct inode_operations *old_ops;
+ umode_t old_mode;

if (!inode || !fattr) {
printk("nfs_refresh_inode: inode or fattr is NULL\n");
@@ -692,7 +696,10 @@
printk("nfs_refresh_inode: inode number mismatch\n");
return;
}
- was_empty = (inode->i_mode == 0);
+
+ old_ops = inode->i_op;
+ old_mode = inode->i_mode;
+
inode->i_mode = fattr->mode;
inode->i_nlink = fattr->nlink;
inode->i_uid = fattr->uid;
@@ -723,10 +730,23 @@
else if (S_ISBLK(inode->i_mode))
inode->i_op = &blkdev_inode_operations;
else if (S_ISFIFO(inode->i_mode)) {
- if (was_empty)
+ /*
+ * Make sure FIFO initialization is done just once
+ */
+ if (old_mode == 0 && !inode->i_pipe)
init_fifo(inode);
} else
inode->i_op = NULL;
+ /*
+ * Check whether the i_op pointer changed ... we don't want
+ * pre-tested i_op entries to become invalid or NULL.
+ */
+ if (old_ops && inode->i_op != old_ops) {
+ printk("nfs_refresh_inode: mode changed i_op, old=%x new=%x\n",
+ old_mode, inode->i_mode);
+ /* For now, just complain */
+ /* inode->i_op = &inode_error_operations; */
+ }
nfs_lookup_cache_refresh(inode, fattr);
}

--- linux-2.0.31/fs/nfs/nfsiod.c.old Sat Jun 29 05:00:46 1996
+++ linux-2.0.31/fs/nfs/nfsiod.c Mon Aug 11 17:58:58 1997
@@ -87,6 +87,7 @@
nfsiod(void)
{
struct nfsiod_req request, *req = &request;
+ struct nfsiod_req *next, **prev;
int result;

dprintk("BIO: nfsiod %d starting\n", current->pid);
@@ -101,10 +102,26 @@
dprintk("BIO: nfsiod %d waiting\n", current->pid);
interruptible_sleep_on(&req->rq_wait);

+ /*
+ * Make sure our request isn't still in the free list ...
+ * we might have been awakened by a signal.
+ */
+ prev = &free_list;
+ while ((next = *prev) != NULL) {
+ if (next == &request) {
+ printk("nfsiod: request still in free list!\n");
+ *prev = next->rq_next;
+ break;
+ }
+ prev = &next->rq_next;
+ }
+
if (current->signal & ~current->blocked)
break;
- if (!req->rq_rpcreq.rq_slot)
+ if (!req->rq_rpcreq.rq_slot) {
+ printk("nfsiod: no slot specified, sleeping again\n");
continue;
+ }
dprintk("BIO: nfsiod %d woken up; calling nfs_rpc_doio.\n",
current->pid);
active++;
\
 
 \ /
  Last update: 2005-03-22 13:40    [W:0.112 / U:0.024 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site