patch-2.3.23 linux/fs/nfs/inode.c

Next file: linux/fs/nfs/nfs2xdr.c
Previous file: linux/fs/nfs/dir.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.22/linux/fs/nfs/inode.c linux/fs/nfs/inode.c
@@ -37,7 +37,7 @@
 #define NFS_PARANOIA 1
 
 static struct inode * __nfs_fhget(struct super_block *, struct nfs_fattr *);
-static void nfs_zap_caches(struct inode *);
+void nfs_zap_caches(struct inode *);
 static void nfs_invalidate_inode(struct inode *);
 
 static void nfs_read_inode(struct inode *);
@@ -78,6 +78,8 @@
 	inode->i_mode = 0;
 	inode->i_rdev = 0;
 	inode->i_op = NULL;
+	NFS_FILEID(inode) = 0;
+	NFS_FSID(inode) = 0;
 	NFS_CACHEINV(inode);
 	NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
 }
@@ -415,13 +417,15 @@
 		dprintk("nfs_free_dentries: found %s/%s, d_count=%d, hashed=%d\n",
 			dentry->d_parent->d_name.name, dentry->d_name.name,
 			dentry->d_count, !list_empty(&dentry->d_hash));
+		if (!list_empty(&dentry->d_subdirs))
+			shrink_dcache_parent(dentry);
 		if (!dentry->d_count) {
 			dget(dentry);
 			d_drop(dentry);
 			dput(dentry);
 			goto restart;
 		}
-		if (!list_empty(&dentry->d_hash))
+		if (list_empty(&dentry->d_hash))
 			unhashed++;
 	}
 	return unhashed;
@@ -430,7 +434,7 @@
 /*
  * Invalidate the local caches
  */
-static void
+void
 nfs_zap_caches(struct inode *inode)
 {
 	NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
@@ -466,6 +470,8 @@
 	 * do this once. (We don't allow inodes to change types.)
 	 */
 	if (inode->i_mode == 0) {
+		NFS_FILEID(inode) = fattr->fileid;
+		NFS_FSID(inode) = fattr->fsid;
 		inode->i_mode = fattr->mode;
 		if (S_ISREG(inode->i_mode))
 			inode->i_op = &nfs_file_inode_operations;
@@ -487,6 +493,54 @@
 }
 
 /*
+ * In NFSv3 we can have 64bit inode numbers. In order to support
+ * this, and re-exported directories (also seen in NFSv2)
+ * we are forced to allow 2 different inodes to have the same
+ * i_ino.
+ */
+static int
+nfs_find_actor(struct inode *inode, unsigned long ino, void *opaque)
+{
+	struct nfs_fattr *fattr = (struct nfs_fattr *)opaque;
+	if (NFS_FSID(inode) != fattr->fsid)
+		return 0;
+	if (NFS_FILEID(inode) != fattr->fileid)
+		return 0;
+	return 1;
+}
+
+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;
+
+	/*
+	 * If the inode seems stale, free up cached dentries.
+	 */
+	unhashed = nfs_free_dentries(inode);
+
+	/* Assume we're holding an i_count
+	 *
+	 * 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
@@ -545,54 +599,40 @@
 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;
+	unsigned long ino;
 
-retry:
-	inode = iget(sb, fattr->fileid);
-	if (!inode)
+	if (!fattr->nlink) {
+		printk("NFS: Buggy server - nlink == 0!\n");
 		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;
-		}
+	ino = fattr->fileid;
+
+	while((inode = iget4(sb, ino, nfs_find_actor, fattr)) != NULL) {
+
+		/*
+		 * 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.)
+		 */
+		if (!nfs_inode_is_stale(inode,fattr))
+			break;
+
+		dprintk("__nfs_fhget: inode %ld still busy, i_count=%d\n",
+		       inode->i_ino, inode->i_count);
+		nfs_zap_caches(inode);
+		remove_inode_hash(inode);
 		iput(inode);
-		goto retry;
 	}
+
+	if (!inode)
+		goto out_no_inode;
+
 	nfs_fill_inode(inode, fattr);
 	dprintk("NFS: __nfs_fhget(%x/%ld ct=%d)\n",
 		inode->i_dev, inode->i_ino, inode->i_count);
@@ -603,18 +643,14 @@
 out_no_inode:
 	printk("__nfs_fhget: iget failed\n");
 	goto out;
-out_bad_id:
-	printk("__nfs_fhget: unexpected inode from iget\n");
-	goto out;
 }
 
 int
 nfs_notify_change(struct dentry *dentry, struct iattr *attr)
 {
 	struct inode *inode = dentry->d_inode;
-	int error;
-	struct nfs_sattr sattr;
 	struct nfs_fattr fattr;
+	int error;
 
 	/*
 	 * Make sure the inode is up-to-date.
@@ -627,54 +663,29 @@
 		goto out;
 	}
 
-	sattr.mode = (u32) -1;
-	if (attr->ia_valid & ATTR_MODE) 
-		sattr.mode = attr->ia_mode;
-
-	sattr.uid = (u32) -1;
-	if (attr->ia_valid & ATTR_UID)
-		sattr.uid = attr->ia_uid;
-
-	sattr.gid = (u32) -1;
-	if (attr->ia_valid & ATTR_GID)
-		sattr.gid = attr->ia_gid;
-
-	sattr.size = (u32) -1;
-	if ((attr->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode))
-		sattr.size = attr->ia_size;
-
-	sattr.mtime.seconds = sattr.mtime.useconds = (u32) -1;
-	if (attr->ia_valid & ATTR_MTIME) {
-		sattr.mtime.seconds = attr->ia_mtime;
-		sattr.mtime.useconds = 0;
-	}
-
-	sattr.atime.seconds = sattr.atime.useconds = (u32) -1;
-	if (attr->ia_valid & ATTR_ATIME) {
-		sattr.atime.seconds = attr->ia_atime;
-		sattr.atime.useconds = 0;
-	}
+	if (!S_ISREG(inode->i_mode))
+		attr->ia_valid &= ~ATTR_SIZE;
 
 	error = nfs_wb_all(inode);
 	if (error)
 		goto out;
 
 	error = nfs_proc_setattr(NFS_DSERVER(dentry), NFS_FH(dentry),
-				&sattr, &fattr);
+				&fattr, attr);
 	if (error)
 		goto out;
 	/*
 	 * If we changed the size or mtime, update the inode
 	 * now to avoid invalidating the page cache.
 	 */
-	if (sattr.size != (u32) -1) {
-		if (sattr.size != fattr.size)
-			printk("nfs_notify_change: sattr=%d, fattr=%d??\n",
-				sattr.size, fattr.size);
-		inode->i_size  = sattr.size;
+	if (attr->ia_valid & ATTR_SIZE) {
+		if (attr->ia_size != fattr.size)
+			printk("nfs_notify_change: attr=%ld, fattr=%d??\n",
+				attr->ia_size, fattr.size);
+		inode->i_size  = attr->ia_size;
 		inode->i_mtime = fattr.mtime.seconds;
 	}
-	if (sattr.mtime.seconds != (u32) -1)
+	if (attr->ia_valid & ATTR_MTIME)
 		inode->i_mtime = fattr.mtime.seconds;
 	error = nfs_refresh_inode(inode, &fattr);
 out:
@@ -682,6 +693,34 @@
 }
 
 /*
+ * Wait for the inode to get unlocked.
+ * (Used for NFS_INO_LOCKED and NFS_INO_REVALIDATING).
+ */
+int
+nfs_wait_on_inode(struct inode *inode, int flag)
+{
+	struct task_struct	*tsk = current;
+	DECLARE_WAITQUEUE(wait, tsk);
+	int			intr, error = 0;
+
+	intr = NFS_SERVER(inode)->flags & NFS_MOUNT_INTR;
+	add_wait_queue(&inode->i_wait, &wait);
+	for (;;) {
+		set_task_state(tsk, (intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE));
+		error = 0;
+		if (!(NFS_FLAGS(inode) & flag))
+			break;
+		error = -ERESTARTSYS;
+		if (intr && signalled())
+			break;
+		schedule();
+	}
+	set_task_state(tsk, TASK_RUNNING);
+	remove_wait_queue(&inode->i_wait, &wait);
+	return error;
+}
+
+/*
  * Externally visible revalidation function
  */
 int
@@ -711,7 +750,7 @@
  * the cached attributes have to be refreshed.
  */
 int
-_nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry)
+__nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry)
 {
 	struct inode	*inode = dentry->d_inode;
 	int		 status = 0;
@@ -720,6 +759,19 @@
 	dfprintk(PAGECACHE, "NFS: revalidating %s/%s, ino=%ld\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name,
 		inode->i_ino);
+
+	if (!inode || is_bad_inode(inode))
+		return -ESTALE;
+
+	while (NFS_REVALIDATING(inode)) {
+		status = nfs_wait_on_inode(inode, NFS_INO_REVALIDATING);
+		if (status < 0)
+			return status;
+		if (time_before(jiffies,NFS_READTIME(inode)+NFS_ATTRTIMEO(inode)))
+			return 0;
+	}
+	NFS_FLAGS(inode) |= NFS_INO_REVALIDATING;
+
 	status = nfs_proc_getattr(server, NFS_FH(dentry), &fattr);
 	if (status) {
 		int error;
@@ -759,6 +811,8 @@
 	dfprintk(PAGECACHE, "NFS: %s/%s revalidation complete\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name);
 out:
+	NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING;
+	wake_up(&inode->i_wait);
 	return status;
 }
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)