When metacopy feature is enabled and filemap operations supported, copy up only metadata when opening a file O_RDWR and defer copy up of data until the first data access file operation. Copy up on first page fault will be handled by a followup patch. Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> --- fs/overlayfs/copy_up.c | 5 +--- fs/overlayfs/file.c | 63 +++++++++++++++++++++++++++++++++++----- fs/overlayfs/overlayfs.h | 10 ++++++- fs/overlayfs/util.c | 4 +-- 4 files changed, 68 insertions(+), 14 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 05f853c7db0d..92a27ba65c78 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -731,10 +731,7 @@ static bool ovl_need_meta_copy_up(struct dentry *dentry, umode_t mode, if (!S_ISREG(mode)) return false; - if (flags && ((OPEN_FMODE(flags) & FMODE_WRITE) || (flags & O_TRUNC))) - return false; - - return true; + return !ovl_open_flags_need_data_copy_up(flags); } /* Copy up data of an inode which was copied up metadata only in the past. */ diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 5f648979adbf..e02b61af9488 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -90,10 +90,24 @@ static int ovl_real_fdget_meta(const struct file *file, struct fd *real, { struct inode *inode = file_inode(file); struct inode *realinode; + int err; real->flags = 0; real->file = file->private_data; + /* + * Lazy copy up caches the meta copy upper file on open O_RDWR. + * We need to promote upper inode to full data copy up before + * we allow access to real file data on a writable file, otherwise + * we may try to open a lower file O_RDWR or perform data operations + * (e.g. fallocate) on the metacopy inode. + */ + if (!allow_meta) { + err = ovl_maybe_copy_up(file_dentry(file), file->f_flags); + if (err) + return err; + } + if (allow_meta) realinode = ovl_inode_real(inode); else @@ -127,11 +141,34 @@ static bool ovl_filemap_support(struct file *file) return ofs->upper_mnt && ovl_aops.writepage; } -static bool ovl_should_use_filemap(struct file *file) +static bool ovl_should_use_filemap_meta(struct file *file, bool meta_only) { + int err; + if (!ovl_filemap_support(file)) return false; + /* + * If file was opened O_RDWR with lazy copy up of data, the first + * data access file operation will trigger data copy up. If file was + * opened O_RDWR and @meta_only is true, we use overlay inode filemap + * operations, but defer data copy up further. + * + * For example, mmap() and fsync() are metadata only operations that + * do not trigger lazy copy up of data, but read() (on a file open for + * write) is a data access operation that does trigger data copy up. + */ + if (!meta_only) { + err = ovl_maybe_copy_up(file_dentry(file), file->f_flags); + if (err) { + pr_warn_ratelimited("overlayfs: failed lazy copy up data (%pd2, err=%i)\n", + file_dentry(file), err); + return false; + } + } else if (file->f_mode & FMODE_WRITE) { + return true; + } + /* * Use overlay inode page cache for all inodes that could be dirty, * including pure upper inodes, so ovl_sync_fs() can sync all dirty @@ -140,9 +177,14 @@ static bool ovl_should_use_filemap(struct file *file) return ovl_has_upperdata(file_inode(file)); } +static bool ovl_should_use_filemap(struct file *file) +{ + return ovl_should_use_filemap_meta(file, false); +} + static int ovl_flush_filemap(struct file *file, loff_t offset, loff_t len) { - if (!ovl_should_use_filemap(file)) + if (!ovl_should_use_filemap_meta(file, true)) return 0; return filemap_write_and_wait_range(file_inode(file)->i_mapping, @@ -152,16 +194,23 @@ static int ovl_flush_filemap(struct file *file, loff_t offset, loff_t len) static int ovl_open(struct inode *inode, struct file *file) { struct file *realfile; + int metacopy = 0; int err; - err = ovl_maybe_copy_up(file_dentry(file), file->f_flags); + /* (O_RDWR | O_WRONLY) signals that we want meta copy up */ + if (ovl_filemap_support(file) && (file->f_flags & O_ACCMODE) == O_RDWR) + metacopy = (O_RDWR | O_WRONLY); + + /* Allow to copy up meta and defer copy up data to first data access */ + err = ovl_maybe_copy_up(file_dentry(file), file->f_flags | metacopy); if (err) return err; /* No longer need these flags, so don't pass them on to underlying fs */ file->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); - realfile = ovl_open_realfile(file, ovl_inode_realdata(inode)); + realfile = ovl_open_realfile(file, metacopy ? ovl_inode_real(inode) : + ovl_inode_realdata(inode)); if (IS_ERR(realfile)) return PTR_ERR(realfile); @@ -337,7 +386,7 @@ static int ovl_real_fsync(struct file *file, loff_t start, loff_t end, static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync) { - if (ovl_should_use_filemap(file)) + if (ovl_should_use_filemap_meta(file, true)) return __generic_file_fsync(file, start, end, datasync); return ovl_real_fsync(file, start, end, datasync); @@ -381,7 +430,7 @@ static int ovl_real_mmap(struct file *file, struct vm_area_struct *vma) static int ovl_mmap(struct file *file, struct vm_area_struct *vma) { - if (ovl_should_use_filemap(file)) + if (ovl_should_use_filemap_meta(file, true)) generic_file_mmap(file, vma); return ovl_real_mmap(file, vma); @@ -441,7 +490,7 @@ extern int generic_fadvise(struct file *file, loff_t offset, loff_t len, static int ovl_fadvise(struct file *file, loff_t offset, loff_t len, int advice) { - if (ovl_should_use_filemap(file)) + if (ovl_should_use_filemap_meta(file, true)) return generic_fadvise(file, offset, len, advice); /* XXX: Should we allow messing with lower shared page cache? */ diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index ab61c07e8583..6baa3460c173 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -195,14 +195,22 @@ static inline struct dentry *ovl_do_tmpfile(struct dentry *dentry, umode_t mode) return ret; } -static inline bool ovl_open_flags_need_copy_up(int flags) +static inline bool ovl_open_flags_need_data_copy_up(int flags) { if (!flags) return false; + /* Either O_RDWR or O_WRONLY will trigger data copy up */ return ((OPEN_FMODE(flags) & FMODE_WRITE) || (flags & O_TRUNC)); } +static inline bool ovl_open_flags_need_copy_up(int flags) +{ + /* O_RDWR|O_WRONLY together will trigger meta copy up */ + return (flags & O_ACCMODE) == (O_RDWR | O_WRONLY) || + ovl_open_flags_need_data_copy_up(flags); +} + /* util.c */ int ovl_want_write(struct dentry *dentry); void ovl_drop_write(struct dentry *dentry); diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 7c01327b1852..df6d13185f40 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -367,7 +367,7 @@ void ovl_set_upperdata(struct inode *inode) /* Caller should hold ovl_inode->lock */ bool ovl_dentry_needs_data_copy_up_locked(struct dentry *dentry, int flags) { - if (!ovl_open_flags_need_copy_up(flags)) + if (!ovl_open_flags_need_data_copy_up(flags)) return false; return !ovl_test_flag(OVL_UPPERDATA, d_inode(dentry)); @@ -375,7 +375,7 @@ bool ovl_dentry_needs_data_copy_up_locked(struct dentry *dentry, int flags) bool ovl_dentry_needs_data_copy_up(struct dentry *dentry, int flags) { - if (!ovl_open_flags_need_copy_up(flags)) + if (!ovl_open_flags_need_data_copy_up(flags)) return false; return !ovl_has_upperdata(d_inode(dentry)); -- 2.17.1