kernfs: implement i_generation
authorShaohua Li <shli@fb.com>
Wed, 12 Jul 2017 18:49:47 +0000 (11:49 -0700)
committerJens Axboe <axboe@kernel.dk>
Sat, 29 Jul 2017 15:00:03 +0000 (09:00 -0600)
Set i_generation for kernfs inode. This is required to implement
exportfs operations. The generation is 32-bit, so it's possible the
generation wraps up and we find stale files. To reduce the posssibility,
we don't reuse inode numer immediately. When the inode number allocation
wraps, we increase generation number. In this way generation/inode
number consist of a 64-bit number which is unlikely duplicated. This
does make the idr tree more sparse and waste some memory. Since idr
manages 32-bit keys, idr uses a 6-level radix tree, each level covers 6
bits of the key. In a 100k inode kernfs, the worst case will have around
300k radix tree node. Each node is 576bytes, so the tree will use about
~150M memory. Sounds not too bad, if this really is a problem, we should
find better data structure.

Acked-by: Tejun Heo <tj@kernel.org>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Shaohua Li <shli@fb.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
fs/kernfs/dir.c
fs/kernfs/inode.c
include/linux/kernfs.h

index 8ad7a17895feca0181b75762c50fbb24faacc8bf..33f711f6b86ec4e174ce8f74b13eb1d2701d0095 100644 (file)
@@ -623,6 +623,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,12 +637,17 @@ 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->generation = gen;
 
        atomic_set(&kn->count, 1);
        atomic_set(&kn->active, KN_DEACTIVATED_BIAS);
@@ -884,6 +891,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);
index fb4b4a79a0d6b4bb329f43ca4ace01ed35cbe74c..79cdae4758fb357bd933e7043b8480a0c5aa0740 100644 (file)
@@ -220,6 +220,7 @@ static void kernfs_init_inode(struct kernfs_node *kn, struct inode *inode)
        inode->i_private = kn;
        inode->i_mapping->a_ops = &kernfs_aops;
        inode->i_op = &kernfs_iops;
+       inode->i_generation = kn->generation;
 
        set_default_inode_attr(inode, kn->mode);
        kernfs_refresh_inode(kn, inode);
index 5f5d602eb43397599db7f6c987162346bf653537..8c00d28f468abf2aa31965fedab7032567162c8e 100644 (file)
@@ -135,6 +135,7 @@ struct kernfs_node {
        umode_t                 mode;
        unsigned int            ino;
        struct kernfs_iattrs    *iattr;
+       u32                     generation;
 };
 
 /*
@@ -164,6 +165,7 @@ struct kernfs_root {
 
        /* private fields, do not use outside kernfs proper */
        struct idr              ino_idr;
+       u32                     next_generation;
        struct kernfs_syscall_ops *syscall_ops;
 
        /* list of kernfs_super_info of this root, protected by kernfs_mutex */