diff options
author | Amir Goldstein <amir73il@gmail.com> | 2017-01-17 06:34:57 +0200 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2017-02-07 15:47:14 +0100 |
commit | 01ad3eb8a07385bc8849f0ee7c800e7c8bd7287e (patch) | |
tree | dd56100b213de1a7983f2f1a2597ed169dd31d62 /fs/overlayfs | |
parent | 39d3d60a54df05a1a32fa71159d7a26a530dee6c (diff) |
ovl: concurrent copy up of regular files
Now that copy up of regular file is done using O_TMPFILE,
we don't need to hold rename_lock throughout copy up.
Use the copy up waitqueue to synchronize concurrent copy up
of the same file. Different regular files can be copied up
concurrently.
The upper dir inode_lock is taken instead of rename_lock,
because it is needed for lookup and later for linking the
temp file, but it is released while copying up data.
Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs/overlayfs')
-rw-r--r-- | fs/overlayfs/copy_up.c | 35 |
1 files changed, 31 insertions, 4 deletions
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 6e39e90b5605..48eb8812ac5b 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -291,7 +291,16 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir, BUG_ON(upperpath.dentry != NULL); upperpath.dentry = temp; - err = ovl_copy_up_data(lowerpath, &upperpath, stat->size); + if (tmpfile) { + inode_unlock(udir); + err = ovl_copy_up_data(lowerpath, &upperpath, + stat->size); + inode_lock_nested(udir, I_MUTEX_PARENT); + } else { + err = ovl_copy_up_data(lowerpath, &upperpath, + stat->size); + } + if (err) goto out_cleanup; } @@ -353,8 +362,6 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, struct dentry *upperdir; const char *link = NULL; struct ovl_fs *ofs = dentry->d_sb->s_fs_info; - /* Should we copyup with O_TMPFILE or with workdir? */ - bool tmpfile = S_ISREG(stat->mode) && ofs->tmpfile; if (WARN_ON(!workdir)) return -EROFS; @@ -374,6 +381,25 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, return PTR_ERR(link); } + /* Should we copyup with O_TMPFILE or with workdir? */ + if (S_ISREG(stat->mode) && ofs->tmpfile) { + err = ovl_copy_up_start(dentry); + /* err < 0: interrupted, err > 0: raced with another copy-up */ + if (unlikely(err)) { + pr_debug("ovl_copy_up_start(%pd2) = %i\n", dentry, err); + if (err > 0) + err = 0; + goto out_done; + } + + inode_lock_nested(upperdir->d_inode, I_MUTEX_PARENT); + err = ovl_copy_up_locked(workdir, upperdir, dentry, lowerpath, + stat, link, &pstat, true); + inode_unlock(upperdir->d_inode); + ovl_copy_up_end(dentry); + goto out_done; + } + err = -EIO; if (lock_rename(workdir, upperdir) != NULL) { pr_err("overlayfs: failed to lock workdir+upperdir\n"); @@ -386,9 +412,10 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, } err = ovl_copy_up_locked(workdir, upperdir, dentry, lowerpath, - stat, link, &pstat, tmpfile); + stat, link, &pstat, false); out_unlock: unlock_rename(workdir, upperdir); +out_done: do_delayed_call(&done); return err; |