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

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

diff -u --recursive --new-file v2.1.58/linux/fs/nfs/inode.c linux/fs/nfs/inode.c
@@ -33,6 +33,7 @@
 #include <asm/uaccess.h>
 
 #define NFSDBG_FACILITY		NFSDBG_VFS
+#define NFS_PARANOIA 1
 
 extern void nfs_invalidate_dircache_sb(struct super_block *);
 
@@ -72,6 +73,7 @@
 	inode->i_rdev = 0;
 	inode->i_op = NULL;
 	NFS_CACHEINV(inode);
+	NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
 }
 
 static void
@@ -356,14 +358,48 @@
 		printk("nfs_fhget: iget failed\n");
 		return NULL;
 	}
-	if (inode->i_dev == sb->s_dev) {
-		if (inode->i_ino != fattr->fileid) {
-			printk("nfs_fhget: unexpected inode from iget\n");
-			return inode;
-		}
-		*NFS_FH(inode) = *fhandle;
-		nfs_refresh_inode(inode, fattr);
+#ifdef NFS_PARANOIA
+if (inode->i_dev != sb->s_dev)
+printk("nfs_fhget: impossible\n");
+#endif
+
+	if (inode->i_ino != fattr->fileid) {
+		printk("nfs_fhget: unexpected inode from iget\n");
+		return inode;
+	}
+
+	/*
+	 * Check whether the mode has been set, as we only want to
+	 * do this once. (We don't allow inodes to change types.)
+	 */
+	if (inode->i_mode == 0) {
+		inode->i_mode = fattr->mode;
+		if (S_ISREG(inode->i_mode))
+			inode->i_op = &nfs_file_inode_operations;
+		else if (S_ISDIR(inode->i_mode))
+			inode->i_op = &nfs_dir_inode_operations;
+		else if (S_ISLNK(inode->i_mode))
+			inode->i_op = &nfs_symlink_inode_operations;
+		else if (S_ISCHR(inode->i_mode)) {
+			inode->i_op = &chrdev_inode_operations;
+			inode->i_rdev = to_kdev_t(fattr->rdev);
+		} else if (S_ISBLK(inode->i_mode)) {
+			inode->i_op = &blkdev_inode_operations;
+			inode->i_rdev = to_kdev_t(fattr->rdev);
+		} else if (S_ISFIFO(inode->i_mode))
+			init_fifo(inode);
+		else
+			inode->i_op = NULL;
+		/*
+		 * Preset the size and mtime, as there's no need
+		 * to invalidate the caches.
+		 */ 
+		inode->i_size  = fattr->size;
+		inode->i_mtime = fattr->mtime.seconds;
+		NFS_OLDMTIME(inode) = fattr->mtime.seconds;
 	}
+	*NFS_FH(inode) = *fhandle;
+	nfs_refresh_inode(inode, fattr);
 	dprintk("NFS: fhget(%x/%ld ct=%d)\n",
 		inode->i_dev, inode->i_ino,
 		inode->i_count);
@@ -378,6 +414,17 @@
 	struct nfs_fattr fattr;
 	int error;
 
+	/*
+	 * Make sure the inode is up-to-date.
+	 */
+	error = nfs_revalidate(inode);
+	if (error) {
+#ifdef NFS_PARANOIA
+printk("nfs_notify_change: revalidate failed, error=%d\n", error);
+#endif
+		goto out;
+	}
+
 	sattr.mode = (u32) -1;
 	if (attr->ia_valid & ATTR_MODE) 
 		sattr.mode = attr->ia_mode;
@@ -390,7 +437,6 @@
 	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;
@@ -408,11 +454,25 @@
 	}
 
 	error = nfs_proc_setattr(NFS_SERVER(inode), NFS_FH(inode),
-		&sattr, &fattr);
-	if (!error) {
+				&sattr, &fattr);
+	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);
 		nfs_truncate_dirty_pages(inode, sattr.size);
-		nfs_refresh_inode(inode, &fattr);
+		inode->i_size  = sattr.size;
+		inode->i_mtime = fattr.mtime.seconds;
 	}
+	if (sattr.mtime.seconds != (u32) -1)
+		inode->i_mtime = fattr.mtime.seconds;
+	error = nfs_refresh_inode(inode, &fattr);
+out:
 	return error;
 }
 
@@ -428,54 +488,152 @@
 /*
  * This function is called whenever some part of NFS notices that
  * the cached attributes have to be refreshed.
- *
- * This is a bit tricky because we have to make sure all dirty pages
- * have been sent off to the server before calling invalidate_inode_pages.
- * To make sure no other process adds more write requests while we try
- * our best to flush them, we make them sleep during the attribute refresh.
- *
- * A very similar scenario holds for the dir cache.
  */
 int
 _nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
 {
 	struct nfs_fattr fattr;
-	int		 status;
+	int		 status = 0;
 
 	if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode))
-		return 0;
+		goto out;
 
 	dfprintk(PAGECACHE, "NFS: revalidating %x/%ld inode\n",
 			inode->i_dev, inode->i_ino);
-	NFS_READTIME(inode) = jiffies;
-	if ((status = nfs_proc_getattr(server, NFS_FH(inode), &fattr)) < 0)
+	status = nfs_proc_getattr(server, NFS_FH(inode), &fattr);
+	if (status) {
+#ifdef NFS_PARANOIA
+printk("nfs_revalidate_inode: getattr failed, error=%d\n", status);
+#endif
 		goto done;
+	}
 
-	nfs_refresh_inode(inode, &fattr);
-	if (fattr.mtime.seconds != NFS_OLDMTIME(inode)) {
-		if (!S_ISDIR(inode->i_mode)) {
-			/* This sends off all dirty pages off to the server.
-			 * Note that this function must not sleep. */
-			nfs_invalidate_pages(inode);
-			invalidate_inode_pages(inode);
-		} else {
-			nfs_invalidate_dircache(inode);
-		}
-
-		NFS_OLDMTIME(inode)  = fattr.mtime.seconds;
-		NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
-	} else {
+	status = nfs_refresh_inode(inode, &fattr);
+	if (status)
+		goto done;
+	if (fattr.mtime.seconds == NFS_OLDMTIME(inode)) {
 		/* Update attrtimeo value */
 		if ((NFS_ATTRTIMEO(inode) <<= 1) > NFS_MAXATTRTIMEO(inode))
 			NFS_ATTRTIMEO(inode) = NFS_MAXATTRTIMEO(inode);
 	}
-	status = 0;
+	NFS_OLDMTIME(inode) = fattr.mtime.seconds;
 
 done:
 	dfprintk(PAGECACHE,
 		"NFS: inode %x/%ld revalidation complete (status %d).\n",
 				inode->i_dev, inode->i_ino, status);
+out:
 	return status;
+}
+
+/*
+ * Many nfs protocol calls return the new file attributes after
+ * an operation.  Here we update the inode to reflect the state
+ * of the server's inode.
+ *
+ * This is a bit tricky because we have to make sure all dirty pages
+ * have been sent off to the server before calling invalidate_inode_pages.
+ * To make sure no other process adds more write requests while we try
+ * our best to flush them, we make them sleep during the attribute refresh.
+ *
+ * A very similar scenario holds for the dir cache.
+ */
+int
+nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
+{
+	int invalid = 0;
+	int error = -EIO;
+
+	dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d)\n",
+		 inode->i_dev, inode->i_ino, inode->i_count);
+
+	if (!inode || !fattr) {
+		printk("nfs_refresh_inode: inode or fattr is NULL\n");
+		goto out;
+	}
+	if (inode->i_ino != fattr->fileid) {
+		printk("nfs_refresh_inode: inode number mismatch\n");
+		goto out;
+	}
+
+	/*
+	 * Make sure the inode's type hasn't changed.
+	 */
+	if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
+		goto out_changed;
+
+	/*
+	 * If the size or mtime changed from outside, we want
+	 * to invalidate the local caches immediately.
+	 */
+	if (inode->i_size != fattr->size) {
+#ifdef NFS_DEBUG_VERBOSE
+printk("NFS: size change on %x/%ld\n", inode->i_dev, inode->i_ino);
+#endif
+		invalid = 1;
+	}
+	if (inode->i_mtime != fattr->mtime.seconds) {
+#ifdef NFS_DEBUG_VERBOSE
+printk("NFS: mtime change on %x/%ld\n", inode->i_dev, inode->i_ino);
+#endif
+		invalid = 1;
+	}
+
+	inode->i_mode = fattr->mode;
+	inode->i_nlink = fattr->nlink;
+	inode->i_uid = fattr->uid;
+	inode->i_gid = fattr->gid;
+
+	inode->i_size = fattr->size;
+	inode->i_blocks = fattr->blocks;
+	inode->i_atime = fattr->atime.seconds;
+	inode->i_mtime = fattr->mtime.seconds;
+	inode->i_ctime = fattr->ctime.seconds;
+	/*
+	 * Update the read time so we don't revalidate too often.
+	 */
+	NFS_READTIME(inode) = jiffies;
+	error = 0;
+	if (invalid)
+		goto out_invalid;
+out:
+	return error;
+
+out_changed:
+	/*
+	 * Big trouble! The inode has become a different object.
+	 */
+#ifdef NFS_PARANOIA
+printk("nfs_refresh_inode: mode changed, %07o to %07o\n",
+inode->i_mode, fattr->mode);
+#endif
+	fattr->mode = inode->i_mode; /* save mode */
+	make_bad_inode(inode);
+	inode->i_mode = fattr->mode; /* restore mode */
+	inode->i_nlink = 0;
+	/*
+	 * No need to worry about unhashing the dentry, as the
+	 * lookup validation will know that the inode is bad.
+	 * (But we fall through to invalidate the caches.)
+	 */
+
+out_invalid:
+	/*
+	 * Invalidate the local caches
+	 */
+#ifdef NFS_DEBUG_VERBOSE
+printk("nfs_refresh_inode: invalidating %ld pages\n", inode->i_nrpages);
+#endif
+	if (!S_ISDIR(inode->i_mode)) {
+		/* This sends off all dirty pages off to the server.
+		 * Note that this function must not sleep. */
+		nfs_invalidate_pages(inode);
+		invalidate_inode_pages(inode);
+	} else
+		nfs_invalidate_dircache(inode);
+	NFS_CACHEINV(inode);
+	NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+	goto out;
 }
 
 /*

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov