Messages in this thread Patch in this message | | | Subject | Re: NFS problem: __nfs_fhget: inode still busy | From | Trond Myklebust <> | Date | 16 Sep 1999 09:56:15 +0200 |
| |
Paul Jakma <paul@clubi.ie> writes:
> I have an nfs client which virtually hangs immediately after boot. it > continually prints out the message: > > __nfs_fhget: inode 8227 still busy, i_count=1 > > This happens just after finishing executing rc.local. (RH5 boot-up > scripts). It's then supposed to start xdm, but never get's that far. > Inode number is always the same.
This is due to the stale inode code getting activated, and trying to clean out an inode that is in use. I have a patch which softens the stale inode code which may (or may not) help.
Please try it out and see if it helps...
Cheers, Trond
diff -u --recursive --new-file linux-2.2.12.orig/fs/dcache.c linux-2.2.12/fs/dcache.c --- linux-2.2.12.orig/fs/dcache.c Mon Apr 26 08:17:56 1999 +++ linux-2.2.12/fs/dcache.c Mon Aug 30 16:13:24 1999 @@ -168,6 +168,11 @@ int d_invalidate(struct dentry * dentry) { /* + * If it's already been dropped, return OK. + */ + if (&list_empty(&dentry->d_hash)) + return 0; + /* * Check whether to do a partial shrink_dcache * to get rid of unused child entries. */ diff -u --recursive --new-file linux-2.2.12.orig/fs/nfs/dir.c linux-2.2.12/fs/nfs/dir.c --- linux-2.2.12.orig/fs/nfs/dir.c Mon Aug 9 21:04:57 1999 +++ linux-2.2.12/fs/nfs/dir.c Mon Aug 30 16:15:54 1999 @@ -395,13 +395,14 @@ * If mtime is close to present time, we revalidate * more often. */ +#define NFS_REVALIDATE_NEGATIVE (1 * HZ) static inline int nfs_neg_need_reval(struct dentry *dentry) { - unsigned long timeout = 30 * HZ; + unsigned long timeout = NFS_ATTRTIMEO(dentry->d_parent->d_inode); long diff = CURRENT_TIME - dentry->d_parent->d_inode->i_mtime; - if (diff < 5*60) - timeout = 1 * HZ; + if (diff < 5*60 && timeout > NFS_REVALIDATE_NEGATIVE) + timeout = NFS_REVALIDATE_NEGATIVE; return time_after(jiffies, dentry->d_time + timeout); } @@ -462,12 +463,8 @@ goto out_bad; /* Filehandle matches? */ - if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) { - if (!list_empty(&dentry->d_subdirs)) - shrink_dcache_parent(dentry); - if (dentry->d_count < 2) - goto out_bad; - } + if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) + goto out_bad; /* Ok, remeber that we successfully checked it.. */ nfs_renew_times(dentry); @@ -476,6 +473,9 @@ out_valid: return 1; out_bad: + d_drop(dentry); + if (!list_empty(&dentry->d_subdirs)) + shrink_dcache_parent(dentry); if (dentry->d_parent->d_inode) nfs_invalidate_dircache(dentry->d_parent->d_inode); if (inode && S_ISDIR(inode->i_mode)) diff -u --recursive --new-file linux-2.2.12.orig/fs/nfs/inode.c linux-2.2.12/fs/nfs/inode.c --- linux-2.2.12.orig/fs/nfs/inode.c Mon Aug 9 21:05:05 1999 +++ linux-2.2.12/fs/nfs/inode.c Mon Aug 30 16:14:37 1999 @@ -492,6 +492,48 @@ } /* + * The following may seem pretty minimal, but the stateless nature + * of NFS means that we can't do too much more. Previous attempts to use + * fattr->nlink to determine how well the cached state matches the + * server suffer from races with stale dentries. You also risk killing + * off processes by just doing 'mv file newdir' on the server. + * + * FIXME: Of course, if 2 exported files have the same fileid (but + * different fsid which makes it legal) you're still buggered... + * Trond, August 1999. + */ +static int +nfs_inode_is_stale(struct inode *inode, struct nfs_fattr *fattr) +{ + int unhashed; + int is_stale = 0; + + if (inode->i_mode && + (fattr->mode & S_IFMT) != (inode->i_mode & S_IFMT)) + is_stale = 1; + + if (is_bad_inode(inode)) + is_stale = 1; + + /* + * Free up unused cached dentries to see if it's wise to unhash + * the inode (which we can do if all the dentries have been unhashed). + */ + unhashed = nfs_free_dentries(inode); + + /* Assume we're holding 1 lock on the inode from 'iget' + * + * NB: sockets sometimes have volatile file handles + * don't invalidate their inodes even if all dentries are + * unhashed. */ + if (unhashed && inode->i_count == unhashed + 1 + && !S_ISSOCK(inode->i_mode) && !S_ISFIFO(inode->i_mode)) + is_stale = 1; + + return is_stale; +} + +/* * This is our own version of iget that looks up inodes by file handle * instead of inode number. We use this technique instead of using * the vfs read_inode function because there is no way to pass the @@ -550,53 +592,26 @@ static struct inode * __nfs_fhget(struct super_block *sb, struct nfs_fattr *fattr) { - struct inode *inode; - int max_count, stale_inode, unhashed = 0; + struct inode *inode = NULL; -retry: - inode = iget(sb, fattr->fileid); - if (!inode) + if (!fattr) goto out_no_inode; - /* N.B. This should be impossible ... */ - if (inode->i_ino != fattr->fileid) - goto out_bad_id; - /* - * Check for busy inodes, and attempt to get rid of any - * unused local references. If successful, we release the - * inode and try again. - * - * Note that the busy test uses the values in the fattr, - * as the inode may have become a different object. - * (We can probably handle modes changes here, too.) - */ - stale_inode = inode->i_mode && - ((fattr->mode ^ inode->i_mode) & S_IFMT); - stale_inode |= inode->i_count && inode->i_count == unhashed; - max_count = S_ISDIR(fattr->mode) ? 1 : fattr->nlink; - if (stale_inode || inode->i_count > max_count + unhashed) { - dprintk("__nfs_fhget: inode %ld busy, i_count=%d, i_nlink=%d\n", - inode->i_ino, inode->i_count, inode->i_nlink); - unhashed = nfs_free_dentries(inode); - if (stale_inode || inode->i_count > max_count + unhashed) { - printk("__nfs_fhget: inode %ld still busy, i_count=%d\n", - inode->i_ino, inode->i_count); - if (!list_empty(&inode->i_dentry)) { - struct dentry *dentry; - dentry = list_entry(inode->i_dentry.next, - struct dentry, d_alias); - printk("__nfs_fhget: killing %s/%s filehandle\n", - dentry->d_parent->d_name.name, - dentry->d_name.name); - memset(dentry->d_fsdata, 0, - sizeof(struct nfs_fh)); - } - remove_inode_hash(inode); - nfs_invalidate_inode(inode); - unhashed = 0; - } + while (!inode) { + inode = iget(sb, fattr->fileid); + if (!inode) + goto out_no_inode; + /* N.B. This should be impossible ... */ + if (inode->i_ino != fattr->fileid) + goto out_bad_id; + + if (!nfs_inode_is_stale(inode,fattr)) + break; + + remove_inode_hash(inode); + nfs_invalidate_inode(inode); iput(inode); - goto retry; + inode = NULL; } nfs_fill_inode(inode, fattr); dprintk("NFS: __nfs_fhget(%x/%ld ct=%d)\n", @@ -751,6 +766,8 @@ fh = (u32 *) &fhandle; dfprintk(PAGECACHE, " %08x%08x%08x%08x%08x%08x%08x%08x\n", fh[0],fh[1],fh[2],fh[3],fh[4],fh[5],fh[6],fh[7]); + if (!IS_ROOT(dentry)) + d_drop(dentry); goto out; } - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/
| |