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
- Lines: 349
- Date:
Mon Oct 18 11:26:37 1999
- Orig file:
v2.3.22/linux/fs/nfs/inode.c
- Orig date:
Wed Jun 16 19:26:27 1999
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)