]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - fs/kernfs/dir.c
scsi: aacraid: Add a small delay after IOP reset
[linux.git] / fs / kernfs / dir.c
index 8ad7a17895feca0181b75762c50fbb24faacc8bf..89d1dc19340b09d4d9cd9db44ae3dfc9413bea98 100644 (file)
@@ -508,6 +508,10 @@ void kernfs_put(struct kernfs_node *kn)
        struct kernfs_node *parent;
        struct kernfs_root *root;
 
+       /*
+        * kernfs_node is freed with ->count 0, kernfs_find_and_get_node_by_ino
+        * depends on this to filter reused stale node
+        */
        if (!kn || !atomic_dec_and_test(&kn->count))
                return;
        root = kernfs_root(kn);
@@ -535,7 +539,7 @@ void kernfs_put(struct kernfs_node *kn)
        }
        kfree(kn->iattr);
        spin_lock(&kernfs_idr_lock);
-       idr_remove(&root->ino_idr, kn->ino);
+       idr_remove(&root->ino_idr, kn->id.ino);
        spin_unlock(&kernfs_idr_lock);
        kmem_cache_free(kernfs_node_cache, kn);
 
@@ -562,7 +566,7 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
        if (d_really_is_negative(dentry))
                goto out_bad_unlocked;
 
-       kn = dentry->d_fsdata;
+       kn = kernfs_dentry_node(dentry);
        mutex_lock(&kernfs_mutex);
 
        /* The kernfs node has been deactivated */
@@ -570,7 +574,7 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
                goto out_bad;
 
        /* The kernfs node has been moved? */
-       if (dentry->d_parent->d_fsdata != kn->parent)
+       if (kernfs_dentry_node(dentry->d_parent) != kn->parent)
                goto out_bad;
 
        /* The kernfs node has been renamed */
@@ -590,14 +594,8 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
        return 0;
 }
 
-static void kernfs_dop_release(struct dentry *dentry)
-{
-       kernfs_put(dentry->d_fsdata);
-}
-
 const struct dentry_operations kernfs_dops = {
        .d_revalidate   = kernfs_dop_revalidate,
-       .d_release      = kernfs_dop_release,
 };
 
 /**
@@ -613,8 +611,9 @@ const struct dentry_operations kernfs_dops = {
  */
 struct kernfs_node *kernfs_node_from_dentry(struct dentry *dentry)
 {
-       if (dentry->d_sb->s_op == &kernfs_sops)
-               return dentry->d_fsdata;
+       if (dentry->d_sb->s_op == &kernfs_sops &&
+           !d_really_is_negative(dentry))
+               return kernfs_dentry_node(dentry);
        return NULL;
 }
 
@@ -623,6 +622,8 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
                                             unsigned flags)
 {
        struct kernfs_node *kn;
+       u32 gen;
+       int cursor;
        int ret;
 
        name = kstrdup_const(name, GFP_KERNEL);
@@ -635,13 +636,23 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
 
        idr_preload(GFP_KERNEL);
        spin_lock(&kernfs_idr_lock);
-       ret = idr_alloc(&root->ino_idr, kn, 1, 0, GFP_ATOMIC);
+       cursor = idr_get_cursor(&root->ino_idr);
+       ret = idr_alloc_cyclic(&root->ino_idr, kn, 1, 0, GFP_ATOMIC);
+       if (ret >= 0 && ret < cursor)
+               root->next_generation++;
+       gen = root->next_generation;
        spin_unlock(&kernfs_idr_lock);
        idr_preload_end();
        if (ret < 0)
                goto err_out2;
-       kn->ino = ret;
+       kn->id.ino = ret;
+       kn->id.generation = gen;
 
+       /*
+        * set ino first. This barrier is paired with atomic_inc_not_zero in
+        * kernfs_find_and_get_node_by_ino
+        */
+       smp_mb__before_atomic();
        atomic_set(&kn->count, 1);
        atomic_set(&kn->active, KN_DEACTIVATED_BIAS);
        RB_CLEAR_NODE(&kn->rb);
@@ -673,6 +684,54 @@ struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
        return kn;
 }
 
+/*
+ * kernfs_find_and_get_node_by_ino - get kernfs_node from inode number
+ * @root: the kernfs root
+ * @ino: inode number
+ *
+ * RETURNS:
+ * NULL on failure. Return a kernfs node with reference counter incremented
+ */
+struct kernfs_node *kernfs_find_and_get_node_by_ino(struct kernfs_root *root,
+                                                   unsigned int ino)
+{
+       struct kernfs_node *kn;
+
+       rcu_read_lock();
+       kn = idr_find(&root->ino_idr, ino);
+       if (!kn)
+               goto out;
+
+       /*
+        * Since kernfs_node is freed in RCU, it's possible an old node for ino
+        * is freed, but reused before RCU grace period. But a freed node (see
+        * kernfs_put) or an incompletedly initialized node (see
+        * __kernfs_new_node) should have 'count' 0. We can use this fact to
+        * filter out such node.
+        */
+       if (!atomic_inc_not_zero(&kn->count)) {
+               kn = NULL;
+               goto out;
+       }
+
+       /*
+        * The node could be a new node or a reused node. If it's a new node,
+        * we are ok. If it's reused because of RCU (because of
+        * SLAB_TYPESAFE_BY_RCU), the __kernfs_new_node always sets its 'ino'
+        * before 'count'. So if 'count' is uptodate, 'ino' should be uptodate,
+        * hence we can use 'ino' to filter stale node.
+        */
+       if (kn->id.ino != ino)
+               goto out;
+       rcu_read_unlock();
+
+       return kn;
+out:
+       rcu_read_unlock();
+       kernfs_put(kn);
+       return NULL;
+}
+
 /**
  *     kernfs_add_one - add kernfs_node to parent without warning
  *     @kn: kernfs_node to be added
@@ -884,6 +943,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops,
 
        idr_init(&root->ino_idr);
        INIT_LIST_HEAD(&root->supers);
+       root->next_generation = 1;
 
        kn = __kernfs_new_node(root, "", S_IFDIR | S_IRUGO | S_IXUGO,
                               KERNFS_DIR);
@@ -991,7 +1051,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir,
                                        unsigned int flags)
 {
        struct dentry *ret;
-       struct kernfs_node *parent = dentry->d_parent->d_fsdata;
+       struct kernfs_node *parent = dir->i_private;
        struct kernfs_node *kn;
        struct inode *inode;
        const void *ns = NULL;
@@ -1008,8 +1068,6 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir,
                ret = NULL;
                goto out_unlock;
        }
-       kernfs_get(kn);
-       dentry->d_fsdata = kn;
 
        /* attach dentry and inode */
        inode = kernfs_get_inode(dir->i_sb, kn);
@@ -1046,7 +1104,7 @@ static int kernfs_iop_mkdir(struct inode *dir, struct dentry *dentry,
 
 static int kernfs_iop_rmdir(struct inode *dir, struct dentry *dentry)
 {
-       struct kernfs_node *kn  = dentry->d_fsdata;
+       struct kernfs_node *kn  = kernfs_dentry_node(dentry);
        struct kernfs_syscall_ops *scops = kernfs_root(kn)->syscall_ops;
        int ret;
 
@@ -1066,7 +1124,7 @@ static int kernfs_iop_rename(struct inode *old_dir, struct dentry *old_dentry,
                             struct inode *new_dir, struct dentry *new_dentry,
                             unsigned int flags)
 {
-       struct kernfs_node *kn  = old_dentry->d_fsdata;
+       struct kernfs_node *kn = kernfs_dentry_node(old_dentry);
        struct kernfs_node *new_parent = new_dir->i_private;
        struct kernfs_syscall_ops *scops = kernfs_root(kn)->syscall_ops;
        int ret;
@@ -1579,7 +1637,7 @@ static struct kernfs_node *kernfs_dir_next_pos(const void *ns,
 static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx)
 {
        struct dentry *dentry = file->f_path.dentry;
-       struct kernfs_node *parent = dentry->d_fsdata;
+       struct kernfs_node *parent = kernfs_dentry_node(dentry);
        struct kernfs_node *pos = file->private_data;
        const void *ns = NULL;
 
@@ -1596,7 +1654,7 @@ static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx)
                const char *name = pos->name;
                unsigned int type = dt_type(pos);
                int len = strlen(name);
-               ino_t ino = pos->ino;
+               ino_t ino = pos->id.ino;
 
                ctx->pos = pos->hash;
                file->private_data = pos;