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
- Lines: 306
- Date:
Wed Oct 15 21:08:46 1997
- Orig file:
v2.1.58/linux/fs/nfs/inode.c
- Orig date:
Wed Oct 15 16:04:23 1997
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