Sharing inode with different whiteout files for saving inode and speeding up delete operation. Signed-off-by: Chengguang Xu <cgxu519@xxxxxxxxxxxx> --- fs/overlayfs/dir.c | 50 ++++++++++++++++++++++++++++++++-------- fs/overlayfs/overlayfs.h | 11 +++++++-- fs/overlayfs/ovl_entry.h | 4 ++++ fs/overlayfs/readdir.c | 3 ++- fs/overlayfs/super.c | 10 ++++++++ fs/overlayfs/util.c | 3 ++- 6 files changed, 68 insertions(+), 13 deletions(-) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 279009dee366..e48ba7de1bcb 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -62,35 +62,66 @@ struct dentry *ovl_lookup_temp(struct dentry *workdir) } /* caller holds i_mutex on workdir */ -static struct dentry *ovl_whiteout(struct dentry *workdir) +static struct dentry *ovl_whiteout(struct ovl_fs *ofs, struct dentry *workdir) { int err; + bool same = true; + bool again = true; struct dentry *whiteout; struct inode *wdir = workdir->d_inode; + if (ofs->workdir != workdir) + same = false; +retry: whiteout = ovl_lookup_temp(workdir); if (IS_ERR(whiteout)) return whiteout; + if (same && ofs->whiteout) { + if (ovl_whiteout_linkable(ofs, workdir, ofs->whiteout)) { + err = ovl_do_link(ofs->whiteout, wdir, whiteout); + if (!err) + return whiteout; + + if (!again) + goto out; + } + + dput(ofs->whiteout); + ofs->whiteout = NULL; + } + err = ovl_do_whiteout(wdir, whiteout); - if (err) { - dput(whiteout); - whiteout = ERR_PTR(err); + if (!err) { + if (!same || ofs->whiteout_link_max == 1) + return whiteout; + + if (!again) { + WARN_ON_ONCE(1); + return whiteout; + } + + dget(whiteout); + ofs->whiteout = whiteout; + again = false; + goto retry; } - return whiteout; +out: + dput(whiteout); + return ERR_PTR(err); } /* Caller must hold i_mutex on both workdir and dir */ -int ovl_cleanup_and_whiteout(struct dentry *workdir, struct inode *dir, - struct dentry *dentry) +int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, struct dentry *workdir, + struct inode *dir, struct dentry *dentry) { struct inode *wdir = workdir->d_inode; struct dentry *whiteout; int err; int flags = 0; - whiteout = ovl_whiteout(workdir); + whiteout = ovl_whiteout(ofs, workdir); err = PTR_ERR(whiteout); if (IS_ERR(whiteout)) return err; @@ -715,6 +746,7 @@ static bool ovl_matches_upper(struct dentry *dentry, struct dentry *upper) static int ovl_remove_and_whiteout(struct dentry *dentry, struct list_head *list) { + struct ovl_fs *ofs = dentry->d_sb->s_fs_info; struct dentry *workdir = ovl_workdir(dentry); struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); struct dentry *upper; @@ -748,7 +780,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, goto out_dput_upper; } - err = ovl_cleanup_and_whiteout(workdir, d_inode(upperdir), upper); + err = ovl_cleanup_and_whiteout(ofs, workdir, d_inode(upperdir), upper); if (err) goto out_d_drop; diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index e6f3670146ed..cc7bcc3fb916 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -225,6 +225,13 @@ static inline bool ovl_open_flags_need_copy_up(int flags) return ((OPEN_FMODE(flags) & FMODE_WRITE) || (flags & O_TRUNC)); } +static inline bool ovl_whiteout_linkable(struct ovl_fs *ofs, + struct dentry *workdir, + struct dentry *dentry) +{ + return dentry->d_inode->i_nlink < ofs->whiteout_link_max; +} + /* util.c */ int ovl_want_write(struct dentry *dentry); void ovl_drop_write(struct dentry *dentry); @@ -455,8 +462,8 @@ static inline void ovl_copyflags(struct inode *from, struct inode *to) /* dir.c */ extern const struct inode_operations ovl_dir_inode_operations; -int ovl_cleanup_and_whiteout(struct dentry *workdir, struct inode *dir, - struct dentry *dentry); +int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, struct dentry *workdir, + struct inode *dir, struct dentry *dentry); struct ovl_cattr { dev_t rdev; umode_t mode; diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 5762d802fe01..de5f230b6e6b 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -77,6 +77,10 @@ struct ovl_fs { int xino_mode; /* For allocation of non-persistent inode numbers */ atomic_long_t last_ino; + /* Whiteout dentry cache */ + struct dentry *whiteout; + /* Whiteout max link count */ + unsigned int whiteout_link_max; }; static inline struct ovl_fs *OVL_FS(struct super_block *sb) diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index e452ff7d583d..1e921115e6aa 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -1146,7 +1146,8 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs) * Whiteout orphan index to block future open by * handle after overlay nlink dropped to zero. */ - err = ovl_cleanup_and_whiteout(indexdir, dir, index); + err = ovl_cleanup_and_whiteout(ofs, indexdir, dir, + index); } else { /* Cleanup orphan index entries */ err = ovl_cleanup(dir, index); diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 732ad5495c92..340c4c05c410 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -26,6 +26,10 @@ struct ovl_dir_cache; #define OVL_MAX_STACK 500 +static unsigned int ovl_whiteout_link_max_def = 60000; +module_param_named(whiteout_link_max, ovl_whiteout_link_max_def, uint, 0644); +MODULE_PARM_DESC(whiteout_link_max, "Maximum count of whiteout file link"); + static bool ovl_redirect_dir_def = IS_ENABLED(CONFIG_OVERLAY_FS_REDIRECT_DIR); module_param_named(redirect_dir, ovl_redirect_dir_def, bool, 0644); MODULE_PARM_DESC(redirect_dir, @@ -219,6 +223,7 @@ static void ovl_free_fs(struct ovl_fs *ofs) iput(ofs->upperdir_trap); dput(ofs->indexdir); dput(ofs->workdir); + dput(ofs->whiteout); if (ofs->workdir_locked) ovl_inuse_unlock(ofs->workbasedir); dput(ofs->workbasedir); @@ -1762,6 +1767,11 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) if (!ofs->workdir) sb->s_flags |= SB_RDONLY; + else + ofs->whiteout_link_max = min_not_zero( + ofs->workdir->d_sb->s_max_links, + ovl_whiteout_link_max_def ? + ovl_whiteout_link_max_def : 1); sb->s_stack_depth = ofs->upper_mnt->mnt_sb->s_stack_depth; sb->s_time_gran = ofs->upper_mnt->mnt_sb->s_time_gran; diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 36b60788ee47..18df65ee81a8 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -669,6 +669,7 @@ bool ovl_need_index(struct dentry *dentry) /* Caller must hold OVL_I(inode)->lock */ static void ovl_cleanup_index(struct dentry *dentry) { + struct ovl_fs *ofs = dentry->d_sb->s_fs_info; struct dentry *indexdir = ovl_indexdir(dentry->d_sb); struct inode *dir = indexdir->d_inode; struct dentry *lowerdentry = ovl_dentry_lower(dentry); @@ -707,7 +708,7 @@ static void ovl_cleanup_index(struct dentry *dentry) index = NULL; } else if (ovl_index_all(dentry->d_sb)) { /* Whiteout orphan index to block future open by handle */ - err = ovl_cleanup_and_whiteout(indexdir, dir, index); + err = ovl_cleanup_and_whiteout(ofs, indexdir, dir, index); } else { /* Cleanup orphan index entries */ err = ovl_cleanup(dir, index); -- 2.20.1