overlayfs: Delayed copy up of data
v7 cover overlayfs: Delayed copy up of data
Hi,
Here is the V7 of the patches. These patches apply on top of overlayfs-next branch of miklos's tree.
I have taken care of most of the comments from previous iteration.
These patches are very lightly tested. First I want to make sure that I have addressed all design concerns. Once that is done, will do more extensive testing.
Following are changes from V6.
- Cherry picked Amir's patch to disable redirect_dir and index feature if upper does not support xattr.
- Disable metacopy if upper does not support xattr.
- Merged encryption and compression patch.
- Merged barriers patch into core patch.
- Now OVL_UPPERDATA is set for all kind of dentries. Read side of OVL_UPPERDATA first checks whether dentry in question should have this flag set or not.
- Now smp_rmb() is called only when checking OVL_UPPERDATA from lockless path.
- Miscellaneous code cleanus as asked by Amir.
Original Description
--------------------
In one of the recent converstions, people mentioned that chown/chmod lead to copy up files as well as data. We could optimize it so that only metadata is copied up during chown/chmod and data is copied up when file is opened for WRITE.
This optimization potentially could be useful with containers and user namespaces. In popular scenario, people end up doing chown() on whole image directory tree based on container mappings. And this chown copies up everything, breaking sharing of page cache between containers.
With these patches, only metadat is copied up during chown() and if file is opened for READ, d_real() returns lower dentry/inode. That way, different containers can still continue to use page cache. That's the use case I have in mind.
Basically, I am relying on storing OVL_XATTR_ORIGIN in upper inode during copy up. I use that information to get to lower inode later and do data copy up when necessary.
I also store OVL_XATTR_METACOPY in upper inode to mark that only metadata has been copied up and data copy up still might be required.
Any feedback is helpful.
Thanks
Vivek
Amir Goldstein (1):
ovl: disable redirect_dir and index when no xattr support
Vivek Goyal (13):
ovl: ovl_check_setxattr() get rid of redundant -EOPNOTSUPP check
ovl: Create origin xattr on copy up for all files
ovl: Provide a mount option metacopy=on/off for metadata copyup
ovl: During copy up, first copy up metadata and then data
ovl: Move couple of copy up functions in copy_up.c
ovl: Copy up only metadata during copy up where it makes sense
ovl: Add helper ovl_already_copied_up()
ovl: A new xattr OVL_XATTR_METACOPY for file on upper
ovl: Fix ovl_getattr() to get number of blocks from lower
ovl: Set OVL_UPPERDATA flag during ovl_lookup()
ovl: Do not expose metacopy only upper dentry from d_real()
ovl: Fix encryption/compression status of a metacopy only file
ovl: Enable metadata only feature
fs/overlayfs/Kconfig | 8 +++ fs/overlayfs/copy_up.c | 161 ++++++++++++++++++++++++++++++++++------------- fs/overlayfs/dir.c | 1 + fs/overlayfs/inode.c | 58 ++++++++--------- fs/overlayfs/namei.c | 40 ++++++++++++ fs/overlayfs/overlayfs.h | 9 ++- fs/overlayfs/ovl_entry.h | 1 + fs/overlayfs/super.c | 61 ++++++++++++++++-- fs/overlayfs/util.c | 107 +++++++++++++++++++++++++++---- 9 files changed, 352 insertions(+), 94 deletions(-)
Signature · 1 line
2.13.6
PATCH v7 1/14 ovl: disable redirect_dir and index when no xattr support
From: Amir Goldstein <[email protected]>
Overlayfs falls back to index=off if lower/upper fs does not support file handles. We should do the same if upper fs does not support xattr.
The redirect_dir feature is implicitly disabled when upper fs does not support xattr via the check in ovl_redirect_dir(). Make the feature explicitly disabled in this case by emitting a warning at mount time and setting redirect_dir to off so its true state is visible in /proc/mounts.
--- fs/overlayfs/super.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/overlayfs/super.c +3 -1
@@ -921,7 +921,9 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) err =ovl_do_setxattr (ofs->workdir,OVL_XATTR_OPAQUE ,"0" ,1 ,0 );if (err) { ofs->noxattr = true; -pr_warn ("overlayfs: upper fs does not support xattr.\n" ); + ofs->config.redirect_dir = false; + ofs->config.index = false; +pr_warn ("overlayfs: upper fs does not support xattr, falling back to redirect_dir=off, index=off and no opaque dir.\n" ); }else {vfs_removexattr (ofs->workdir,OVL_XATTR_OPAQUE ); }
Signature · 1 line
2.13.6
PATCH v7 2/14 ovl: ovl_check_setxattr() get rid of redundant -EOPNOTSUPP check
In reply to Vivek GoyalAt mount time we check if upper supports xattr or not and set ofs->noxattr field accordingly. But in ovl_check_setxattr() still seems to have logic which expects that ovl_do_setxattr() can return -EOPNOTSUPP. Not sure when and how can we hit this code. Feels redundant to me. So I am removing this code.
--- fs/overlayfs/util.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-)
fs/overlayfs/util.c +1 -10
@@ -365,21 +365,12 @@ int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry, const char *name, const void *value, size_t size, int xerr) { - int err; struct ovl_fs *ofs = dentry->d_sb->s_fs_info;if (ofs->noxattr)return xerr; - err =ovl_do_setxattr (upperdentry, name, value, size,0 ); - -if (err == -EOPNOTSUPP ) { -pr_warn ("overlayfs: cannot set %s xattr on upper\n" , name); - ofs->noxattr = true; -return xerr; - } - -return err; +return ovl_do_setxattr (upperdentry, name, value, size,0 ); }int ovl_set_impure (struct dentry *dentry,struct dentry *upperdentry)
Signature · 1 line
2.13.6
PATCH v7 3/14 ovl: Create origin xattr on copy up for all files
In reply to Vivek GoyalRight now my understanding is that origin xattr is created for all copied up files if index=on. And if index=off, then we create it for all type of files except hardlinks (nlink != 1).
With metadata only copy up, I will still require origin xattr to copy up data later, so create it even for hardlinks even with index=off.
Given ->origin is always true now, get rid of this field from "struct ovl_copy_up_ctx".
--- fs/overlayfs/copy_up.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-)
fs/overlayfs/copy_up.c +3 -12
@@ -326,7 +326,6 @@ struct ovl_copy_up_ctx {struct qstr destname;struct dentry *workdir;bool tmpfile; -bool origin; };static int ovl_link_up (struct ovl_copy_up_ctx *c)
@@ -469,15 +468,10 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)/* * Store identifier of lower inode in upper inode xattr to * allow lookup of the copy up origin inode. -* -* Don't set origin when we are breaking the association with a lower -* hard link. */ -if (c->origin) { - err =ovl_set_origin (c->dentry, c->lowerpath.dentry, temp); -if (err) -return err; - } + err =ovl_set_origin (c->dentry, c->lowerpath.dentry, temp); +if (err) +return err;return 0 ; }
@@ -542,9 +536,6 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c) c->stat.nlink > 1) indexed = true; -if (S_ISDIR (c->stat.mode) || c->stat.nlink ==1 || indexed) - c->origin = true; -if (indexed) { c->destdir =ovl_indexdir (c->dentry->d_sb); err =ovl_get_index_name (c->lowerpath.dentry, &c->destname);
Signature · 1 line
2.13.6
PATCH v7 4/14 ovl: Provide a mount option metacopy=on/off for metadata copyup
In reply to Vivek GoyalBy default metadata only copy up is disabled. Provide a mount option so that users can choose one way or other.
Also provide a kernel config and module option to enable/disable metacopy feature.
Like index feature, we verify on mount that upper root is not being reused with a different lower root. This hopes to get the configuration right and detect the copied layers use case. But this does only so much as we don't verify all the lowers. So it is possible that a lower is missing and later data copy up fails.
--- fs/overlayfs/Kconfig | 8 ++++++++ fs/overlayfs/ovl_entry.h | 1 + fs/overlayfs/super.c | 40 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 46 insertions(+), 3 deletions(-)
fs/overlayfs/Kconfig +8 -0
@@ -43,3 +43,11 @@ config OVERLAY_FS_INDEX outcomes. However, mounting the same overlay with an old kernel read-write and then mounting it again with a new kernel, will have unexpected results. + +config OVERLAY_FS_METACOPY + bool "Overlayfs: turn on metadata only copy up feature by default" + depends on OVERLAY_FS + help + If this config option is enabled then overlay filesystems will + copy up only metadata where appropriate and data copy up will + happen when a file is opended for WRITE operation.
fs/overlayfs/ovl_entry.h +1 -0
@@ -15,6 +15,7 @@ struct ovl_config {bool default_permissions;bool redirect_dir;bool index; +bool metacopy; };struct ovl_layer {
fs/overlayfs/super.c +37 -3
@@ -46,6 +46,11 @@ static void ovl_entry_stack_free(struct ovl_entry *oe)dput (oe->lowerstack[i].dentry); } +static bool ovl_metacopy_def =IS_ENABLED (CONFIG_OVERLAY_FS_METACOPY ); +module_param_named (metacopy, ovl_metacopy_def, bool,0644 ); +MODULE_PARM_DESC (ovl_metacopy_def, +"Default to on or off for the metadata only copy up feature" ); +static void ovl_dentry_release (struct dentry *dentry) {struct ovl_entry *oe = dentry->d_fsdata;
@@ -319,6 +324,9 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)if (ofs->config.index != ovl_index_def)seq_printf (m,",index=%s" , ofs->config.index ?"on" :"off" ); +if (ofs->config.metacopy != ovl_metacopy_def) +seq_printf (m,",metacopy=%s" , + ofs->config.metacopy ?"on" :"off" );return 0 ; }
@@ -352,6 +360,8 @@ enum {OPT_REDIRECT_DIR_OFF ,OPT_INDEX_ON ,OPT_INDEX_OFF , +OPT_METACOPY_ON , +OPT_METACOPY_OFF , OPT_ERR, };
@@ -364,6 +374,8 @@ static const match_table_t ovl_tokens = { {OPT_REDIRECT_DIR_OFF, "redirect_dir=off"}, {OPT_INDEX_ON, "index=on"}, {OPT_INDEX_OFF, "index=off"}, + {OPT_METACOPY_ON, "metacopy=on"}, + {OPT_METACOPY_OFF, "metacopy=off"}, {OPT_ERR, NULL} };
@@ -444,6 +456,14 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) config->index = false;break ; +case OPT_METACOPY_ON : + config->metacopy = true; +break ; + +case OPT_METACOPY_OFF : + config->metacopy = false; +break ; +default :pr_err ("overlayfs: unrecognized mount option \"%s\" or missing value\n" , p);return -EINVAL ;
@@ -657,9 +677,11 @@ static int ovl_lower_dir(const char *name, struct path *path,* The inodes index feature needs to encode and decode file * handles, so it requires that all layers support them. */ -if (ofs->config.index && !ovl_can_decode_fh (path->dentry->d_sb)) { +if ((ofs->config.index || ofs->config.metacopy) && + !ovl_can_decode_fh (path->dentry->d_sb)) { +pr_warn ("overlayfs: fs on '%s' does not support file handles, falling back to index=off and metacopy=off.\n" , name); ofs->config.index = false; -pr_warn ("overlayfs: fs on '%s' does not support file handles, falling back to index=off.\n" , name); + ofs->config.metacopy = false; }return 0 ;
@@ -923,7 +945,8 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) ofs->noxattr = true; ofs->config.redirect_dir = false; ofs->config.index = false; -pr_warn ("overlayfs: upper fs does not support xattr, falling back to redirect_dir=off, index=off and no opaque dir.\n" ); + ofs->config.metacopy = false; +pr_warn ("overlayfs: upper fs does not support xattr, falling back to redirect_dir=off, index=off, metacopy=off and no opaque dir.\n" ); }else {vfs_removexattr (ofs->workdir,OVL_XATTR_OPAQUE ); }
@@ -1164,6 +1187,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ofs->config.redirect_dir = ovl_redirect_dir_def; ofs->config.index = ovl_index_def; + ofs->config.metacopy = ovl_metacopy_def; err =ovl_parse_opt ((char *) data, &ofs->config);if (err) goto out_err;
@@ -1209,6 +1233,16 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)else if (ofs->upper_mnt->mnt_sb != ofs->same_sb) ofs->same_sb =NULL ; +if (!(ovl_force_readonly (ofs)) && ofs->config.metacopy) { +/* Verify lower root is upper root origin */ + err =ovl_verify_origin (upperpath.dentry, + oe->lowerstack[0 ].dentry, false, true); +if (err) { +pr_err ("overlayfs: failed to verify upper root origin\n" ); + goto out_free_oe; + } + } +if (!(ovl_force_readonly (ofs)) && ofs->config.index) { err =ovl_get_indexdir (ofs, oe, &upperpath);if (err)
Signature · 1 line
2.13.6
Amir Goldstein
maintainerRe: [PATCH v7 04/14] ovl: Provide a mount option metacopy=on/off for metadata copyup
Quoted 143 lines · Vivek Goyal
By default metadata only copy up is disabled. Provide a mount option so that users can choose one way or other.
Also provide a kernel config and module option to enable/disable metacopy feature.
Like index feature, we verify on mount that upper root is not being reused with a different lower root. This hopes to get the configuration right and detect the copied layers use case. But this does only so much as we don't verify all the lowers. So it is possible that a lower is missing and later data copy up fails.
Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/Kconfig | 8 ++++++++ fs/overlayfs/ovl_entry.h | 1 + fs/overlayfs/super.c | 40 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 46 insertions(+), 3 deletions(-)fs/overlayfs/Kconfig +8
-0@@ -43,3 +43,11 @@ config OVERLAY_FS_INDEX outcomes. However, mounting the same overlay with an old kernel read-write and then mounting it again with a new kernel, will have unexpected results. + +config OVERLAY_FS_METACOPY + bool "Overlayfs: turn on metadata only copy up feature by default" + depends on OVERLAY_FS + help + If this config option is enabled then overlay filesystems will + copy up only metadata where appropriate and data copy up will + happen when a file is opended for WRITE operation.fs/overlayfs/ovl_entry.h +1
-0@@ -15,6 +15,7 @@ struct ovl_config {bool default_permissions;bool redirect_dir;bool index; +bool metacopy; };struct ovl_layer {fs/overlayfs/super.c +28
-3@@ -46,6 +46,11 @@ static void ovl_entry_stack_free(struct ovl_entry *oe)dput (oe->lowerstack[i].dentry); } +static bool ovl_metacopy_def =IS_ENABLED (CONFIG_OVERLAY_FS_METACOPY ); +module_param_named (metacopy, ovl_metacopy_def, bool,0644 ); +MODULE_PARM_DESC (ovl_metacopy_def, +"Default to on or off for the metadata only copy up feature" ); +static void ovl_dentry_release (struct dentry *dentry) {struct ovl_entry *oe = dentry->d_fsdata;@@ -319,6 +324,9 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)if (ofs->config.index != ovl_index_def)seq_printf (m,",index=%s" , ofs->config.index ?"on" :"off" ); +if (ofs->config.metacopy != ovl_metacopy_def) +seq_printf (m,",metacopy=%s" , + ofs->config.metacopy ?"on" :"off" );return 0 ; }@@ -352,6 +360,8 @@ enum {OPT_REDIRECT_DIR_OFF ,OPT_INDEX_ON ,OPT_INDEX_OFF , +OPT_METACOPY_ON , +OPT_METACOPY_OFF , OPT_ERR, };@@ -364,6 +374,8 @@ static const match_table_t ovl_tokens = { {OPT_REDIRECT_DIR_OFF, "redirect_dir=off"}, {OPT_INDEX_ON, "index=on"}, {OPT_INDEX_OFF, "index=off"}, + {OPT_METACOPY_ON, "metacopy=on"}, + {OPT_METACOPY_OFF, "metacopy=off"}, {OPT_ERR, NULL} };@@ -444,6 +456,14 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) config->index = false;break ; +case OPT_METACOPY_ON : + config->metacopy = true; +break ; + +case OPT_METACOPY_OFF : + config->metacopy = false; +break ; +default :pr_err ("overlayfs: unrecognized mount option \"%s\" or missing value\n" , p);return -EINVAL ;@@ -657,9 +677,11 @@ static int ovl_lower_dir(const char *name, struct path *path,* The inodes index feature needs to encode and decode file * handles, so it requires that all layers support them. */ -if (ofs->config.index && !ovl_can_decode_fh (path->dentry->d_sb)) { +if ((ofs->config.index || ofs->config.metacopy) && + !ovl_can_decode_fh (path->dentry->d_sb)) { +pr_warn ("overlayfs: fs on '%s' does not support file handles, falling back to index=off and metacopy=off.\n" , name); ofs->config.index = false; -pr_warn ("overlayfs: fs on '%s' does not support file handles, falling back to index=off.\n" , name); + ofs->config.metacopy = false; }return 0 ;@@ -923,7 +945,8 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) ofs->noxattr = true; ofs->config.redirect_dir = false; ofs->config.index = false; -pr_warn ("overlayfs: upper fs does not support xattr, falling back to redirect_dir=off, index=off and no opaque dir.\n" ); + ofs->config.metacopy = false; +pr_warn ("overlayfs: upper fs does not support xattr, falling back to redirect_dir=off, index=off, metacopy=off and no opaque dir.\n" ); }else {vfs_removexattr (ofs->workdir,OVL_XATTR_OPAQUE ); }@@ -1164,6 +1187,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ofs->config.redirect_dir = ovl_redirect_dir_def; ofs->config.index = ovl_index_def; + ofs->config.metacopy = ovl_metacopy_def; err =ovl_parse_opt ((char *) data, &ofs->config);if (err) goto out_err;@@ -1209,6 +1233,16 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)else if (ofs->upper_mnt->mnt_sb != ofs->same_sb) ofs->same_sb =NULL ; +if (!(ovl_force_readonly (ofs)) && ofs->config.metacopy) {
fs/overlayfs/super.c static int ovl_fill_super(struct super_block *sb, void *data, int silent) · line 1246
Quoted 10 lines
fs/overlayfs/super.c +9
-0+/* Verify lower root is upper root origin */ + err =ovl_verify_origin (upperpath.dentry, + oe->lowerstack[0 ].dentry, false, true); +if (err) { +pr_err ("overlayfs: failed to verify upper root origin\n" ); + goto out_free_oe; + } + } +
Fs can have upper but no workdir and you still need to verify lower If metacopy is enabled
Amir.
Re: [PATCH v7 04/14] ovl: Provide a mount option metacopy=on/off for metadata copyup
On Fri, Nov 17, 2017 at 01:23:58PM +0200, Amir Goldstein wrote:
[..]
Quoted 12 lines
Quoted 12 lines
fs/overlayfs/super.c +1
-0@@ -1164,6 +1187,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ofs->config.redirect_dir = ovl_redirect_dir_def; ofs->config.index = ovl_index_def; + ofs->config.metacopy = ovl_metacopy_def; err =ovl_parse_opt ((char *) data, &ofs->config);if (err) goto out_err;@@ -1209,6 +1233,16 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)else if (ofs->upper_mnt->mnt_sb != ofs->same_sb) ofs->same_sb =NULL ;
Quoted 14 lines
fs/overlayfs/super.c static int ovl_fill_super(struct super_block *sb, void *data, int silent)
Quoted 11 lines
fs/overlayfs/super.c +10
-0+if (!(ovl_force_readonly (ofs)) && ofs->config.metacopy) { +/* Verify lower root is upper root origin */ + err =ovl_verify_origin (upperpath.dentry, + oe->lowerstack[0 ].dentry, false, true); +if (err) { +pr_err ("overlayfs: failed to verify upper root origin\n" ); + goto out_free_oe; + } + } +Fs can have upper but no workdir and you still need to verify lower If metacopy is enabled
Hmm..., trying to understand this. This probably is more involved.
So first use case is that if metacopy is being enabled, verify lower root is upper root origin. (Even if it is read-only fs).
if (ofs->upper_mnt && ofs->config.metacopy)
ovl_verify_origin().
But this does not cover the case of same fs being remounted with metacopy=off. In that case we will not do metacopy only copy ups but existing metacopy inodes will still require that lower does not change.
Will it make sense to set OVL_METACOPY xattr on upper when metacopy=on is done first time on mount. And later in subsequent mounts, if METACOPY is set on upper, make sure to verify origin and make sure lower supports file handles etc.
Thanks
Vivek
Amir Goldstein
maintainerRe: [PATCH v7 04/14] ovl: Provide a mount option metacopy=on/off for metadata copyup
Quoted 30 lines · Vivek Goyal
On Fri, Nov 17, 2017 at 01:23:58PM +0200, Amir Goldstein wrote:
[..]
Quoted 12 lines
Quoted 12 lines
fs/overlayfs/super.c +1
-0@@ -1164,6 +1187,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ofs->config.redirect_dir = ovl_redirect_dir_def; ofs->config.index = ovl_index_def; + ofs->config.metacopy = ovl_metacopy_def; err =ovl_parse_opt ((char *) data, &ofs->config);if (err) goto out_err;@@ -1209,6 +1233,16 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)else if (ofs->upper_mnt->mnt_sb != ofs->same_sb) ofs->same_sb =NULL ;Quoted 14 lines
fs/overlayfs/super.c static int ovl_fill_super(struct super_block *sb, void *data, int silent)
Quoted 11 lines
fs/overlayfs/super.c +10
-0+if (!(ovl_force_readonly (ofs)) && ofs->config.metacopy) { +/* Verify lower root is upper root origin */ + err =ovl_verify_origin (upperpath.dentry, + oe->lowerstack[0 ].dentry, false, true); +if (err) { +pr_err ("overlayfs: failed to verify upper root origin\n" ); + goto out_free_oe; + } + } +Fs can have upper but no workdir and you still need to verify lower If metacopy is enabled
Quoted 7 lines
Hmm..., trying to understand this. This probably is more involved.
So first use case is that if metacopy is being enabled, verify lower root is upper root origin. (Even if it is read-only fs).
if (
ofs->upper_mnt&&ofs->config.metacopy)
ovl_verify_origin().
Yes, this is needed in case workdir/work dir creation fails and fs is mounted read-only. You should not allow it with metacopy on unverified lower.
Quoted 10 lines
But this does not cover the case of same fs being remounted with metacopy=off. In that case we will not do metacopy only copy ups but existing metacopy inodes will still require that lower does not change.
Will it make sense to set
OVL_METACOPYxattr on upper when metacopy=on is done first time on mount. And later in subsequent mounts, if METACOPY is set on upper, make sure to verify origin and make sure lower supports file handles etc.
This is an related to what we discussed at length on the ovl-features patches.
Yes, we SHOULD mark that the metacopy feature was enabled, at least on first meta_copy_up().
No, we should not do that by marking root upper with metacopy xattr. this would be very confusing and inconsistent.
We can either set trusted.overlay.features on upper root as Miklos suggested or incompat_feature/ directory as I suggested, but we need to decide on a consistent scheme we can use for other features as well.
I guess metacopy must have this infrastructure in place, because as you correctly noted, we should not mount an overlay with metacopied inodes without verifying lower and file handle support.
Amir.
Re: [PATCH v7 04/14] ovl: Provide a mount option metacopy=on/off for metadata copyup
Quoted 56 lines · Amir Goldstein
Quoted 30 lines · Vivek Goyal
On Fri, Nov 17, 2017 at 01:23:58PM +0200, Amir Goldstein wrote:
[..]
Quoted 12 lines
Quoted 12 lines
fs/overlayfs/super.c +1
-0@@ -1164,6 +1187,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ofs->config.redirect_dir = ovl_redirect_dir_def; ofs->config.index = ovl_index_def; + ofs->config.metacopy = ovl_metacopy_def; err =ovl_parse_opt ((char *) data, &ofs->config);if (err) goto out_err;@@ -1209,6 +1233,16 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)else if (ofs->upper_mnt->mnt_sb != ofs->same_sb) ofs->same_sb =NULL ;Quoted 14 lines
fs/overlayfs/super.c static int ovl_fill_super(struct super_block *sb, void *data, int silent)
Quoted 11 lines
fs/overlayfs/super.c +10
-0+if (!(ovl_force_readonly (ofs)) && ofs->config.metacopy) { +/* Verify lower root is upper root origin */ + err =ovl_verify_origin (upperpath.dentry, + oe->lowerstack[0 ].dentry, false, true); +if (err) { +pr_err ("overlayfs: failed to verify upper root origin\n" ); + goto out_free_oe; + } + } +Fs can have upper but no workdir and you still need to verify lower If metacopy is enabled
Quoted 7 lines
Hmm..., trying to understand this. This probably is more involved.
So first use case is that if metacopy is being enabled, verify lower root is upper root origin. (Even if it is read-only fs).
if (
ofs->upper_mnt&&ofs->config.metacopy)
ovl_verify_origin().Yes, this is needed in case workdir/work dir creation fails and fs is mounted read-only. You should not allow it with metacopy on unverified lower.
Quoted 10 lines
But this does not cover the case of same fs being remounted with metacopy=off. In that case we will not do metacopy only copy ups but existing metacopy inodes will still require that lower does not change.
Will it make sense to set
OVL_METACOPYxattr on upper when metacopy=on is done first time on mount. And later in subsequent mounts, if METACOPY is set on upper, make sure to verify origin and make sure lower supports file handles etc.This is an related to what we discussed at length on the ovl-features patches.
Quoted 8 lines
Yes, we SHOULD mark that the metacopy feature was enabled, at least on first
meta_copy_up().No, we should not do that by marking root upper with metacopy xattr. this would be very confusing and inconsistent.
We can either set trusted.overlay.features on upper root as Miklos suggested or incompat_feature/ directory as I suggested, but we need to decide on a consistent scheme we can use for other features as well.
Agreed. We need a generic sheme to mark what features have been enabled and what are incomapt features and which ones are rocompat etc and then all current and future overlayfs features can make use of that generic infrastructure.
Quoted 4 lines
I guess metacopy must have this infrastructure in place, because as you correctly noted, we should not mount an overlay with metacopied inodes without verifying lower and file handle support.
Ok, so for now, I will just verify lower when metacopy is enabled. For other case, will wait for something generic to be merged upstream.
Vivek
PATCH v7 5/14 ovl: During copy up, first copy up metadata and then data
In reply to Vivek GoyalJust a little re-ordering of code. This helps with next patch where after copying up metadata, we skip data copying step, if needed.
--- fs/overlayfs/copy_up.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-)
fs/overlayfs/copy_up.c +14 -15
@@ -443,6 +443,19 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp) {int err; + err =ovl_copy_xattr (c->lowerpath.dentry, temp); +if (err) +return err; + +/* +* Store identifier of lower inode in upper inode xattr to +* allow lookup of the copy up origin inode. +* +*/ + err =ovl_set_origin (c->dentry, c->lowerpath.dentry, temp); +if (err) +return err; +if (S_ISREG (c->stat.mode)) {struct path upperpath;
@@ -455,25 +468,11 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)return err; } - err =ovl_copy_xattr (c->lowerpath.dentry, temp); -if (err) -return err; -inode_lock (temp->d_inode); err =ovl_set_attr (temp, &c->stat);inode_unlock (temp->d_inode); -if (err) -return err; - -/* -* Store identifier of lower inode in upper inode xattr to -* allow lookup of the copy up origin inode. -*/ - err =ovl_set_origin (c->dentry, c->lowerpath.dentry, temp); -if (err) -return err; -return 0 ; +return err; }static int ovl_copy_up_locked (struct ovl_copy_up_ctx *c)
Signature · 1 line
2.13.6
PATCH v7 6/14 ovl: Move couple of copy up functions in copy_up.c
In reply to Vivek GoyalRight now two copy up related functions are part of inode.c. Amir suggested it might be better to move these to copy_up.c. Hence this patch does that. There will one more related function which will come in later patch.
--- fs/overlayfs/copy_up.c | 30 ++++++++++++++++++++++++++++++ fs/overlayfs/inode.c | 30 ------------------------------ 2 files changed, 30 insertions(+), 30 deletions(-)
fs/overlayfs/copy_up.c +30 -0
@@ -232,6 +232,36 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)return err; } +static bool ovl_open_need_copy_up (struct dentry *dentry,int flags) +{ +if (ovl_dentry_upper (dentry) && +ovl_dentry_has_upper_alias (dentry)) +return false; + +if (special_file (d_inode (dentry)->i_mode)) +return false; + +if (!(OPEN_FMODE (flags) &FMODE_WRITE ) && !(flags &O_TRUNC )) +return false; + +return true; +} + +int ovl_open_maybe_copy_up (struct dentry *dentry,unsigned int file_flags) +{ +int err =0 ; + +if (ovl_open_need_copy_up (dentry, file_flags)) { + err =ovl_want_write (dentry); +if (!err) { + err =ovl_copy_up_flags (dentry, file_flags); +ovl_drop_write (dentry); + } + } + +return err; +} +struct ovl_fh *ovl_encode_fh (struct dentry *lower,bool is_upper) {struct ovl_fh *fh;
fs/overlayfs/inode.c +0 -30
@@ -341,36 +341,6 @@ struct posix_acl *ovl_get_acl(struct inode *inode, int type)return acl; } -static bool ovl_open_need_copy_up (struct dentry *dentry,int flags) -{ -if (ovl_dentry_upper (dentry) && -ovl_dentry_has_upper_alias (dentry)) -return false; - -if (special_file (d_inode (dentry)->i_mode)) -return false; - -if (!(OPEN_FMODE (flags) &FMODE_WRITE ) && !(flags &O_TRUNC )) -return false; - -return true; -} - -int ovl_open_maybe_copy_up (struct dentry *dentry,unsigned int file_flags) -{ -int err =0 ; - -if (ovl_open_need_copy_up (dentry, file_flags)) { - err =ovl_want_write (dentry); -if (!err) { - err =ovl_copy_up_flags (dentry, file_flags); -ovl_drop_write (dentry); - } - } - -return err; -} -int ovl_update_time (struct inode *inode,struct timespec *ts,int flags) {struct dentry *alias;
Signature · 1 line
2.13.6
Amir Goldstein
maintainerRe: [PATCH v7 06/14] ovl: Move couple of copy up functions in copy_up.c
Quoted 2 lines · Vivek Goyal
Right now two copy up related functions are part of
inode.c. Amir suggested it might be better to move these tocopy_up.c. Hence this patch does that.
IMO it is better to phrase simply "Move the copy up helpers to copy_up.c."
Quoted 36 lines
There will one more related function which will come in later patch.
Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/copy_up.c | 30 ++++++++++++++++++++++++++++++ fs/overlayfs/inode.c | 30 ------------------------------ 2 files changed, 30 insertions(+), 30 deletions(-)fs/overlayfs/copy_up.c +20
-0@@ -232,6 +232,36 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)return err; } +static bool ovl_open_need_copy_up (struct dentry *dentry,int flags) +{ +if (ovl_dentry_upper (dentry) && +ovl_dentry_has_upper_alias (dentry)) +return false; + +if (special_file (d_inode (dentry)->i_mode)) +return false; + +if (!(OPEN_FMODE (flags) &FMODE_WRITE ) && !(flags &O_TRUNC )) +return false; + +return true; +} + +int ovl_open_maybe_copy_up (struct dentry *dentry,unsigned int file_flags) +{ +int err =0 ; + +if (ovl_open_need_copy_up (dentry, file_flags)) {
fs/overlayfs/copy_up.c int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat) · line 264
Quoted 10 lines
fs/overlayfs/copy_up.c +10
-0+ err =ovl_want_write (dentry); +if (!err) { + err =ovl_copy_up_flags (dentry, file_flags); +ovl_drop_write (dentry); + } + } + +return err; +} +
I would put there at the bottom ,below ovl_copy_up_flags, where they belong logically (this file is sorted bottom up.
And also need to move the declaration of ovl_open_maybe_copy_up() in overlayfs.h to the respective location.
Thanks,
Amir.
Re: [PATCH v7 06/14] ovl: Move couple of copy up functions in copy_up.c
Quoted 47 lines · Amir Goldstein
Quoted 2 lines · Vivek Goyal
Right now two copy up related functions are part of
inode.c. Amir suggested it might be better to move these tocopy_up.c. Hence this patch does that.IMO it is better to phrase simply "Move the copy up helpers to
copy_up.c."Quoted 41 lines
There will one more related function which will come in later patch.
Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/copy_up.c | 30 ++++++++++++++++++++++++++++++ fs/overlayfs/inode.c | 30 ------------------------------ 2 files changed, 30 insertions(+), 30 deletions(-)fs/overlayfs/copy_up.c +25
-0@@ -232,6 +232,36 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)return err; } +static bool ovl_open_need_copy_up (struct dentry *dentry,int flags) +{ +if (ovl_dentry_upper (dentry) && +ovl_dentry_has_upper_alias (dentry)) +return false; + +if (special_file (d_inode (dentry)->i_mode)) +return false; + +if (!(OPEN_FMODE (flags) &FMODE_WRITE ) && !(flags &O_TRUNC )) +return false; + +return true; +} + +int ovl_open_maybe_copy_up (struct dentry *dentry,unsigned int file_flags) +{ +int err =0 ; + +if (ovl_open_need_copy_up (dentry, file_flags)) { + err =ovl_want_write (dentry); +if (!err) { + err =ovl_copy_up_flags (dentry, file_flags); +ovl_drop_write (dentry); + }
Quoted 8 lines
fs/overlayfs/copy_up.c int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat) · line 264
Quoted 5 lines
fs/overlayfs/copy_up.c +5
-0+ } + +return err; +} +I would put there at the bottom ,below
ovl_copy_up_flags, where they belong logically (this file is sorted bottom up.
Ok, I will keep these functions below ovl_copy_up_flag().
But ovl_copy_up_meta_inode_data() is called by ovl_copy_up_one(). So will keep that above ovl_copy_up_one().
Quoted 3 lines
And also need to move the declaration of
ovl_open_maybe_copy_up()inoverlayfs.hto the respective location.
Ok, will do.
Vivek
Amir Goldstein
maintainerRe: [PATCH v7 06/14] ovl: Move couple of copy up functions in copy_up.c
Quoted 57 lines · Vivek Goyal
Quoted 47 lines · Amir Goldstein
Quoted 2 lines · Vivek Goyal
Right now two copy up related functions are part of
inode.c. Amir suggested it might be better to move these tocopy_up.c. Hence this patch does that.IMO it is better to phrase simply "Move the copy up helpers to
copy_up.c."Quoted 41 lines
There will one more related function which will come in later patch.
Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/copy_up.c | 30 ++++++++++++++++++++++++++++++ fs/overlayfs/inode.c | 30 ------------------------------ 2 files changed, 30 insertions(+), 30 deletions(-)fs/overlayfs/copy_up.c +25
-0@@ -232,6 +232,36 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)return err; } +static bool ovl_open_need_copy_up (struct dentry *dentry,int flags) +{ +if (ovl_dentry_upper (dentry) && +ovl_dentry_has_upper_alias (dentry)) +return false; + +if (special_file (d_inode (dentry)->i_mode)) +return false; + +if (!(OPEN_FMODE (flags) &FMODE_WRITE ) && !(flags &O_TRUNC )) +return false; + +return true; +} + +int ovl_open_maybe_copy_up (struct dentry *dentry,unsigned int file_flags) +{ +int err =0 ; + +if (ovl_open_need_copy_up (dentry, file_flags)) { + err =ovl_want_write (dentry); +if (!err) { + err =ovl_copy_up_flags (dentry, file_flags); +ovl_drop_write (dentry); + }Quoted 8 lines
fs/overlayfs/copy_up.c int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat) · line 264
Quoted 5 lines
fs/overlayfs/copy_up.c +5
-0+ } + +return err; +} +I would put there at the bottom ,below
ovl_copy_up_flags, where they belong logically (this file is sorted bottom up.
Quoted 5 lines
Ok, I will keep these functions below
ovl_copy_up_flag().But
ovl_copy_up_meta_inode_data()is called byovl_copy_up_one(). So will keep that aboveovl_copy_up_one().
Of course, it is an inner function and those are outer functions.
Amir.
PATCH v7 7/14 ovl: Copy up only metadata during copy up where it makes sense
In reply to Vivek GoyalIf it makes sense to copy up only metadata during copy up, do it. This is done for regular files which are not opened for WRITE and have origin being saved.
Right now ->metacopy is set to 0 always. Last patch in the series will remove the hard coded statement and enable metacopy feature.
--- fs/overlayfs/copy_up.c | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-)
fs/overlayfs/copy_up.c +31 -7
@@ -262,6 +262,26 @@ int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags)return err; } +static bool ovl_need_meta_copy_up (struct dentry *dentry,umode_t mode, +int flags) +{ +struct ovl_fs *ofs = dentry->d_sb->s_fs_info; + +/* TODO: Will enable metacopy in last patch of series */ +return false; + +if (!ofs->config.metacopy) +return false; + +if (!S_ISREG (mode)) +return false; + +if (flags && ((OPEN_FMODE (flags) &FMODE_WRITE ) || (flags &O_TRUNC ))) +return false; + +return true; +} +struct ovl_fh *ovl_encode_fh (struct dentry *lower,bool is_upper) {struct ovl_fh *fh;
@@ -335,11 +355,8 @@ static int ovl_set_origin(struct dentry *dentry, struct dentry *lower,return PTR_ERR (fh); } -/* -* Do not fail when upper doesn't support xattrs. -*/ err =ovl_check_setxattr (dentry, upper,OVL_XATTR_ORIGIN , fh, - fh ? fh->len :0 ,0 ); + fh ? fh->len :0 , -EOPNOTSUPP );kfree (fh);return err;
@@ -356,6 +373,7 @@ struct ovl_copy_up_ctx {struct qstr destname;struct dentry *workdir;bool tmpfile; +bool metacopy; };static int ovl_link_up (struct ovl_copy_up_ctx *c)
@@ -483,10 +501,14 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)* */ err =ovl_set_origin (c->dentry, c->lowerpath.dentry, temp); -if (err) -return err; +if (err) { +if (err != -EOPNOTSUPP ) +return err; +/* Upper does not support xattr. Copy up data as well */ + c->metacopy = false; + } -if (S_ISREG (c->stat.mode)) { +if (S_ISREG (c->stat.mode) && !c->metacopy) {struct path upperpath;ovl_path_upper (c->dentry, &upperpath);
@@ -631,6 +653,8 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,if (err)return err; + ctx.metacopy =ovl_need_meta_copy_up (dentry, ctx.stat.mode, flags); +ovl_path_upper (parent, &parentpath); ctx.destdir = parentpath.dentry; ctx.destname = dentry->d_name;
Signature · 1 line
2.13.6
Re: [PATCH v7 07/14] ovl: Copy up only metadata during copy up where it makes sense
Quoted 8 lines · Vivek Goyal
If it makes sense to copy up only metadata during copy up, do it. This is done for regular files which are not opened for WRITE and have origin being saved.
Right now ->metacopy is set to 0 always. Last patch in the series will remove the hard coded statement and enable metacopy feature.
Signed-off-by Vivek Goyal <[email protected]>
PATCH v7 8/14 ovl: Add helper ovl_already_copied_up()
In reply to Vivek GoyalThere are couple of places where we need to know if file is already copied up (in lockless manner). Right now its open coded and there are only two conditions to check. Soon this patch series will introduce another condition to check and Amir wants to introduce one more. So introduce a helper instead to check this so that code is easier to read.
--- fs/overlayfs/copy_up.c | 19 ++----------------- fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 24 +++++++++++++++++++++++- 3 files changed, 26 insertions(+), 18 deletions(-)
fs/overlayfs/copy_up.c +2 -17
@@ -234,8 +234,7 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)static bool ovl_open_need_copy_up (struct dentry *dentry,int flags) { -if (ovl_dentry_upper (dentry) && -ovl_dentry_has_upper_alias (dentry)) +if (ovl_already_copied_up (dentry))return false;if (special_file (d_inode (dentry)->i_mode))
@@ -701,21 +700,7 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)struct dentry *next;struct dentry *parent; -/* -* Check if copy-up has happened as well as for upper alias (in -* case of hard links) is there. -* -* Both checks are lockless: -* - false negatives: will recheck under oi->lock -* - false positives: -* + ovl_dentry_upper() uses memory barriers to ensure the -* upper dentry is up-to-date -* + ovl_dentry_has_upper_alias() relies on locking of -* upper parent i_rwsem to prevent reordering copy-up -* with rename. -*/ -if (ovl_dentry_upper (dentry) && -ovl_dentry_has_upper_alias (dentry)) +if (ovl_already_copied_up (dentry))break ; next =dget (dentry);
fs/overlayfs/overlayfs.h +1 -0
@@ -227,6 +227,7 @@ bool ovl_is_whiteout(struct dentry *dentry);struct file *ovl_path_open (struct path *path,int flags);int ovl_copy_up_start (struct dentry *dentry);void ovl_copy_up_end (struct dentry *dentry); +bool ovl_already_copied_up (struct dentry *dentry);bool ovl_check_origin_xattr (struct dentry *dentry);bool ovl_check_dir_xattr (struct dentry *dentry,const char *name); int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
fs/overlayfs/util.c +23 -1
@@ -314,13 +314,35 @@ struct file *ovl_path_open(struct path *path, int flags)return dentry_open (path, flags |O_NOATIME ,current_cred ()); } +bool ovl_already_copied_up (struct dentry *dentry) +{ +/* +* Check if copy-up has happened as well as for upper alias (in +* case of hard links) is there. +* +* Both checks are lockless: +* - false negatives: will recheck under oi->lock +* - false positives: +* + ovl_dentry_upper() uses memory barriers to ensure the +* upper dentry is up-to-date +* + ovl_dentry_has_upper_alias() relies on locking of +* upper parent i_rwsem to prevent reordering copy-up +* with rename. +*/ +if (ovl_dentry_upper (dentry) && +ovl_dentry_has_upper_alias (dentry)) +return true; + +return false; +} +int ovl_copy_up_start (struct dentry *dentry) {struct ovl_inode *oi =OVL_I (d_inode (dentry));int err; err =mutex_lock_interruptible (&oi->lock); -if (!err &&ovl_dentry_has_upper_alias (dentry)) { +if (!err &&ovl_already_copied_up (dentry)) { err =1 ;/* Already copied up */ mutex_unlock (&oi->lock); }
Signature · 1 line
2.13.6
PATCH v7 9/14 ovl: A new xattr OVL_XATTR_METACOPY for file on upper
In reply to Vivek GoyalNow we will have the capability to have upper inodes which might be only metadata copy up and data is still on lower inode. So add a new xattr OVL_XATTR_METACOPY to distinguish between two cases.
Presence of OVL_XATTR_METACOPY reflects that file has been copied up metadata only and and data will be copied up later from lower origin.
So this xattr is set when a metadata copy takes place and cleared when data copy takes place.
We also use a bit in ovl_inode->flags to cache OVL_UPPERDATA which reflects whether ovl inode has data or not (as opposed to metadata only copy up).
If a file is copied up metadata only and later when same file is opened for WRITE, then data copy up takes place. We copy up data, remove METACOPY xattr and then set the UPPERDATA flag in ovl_entry->flags. While all these operations happen with oi->lock held, read side of oi->flags can be lockless. That is another thread on another cpu can check if UPPERDATA flag is set or not.
So this gives us an ordering requirement w.r.t UPPERDATA flag. That is, if another cpu sees UPPERDATA flag set, then it should be guaranteed that effects of data copy up and remove xattr operations are also visible.
For example.
CPU1 CPU2
ovl_d_real() acquire(oi->lock)
ovl_open_maybe_copy_up() ovl_copy_up_data()
open_open_need_copy_up() vfs_removexattr()
ovl_already_copied_up()
ovl_dentry_needs_data_copy_up() ovl_set_flag(OVL_UPPERDATA)
ovl_test_flag(OVL_UPPERDATA) release(oi->lock)Say CPU2 is copying up data and in the end sets UPPERDATA flag. But if CPU1 perceives the effects of setting UPPERDATA flag but not the effects of preceeding operations (ex. upper that is not fully copied up), it will be a problem.
Hence this patch introduces smp_wmb() on setting UPPERDATA flag operation and smp_rmb() on UPPERDATA flag test operation.
May be some other lock or barrier is already covering it. But I am not sure what that is and is it obvious enough that we will not break it in future.
So hence trying to be safe here and introducing barriers explicitly for UPPERDATA flag/bit.
Note, we now set OVL_UPPERDATA on all kind of dentires and check OVL_UPPERDATA only where it makes sense. If a file is created on upper, we set OVL_UPPERDATA. This can be accessed lockless in ovl_d_real() path again. This time ordering dependency is w.r.t oe->__upperdata. We need to make surr OVL_UPPERDATA is visible before oe->__upperdata is visible. Otherwise following code path might try to copy up an upper only file.
ovl_d_real()
ovl_open_maybe_copy_up()
open_open_need_copy_up()
ovl_already_copied_up()
ovl_dentry_needs_data_copy_up()
ovl_test_flag(OVL_UPPERDATA)
<-- OVL_UPPERDATA is not visible yet. copy up file.
I have not introduced any barrier to take care of this. There is already a check ovl_should_check_upperdata() to make sure lower dentry is there otherwise return false (even if OVL_UPPERDATA is not visible yet).
--- fs/overlayfs/copy_up.c | 55 +++++++++++++++++++++++++++++++--- fs/overlayfs/dir.c | 1 + fs/overlayfs/overlayfs.h | 10 +++++-- fs/overlayfs/util.c | 78 +++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 134 insertions(+), 10 deletions(-)
fs/overlayfs/copy_up.c +51 -4
@@ -195,6 +195,16 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)return error; } +static int ovl_set_size (struct dentry *upperdentry,struct kstat *stat) +{ +struct iattr attr = { + .ia_valid =ATTR_SIZE , + .ia_size = stat->size, + }; + +return notify_change (upperdentry, &attr,NULL ); +} +static int ovl_set_timestamps (struct dentry *upperdentry,struct kstat *stat) {struct iattr attr = {
@@ -234,7 +244,7 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)static bool ovl_open_need_copy_up (struct dentry *dentry,int flags) { -if (ovl_already_copied_up (dentry)) +if (ovl_already_copied_up (dentry, flags))return false;if (special_file (d_inode (dentry)->i_mode))
@@ -486,6 +496,28 @@ static int ovl_get_tmpfile(struct ovl_copy_up_ctx *c, struct dentry **tempp) goto out; } +/* Copy up data of an inode which was copied up metadata only in the past. */ +static int ovl_copy_up_meta_inode_data (struct ovl_copy_up_ctx *c) +{ +struct path upperpath; +int err; + +ovl_path_upper (c->dentry, &upperpath); +if (WARN_ON (upperpath.dentry ==NULL )) +return -EIO ; + + err =ovl_copy_up_data (&c->lowerpath, &upperpath, c->stat.size); +if (err) +return err; + + err=vfs_removexattr (upperpath.dentry,OVL_XATTR_METACOPY ); +if (err) +return err; + +ovl_set_upperdata (c->dentry); +return err; +} +static int ovl_copy_up_inode (struct ovl_copy_up_ctx *c,struct dentry *temp) {int err;
@@ -519,8 +551,19 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)return err; } +if (c->metacopy) { + err =ovl_check_setxattr (c->dentry, temp,OVL_XATTR_METACOPY , +NULL ,0 , -EOPNOTSUPP ); +if (err) +return err; + } + +inode_lock (temp->d_inode); - err =ovl_set_attr (temp, &c->stat); +if (c->metacopy) + err =ovl_set_size (temp, &c->stat); +if (!err) + err =ovl_set_attr (temp, &c->stat);inode_unlock (temp->d_inode);return err;
@@ -552,6 +595,8 @@ static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)if (err) goto out_cleanup; +if (!c->metacopy) +ovl_set_upperdata (c->dentry); inode =d_inode (c->dentry);ovl_inode_update (inode, newdentry);if (S_ISDIR (inode->i_mode))
@@ -674,7 +719,7 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, }ovl_do_check_copy_up (ctx.lowerpath.dentry); - err =ovl_copy_up_start (dentry); + err =ovl_copy_up_start (dentry, flags);/* err < 0: interrupted, err > 0: raced with another copy-up */ if (unlikely (err)) {if (err >0 )
@@ -684,6 +729,8 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, err =ovl_do_copy_up (&ctx);if (!err && !ovl_dentry_has_upper_alias (dentry)) err =ovl_link_up (&ctx); +if (!err &&ovl_dentry_needs_data_copy_up_locked (dentry, flags)) + err =ovl_copy_up_meta_inode_data (&ctx);ovl_copy_up_end (dentry); }do_delayed_call (&done);
@@ -700,7 +747,7 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)struct dentry *next;struct dentry *parent; -if (ovl_already_copied_up (dentry)) +if (ovl_already_copied_up (dentry, flags))break ; next =dget (dentry);
fs/overlayfs/dir.c +1 -0
@@ -158,6 +158,7 @@ static void ovl_instantiate(struct dentry *dentry, struct inode *inode,ovl_dentry_version_inc (dentry->d_parent, false);ovl_dentry_set_upper_alias (dentry);if (!hardlink) { +ovl_set_flag (OVL_UPPERDATA , inode);ovl_inode_update (inode, newdentry);ovl_copyattr (newdentry->d_inode, inode); }else {
fs/overlayfs/overlayfs.h +8 -2
@@ -27,6 +27,7 @@ enum ovl_path_type {#define OVL_XATTR_ORIGIN OVL_XATTR_PREFIX "origin"#define OVL_XATTR_IMPURE OVL_XATTR_PREFIX "impure"#define OVL_XATTR_NLINK OVL_XATTR_PREFIX "nlink" +#define OVL_XATTR_METACOPY OVL_XATTR_PREFIX "metacopy"enum ovl_flag {/* Pure upper dir that may contain non pure upper entries */
@@ -34,6 +35,7 @@ enum ovl_flag {/* Non-merge dir that may contain whiteout entries */ OVL_WHITEOUTS ,OVL_INDEX , + OVL_UPPERDATA, };/*
@@ -215,6 +217,10 @@ bool ovl_dentry_is_whiteout(struct dentry *dentry);void ovl_dentry_set_opaque (struct dentry *dentry);bool ovl_dentry_has_upper_alias (struct dentry *dentry);void ovl_dentry_set_upper_alias (struct dentry *dentry); +bool ovl_dentry_needs_data_copy_up (struct dentry *dentry,int flags); +bool ovl_dentry_needs_data_copy_up_locked (struct dentry *dentry,int flags); +bool ovl_has_upperdata (struct dentry *dentry); +void ovl_set_upperdata (struct dentry *dentry);bool ovl_redirect_dir (struct super_block *sb);const char *ovl_dentry_get_redirect (struct dentry *dentry);void ovl_dentry_set_redirect (struct dentry *dentry,const char *redirect);
@@ -225,9 +231,9 @@ void ovl_dentry_version_inc(struct dentry *dentry, bool impurity);u64 ovl_dentry_version_get (struct dentry *dentry);bool ovl_is_whiteout (struct dentry *dentry);struct file *ovl_path_open (struct path *path,int flags); -int ovl_copy_up_start (struct dentry *dentry); +int ovl_copy_up_start (struct dentry *dentry,int flags);void ovl_copy_up_end (struct dentry *dentry); -bool ovl_already_copied_up (struct dentry *dentry); +bool ovl_already_copied_up (struct dentry *dentry,int flags);bool ovl_check_origin_xattr (struct dentry *dentry);bool ovl_check_dir_xattr (struct dentry *dentry,const char *name); int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
fs/overlayfs/util.c +74 -4
106 lines not on this page · raw message
Signature · 1 line
2.13.6
Amir Goldstein
maintainerRe: [PATCH v7 09/14] ovl: A new xattr OVL_XATTR_METACOPY for file on upper
Quoted 38 lines · Vivek Goyal
Now we will have the capability to have upper inodes which might be only metadata copy up and data is still on lower inode. So add a new xattr
OVL_XATTR_METACOPYto distinguish between two cases.Presence of
OVL_XATTR_METACOPYreflects that file has been copied up metadata only and and data will be copied up later from lower origin.
So this xattr is set when a metadata copy takes place and cleared when data copy takes place.We also use a bit in
ovl_inode->flagsto cacheOVL_UPPERDATAwhich reflects whether ovl inode has data or not (as opposed to metadata only copy up).If a file is copied up metadata only and later when same file is opened for WRITE, then data copy up takes place. We copy up data, remove METACOPY xattr and then set the UPPERDATA flag in
ovl_entry->flags. While all these operations happen withoi->lockheld, read side ofoi->flagscan be lockless. That is another thread on another cpu can check if UPPERDATA flag is set or not.So this gives us an ordering requirement w.r.t UPPERDATA flag. That is, if another cpu sees UPPERDATA flag set, then it should be guaranteed that effects of data copy up and remove xattr operations are also visible.
For example.
CPU1 CPU2
ovl_d_real()acquire(oi->lock)
ovl_open_maybe_copy_up()ovl_copy_up_data()
open_open_need_copy_up()vfs_removexattr()
ovl_already_copied_up()ovl_dentry_needs_data_copy_up() ovl_set_flag(OVL_UPPERDATA) ovl_test_flag(OVL_UPPERDATA) release(oi->lock)Say CPU2 is copying up data and in the end sets UPPERDATA flag. But if CPU1 perceives the effects of setting UPPERDATA flag but not the effects of preceeding operations (ex. upper that is not fully copied up), it will be a problem.
Quoted 9 lines
Hence this patch introduces
smp_wmb()on setting UPPERDATA flag operation andsmp_rmb()on UPPERDATA flag test operation.May be some other lock or barrier is already covering it. But I am not sure what that is and is it obvious enough that we will not break it in future.
So hence trying to be safe here and introducing barriers explicitly for UPPERDATA flag/bit.
Vivek,
I like this version a lot more, but IMO it could still be even simpler.
Quoted 7 lines
Note, we now set
OVL_UPPERDATAon all kind of dentires and checkOVL_UPPERDATAonly where it makes sense. If a file is created on upper, we setOVL_UPPERDATA. This can be accessed lockless inovl_d_real()path again. This time ordering dependency is w.r.toe->__upperdata. We need to make surrOVL_UPPERDATAis visible beforeoe->__upperdatais visible. Otherwise following code path might try to copy up an upper only file.
Why all this explanations?
Just use ovl_set_upperdata() when creating upper and be done with it. Creating new upper is expensive anyway, so I don't think we should care about an unneeded smp_wmb() and probably Miklos will know to tell why it is not needed anyway.
It is very easy to make sure that the OVL_UPPERDATA is always set for the pure upper and non regular file cases and then we have no need for ovl_should_check_upperdata().
Simple is better.
There is just one more thing you need to do before killing ovl_should_check_upperdata() - set the OVL_UPPERDATA on the root inode in ovl_fill_super().
Quoted 142 lines
ovl_d_real()
ovl_open_maybe_copy_up()
open_open_need_copy_up()
ovl_already_copied_up()
ovl_dentry_needs_data_copy_up()
ovl_test_flag(OVL_UPPERDATA)
<--OVL_UPPERDATAis not visible yet. copy up file.I have not introduced any barrier to take care of this. There is already a check
ovl_should_check_upperdata()to make sure lower dentry is there otherwise return false (even ifOVL_UPPERDATAis not visible yet).Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/copy_up.c | 55 +++++++++++++++++++++++++++++++--- fs/overlayfs/dir.c | 1 + fs/overlayfs/overlayfs.h | 10 +++++-- fs/overlayfs/util.c | 78 +++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 134 insertions(+), 10 deletions(-)fs/overlayfs/copy_up.c +51
-4@@ -195,6 +195,16 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)return error; } +static int ovl_set_size (struct dentry *upperdentry,struct kstat *stat) +{ +struct iattr attr = { + .ia_valid =ATTR_SIZE , + .ia_size = stat->size, + }; + +return notify_change (upperdentry, &attr,NULL ); +} +static int ovl_set_timestamps (struct dentry *upperdentry,struct kstat *stat) {struct iattr attr = {@@ -234,7 +244,7 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)static bool ovl_open_need_copy_up (struct dentry *dentry,int flags) { -if (ovl_already_copied_up (dentry)) +if (ovl_already_copied_up (dentry, flags))return false;if (special_file (d_inode (dentry)->i_mode))@@ -486,6 +496,28 @@ static int ovl_get_tmpfile(struct ovl_copy_up_ctx *c, struct dentry **tempp) goto out; } +/* Copy up data of an inode which was copied up metadata only in the past. */ +static int ovl_copy_up_meta_inode_data (struct ovl_copy_up_ctx *c) +{ +struct path upperpath; +int err; + +ovl_path_upper (c->dentry, &upperpath); +if (WARN_ON (upperpath.dentry ==NULL )) +return -EIO ; + + err =ovl_copy_up_data (&c->lowerpath, &upperpath, c->stat.size); +if (err) +return err; + + err=vfs_removexattr (upperpath.dentry,OVL_XATTR_METACOPY ); +if (err) +return err; + +ovl_set_upperdata (c->dentry); +return err; +} +static int ovl_copy_up_inode (struct ovl_copy_up_ctx *c,struct dentry *temp) {int err;@@ -519,8 +551,19 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)return err; } +if (c->metacopy) { + err =ovl_check_setxattr (c->dentry, temp,OVL_XATTR_METACOPY , +NULL ,0 , -EOPNOTSUPP ); +if (err) +return err; + } + +inode_lock (temp->d_inode); - err =ovl_set_attr (temp, &c->stat); +if (c->metacopy) + err =ovl_set_size (temp, &c->stat); +if (!err) + err =ovl_set_attr (temp, &c->stat);inode_unlock (temp->d_inode);return err;@@ -552,6 +595,8 @@ static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)if (err) goto out_cleanup; +if (!c->metacopy) +ovl_set_upperdata (c->dentry); inode =d_inode (c->dentry);ovl_inode_update (inode, newdentry);if (S_ISDIR (inode->i_mode))@@ -674,7 +719,7 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, }ovl_do_check_copy_up (ctx.lowerpath.dentry); - err =ovl_copy_up_start (dentry); + err =ovl_copy_up_start (dentry, flags);/* err < 0: interrupted, err > 0: raced with another copy-up */ if (unlikely (err)) {if (err >0 )@@ -684,6 +729,8 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, err =ovl_do_copy_up (&ctx);if (!err && !ovl_dentry_has_upper_alias (dentry)) err =ovl_link_up (&ctx); +if (!err &&ovl_dentry_needs_data_copy_up_locked (dentry, flags)) + err =ovl_copy_up_meta_inode_data (&ctx);ovl_copy_up_end (dentry); }do_delayed_call (&done);@@ -700,7 +747,7 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)struct dentry *next;struct dentry *parent; -if (ovl_already_copied_up (dentry)) +if (ovl_already_copied_up (dentry, flags))break ; next =dget (dentry);fs/overlayfs/dir.c +0
-0
fs/overlayfs/dir.c static void ovl_instantiate(struct dentry *dentry, struct inode *inode, · line 161
Quoted 4 lines
fs/overlayfs/dir.c +1
-0@@ -158,6 +158,7 @@ static void ovl_instantiate(struct dentry *dentry, struct inode *inode,ovl_dentry_version_inc (dentry->d_parent, false);ovl_dentry_set_upper_alias (dentry);if (!hardlink) { +ovl_set_flag (OVL_UPPERDATA , inode);
Call ovl_set_upperdata() instead?
Quoted 58 lines
fs/overlayfs/dir.c +0
-0ovl_inode_update (inode, newdentry);ovl_copyattr (newdentry->d_inode, inode); }else {fs/overlayfs/overlayfs.h +8
-2@@ -27,6 +27,7 @@ enum ovl_path_type {#define OVL_XATTR_ORIGIN OVL_XATTR_PREFIX "origin"#define OVL_XATTR_IMPURE OVL_XATTR_PREFIX "impure"#define OVL_XATTR_NLINK OVL_XATTR_PREFIX "nlink" +#define OVL_XATTR_METACOPY OVL_XATTR_PREFIX "metacopy"enum ovl_flag {/* Pure upper dir that may contain non pure upper entries */ @@ -34,6 +35,7 @@ enum ovl_flag {/* Non-merge dir that may contain whiteout entries */ OVL_WHITEOUTS ,OVL_INDEX , + OVL_UPPERDATA, };/* @@ -215,6 +217,10 @@ bool ovl_dentry_is_whiteout(struct dentry *dentry);void ovl_dentry_set_opaque (struct dentry *dentry);bool ovl_dentry_has_upper_alias (struct dentry *dentry);void ovl_dentry_set_upper_alias (struct dentry *dentry); +bool ovl_dentry_needs_data_copy_up (struct dentry *dentry,int flags); +bool ovl_dentry_needs_data_copy_up_locked (struct dentry *dentry,int flags); +bool ovl_has_upperdata (struct dentry *dentry); +void ovl_set_upperdata (struct dentry *dentry);bool ovl_redirect_dir (struct super_block *sb);const char *ovl_dentry_get_redirect (struct dentry *dentry);void ovl_dentry_set_redirect (struct dentry *dentry,const char *redirect);@@ -225,9 +231,9 @@ void ovl_dentry_version_inc(struct dentry *dentry, bool impurity);u64 ovl_dentry_version_get (struct dentry *dentry);bool ovl_is_whiteout (struct dentry *dentry);struct file *ovl_path_open (struct path *path,int flags); -int ovl_copy_up_start (struct dentry *dentry); +int ovl_copy_up_start (struct dentry *dentry,int flags);void ovl_copy_up_end (struct dentry *dentry); -bool ovl_already_copied_up (struct dentry *dentry); +bool ovl_already_copied_up (struct dentry *dentry,int flags);bool ovl_check_origin_xattr (struct dentry *dentry);bool ovl_check_dir_xattr (struct dentry *dentry,const char *name); int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,fs/overlayfs/util.c +4
-0@@ -231,6 +231,64 @@ void ovl_dentry_set_upper_alias(struct dentry *dentry) oe->has_upper = true; } +static bool ovl_should_check_upperdata (struct dentry *dentry) { +if (!S_ISREG (d_inode (dentry)->i_mode)) +return false; +
fs/overlayfs/util.c void ovl_dentry_set_upper_alias(struct dentry *dentry) · line 247
Quoted 10 lines
fs/overlayfs/util.c +10
-0+if (!ovl_dentry_lower (dentry)) +return false; + +return true; +} + +bool ovl_has_upperdata (struct dentry *dentry) { +if (!ovl_should_check_upperdata (dentry)) +return true; +
IMO we don't need this check, but you may leave it as if (WARN_ON(!ovl_should_check_upperdata(dentry) after ovl_test_flag() to be on the safe side.
fs/overlayfs/util.c void ovl_dentry_set_upper_alias(struct dentry *dentry) · line 251
Quoted 4 lines
fs/overlayfs/util.c +4
-0+if (!ovl_test_flag (OVL_UPPERDATA ,d_inode (dentry))) +return false; +/* +* Pairs with smp_wmb() in ovl_copy_up_meta_inode_data(). Make sure
To be consistent, you should update the comment to say pairs with... in ovl_set_upperdata(), although it may be useful to leave the information that the main user of ovl_set_upperdata() is ovl_copy_up_meta_inode_data().
Quoted 8 lines
fs/overlayfs/util.c +8
-0+* if setting of OVL_UPPERDATA is visible, then effects of writes +* before that are visible too. +*/ +smp_rmb (); +return true; +} + +void ovl_set_upperdata (struct dentry *dentry) {
fs/overlayfs/util.c void ovl_dentry_set_upper_alias(struct dentry *dentry) · line 269
Quoted 10 lines
fs/overlayfs/util.c +10
-0+/* +* Pairs with smp_rmb() in ovl_has_upperdata(). Make sure +* if OVL_UPPERDATA flag is visible, then effects of write operations +* before it are visible as well. +*/ +smp_wmb (); +ovl_set_flag (OVL_UPPERDATA ,d_inode (dentry)); +} + +static inline bool open_for_write (int flags)
Need ovl_ namespace prefix
and the name sounds like an action (i.e. a request to open for write)
fs/overlayfs/util.c void ovl_dentry_set_upper_alias(struct dentry *dentry) · line 271
Quoted 2 lines
fs/overlayfs/util.c +2
-0+{ +if (flags && (OPEN_FMODE (flags) &FMODE_WRITE ))
What I meant in previous review is that you make a static inline helper in overlayfs.h to be used by this and ovl_open_need_copy_up(). Your helper is missing the O_TRUNC test, i.e.
return ((OPEN_FMODE(flags) & FMODE_WRITE) || (flags & O_TRUNC));
It may sound like we don't *need* to copy up data on O_TRUNC, but in fact, if we don't call copy up functions, we won't be cleaning the metacopy xattr/flag on truncate.
The thing is that the result of O_TRUNC|O_RDONLY is not even well defined by man page ("... Otherwise, the effect of O_TRUNC is unspecified"), but we shouldn't change the way overlayfs treats this strange combination.
fs/overlayfs/util.c void ovl_dentry_set_upper_alias(struct dentry *dentry) · line 277
Quoted 6 lines
fs/overlayfs/util.c +6
-0+return true; + +return false; +} + +/* Caller should hold ovl_entry->locked */
...should hold ovl_inode->lock
Eventually, Miklos may decide that the _locked variants are not needed and that it is
not expensive to call the lockless variants in locked path, but I am happy we can see how this looks like. It looks cleaner to me and not over complicated.
Thanks,
Amir.
Re: [PATCH v7 09/14] ovl: A new xattr OVL_XATTR_METACOPY for file on upper
Quoted 51 lines · Amir Goldstein
Quoted 38 lines · Vivek Goyal
Now we will have the capability to have upper inodes which might be only metadata copy up and data is still on lower inode. So add a new xattr
OVL_XATTR_METACOPYto distinguish between two cases.Presence of
OVL_XATTR_METACOPYreflects that file has been copied up metadata only and and data will be copied up later from lower origin.
So this xattr is set when a metadata copy takes place and cleared when data copy takes place.We also use a bit in
ovl_inode->flagsto cacheOVL_UPPERDATAwhich reflects whether ovl inode has data or not (as opposed to metadata only copy up).If a file is copied up metadata only and later when same file is opened for WRITE, then data copy up takes place. We copy up data, remove METACOPY xattr and then set the UPPERDATA flag in
ovl_entry->flags. While all these operations happen withoi->lockheld, read side ofoi->flagscan be lockless. That is another thread on another cpu can check if UPPERDATA flag is set or not.So this gives us an ordering requirement w.r.t UPPERDATA flag. That is, if another cpu sees UPPERDATA flag set, then it should be guaranteed that effects of data copy up and remove xattr operations are also visible.
For example.
CPU1 CPU2
ovl_d_real()acquire(oi->lock)
ovl_open_maybe_copy_up()ovl_copy_up_data()
open_open_need_copy_up()vfs_removexattr()
ovl_already_copied_up()ovl_dentry_needs_data_copy_up() ovl_set_flag(OVL_UPPERDATA) ovl_test_flag(OVL_UPPERDATA) release(oi->lock)Say CPU2 is copying up data and in the end sets UPPERDATA flag. But if CPU1 perceives the effects of setting UPPERDATA flag but not the effects of preceeding operations (ex. upper that is not fully copied up), it will be a problem.
Quoted 9 lines
Hence this patch introduces
smp_wmb()on setting UPPERDATA flag operation andsmp_rmb()on UPPERDATA flag test operation.May be some other lock or barrier is already covering it. But I am not sure what that is and is it obvious enough that we will not break it in future.
So hence trying to be safe here and introducing barriers explicitly for UPPERDATA flag/bit.
Vivek,
[..]
Quoted 10 lines
I like this version a lot more, but IMO it could still be even simpler.
Quoted 7 lines
Note, we now set
OVL_UPPERDATAon all kind of dentires and checkOVL_UPPERDATAonly where it makes sense. If a file is created on upper, we setOVL_UPPERDATA. This can be accessed lockless inovl_d_real()path again. This time ordering dependency is w.r.toe->__upperdata. We need to make surrOVL_UPPERDATAis visible beforeoe->__upperdatais visible. Otherwise following code path might try to copy up an upper only file.
Quoted 2 lines
Why all this explanations?
Just useovl_set_upperdata()when creating upper and be done with it.
Just using ovl_set_upperdata() will not do away with ordering dependency right? I mean, setting OVL_UPPERDATA in file creation path has different ordering requirement (same is the case of >has_upper). And I wanted to highlight that ordering requirement in changelogs.
I can get rid of it. But this seems such a subtle requirement, I think its a good idea to talk about it explicitly.
Quoted 3 lines
Creating new upper is expensive anyway, so I don't think we should care about an unneeded
smp_wmb()and probably Miklos will know to tell why it is not needed anyway.
Ok, I can call ovl_set_upperdata() in creation path and not worry about extra unneeded smp_wmb().
Quoted 5 lines
It is very easy to make sure that the
OVL_UPPERDATAis always set for the pure upper and non regular file cases and then we have no need forovl_should_check_upperdata().
Simple is better.
It avoids lots of smp_rmb() calls on files which should not have OVL_UPPERDATA. Now I don't know what is more expensive. smp_rmb() or calling ovl_should_check_upperdata().
Also, it calls ovl_dentry_lower() and that covers the possible race with setting of OVL_UPPERDATA on file creation and setting of oi->__upperdentry.
So if I remove this check, in-theory we open that race.
Quoted 4 lines
There is just one more thing you need to do before killing
ovl_should_check_upperdata()- set theOVL_UPPERDATAon the root inode inovl_fill_super().
That's a good point. Will do.
[..]
Quoted 11 lines
fs/overlayfs/dir.c static void ovl_instantiate(struct dentry *dentry, struct inode *inode, · line 161
Quoted 9 lines
fs/overlayfs/dir.c +1
-0@@ -158,6 +158,7 @@ static void ovl_instantiate(struct dentry *dentry, struct inode *inode,ovl_dentry_version_inc (dentry->d_parent, false);ovl_dentry_set_upper_alias (dentry);if (!hardlink) { +ovl_set_flag (OVL_UPPERDATA , inode);Call
ovl_set_upperdata()instead?
Will do.
[..]
Quoted 9 lines
fs/overlayfs/dir.c static void ovl_instantiate(struct dentry *dentry, struct inode *inode,
Quoted 4 lines
fs/overlayfs/dir.c +4
-0+bool ovl_has_upperdata (struct dentry *dentry) { +if (!ovl_should_check_upperdata (dentry)) +return true; +IMO we don't need this check, but you may leave it as if (WARN_ON(!ovl_should_check_upperdata(dentry) after
ovl_test_flag()to be on the safe side.
I primarily kept it as it avoided smp_rmb() for directories and non-regular files.
Also not sure how can I use it as WARN_ON(). Now OVL_UPPERDATA is set on all files/dir. So we will hit WARN_ON() all the time?
Quoted 11 lines
fs/overlayfs/dir.c
Quoted 6 lines
fs/overlayfs/dir.c +4
-0+if (!ovl_test_flag (OVL_UPPERDATA ,d_inode (dentry))) +return false; +/* +* Pairs with smp_wmb() in ovl_copy_up_meta_inode_data(). Make sure To be consistent, you should update the comment to say pairs with... in
ovl_set_upperdata(), although it may be useful to leave the information that the main user ofovl_set_upperdata()isovl_copy_up_meta_inode_data().
Ok.
Quoted 13 lines
Quoted 13 lines
fs/overlayfs/dir.c +13
-0+* if setting of OVL_UPPERDATA is visible, then effects of writes +* before that are visible too. +*/ +smp_rmb (); +return true; +} + +void ovl_set_upperdata (struct dentry *dentry) { +/* +* Pairs with smp_rmb() in ovl_has_upperdata(). Make sure +* if OVL_UPPERDATA flag is visible, then effects of write operations +* before it are visible as well. +*/
Quoted 8 lines
fs/overlayfs/dir.c
Quoted 5 lines
fs/overlayfs/dir.c +5
-0+smp_wmb (); +ovl_set_flag (OVL_UPPERDATA ,d_inode (dentry)); +} + +static inline bool open_for_write (int flags)Need
ovl_namespace prefix
and the name sounds like an action (i.e. a request to open for write)
ovl_opened_for_write_or_trunc()?
Quoted 10 lines
fs/overlayfs/dir.c
Quoted 3 lines
fs/overlayfs/dir.c +2
-0+{ +if (flags && (OPEN_FMODE (flags) &FMODE_WRITE ))What I meant in previous review is that you make a static inline helper in
overlayfs.hto be used by this andovl_open_need_copy_up(). Your helper is missing theO_TRUNCtest, i.e.return ((
OPEN_FMODE(flags)&FMODE_WRITE) || (flags &O_TRUNC));
Quoted 7 lines
It may sound like we don't *need* to copy up data on
O_TRUNC, but in fact, if we don't call copy up functions, we won't be cleaning the metacopy xattr/flag on truncate.The thing is that the result of O_TRUNC|O_RDONLY is not even well defined by man page ("... Otherwise, the effect of
O_TRUNCis unspecified"), but we shouldn't change the way overlayfs treats this strange combination.
Hmm.., I had not thought about O_TRUNC|O_RDONLY for a metacopy only file. Will check for it as well.
Vivek
Amir Goldstein
maintainerRe: [PATCH v7 09/14] ovl: A new xattr OVL_XATTR_METACOPY for file on upper
Quoted 67 lines · Vivek Goyal
Quoted 51 lines · Amir Goldstein
Quoted 38 lines · Vivek Goyal
Now we will have the capability to have upper inodes which might be only metadata copy up and data is still on lower inode. So add a new xattr
OVL_XATTR_METACOPYto distinguish between two cases.Presence of
OVL_XATTR_METACOPYreflects that file has been copied up metadata only and and data will be copied up later from lower origin.
So this xattr is set when a metadata copy takes place and cleared when data copy takes place.We also use a bit in
ovl_inode->flagsto cacheOVL_UPPERDATAwhich reflects whether ovl inode has data or not (as opposed to metadata only copy up).If a file is copied up metadata only and later when same file is opened for WRITE, then data copy up takes place. We copy up data, remove METACOPY xattr and then set the UPPERDATA flag in
ovl_entry->flags. While all these operations happen withoi->lockheld, read side ofoi->flagscan be lockless. That is another thread on another cpu can check if UPPERDATA flag is set or not.So this gives us an ordering requirement w.r.t UPPERDATA flag. That is, if another cpu sees UPPERDATA flag set, then it should be guaranteed that effects of data copy up and remove xattr operations are also visible.
For example.
CPU1 CPU2
ovl_d_real()acquire(oi->lock)
ovl_open_maybe_copy_up()ovl_copy_up_data()
open_open_need_copy_up()vfs_removexattr()
ovl_already_copied_up()ovl_dentry_needs_data_copy_up() ovl_set_flag(OVL_UPPERDATA) ovl_test_flag(OVL_UPPERDATA) release(oi->lock)Say CPU2 is copying up data and in the end sets UPPERDATA flag. But if CPU1 perceives the effects of setting UPPERDATA flag but not the effects of preceeding operations (ex. upper that is not fully copied up), it will be a problem.
Quoted 9 lines
Hence this patch introduces
smp_wmb()on setting UPPERDATA flag operation andsmp_rmb()on UPPERDATA flag test operation.May be some other lock or barrier is already covering it. But I am not sure what that is and is it obvious enough that we will not break it in future.
So hence trying to be safe here and introducing barriers explicitly for UPPERDATA flag/bit.
Vivek,
[..]
Quoted 10 lines
I like this version a lot more, but IMO it could still be even simpler.
Quoted 7 lines
Note, we now set
OVL_UPPERDATAon all kind of dentires and checkOVL_UPPERDATAonly where it makes sense. If a file is created on upper, we setOVL_UPPERDATA. This can be accessed lockless inovl_d_real()path again. This time ordering dependency is w.r.toe->__upperdata. We need to make surrOVL_UPPERDATAis visible beforeoe->__upperdatais visible. Otherwise following code path might try to copy up an upper only file.Quoted 2 lines
Why all this explanations?
Just useovl_set_upperdata()when creating upper and be done with it.
Quoted 8 lines
Just using
ovl_set_upperdata()will not do away with ordering dependency right? I mean, settingOVL_UPPERDATAin file creation path has different ordering requirement (same is the case of >has_upper). And I wanted to highlight that ordering requirement in changelogs.I can get rid of it. But this seems such a subtle requirement, I think its a good idea to talk about it explicitly.
I am not following. you only need to ovl_set_upperdata() before ovl_inode_update(), just like in regular copy up. Am I missing something subtle?
Quoted 8 lines
Quoted 3 lines
Creating new upper is expensive anyway, so I don't think we should care about an unneeded
smp_wmb()and probably Miklos will know to tell why it is not needed anyway.Ok, I can call
ovl_set_upperdata()in creation path and not worry about extra unneededsmp_wmb().Quoted 1 line
Quoted 8 lines
Quoted 4 lines
It is very easy to make sure that the
OVL_UPPERDATAis always set for the pure upper and non regular file cases and then we have no need forovl_should_check_upperdata().
Simple is better.It avoids lots of
smp_rmb()calls on files which should not haveOVL_UPPERDATA. Now I don't know what is more expensive.smp_rmb()or callingovl_should_check_upperdata().
You are right. smp_rmb() is potentially more expensive with many CPUs.
Quoted 41 lines
Also, it calls
ovl_dentry_lower()and that covers the possible race with setting ofOVL_UPPERDATAon file creation and setting ofoi->__upperdentry.So if I remove this check, in-theory we open that race.
Quoted 4 lines
There is just one more thing you need to do before killing
ovl_should_check_upperdata()- set theOVL_UPPERDATAon the root inode inovl_fill_super().That's a good point. Will do.
[..]
Quoted 11 lines
fs/overlayfs/dir.c static void ovl_instantiate(struct dentry *dentry, struct inode *inode, · line 161
Quoted 9 lines
fs/overlayfs/dir.c +1
-0@@ -158,6 +158,7 @@ static void ovl_instantiate(struct dentry *dentry, struct inode *inode,ovl_dentry_version_inc (dentry->d_parent, false);ovl_dentry_set_upper_alias (dentry);if (!hardlink) { +ovl_set_flag (OVL_UPPERDATA , inode);Call
ovl_set_upperdata()instead?Will do.
[..]
Quoted 9 lines
fs/overlayfs/dir.c static void ovl_instantiate(struct dentry *dentry, struct inode *inode,
Quoted 4 lines
fs/overlayfs/dir.c +4
-0+bool ovl_has_upperdata (struct dentry *dentry) { +if (!ovl_should_check_upperdata (dentry)) +return true; +IMO we don't need this check, but you may leave it as if (WARN_ON(!ovl_should_check_upperdata(dentry) after
ovl_test_flag()to be on the safe side.
Quoted 3 lines
I primarily kept it as it avoided
smp_rmb()for directories and non-regular files.
You are right. I forgot.
Quoted 3 lines
Also not sure how can I use it as
WARN_ON(). NowOVL_UPPERDATAis set on all files/dir. So we will hitWARN_ON()all the time?
Right, my bad.
Quoted 36 lines
Quoted 11 lines
fs/overlayfs/dir.c
Quoted 6 lines
fs/overlayfs/dir.c +4
-0+if (!ovl_test_flag (OVL_UPPERDATA ,d_inode (dentry))) +return false; +/* +* Pairs with smp_wmb() in ovl_copy_up_meta_inode_data(). Make sure To be consistent, you should update the comment to say pairs with... in
ovl_set_upperdata(), although it may be useful to leave the information that the main user ofovl_set_upperdata()isovl_copy_up_meta_inode_data().Ok.
Quoted 13 lines
Quoted 13 lines
fs/overlayfs/dir.c +13
-0+* if setting of OVL_UPPERDATA is visible, then effects of writes +* before that are visible too. +*/ +smp_rmb (); +return true; +} + +void ovl_set_upperdata (struct dentry *dentry) { +/* +* Pairs with smp_rmb() in ovl_has_upperdata(). Make sure +* if OVL_UPPERDATA flag is visible, then effects of write operations +* before it are visible as well. +*/ Quoted 8 lines
fs/overlayfs/dir.c
Quoted 5 lines
fs/overlayfs/dir.c +5
-0+smp_wmb (); +ovl_set_flag (OVL_UPPERDATA ,d_inode (dentry)); +} + +static inline bool open_for_write (int flags)Need
ovl_namespace prefix
and the name sounds like an action (i.e. a request to open for write)
Quoted 1 line
ovl_opened_for_write_or_trunc()?
OK. maybe ovl_open_flags_need_copy_up() ?
Amir.
Re: [PATCH v7 09/14] ovl: A new xattr OVL_XATTR_METACOPY for file on upper
On Sat, Nov 18, 2017 at 09:06:41AM +0200, Amir Goldstein wrote:
[..]
Quoted 16 lines
Quoted 16 lines
Quoted 10 lines
Quoted 7 lines
Note, we now set
OVL_UPPERDATAon all kind of dentires and checkOVL_UPPERDATAonly where it makes sense. If a file is created on upper, we setOVL_UPPERDATA. This can be accessed lockless inovl_d_real()path again. This time ordering dependency is w.r.toe->__upperdata. We need to make surrOVL_UPPERDATAis visible beforeoe->__upperdatais visible. Otherwise following code path might try to copy up an upper only file.Why all this explanations?
Just useovl_set_upperdata()when creating upper and be done with it.Just using
ovl_set_upperdata()will not do away with ordering dependency right? I mean, settingOVL_UPPERDATAin file creation path has different ordering requirement (same is the case of >has_upper). And I wanted to highlight that ordering requirement in changelogs.
Quoted 7 lines
Quoted 3 lines
I can get rid of it. But this seems such a subtle requirement, I think its a good idea to talk about it explicitly.
I am not following. you only need to
ovl_set_upperdata()beforeovl_inode_update(), just like in regular copy up. Am I missing something subtle?
Hi Amir,
Right. This is ordering dependency between setting of OVL_UPPERDATA and oi->__upperdentry.
So as per barrier documentation file, read/write barrier should look as follows.
CPU1 CPU2
set OVL_UPPERDATA smp_rmb()
smp_wmb() Load oi->__upperdentry
set oi->__upperdentrySo we need to place smp_wmb() after setting OVL_UPPERDATA and place smp_rmb() just before load of oi->__upperdentry.
Instead of smp_rmb() we are relying on checking for ovl_dentry_lower(). That is even if OVL_UPPERDATA is not visible on CPU2, but oi->__upperdentry is visible, then we don't try to copy up a file which does not have a lower.
So ovl_dentry_lower() is preventing the race which *in-theory* can trigger trying to copy up file which does not have lower.
Does this make sense?
[..]
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
Quoted 1 line
+static inline bool
open_for_write(int flags)Need
ovl_namespace prefix
and the name sounds like an action (i.e. a request to open for write)
ovl_opened_for_write_or_trunc()?OK. maybe
ovl_open_flags_need_copy_up()?
Ok, will use ovl_open_flags_need_copy_up().
Vivek
Amir Goldstein
maintainerRe: [PATCH v7 09/14] ovl: A new xattr OVL_XATTR_METACOPY for file on upper
Quoted 48 lines · Vivek Goyal
On Sat, Nov 18, 2017 at 09:06:41AM +0200, Amir Goldstein wrote:
[..]
Quoted 16 lines
Quoted 16 lines
Quoted 10 lines
Quoted 7 lines
Note, we now set
OVL_UPPERDATAon all kind of dentires and checkOVL_UPPERDATAonly where it makes sense. If a file is created on upper, we setOVL_UPPERDATA. This can be accessed lockless inovl_d_real()path again. This time ordering dependency is w.r.toe->__upperdata. We need to make surrOVL_UPPERDATAis visible beforeoe->__upperdatais visible. Otherwise following code path might try to copy up an upper only file.Why all this explanations?
Just useovl_set_upperdata()when creating upper and be done with it.Just using
ovl_set_upperdata()will not do away with ordering dependency right? I mean, settingOVL_UPPERDATAin file creation path has different ordering requirement (same is the case of >has_upper). And I wanted to highlight that ordering requirement in changelogs.Quoted 7 lines
Quoted 3 lines
I can get rid of it. But this seems such a subtle requirement, I think its a good idea to talk about it explicitly.
I am not following. you only need to
ovl_set_upperdata()beforeovl_inode_update(), just like in regular copy up. Am I missing something subtle?Hi Amir,
Right. This is ordering dependency between setting of
OVL_UPPERDATAandoi->__upperdentry.So as per barrier documentation file, read/write barrier should look as follows.
CPU1 CPU2 set OVL_UPPERDATA smp_rmb() smp_wmb() Load oi->__upperdentry set oi->__upperdentrySo we need to place
smp_wmb()after settingOVL_UPPERDATAand placesmp_rmb()just before load ofoi->__upperdentry.Instead of
smp_rmb()we are relying on checking forovl_dentry_lower(). That is even ifOVL_UPPERDATAis not visible on CPU2, butoi->__upperdentryis visible, then we don't try to copy up a file which does not have a lower.
Quoted 5 lines
So
ovl_dentry_lower()is preventing the race which *in-theory* can trigger trying to copy up file which does not have lower.Does this make sense?
Maybe. Brain overflow.
To put it shortly, if there is a problem with setting OVL_UPPERDATA on new upper then there is a problem with setting upper alias as well (as you pointed out). I don't think that is really the case, but I am not the one to explain why. It just does not make sense that d_instantiate doesn't make sure that inode fields are consistent before attaching the inode to dentry.
I think the barriers hide in raw_write_seqcount_begin()/raw_seqcount_begin() for the lockless dentry cache lookup, but I'm not an expert.
Anyway, I find the commit message text from "If a file is created on upper,..." very confusing.
I suggest that if you are not sure about this leave a "XXX" comment in the code path of creating new upper and setting OVL_UPPERDATA flag or "TODO" comment if you thing there is something to be done.
It is easy to later apply a patch to remove the XXX comment after explaining it or remove TODO comment after fixing it.
A confusing text remains in git log forever and get git history researchers off track.
Amir.
Re: [PATCH v7 09/14] ovl: A new xattr OVL_XATTR_METACOPY for file on upper
Quoted 64 lines · Amir Goldstein
Quoted 48 lines · Vivek Goyal
On Sat, Nov 18, 2017 at 09:06:41AM +0200, Amir Goldstein wrote:
[..]
Quoted 16 lines
Quoted 16 lines
Quoted 10 lines
Quoted 7 lines
Note, we now set
OVL_UPPERDATAon all kind of dentires and checkOVL_UPPERDATAonly where it makes sense. If a file is created on upper, we setOVL_UPPERDATA. This can be accessed lockless inovl_d_real()path again. This time ordering dependency is w.r.toe->__upperdata. We need to make surrOVL_UPPERDATAis visible beforeoe->__upperdatais visible. Otherwise following code path might try to copy up an upper only file.Why all this explanations?
Just useovl_set_upperdata()when creating upper and be done with it.Just using
ovl_set_upperdata()will not do away with ordering dependency right? I mean, settingOVL_UPPERDATAin file creation path has different ordering requirement (same is the case of >has_upper). And I wanted to highlight that ordering requirement in changelogs.Quoted 7 lines
Quoted 3 lines
I can get rid of it. But this seems such a subtle requirement, I think its a good idea to talk about it explicitly.
I am not following. you only need to
ovl_set_upperdata()beforeovl_inode_update(), just like in regular copy up. Am I missing something subtle?Hi Amir,
Right. This is ordering dependency between setting of
OVL_UPPERDATAandoi->__upperdentry.So as per barrier documentation file, read/write barrier should look as follows.
CPU1 CPU2 set OVL_UPPERDATA smp_rmb() smp_wmb() Load oi->__upperdentry set oi->__upperdentrySo we need to place
smp_wmb()after settingOVL_UPPERDATAand placesmp_rmb()just before load ofoi->__upperdentry.Instead of
smp_rmb()we are relying on checking forovl_dentry_lower(). That is even ifOVL_UPPERDATAis not visible on CPU2, butoi->__upperdentryis visible, then we don't try to copy up a file which does not have a lower.Quoted 5 lines
So
ovl_dentry_lower()is preventing the race which *in-theory* can trigger trying to copy up file which does not have lower.Does this make sense?
Maybe. Brain overflow.
To put it shortly, if there is a problem with settingOVL_UPPERDATAon new upper then there is a problem with setting upper alias as well (as you pointed out). I don't think that is really the case, but I am not the one to explain why. It just does not make sense thatd_instantiatedoesn't make sure that inode fields are consistent before attaching the inode to dentry.
I think the barriers hide in raw_write_seqcount_begin()/raw_seqcount_begin() for the lockless dentry cache lookup, but I'm not an expert.
Quoted 8 lines
Anyway, I find the commit message text from "If a file is created on upper,..." very confusing.
I suggest that if you are not sure about this leave a "XXX" comment in the code path of creating new upper and settingOVL_UPPERDATAflag or "TODO" comment if you thing there is something to be done.It is easy to later apply a patch to remove the XXX comment after explaining it or remove TODO comment after fixing it.
Ok, for now, I will just put a comment in code and deal with it later if need be. Once it happens, it will be a NULL pointer dereference as we might try to copy up a file which does not have a lower. So we will know if problem happens. :-)
Vivek
Quoted 4 lines
A confusing text remains in git log forever and get git history researchers off track.
Amir.
PATCH v7 10/14 ovl: Fix ovl_getattr() to get number of blocks from lower
In reply to Vivek GoyalIf an inode has been copied up metadata only, then we need to query the number of blocks from lower and fill up the stat->st_blocks.
We need to be careful about races where we are doing stat on one cpu and data copy up is taking place on other cpu. We want to return stat->st_blocks either from lower or stable upper and not something in between. Hence, ovl_has_upperdata() is called first to figure out whether block reporting will take place from lower or upper.
--- fs/overlayfs/inode.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
fs/overlayfs/inode.c +9 -1
@@ -76,6 +76,10 @@ int ovl_getattr(const struct path *path, struct kstat *stat,bool is_dir =S_ISDIR (dentry->d_inode->i_mode);bool samefs =ovl_same_sb (dentry->d_sb);int err; +bool metacopy = false; + +if (ovl_dentry_upper (dentry) && !ovl_has_upperdata (dentry)) + metacopy = true; type =ovl_path_real (dentry, &realpath); old_cred =ovl_override_creds (dentry->d_sb);
@@ -93,7 +97,8 @@ int ovl_getattr(const struct path *path, struct kstat *stat,if (!is_dir || samefs) {if (OVL_TYPE_ORIGIN (type)) {struct kstat lowerstat; -u32 lowermask =STATX_INO | (!is_dir ?STATX_NLINK :0 ); +u32 lowermask =STATX_INO |STATX_BLOCKS | + (!is_dir ?STATX_NLINK :0 );ovl_path_lower (dentry, &realpath); err = vfs_getattr(&realpath, &lowerstat,
@@ -117,6 +122,9 @@ int ovl_getattr(const struct path *path, struct kstat *stat,WARN_ON_ONCE (stat->dev != lowerstat.dev); else stat->dev = ovl_get_pseudo_dev(dentry); + +if (metacopy) + stat->blocks = lowerstat.blocks; }if (samefs) {/*
Signature · 1 line
2.13.6
Re: [PATCH v7 10/14] ovl: Fix ovl_getattr() to get number of blocks from lower
Quoted 10 lines · Vivek Goyal
If an inode has been copied up metadata only, then we need to query the number of blocks from lower and fill up the
stat->st_blocks.We need to be careful about races where we are doing stat on one cpu and data copy up is taking place on other cpu. We want to return
stat->st_blockseither from lower or stable upper and not something in between. Hence,ovl_has_upperdata()is called first to figure out whether block reporting will take place from lower or upper.Signed-off-by Vivek Goyal <[email protected]>
PATCH v7 11/14 ovl: Set OVL_UPPERDATA flag during ovl_lookup()
In reply to Vivek GoyalDuring lookup, check for presence of OVL_XATTR_METACOPY and if not present, set OVL_UPPERDATA bit in flags. This is only done for upper inode with a corresponding lower dentry and file needs to be a regular file. Basically any file which is eligible for metadata only copy up.
--- fs/overlayfs/namei.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+)
fs/overlayfs/namei.c +40 -0
@@ -25,6 +25,28 @@ struct ovl_lookup_data {char *redirect; }; +/* err < 0, 0 if no metacopy xattr, 1 if metacopy xattr found */ +static int ovl_check_metacopy (struct dentry *dentry) +{ +int res; + +/* Only regular files can have metacopy xattr */ +if (!S_ISREG (d_inode (dentry)->i_mode)) +return 0 ; + + res =vfs_getxattr (dentry,OVL_XATTR_METACOPY ,NULL ,0 ); +if (res <0 ) { +if (res == -ENODATA || res == -EOPNOTSUPP ) +return 0 ; + goto out; + } + +return 1 ; +out: +pr_warn_ratelimited ("overlayfs: failed to get metacopy (%i)\n" , res); +return res; +} +static int ovl_check_redirect (struct dentry *dentry,struct ovl_lookup_data *d,size_t prelen,const char *post) {
@@ -602,6 +624,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,struct dentry *this;unsigned int i;int err; +bool metacopy = false;struct ovl_lookup_data d = { .name = dentry->d_name, .is_dir = false,
@@ -642,6 +665,20 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, roe->numlower, &stack, &ctr);if (err) goto out_put_upper; + + err =ovl_check_metacopy (upperdentry); + metacopy = err; +if (err <0 ) + goto out_put_upper; +if (metacopy && !ctr) { +/* +* Found an upper with metacopy set but +* at the same time there is no lower +* dentry. Something is not right. +*/ + err = -ESTALE ; + goto out_put_upper; + } }if (d.redirect) {
@@ -726,6 +763,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,OVL_I (inode)->redirect = upperredirect;if (index)ovl_set_flag (OVL_INDEX , inode); + +if (upperdentry && !metacopy) +ovl_set_flag (OVL_UPPERDATA , inode); }revert_creds (old_cred);
Signature · 1 line
2.13.6
Re: [PATCH v7 11/14] ovl: Set OVL_UPPERDATA flag during ovl_lookup()
Quoted 4 lines · Vivek Goyal
During lookup, check for presence of
OVL_XATTR_METACOPYand if not present, setOVL_UPPERDATAbit in flags. This is only done for upper inode with a corresponding lower dentry and file needs to be a regular file. Basically any file which is eligible for metadata only copy up.
It is not completely clear that "this is only done" refers to the xattr check and
not to setting the flag. Suggest to add text like this:
"... For the rest of the files, OVL_UPPERDATA flag is set unconditionally if upper inode exists."
Quoted 2 lines
Signed-off-by Vivek Goyal <[email protected]>
PATCH v7 12/14 ovl: Do not expose metacopy only upper dentry from d_real()
In reply to Vivek Goyald_real() can make a upper metacopy dentry/inode visible to the vfs layer. This is something new and vfs layer does not know that this inode contains only metadata and not data. And this could break things.
So to be safe, do not expose metacopy only dentry/inode to vfs using d_real().
For regular d_real() call (inode == NULL, D_REAL_UPPER not set), if upper dentry inode is metacopy only and does not have data, return lower dentry.
If d_real() is called with flag D_REAL_UPPER, return upper dentry only if it has data (flag OVL_UPPERDATA is set).
Similiarly, if d_real(inode=X) is called, a warning is emitted if returned dentry/inode does not have OVL_UPPERDATA set. This should not happen as we never made this metacopy inode visible to vfs so nobody should be calling overlayfs back with inode=metacopy_inode.
I scanned the code and I don't think it breaks any of the existing code. There are two users of D_REAL_UPPER. may_write_real() and update_ovl_inode_times().
may_write_real(), will get an NULL dentry if upper inode is metacopy only and it will return -EPERM. Effectively, we are disallowing modifications to metacopy only inode from this interface. Though there is opportunity to improve it. (Allow chattr on metacopy inodes).
update_ovl_inode_times() gets inode mtime and ctime from real inode. It should not be broken for metacopy inode as well for following reasons.
- For any metadata operations (setattr, acl etc), overlay always calls ovl_copyattr() and updates ovl inode mtime and ctime. So there is no need to update mtime and ctime int his case. Its already updated.
- For metadata inode, mtime should be same as lower and not change. (data can't be modified on metadata inode without copyup).
- For file writes, ctime and mtime will be updated. But in that case first data will be copied up and this will not be a metadata inode anymore. And furthr call to d_real(D_REAL_UPPER) will return upper inode and new mtime and ctime will be obtainable.
So atime updates should work just fine for metacopy inodes.
--- fs/overlayfs/super.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-)
fs/overlayfs/super.c +16 -3
@@ -84,8 +84,14 @@ static struct dentry *ovl_d_real(struct dentry *dentry,struct dentry *real;int err; -if (flags &D_REAL_UPPER ) -return ovl_dentry_upper (dentry); +if (flags &D_REAL_UPPER ) { + real =ovl_dentry_upper (dentry); +if (!real) +return NULL ; +if (!ovl_has_upperdata (dentry)) +return NULL ; +return real; + }if (!d_is_reg (dentry)) {if (!inode || inode ==d_inode (dentry))
@@ -101,14 +107,21 @@ static struct dentry *ovl_d_real(struct dentry *dentry, real =ovl_dentry_upper (dentry);if (real && (!inode || inode ==d_inode (real))) { +bool metacopy = !ovl_has_upperdata (dentry);if (!inode) { err =ovl_check_append_only (d_inode (real), open_flags);if (err)return ERR_PTR (err); - } + +if (unlikely (metacopy)) + goto lower; + }else if (unlikely (metacopy)) + goto bug; +return real; } +lower: real =ovl_dentry_lower (dentry);if (!real) goto bug;
Signature · 1 line
2.13.6
Re: [PATCH v7 12/14] ovl: Do not expose metacopy only upper dentry from d_real()
Quoted 37 lines · Vivek Goyal
d_real()can make a upper metacopy dentry/inode visible to the vfs layer. This is something new and vfs layer does not know that this inode contains only metadata and not data. And this could break things.So to be safe, do not expose metacopy only dentry/inode to vfs using
d_real().For regular
d_real()call (inode == NULL,D_REAL_UPPERnot set), if upper dentry inode is metacopy only and does not have data, return lower dentry.If
d_real()is called with flagD_REAL_UPPER, return upper dentry only if it has data (flagOVL_UPPERDATAis set).Similiarly, if
d_real(inode=X)is called, a warning is emitted if returned dentry/inode does not haveOVL_UPPERDATAset. This should not happen as we never made this metacopy inode visible to vfs so nobody should be calling overlayfs back with inode=metacopy_inode.I scanned the code and I don't think it breaks any of the existing code. There are two users of
D_REAL_UPPER.may_write_real()andupdate_ovl_inode_times().
may_write_real(), will get an NULL dentry if upper inode is metacopy only and it will return-EPERM. Effectively, we are disallowing modifications to metacopy only inode from this interface. Though there is opportunity to improve it. (Allow chattr on metacopy inodes).
update_ovl_inode_times()gets inode mtime and ctime from real inode. It should not be broken for metacopy inode as well for following reasons.- For any metadata operations (setattr, acl etc), overlay always calls
ovl_copyattr()and updates ovl inode mtime and ctime. So there is no need to update mtime and ctime int his case. Its already updated.- For metadata inode, mtime should be same as lower and not change. (data can't be modified on metadata inode without copyup).
Quoted 8 lines
- For file writes, ctime and mtime will be updated. But in that case first data will be copied up and this will not be a metadata inode anymore. And furthr call to
d_real(D_REAL_UPPER)will return upper inode and new mtime and ctime will be obtainable.So atime updates should work just fine for metacopy inodes.
Signed-off-by Vivek Goyal <[email protected]>
PATCH v7 13/14 ovl: Fix encryption/compression status of a metacopy only file
In reply to Vivek GoyalIf file is metacopy only, it is possible that lower is encrypted while other is not. In that case, report file as encrypted (despite the fact that only data is encrypted while metadata is not).
Similarly if lower is compressed, report that in stat for a metacopy only file.
--- fs/overlayfs/inode.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-)
fs/overlayfs/inode.c +19 -1
@@ -66,6 +66,22 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr)return err; } +#define OVL_STATX_ATTR_MASK (STATX_ATTR_ENCRYPTED | STATX_ATTR_COMPRESSED) +static void ovl_stat_fix_attributes (struct kstat *ustat,struct kstat *lstat) { +unsigned int attr_mask, attr; + + attr_mask = lstat->attributes_mask &OVL_STATX_ATTR_MASK ; +if (!attr_mask) +return ; + + attr = lstat->attributes & attr_mask; +if (!attr) +return ; + + ustat->attributes_mask |= attr_mask; + ustat->attributes |= attr; +} +int ovl_getattr (const struct path *path,struct kstat *stat,u32 request_mask,unsigned int flags) {
@@ -123,8 +139,10 @@ int ovl_getattr(const struct path *path, struct kstat *stat,else stat->dev =ovl_get_pseudo_dev (dentry); -if (metacopy) +if (metacopy) { stat->blocks = lowerstat.blocks; +ovl_stat_fix_attributes (stat, &lowerstat); + } }if (samefs) {/*
Signature · 1 line
2.13.6
Re: [PATCH v7 13/14] ovl: Fix encryption/compression status of a metacopy only file
Quoted 8 lines · Vivek Goyal
If file is metacopy only, it is possible that lower is encrypted while other is not. In that case, report file as encrypted (despite the fact that only data is encrypted while metadata is not).
Similarly if lower is compressed, report that in stat for a metacopy only file.
Signed-off-by Vivek Goyal <[email protected]>
fs/overlayfs/inode.c int ovl_setattr(struct dentry *dentry, struct iattr *attr) · line 69
Quoted 13 lines
--- fs/overlayfs/inode.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-)fs/overlayfs/inode.c +1
-0@@ -66,6 +66,22 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr)return err; } +#define OVL_STATX_ATTR_MASK (STATX_ATTR_ENCRYPTED | STATX_ATTR_COMPRESSED)
nit: add newline
Quoted 33 lines
fs/overlayfs/inode.c +18
-2+static void ovl_stat_fix_attributes (struct kstat *ustat,struct kstat *lstat) { +unsigned int attr_mask, attr; + + attr_mask = lstat->attributes_mask &OVL_STATX_ATTR_MASK ; +if (!attr_mask) +return ; + + attr = lstat->attributes & attr_mask; +if (!attr) +return ; + + ustat->attributes_mask |= attr_mask; + ustat->attributes |= attr; +} +int ovl_getattr (const struct path *path,struct kstat *stat,u32 request_mask,unsigned int flags) {@@ -123,8 +139,10 @@ int ovl_getattr(const struct path *path, struct kstat *stat,else stat->dev =ovl_get_pseudo_dev (dentry); -if (metacopy) +if (metacopy) { stat->blocks = lowerstat.blocks; +ovl_stat_fix_attributes (stat, &lowerstat); + } }if (samefs) {/* -- 2.13.6
PATCH v7 14/14 ovl: Enable metadata only feature
In reply to Vivek GoyalAll the bits are in patches before this. So it is time to enable the metadata only copy up feature.
--- fs/overlayfs/copy_up.c | 3 --- 1 file changed, 3 deletions(-)
fs/overlayfs/copy_up.c +0 -3
@@ -276,9 +276,6 @@ static bool ovl_need_meta_copy_up(struct dentry *dentry, umode_t mode, {struct ovl_fs *ofs = dentry->d_sb->s_fs_info; -/* TODO: Will enable metacopy in last patch of series */ -return false; -if (!ofs->config.metacopy)return false;
Signature · 1 line
2.13.6