]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/nfs/dir.c
PTY: add kernel parameter to overwrite legacy pty count
[linux-2.6-omap-h63xx.git] / fs / nfs / dir.c
index f03a770bacb06da6d5404e927e8f714f9d0ba6bf..e4a04d16b8b06fd4faf22b7ba238f404403a8c7f 100644 (file)
 #include <linux/pagevec.h>
 #include <linux/namei.h>
 #include <linux/mount.h>
+#include <linux/sched.h>
 
 #include "nfs4_fs.h"
 #include "delegation.h"
 #include "iostat.h"
 
-#define NFS_PARANOIA 1
 /* #define NFS_DEBUG_VERBOSE 1 */
 
 static int nfs_opendir(struct inode *, struct file *);
@@ -154,6 +154,8 @@ typedef struct {
        decode_dirent_t decode;
        int             plus;
        int             error;
+       unsigned long   timestamp;
+       int             timestamp_valid;
 } nfs_readdir_descriptor_t;
 
 /* Now we cache directories properly, by stuffing the dirent
@@ -195,6 +197,8 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page)
                }
                goto error;
        }
+       desc->timestamp = timestamp;
+       desc->timestamp_valid = 1;
        SetPageUptodate(page);
        spin_lock(&inode->i_lock);
        NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATIME;
@@ -225,6 +229,10 @@ int dir_decode(nfs_readdir_descriptor_t *desc)
        if (IS_ERR(p))
                return PTR_ERR(p);
        desc->ptr = p;
+       if (desc->timestamp_valid)
+               desc->entry->fattr->time_start = desc->timestamp;
+       else
+               desc->entry->fattr->valid &= ~NFS_ATTR_FATTR;
        return 0;
 }
 
@@ -316,14 +324,16 @@ int find_dirent_page(nfs_readdir_descriptor_t *desc)
                        __FUNCTION__, desc->page_index,
                        (long long) *desc->dir_cookie);
 
+       /* If we find the page in the page_cache, we cannot be sure
+        * how fresh the data is, so we will ignore readdir_plus attributes.
+        */
+       desc->timestamp_valid = 0;
        page = read_cache_page(inode->i_mapping, desc->page_index,
                               (filler_t *)nfs_readdir_filler, desc);
        if (IS_ERR(page)) {
                status = PTR_ERR(page);
                goto out;
        }
-       if (!PageUptodate(page))
-               goto read_error;
 
        /* NOTE: Someone else may have changed the READDIRPLUS flag */
        desc->page = page;
@@ -337,9 +347,6 @@ int find_dirent_page(nfs_readdir_descriptor_t *desc)
  out:
        dfprintk(DIRCACHE, "NFS: %s: returns %d\n", __FUNCTION__, status);
        return status;
- read_error:
-       page_cache_release(page);
-       return -EIO;
 }
 
 /*
@@ -468,6 +475,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
        struct rpc_cred *cred = nfs_file_cred(file);
        struct page     *page = NULL;
        int             status;
+       unsigned long   timestamp;
 
        dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n",
                        (unsigned long long)*desc->dir_cookie);
@@ -477,6 +485,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
                status = -ENOMEM;
                goto out;
        }
+       timestamp = jiffies;
        desc->error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, *desc->dir_cookie,
                                                page,
                                                NFS_SERVER(inode)->dtsize,
@@ -487,6 +496,8 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
        desc->page = page;
        desc->ptr = kmap(page);         /* matching kunmap in nfs_do_filldir */
        if (desc->error >= 0) {
+               desc->timestamp = timestamp;
+               desc->timestamp_valid = 1;
                if ((status = dir_decode(desc)) == 0)
                        desc->entry->prev_cookie = *desc->dir_cookie;
        } else
@@ -597,7 +608,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
        return res;
 }
 
-loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin)
+static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin)
 {
        mutex_lock(&filp->f_path.dentry->d_inode->i_mutex);
        switch (origin) {
@@ -623,7 +634,7 @@ out:
  * All directory operations under NFS are synchronous, so fsync()
  * is a dummy operation.
  */
-int nfs_fsync_dir(struct file *filp, struct dentry *dentry, int datasync)
+static int nfs_fsync_dir(struct file *filp, struct dentry *dentry, int datasync)
 {
        dfprintk(VFS, "NFS: fsync_dir(%s/%s) datasync %d\n",
                        dentry->d_parent->d_name.name, dentry->d_name.name,
@@ -637,19 +648,27 @@ int nfs_fsync_dir(struct file *filp, struct dentry *dentry, int datasync)
  * In the case it has, we assume that the dentries are untrustworthy
  * and may need to be looked up again.
  */
-static inline int nfs_check_verifier(struct inode *dir, struct dentry *dentry)
+static int nfs_check_verifier(struct inode *dir, struct dentry *dentry)
 {
+       unsigned long verf;
+
        if (IS_ROOT(dentry))
                return 1;
-       if ((NFS_I(dir)->cache_validity & NFS_INO_INVALID_ATTR) != 0
-                       || nfs_attribute_timeout(dir))
+       verf = dentry->d_time;
+       if (nfs_caches_unstable(dir)
+                       || verf != NFS_I(dir)->cache_change_attribute)
                return 0;
-       return nfs_verify_change_attribute(dir, (unsigned long)dentry->d_fsdata);
+       return 1;
 }
 
 static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf)
 {
-       dentry->d_fsdata = (void *)verf;
+       dentry->d_time = verf;
+}
+
+static void nfs_refresh_verifier(struct dentry * dentry, unsigned long verf)
+{
+       nfs_set_verifier(dentry, verf);
 }
 
 /*
@@ -748,6 +767,10 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
        nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE);
        inode = dentry->d_inode;
 
+       /* Revalidate parent directory attribute cache */
+       if (nfs_revalidate_inode(NFS_SERVER(dir), dir) < 0)
+               goto out_zap_parent;
+
        if (!inode) {
                if (nfs_neg_need_reval(dir, dentry, nd))
                        goto out_bad;
@@ -761,10 +784,6 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
                goto out_bad;
        }
 
-       /* Revalidate parent directory attribute cache */
-       if (nfs_revalidate_inode(NFS_SERVER(dir), dir) < 0)
-               goto out_zap_parent;
-
        /* Force a full look up iff the parent directory has changed */
        if (nfs_check_verifier(dir, dentry)) {
                if (nfs_lookup_verify_inode(inode, nd))
@@ -785,7 +804,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
                goto out_bad;
 
        nfs_renew_times(dentry);
-       nfs_set_verifier(dentry, verifier);
+       nfs_refresh_verifier(dentry, verifier);
  out_valid:
        unlock_kernel();
        dput(parent);
@@ -843,10 +862,14 @@ static int nfs_dentry_delete(struct dentry *dentry)
 static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
 {
        nfs_inode_return_delegation(inode);
+       if (S_ISDIR(inode->i_mode))
+               /* drop any readdir cache as it could easily be old */
+               NFS_I(inode)->cache_validity |= NFS_INO_INVALID_DATA;
+
        if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
                lock_kernel();
                drop_nlink(inode);
-               nfs_complete_unlink(dentry);
+               nfs_complete_unlink(dentry, inode);
                unlock_kernel();
        }
        /* When creating a negative dentry, we want to renew d_time */
@@ -874,14 +897,13 @@ int nfs_is_exclusive_create(struct inode *dir, struct nameidata *nd)
        return (nd->intent.open.flags & O_EXCL) != 0;
 }
 
-static inline int nfs_reval_fsid(struct vfsmount *mnt, struct inode *dir,
-                                struct nfs_fh *fh, struct nfs_fattr *fattr)
+static inline int nfs_reval_fsid(struct inode *dir, const struct nfs_fattr *fattr)
 {
        struct nfs_server *server = NFS_SERVER(dir);
 
        if (!nfs_fsid_equal(&server->fsid, &fattr->fsid))
-               /* Revalidate fsid on root dir */
-               return __nfs_revalidate_inode(server, mnt->mnt_root->d_inode);
+               /* Revalidate fsid using the parent directory */
+               return __nfs_revalidate_inode(server, dir);
        return 0;
 }
 
@@ -923,7 +945,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
                res = ERR_PTR(error);
                goto out_unlock;
        }
-       error = nfs_reval_fsid(nd->mnt, dir, &fhandle, &fattr);
+       error = nfs_reval_fsid(dir, &fattr);
        if (error < 0) {
                res = ERR_PTR(error);
                goto out_unlock;
@@ -1085,7 +1107,7 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
        verifier = nfs_save_change_attribute(dir);
        ret = nfs4_open_revalidate(dir, dentry, openflags, nd);
        if (!ret)
-               nfs_set_verifier(dentry, verifier);
+               nfs_refresh_verifier(dentry, verifier);
        unlock_kernel();
 out:
        dput(parent);
@@ -1123,10 +1145,25 @@ static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc)
        }
        name.hash = full_name_hash(name.name, name.len);
        dentry = d_lookup(parent, &name);
-       if (dentry != NULL)
-               return dentry;
+       if (dentry != NULL) {
+               /* Is this a positive dentry that matches the readdir info? */
+               if (dentry->d_inode != NULL &&
+                               (NFS_FILEID(dentry->d_inode) == entry->ino ||
+                               d_mountpoint(dentry))) {
+                       if (!desc->plus || entry->fh->size == 0)
+                               return dentry;
+                       if (nfs_compare_fh(NFS_FH(dentry->d_inode),
+                                               entry->fh) == 0)
+                               goto out_renew;
+               }
+               /* No, so d_drop to allow one to be created */
+               d_drop(dentry);
+               dput(dentry);
+       }
        if (!desc->plus || !(entry->fattr->valid & NFS_ATTR_FATTR))
                return NULL;
+       if (name.len > NFS_SERVER(dir)->namelen)
+               return NULL;
        /* Note: caller is already holding the dir->i_mutex! */
        dentry = d_alloc(parent, &name);
        if (dentry == NULL)
@@ -1149,6 +1186,10 @@ static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc)
        nfs_renew_times(dentry);
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
        return dentry;
+out_renew:
+       nfs_renew_times(dentry);
+       nfs_refresh_verifier(dentry, nfs_save_change_attribute(dir));
+       return dentry;
 }
 
 /*
@@ -1204,7 +1245,7 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,
        attr.ia_mode = mode;
        attr.ia_valid = ATTR_MODE;
 
-       if (nd && (nd->flags & LOOKUP_CREATE))
+       if ((nd->flags & LOOKUP_CREATE) != 0)
                open_flags = nd->intent.open.flags;
 
        lock_kernel();
@@ -1322,11 +1363,6 @@ static int nfs_sillyrename(struct inode *dir, struct dentry *dentry)
                atomic_read(&dentry->d_count));
        nfs_inc_stats(dir, NFSIOS_SILLYRENAME);
 
-#ifdef NFS_PARANOIA
-if (!dentry->d_inode)
-printk("NFS: silly-renaming %s/%s, negative dentry??\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
        /*
         * We don't allow a dentry to be silly-renamed twice.
         */
@@ -1377,7 +1413,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
                nfs_renew_times(dentry);
                nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
                d_move(dentry, sdentry);
-               error = nfs_async_unlink(dentry);
+               error = nfs_async_unlink(dir, dentry);
                /* If we return 0 we don't unlink */
        }
        dput(sdentry);
@@ -1443,6 +1479,8 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
        if (atomic_read(&dentry->d_count) > 1) {
                spin_unlock(&dentry->d_lock);
                spin_unlock(&dcache_lock);
+               /* Start asynchronous writeout of the inode */
+               write_inode_now(dentry->d_inode, 0);
                error = nfs_sillyrename(dir, dentry);
                unlock_kernel();
                return error;
@@ -1498,7 +1536,7 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
 
        lock_kernel();
 
-       page = alloc_page(GFP_KERNEL);
+       page = alloc_page(GFP_HIGHUSER);
        if (!page) {
                unlock_kernel();
                return -ENOMEM;
@@ -1641,16 +1679,9 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                        new_inode = NULL;
                        /* instantiate the replacement target */
                        d_instantiate(new_dentry, NULL);
-               } else if (atomic_read(&new_dentry->d_count) > 1) {
-               /* dentry still busy? */
-#ifdef NFS_PARANOIA
-                       printk("nfs_rename: target %s/%s busy, d_count=%d\n",
-                              new_dentry->d_parent->d_name.name,
-                              new_dentry->d_name.name,
-                              atomic_read(&new_dentry->d_count));
-#endif
+               } else if (atomic_read(&new_dentry->d_count) > 1)
+                       /* dentry still busy? */
                        goto out;
-               }
        } else
                drop_nlink(new_inode);
 
@@ -1659,7 +1690,8 @@ go_ahead:
         * ... prune child dentries and writebacks if needed.
         */
        if (atomic_read(&old_dentry->d_count) > 1) {
-               nfs_wb_all(old_inode);
+               if (S_ISREG(old_inode->i_mode))
+                       nfs_wb_all(old_inode);
                shrink_dcache_parent(old_dentry);
        }
        nfs_inode_return_delegation(old_inode);
@@ -1684,7 +1716,7 @@ out:
        if (!error) {
                d_move(old_dentry, new_dentry);
                nfs_renew_times(new_dentry);
-               nfs_set_verifier(new_dentry, nfs_save_change_attribute(new_dir));
+               nfs_refresh_verifier(new_dentry, nfs_save_change_attribute(new_dir));
        }
 
        /* new dentry created? */
@@ -1713,8 +1745,8 @@ int nfs_access_cache_shrinker(int nr_to_scan, gfp_t gfp_mask)
        struct nfs_inode *nfsi;
        struct nfs_access_entry *cache;
 
-       spin_lock(&nfs_access_lru_lock);
 restart:
+       spin_lock(&nfs_access_lru_lock);
        list_for_each_entry(nfsi, &nfs_access_lru_list, access_cache_inode_lru) {
                struct inode *inode;
 
@@ -1739,6 +1771,7 @@ remove_lru_entry:
                        clear_bit(NFS_INO_ACL_LRU_SET, &nfsi->flags);
                }
                spin_unlock(&inode->i_lock);
+               spin_unlock(&nfs_access_lru_lock);
                iput(inode);
                goto restart;
        }