Currently the inode's lifetime is controlled by it's main refcount, i_count. We overload the eviction of the inode (the final unlink) with the deletion of the in-memory object, which leads to some hilarity when we iput() in unfortunate places. In order to make this less of a footgun, we want to separate the notion of "is this inode in use by a user" and "is this inode object currently in use", since deleting an inode is a much heavier operation that deleting the object and cleaning up its memory. To that end, introduce ->i_obj_count to the inode. This will be used to control the lifetime of the inode object itself. We will continue to use the ->i_count refcount as normal to reduce the churn of adding a new refcount to inode. Subsequent patches will expand the usage of ->i_obj_count for internal uses, and then I will separate out the tear down operations of the inode, and then finally adjust the refount rules for ->i_count to be more consistent with all other refcounts. Signed-off-by: Josef Bacik --- fs/inode.c | 20 ++++++++++++++++++++ include/linux/fs.h | 12 ++++++++++++ 2 files changed, 32 insertions(+) diff --git a/fs/inode.c b/fs/inode.c index 13e80b434323..d426f54c05d9 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -235,6 +235,7 @@ int inode_init_always_gfp(struct super_block *sb, struct inode *inode, gfp_t gfp inode->i_flags = 0; inode->i_state = 0; atomic64_set(&inode->i_sequence, 0); + refcount_set(&inode->i_obj_count, 1); atomic_set(&inode->i_count, 1); inode->i_op = &empty_iops; inode->i_fop = &no_open_fops; @@ -831,6 +832,11 @@ static void evict(struct inode *inode) inode_wake_up_bit(inode, __I_NEW); BUG_ON(inode->i_state != (I_FREEING | I_CLEAR)); + /* + * refcount_dec_and_test must be used here to avoid the underflow + * warning. + */ + WARN_ON(!refcount_dec_and_test(&inode->i_obj_count)); destroy_inode(inode); } @@ -1930,6 +1936,20 @@ void iput(struct inode *inode) } EXPORT_SYMBOL(iput); +/** + * iobj_put - put a object reference on an inode + * @inode: inode to put + * + * Puts a object reference on an inode. + */ +void iobj_put(struct inode *inode) +{ + if (!inode) + return; + refcount_dec(&inode->i_obj_count); +} +EXPORT_SYMBOL(iobj_put); + #ifdef CONFIG_BLOCK /** * bmap - find a block number in a file diff --git a/include/linux/fs.h b/include/linux/fs.h index 56041d3387fe..84f5218755c3 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -871,6 +871,7 @@ struct inode { #if defined(CONFIG_IMA) || defined(CONFIG_FILE_LOCKING) atomic_t i_readcount; /* struct files open RO */ #endif + refcount_t i_obj_count; union { const struct file_operations *i_fop; /* former ->i_op->default_file_ops */ void (*free_inode)(struct inode *); @@ -2809,6 +2810,7 @@ extern int current_umask(void); extern void ihold(struct inode * inode); extern void iput(struct inode *); +extern void iobj_put(struct inode *inode); int inode_update_timestamps(struct inode *inode, int flags); int generic_update_time(struct inode *, int); @@ -3364,6 +3366,16 @@ static inline bool is_zero_ino(ino_t ino) return (u32)ino == 0; } +static inline void iobj_get(struct inode *inode) +{ + refcount_inc(&inode->i_obj_count); +} + +static inline unsigned int iobj_count_read(const struct inode *inode) +{ + return refcount_read(&inode->i_obj_count); +} + /* * inode->i_lock must be held */ -- 2.49.0