For the common case of single lower layer, embed ovl_entry with a single lower path in ovl_inode, so no stack allocation is needed. For directory with more than single lower layer and for regular file with lowerdata, the lower stack is stored in an external allocation. Use accessor ovl_lowerstack() to get the embedded or external stack. Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> --- fs/overlayfs/dir.c | 2 ++ fs/overlayfs/export.c | 18 +++++---------- fs/overlayfs/inode.c | 12 ++++------ fs/overlayfs/namei.c | 15 +++++-------- fs/overlayfs/overlayfs.h | 10 +++++---- fs/overlayfs/ovl_entry.h | 14 +++++++----- fs/overlayfs/super.c | 41 +++++++++++++--------------------- fs/overlayfs/util.c | 48 +++++++++++++++++++++++++++++----------- 8 files changed, 81 insertions(+), 79 deletions(-) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 92bdcedfaaec..aa0465c61064 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -262,9 +262,11 @@ static int ovl_set_opaque(struct dentry *dentry, struct dentry *upperdentry) static int ovl_instantiate(struct dentry *dentry, struct inode *inode, struct dentry *newdentry, bool hardlink) { + struct ovl_entry oe = {}; struct ovl_inode_params oip = { .upperdentry = newdentry, .newinode = inode, + .oe = &oe, }; ovl_dir_modified(dentry->d_parent, false); diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c index d4caf57c8e17..9951c504fb8d 100644 --- a/fs/overlayfs/export.c +++ b/fs/overlayfs/export.c @@ -287,30 +287,22 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb, struct dentry *upper = upper_alias ?: index; struct dentry *dentry; struct inode *inode = NULL; - struct ovl_entry *oe; + struct ovl_entry oe; struct ovl_inode_params oip = { - .lowerpath = lowerpath, + .oe = &oe, .index = index, - .numlower = !!lower }; /* We get overlay directory dentries with ovl_lookup_real() */ if (d_is_dir(upper ?: lower)) return ERR_PTR(-EIO); - oe = ovl_alloc_entry(!!lower); - if (!oe) - goto nomem; - oip.upperdentry = dget(upper); - if (lower) { - ovl_lowerstack(oe)->dentry = dget(lower); - ovl_lowerstack(oe)->layer = lowerpath->layer; - } - oip.oe = oe; + /* Should not fail because does not allocate lowerstack */ + ovl_init_entry(&oe, lowerpath, !!lower); inode = ovl_get_inode(sb, &oip); if (IS_ERR(inode)) { - ovl_free_entry(oe); + ovl_destroy_entry(&oe); dput(upper); return ERR_CAST(inode); } diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index c296bd656858..9f29fc3e9fa5 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -1004,12 +1004,8 @@ void ovl_inode_init(struct inode *inode, struct ovl_inode_params *oip, struct ovl_inode *oi = OVL_I(inode); oi->__upperdentry = oip->upperdentry; - oi->oe = oip->oe; + oi->oe = *oip->oe; oi->redirect = oip->redirect; - if (oip->lowerpath && oip->lowerpath->dentry) { - oi->lowerpath.dentry = dget(oip->lowerpath->dentry); - oi->lowerpath.layer = oip->lowerpath->layer; - } if (oip->lowerdata) oi->lowerdata = igrab(d_inode(oip->lowerdata)); @@ -1326,7 +1322,7 @@ struct inode *ovl_get_inode(struct super_block *sb, { struct ovl_fs *ofs = OVL_FS(sb); struct dentry *upperdentry = oip->upperdentry; - struct ovl_path *lowerpath = oip->lowerpath; + struct ovl_path *lowerpath = ovl_lowerstack(oip->oe); struct inode *realinode = upperdentry ? d_inode(upperdentry) : NULL; struct inode *inode; struct dentry *lowerdentry = lowerpath ? lowerpath->dentry : NULL; @@ -1370,7 +1366,7 @@ struct inode *ovl_get_inode(struct super_block *sb, } dput(upperdentry); - ovl_free_entry(oip->oe); + ovl_destroy_entry(oip->oe); kfree(oip->redirect); goto out; } @@ -1405,7 +1401,7 @@ struct inode *ovl_get_inode(struct super_block *sb, /* Check for non-merge dir that may have whiteouts */ if (is_dir) { - if (((upperdentry && lowerdentry) || oip->numlower > 1) || + if (((upperdentry && lowerdentry) || ovl_numlower(oip->oe) > 1) || ovl_path_check_origin_xattr(ofs, &realpath)) { ovl_set_flag(OVL_WHITEOUTS, inode); } diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index a0a1c498dbd1..cdcb2ac5d95c 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -831,7 +831,7 @@ static int ovl_fix_origin(struct ovl_fs *ofs, struct dentry *dentry, struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { - struct ovl_entry *oe; + struct ovl_entry oe; const struct cred *old_cred; struct ovl_fs *ofs = dentry->d_sb->s_fs_info; struct ovl_entry *poe = OVL_E(dentry->d_parent); @@ -1067,13 +1067,10 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, } } - oe = ovl_alloc_entry(ctr); - err = -ENOMEM; - if (!oe) + err = ovl_init_entry(&oe, stack, ctr); + if (err) goto out_put; - ovl_stack_cpy(ovl_lowerstack(oe), stack, ctr); - if (upperopaque) ovl_dentry_set_opaque(dentry); @@ -1105,10 +1102,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, if (upperdentry || ctr) { struct ovl_inode_params oip = { .upperdentry = upperdentry, - .lowerpath = stack, - .oe = oe, + .oe = &oe, .index = index, - .numlower = ctr, .redirect = upperredirect, .lowerdata = (ctr > 1 && !d.is_dir) ? stack[ctr - 1].dentry : NULL, @@ -1135,7 +1130,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, return d_splice_alias(inode, dentry); out_free_oe: - ovl_free_entry(oe); + ovl_destroy_entry(&oe); out_put: dput(index); ovl_stack_free(stack, ctr); diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index e14ca0fd1063..32532342e56a 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -377,8 +377,12 @@ struct ovl_path *ovl_stack_alloc(unsigned int n); void ovl_stack_cpy(struct ovl_path *dst, struct ovl_path *src, unsigned int n); void ovl_stack_put(struct ovl_path *stack, unsigned int n); void ovl_stack_free(struct ovl_path *stack, unsigned int n); -struct ovl_entry *ovl_alloc_entry(unsigned int numlower); -void ovl_free_entry(struct ovl_entry *oe); +struct ovl_path *ovl_alloc_stack(unsigned int n); +void ovl_stack_cpy(struct ovl_path *dst, struct ovl_path *src, unsigned int n); +void ovl_stack_put(struct ovl_path *stack, unsigned int n); +int ovl_init_entry(struct ovl_entry *oe, struct ovl_path *stack, + unsigned int numlower); +void ovl_destroy_entry(struct ovl_entry *oe); bool ovl_dentry_remote(struct dentry *dentry); void ovl_dentry_update_reval(struct dentry *dentry, struct dentry *realdentry); void ovl_dentry_init_reval(struct dentry *dentry, struct dentry *upperdentry, @@ -653,10 +657,8 @@ bool ovl_is_private_xattr(struct super_block *sb, const char *name); struct ovl_inode_params { struct inode *newinode; struct dentry *upperdentry; - struct ovl_path *lowerpath; struct ovl_entry *oe; bool index; - unsigned int numlower; char *redirect; struct dentry *lowerdata; }; diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 201de9da45d3..5d95e937f555 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -49,7 +49,12 @@ struct ovl_path { struct ovl_entry { unsigned int __numlower; - struct ovl_path __lowerstack[]; + union { + /* Embedded path for numlower == 1 */ + struct ovl_path __lowerpath; + /* External stack for numlower > 1 */ + struct ovl_path *__lowerstack; + }; }; /* private information held for overlayfs's superblock */ @@ -117,7 +122,7 @@ static inline unsigned int ovl_numlower(struct ovl_entry *oe) static inline struct ovl_path *ovl_lowerstack(struct ovl_entry *oe) { - return oe ? oe->__lowerstack : NULL; + return oe && oe->__numlower > 1 ? oe->__lowerstack : &oe->__lowerpath; } /* private information held for every overlayfs dentry */ @@ -136,8 +141,7 @@ struct ovl_inode { unsigned long flags; struct inode vfs_inode; struct dentry *__upperdentry; - struct ovl_path lowerpath; - struct ovl_entry *oe; + struct ovl_entry oe; /* synchronize copy up and more */ struct mutex lock; @@ -150,7 +154,7 @@ static inline struct ovl_inode *OVL_I(struct inode *inode) static inline struct ovl_entry *OVL_I_E(struct inode *inode) { - return inode ? OVL_I(inode)->oe : NULL; + return inode ? &OVL_I(inode)->oe : NULL; } static inline struct ovl_entry *OVL_E(struct dentry *dentry) diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index b9e62ccd609f..e01a76de787c 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -171,9 +171,7 @@ static struct inode *ovl_alloc_inode(struct super_block *sb) oi->version = 0; oi->flags = 0; oi->__upperdentry = NULL; - oi->oe = NULL; - oi->lowerpath.dentry = NULL; - oi->lowerpath.layer = NULL; + ovl_init_entry(&oi->oe, NULL, 0); oi->lowerdata = NULL; mutex_init(&oi->lock); @@ -194,8 +192,7 @@ static void ovl_destroy_inode(struct inode *inode) struct ovl_inode *oi = OVL_I(inode); dput(oi->__upperdentry); - dput(oi->lowerpath.dentry); - ovl_free_entry(oi->oe); + ovl_destroy_entry(&oi->oe); if (S_ISDIR(inode->i_mode)) ovl_dir_cache_free(inode); else @@ -1702,7 +1699,7 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, return err; } -static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, +static int ovl_get_lowerstack(struct super_block *sb, struct ovl_entry *oe, const char *lower, unsigned int numlower, struct ovl_fs *ofs, struct ovl_layer *layers) { @@ -1710,16 +1707,15 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, struct path *stack = NULL; struct ovl_path *lowerstack; unsigned int i; - struct ovl_entry *oe; if (!ofs->config.upperdir && numlower == 1) { pr_err("at least 2 lowerdir are needed while upperdir nonexistent\n"); - return ERR_PTR(-EINVAL); + return -EINVAL; } stack = kcalloc(numlower, sizeof(struct path), GFP_KERNEL); if (!stack) - return ERR_PTR(-ENOMEM); + return -ENOMEM; err = -EINVAL; for (i = 0; i < numlower; i++) { @@ -1741,9 +1737,8 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, if (err) goto out_err; - err = -ENOMEM; - oe = ovl_alloc_entry(numlower); - if (!oe) + err = ovl_init_entry(oe, NULL, numlower); + if (err) goto out_err; lowerstack = ovl_lowerstack(oe); @@ -1752,16 +1747,12 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, lowerstack[i].layer = &ofs->layers[i+1]; } -out: +out_err: for (i = 0; i < numlower; i++) path_put(&stack[i]); kfree(stack); - return oe; - -out_err: - oe = ERR_PTR(err); - goto out; + return err; } /* @@ -1847,7 +1838,6 @@ static struct dentry *ovl_get_root(struct super_block *sb, int fsid = lowerpath->layer->fsid; struct ovl_inode_params oip = { .upperdentry = upperdentry, - .lowerpath = lowerpath, .oe = oe, }; @@ -1878,7 +1868,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) { struct path upperpath = { }; struct dentry *root_dentry; - struct ovl_entry *oe; + struct ovl_entry oe; struct ovl_fs *ofs; struct ovl_layer *layers; struct cred *cred; @@ -1991,9 +1981,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) sb->s_stack_depth = upper_sb->s_stack_depth; sb->s_time_gran = upper_sb->s_time_gran; } - oe = ovl_get_lowerstack(sb, splitlower, numlower, ofs, layers); - err = PTR_ERR(oe); - if (IS_ERR(oe)) + err = ovl_get_lowerstack(sb, &oe, splitlower, numlower, ofs, layers); + if (err) goto out_err; /* If the upper fs is nonexistent, we mark overlayfs r/o too */ @@ -2006,7 +1995,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) } if (!ovl_force_readonly(ofs) && ofs->config.index) { - err = ovl_get_indexdir(sb, ofs, oe, &upperpath); + err = ovl_get_indexdir(sb, ofs, &oe, &upperpath); if (err) goto out_free_oe; @@ -2047,7 +2036,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) sb->s_iflags |= SB_I_SKIP_SYNC; err = -ENOMEM; - root_dentry = ovl_get_root(sb, upperpath.dentry, oe); + root_dentry = ovl_get_root(sb, upperpath.dentry, &oe); if (!root_dentry) goto out_free_oe; @@ -2059,7 +2048,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) return 0; out_free_oe: - ovl_free_entry(oe); + ovl_destroy_entry(&oe); out_err: kfree(splitlower); path_put(&upperpath); diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index f5e2c70a57f8..540819ac9b9c 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -111,21 +111,41 @@ void ovl_stack_free(struct ovl_path *stack, unsigned int n) kfree(stack); } -struct ovl_entry *ovl_alloc_entry(unsigned int numlower) -{ - size_t size = offsetof(struct ovl_entry, __lowerstack[numlower]); - struct ovl_entry *oe = kzalloc(size, GFP_KERNEL); +/* On success, takes references on @stack dentries */ +int ovl_init_entry(struct ovl_entry *oe, struct ovl_path *stack, + unsigned int numlower) +{ + oe->__numlower = numlower; + oe->__lowerpath = (struct ovl_path) {}; + + /* No allocated stack for numlower <= 1 */ + if (numlower <= 1) { + if (numlower && stack) + oe->__lowerpath = *stack; + dget(oe->__lowerpath.dentry); + return 0; + } + + oe->__lowerstack = ovl_stack_alloc(numlower); + if (!oe->__lowerstack) + return -ENOMEM; + + if (!stack) + return 0; - if (oe) - oe->__numlower = numlower; + ovl_stack_cpy(oe->__lowerstack, stack, numlower); - return oe; + return 0; } -void ovl_free_entry(struct ovl_entry *oe) +void ovl_destroy_entry(struct ovl_entry *oe) { - ovl_stack_put(ovl_lowerstack(oe), ovl_numlower(oe)); - kfree(oe); + if (oe->__numlower > 1) { + ovl_stack_put(oe->__lowerstack, oe->__numlower); + kfree(oe->__lowerstack); + } else { + dput(oe->__lowerpath.dentry); + } } #define OVL_D_REVALIDATE (DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE) @@ -306,10 +326,12 @@ struct dentry *ovl_i_dentry_upper(struct inode *inode) void ovl_i_path_real(struct inode *inode, struct path *path) { + struct ovl_path *lowerstack = ovl_lowerstack(OVL_I_E(inode)); + path->dentry = ovl_i_dentry_upper(inode); if (!path->dentry) { - path->dentry = OVL_I(inode)->lowerpath.dentry; - path->mnt = OVL_I(inode)->lowerpath.layer->mnt; + path->dentry = lowerstack->dentry; + path->mnt = lowerstack->layer->mnt; } else { path->mnt = ovl_upper_mnt(OVL_FS(inode->i_sb)); } @@ -324,7 +346,7 @@ struct inode *ovl_inode_upper(struct inode *inode) struct inode *ovl_inode_lower(struct inode *inode) { - struct dentry *lowerdentry = OVL_I(inode)->lowerpath.dentry; + struct dentry *lowerdentry = ovl_lowerstack(OVL_I_E(inode))->dentry; return lowerdentry ? d_inode(lowerdentry) : NULL; } -- 2.34.1