ncpfs: get rid of d_validate() nonsense
authorAl Viro <viro@zeniv.linux.org.uk>
Thu, 25 Dec 2014 02:41:47 +0000 (21:41 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Mon, 26 Jan 2015 04:16:26 +0000 (23:16 -0500)
What we want is to have non-counting references to children in
pagecache of parent directory, and avoid picking them after a child
has been freed.  Fine, so let's just have ->d_prune() clear
parent's inode "has directory contents in page cache" flag.
That way we don't need ->d_fsdata for storing offsets, so we can
use it as a quick and dirty "is it referenced from page cache"
flag.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/ncpfs/dir.c
fs/ncpfs/ncp_fs_i.h
fs/ncpfs/ncplib_kernel.h

index 008960101520b49957a49c6de42344475280e964..e7ca827d7694c130e0be25b85fa6e6aed7217ab5 100644 (file)
@@ -77,6 +77,7 @@ static int ncp_hash_dentry(const struct dentry *, struct qstr *);
 static int ncp_compare_dentry(const struct dentry *, const struct dentry *,
                unsigned int, const char *, const struct qstr *);
 static int ncp_delete_dentry(const struct dentry *);
+static void ncp_d_prune(struct dentry *dentry);
 
 const struct dentry_operations ncp_dentry_operations =
 {
@@ -84,6 +85,7 @@ const struct dentry_operations ncp_dentry_operations =
        .d_hash         = ncp_hash_dentry,
        .d_compare      = ncp_compare_dentry,
        .d_delete       = ncp_delete_dentry,
+       .d_prune        = ncp_d_prune,
 };
 
 #define ncp_namespace(i)       (NCP_SERVER(i)->name_space[NCP_FINFO(i)->volNumber])
@@ -384,42 +386,6 @@ finished:
        return val;
 }
 
-static struct dentry *
-ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
-{
-       struct dentry *dent = dentry;
-
-       if (d_validate(dent, parent)) {
-               if (dent->d_name.len <= NCP_MAXPATHLEN &&
-                   (unsigned long)dent->d_fsdata == fpos) {
-                       if (!dent->d_inode) {
-                               dput(dent);
-                               dent = NULL;
-                       }
-                       return dent;
-               }
-               dput(dent);
-       }
-
-       /* If a pointer is invalid, we search the dentry. */
-       spin_lock(&parent->d_lock);
-       list_for_each_entry(dent, &parent->d_subdirs, d_child) {
-               if ((unsigned long)dent->d_fsdata == fpos) {
-                       if (dent->d_inode)
-                               dget(dent);
-                       else
-                               dent = NULL;
-                       spin_unlock(&parent->d_lock);
-                       goto out;
-               }
-       }
-       spin_unlock(&parent->d_lock);
-       return NULL;
-
-out:
-       return dent;
-}
-
 static time_t ncp_obtain_mtime(struct dentry *dentry)
 {
        struct inode *inode = dentry->d_inode;
@@ -435,6 +401,20 @@ static time_t ncp_obtain_mtime(struct dentry *dentry)
        return ncp_date_dos2unix(i.modifyTime, i.modifyDate);
 }
 
+static inline void
+ncp_invalidate_dircache_entries(struct dentry *parent)
+{
+       struct ncp_server *server = NCP_SERVER(parent->d_inode);
+       struct dentry *dentry;
+
+       spin_lock(&parent->d_lock);
+       list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
+               dentry->d_fsdata = NULL;
+               ncp_age_dentry(server, dentry);
+       }
+       spin_unlock(&parent->d_lock);
+}
+
 static int ncp_readdir(struct file *file, struct dir_context *ctx)
 {
        struct dentry *dentry = file->f_path.dentry;
@@ -500,10 +480,21 @@ static int ncp_readdir(struct file *file, struct dir_context *ctx)
                        struct dentry *dent;
                        bool over;
 
-                       dent = ncp_dget_fpos(ctl.cache->dentry[ctl.idx],
-                                               dentry, ctx->pos);
-                       if (!dent)
+                       spin_lock(&dentry->d_lock);
+                       if (!(NCP_FINFO(inode)->flags & NCPI_DIR_CACHE)) { 
+                               spin_unlock(&dentry->d_lock);
+                               goto invalid_cache;
+                       }
+                       dent = ctl.cache->dentry[ctl.idx];
+                       if (unlikely(!lockref_get_not_dead(&dent->d_lockref))) {
+                               spin_unlock(&dentry->d_lock);
+                               goto invalid_cache;
+                       }
+                       spin_unlock(&dentry->d_lock);
+                       if (!dent->d_inode) {
+                               dput(dent);
                                goto invalid_cache;
+                       }
                        over = !dir_emit(ctx, dent->d_name.name,
                                        dent->d_name.len,
                                        dent->d_inode->i_ino, DT_UNKNOWN);
@@ -548,6 +539,9 @@ init_cache:
        ctl.filled = 0;
        ctl.valid  = 1;
 read_really:
+       spin_lock(&dentry->d_lock);
+       NCP_FINFO(inode)->flags |= NCPI_DIR_CACHE;
+       spin_unlock(&dentry->d_lock);
        if (ncp_is_server_root(inode)) {
                ncp_read_volume_list(file, ctx, &ctl);
        } else {
@@ -573,6 +567,13 @@ out:
        return result;
 }
 
+static void ncp_d_prune(struct dentry *dentry)
+{
+       if (!dentry->d_fsdata)  /* not referenced from page cache */
+               return;
+       NCP_FINFO(dentry->d_parent->d_inode)->flags &= ~NCPI_DIR_CACHE;
+}
+
 static int
 ncp_fill_cache(struct file *file, struct dir_context *ctx,
                struct ncp_cache_control *ctrl, struct ncp_entry_info *entry,
@@ -630,6 +631,10 @@ ncp_fill_cache(struct file *file, struct dir_context *ctx,
                        d_instantiate(newdent, inode);
                        if (!hashed)
                                d_rehash(newdent);
+               } else {
+                       spin_lock(&dentry->d_lock);
+                       NCP_FINFO(inode)->flags &= ~NCPI_DIR_CACHE;
+                       spin_unlock(&dentry->d_lock);
                }
        } else {
                struct inode *inode = newdent->d_inode;
@@ -639,12 +644,6 @@ ncp_fill_cache(struct file *file, struct dir_context *ctx,
                mutex_unlock(&inode->i_mutex);
        }
 
-       if (newdent->d_inode) {
-               ino = newdent->d_inode->i_ino;
-               newdent->d_fsdata = (void *) ctl.fpos;
-               ncp_new_dentry(newdent);
-       }
-
        if (ctl.idx >= NCP_DIRCACHE_SIZE) {
                if (ctl.page) {
                        kunmap(ctl.page);
@@ -660,8 +659,13 @@ ncp_fill_cache(struct file *file, struct dir_context *ctx,
                        ctl.cache = kmap(ctl.page);
        }
        if (ctl.cache) {
-               ctl.cache->dentry[ctl.idx] = newdent;
-               valid = 1;
+               if (newdent->d_inode) {
+                       newdent->d_fsdata = newdent;
+                       ctl.cache->dentry[ctl.idx] = newdent;
+                       ino = newdent->d_inode->i_ino;
+                       ncp_new_dentry(newdent);
+               }
+               valid = 1;
        }
        dput(newdent);
 end_advance:
index 4b0bec477846730ed8263c3ad097b3572e839e7d..c4794504f8437fc37520b5c4f46127eb6dd7bb04 100644 (file)
@@ -22,6 +22,7 @@ struct ncp_inode_info {
        int     access;
        int     flags;
 #define NCPI_KLUDGE_SYMLINK    0x0001
+#define NCPI_DIR_CACHE         0x0002
        __u8    file_handle[6];
        struct inode vfs_inode;
 };
index b785f74bfe3c8b607181bdf0bbe27c02b533e8b1..250e443a07f32ec8188aa4f98db5bbeadcf59f19 100644 (file)
@@ -184,36 +184,6 @@ ncp_new_dentry(struct dentry* dentry)
        dentry->d_time = jiffies;
 }
 
-static inline void
-ncp_renew_dentries(struct dentry *parent)
-{
-       struct ncp_server *server = NCP_SERVER(parent->d_inode);
-       struct dentry *dentry;
-
-       spin_lock(&parent->d_lock);
-       list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
-               if (dentry->d_fsdata == NULL)
-                       ncp_age_dentry(server, dentry);
-               else
-                       ncp_new_dentry(dentry);
-       }
-       spin_unlock(&parent->d_lock);
-}
-
-static inline void
-ncp_invalidate_dircache_entries(struct dentry *parent)
-{
-       struct ncp_server *server = NCP_SERVER(parent->d_inode);
-       struct dentry *dentry;
-
-       spin_lock(&parent->d_lock);
-       list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
-               dentry->d_fsdata = NULL;
-               ncp_age_dentry(server, dentry);
-       }
-       spin_unlock(&parent->d_lock);
-}
-
 struct ncp_cache_head {
        time_t          mtime;
        unsigned long   time;   /* cache age */