Skip to content

Commit

Permalink
ovl: whiteout inode sharing
Browse files Browse the repository at this point in the history
Share inode with different whiteout files for saving inode and speeding up
delete operation.

If EMLINK is encountered when linking a shared whiteout, create a new one.
In case of any other error, disable sharing for this super block.

Note: ofs->whiteout is protected by inode lock on workdir.

Signed-off-by: Chengguang Xu <cgxu519@mykernel.net>
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
  • Loading branch information
Chengguang Xu authored and Miklos Szeredi committed May 13, 2020
1 parent 654255f commit c21c839
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 15 deletions.
49 changes: 37 additions & 12 deletions fs/overlayfs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,35 +62,59 @@ 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)
{
int err;
struct dentry *whiteout;
struct dentry *workdir = ofs->workdir;
struct inode *wdir = workdir->d_inode;

whiteout = ovl_lookup_temp(workdir);
if (IS_ERR(whiteout))
return whiteout;
if (!ofs->whiteout) {
whiteout = ovl_lookup_temp(workdir);
if (IS_ERR(whiteout))
goto out;

err = ovl_do_whiteout(wdir, whiteout);
if (err) {
dput(whiteout);
whiteout = ERR_PTR(err);
err = ovl_do_whiteout(wdir, whiteout);
if (err) {
dput(whiteout);
whiteout = ERR_PTR(err);
goto out;
}
ofs->whiteout = whiteout;
}

if (ofs->share_whiteout) {
whiteout = ovl_lookup_temp(workdir);
if (IS_ERR(whiteout))
goto out;

err = ovl_do_link(ofs->whiteout, wdir, whiteout);
if (!err)
goto out;

if (err != -EMLINK) {
pr_warn("Failed to link whiteout - disabling whiteout inode sharing(nlink=%u, err=%i)\n",
ofs->whiteout->d_inode->i_nlink, err);
ofs->share_whiteout = false;
}
dput(whiteout);
}
whiteout = ofs->whiteout;
ofs->whiteout = NULL;
out:
return whiteout;
}

/* Caller must hold i_mutex on both workdir and dir */
int ovl_cleanup_and_whiteout(struct dentry *workdir, struct inode *dir,
int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, struct inode *dir,
struct dentry *dentry)
{
struct inode *wdir = workdir->d_inode;
struct inode *wdir = ofs->workdir->d_inode;
struct dentry *whiteout;
int err;
int flags = 0;

whiteout = ovl_whiteout(workdir);
whiteout = ovl_whiteout(ofs);
err = PTR_ERR(whiteout);
if (IS_ERR(whiteout))
return err;
Expand Down Expand Up @@ -715,6 +739,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 = OVL_FS(dentry->d_sb);
struct dentry *workdir = ovl_workdir(dentry);
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
struct dentry *upper;
Expand Down Expand Up @@ -748,7 +773,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, d_inode(upperdir), upper);
if (err)
goto out_d_drop;

Expand Down
2 changes: 1 addition & 1 deletion fs/overlayfs/overlayfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ 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,
int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, struct inode *dir,
struct dentry *dentry);
struct ovl_cattr {
dev_t rdev;
Expand Down
3 changes: 3 additions & 0 deletions fs/overlayfs/ovl_entry.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ struct ovl_fs {
/* Did we take the inuse lock? */
bool upperdir_locked;
bool workdir_locked;
bool share_whiteout;
/* Traps in ovl inode cache */
struct inode *upperdir_trap;
struct inode *workbasedir_trap;
Expand All @@ -77,6 +78,8 @@ struct ovl_fs {
int xino_mode;
/* For allocation of non-persistent inode numbers */
atomic_long_t last_ino;
/* Whiteout dentry cache */
struct dentry *whiteout;
};

static inline struct ovl_fs *OVL_FS(struct super_block *sb)
Expand Down
2 changes: 1 addition & 1 deletion fs/overlayfs/readdir.c
Original file line number Diff line number Diff line change
Expand Up @@ -1154,7 +1154,7 @@ 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, dir, index);
} else {
/* Cleanup orphan index entries */
err = ovl_cleanup(dir, index);
Expand Down
4 changes: 4 additions & 0 deletions fs/overlayfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ static void ovl_free_fs(struct ovl_fs *ofs)
iput(ofs->indexdir_trap);
iput(ofs->workdir_trap);
iput(ofs->upperdir_trap);
dput(ofs->whiteout);
dput(ofs->indexdir);
dput(ofs->workdir);
if (ofs->workdir_locked)
Expand Down Expand Up @@ -1776,6 +1777,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
if (!cred)
goto out_err;

/* Is there a reason anyone would want not to share whiteouts? */
ofs->share_whiteout = true;

ofs->config.index = ovl_index_def;
ofs->config.nfs_export = ovl_nfs_export_def;
ofs->config.xino = ovl_xino_def();
Expand Down
3 changes: 2 additions & 1 deletion fs/overlayfs/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,8 @@ 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(OVL_FS(dentry->d_sb),
dir, index);
} else {
/* Cleanup orphan index entries */
err = ovl_cleanup(dir, index);
Expand Down

0 comments on commit c21c839

Please sign in to comment.