ext4: fix __ext4_new_inode() journal credits calculation
ea_inode feature allows creating extended attributes that are up to 64k in size. Update __ext4_new_inode() to pick increased credit limits. To avoid overallocating too many journal credits, update __ext4_xattr_set_credits() to make a distinction between xattr create vs update. This helps __ext4_new_inode() because all attributes are known to be new, so we can save credits that are normally needed to delete old values. Also, have fscrypt specify its maximum context size so that we don't end up allocating credits for 64k size. Signed-off-by: Tahsin Erdogan <tahsin@google.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
parent
ad47f95339
commit
af65207c76
|
@ -260,6 +260,7 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child,
|
||||||
memcpy(ctx.master_key_descriptor, ci->ci_master_key,
|
memcpy(ctx.master_key_descriptor, ci->ci_master_key,
|
||||||
FS_KEY_DESCRIPTOR_SIZE);
|
FS_KEY_DESCRIPTOR_SIZE);
|
||||||
get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE);
|
get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE);
|
||||||
|
BUILD_BUG_ON(sizeof(ctx) != FSCRYPT_SET_CONTEXT_MAX_SIZE);
|
||||||
res = parent->i_sb->s_cop->set_context(child, &ctx,
|
res = parent->i_sb->s_cop->set_context(child, &ctx,
|
||||||
sizeof(ctx), fs_data);
|
sizeof(ctx), fs_data);
|
||||||
if (res)
|
if (res)
|
||||||
|
|
|
@ -183,7 +183,7 @@ ext4_get_acl(struct inode *inode, int type)
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
__ext4_set_acl(handle_t *handle, struct inode *inode, int type,
|
__ext4_set_acl(handle_t *handle, struct inode *inode, int type,
|
||||||
struct posix_acl *acl)
|
struct posix_acl *acl, int xattr_flags)
|
||||||
{
|
{
|
||||||
int name_index;
|
int name_index;
|
||||||
void *value = NULL;
|
void *value = NULL;
|
||||||
|
@ -218,7 +218,7 @@ __ext4_set_acl(handle_t *handle, struct inode *inode, int type,
|
||||||
}
|
}
|
||||||
|
|
||||||
error = ext4_xattr_set_handle(handle, inode, name_index, "",
|
error = ext4_xattr_set_handle(handle, inode, name_index, "",
|
||||||
value, size, 0);
|
value, size, xattr_flags);
|
||||||
|
|
||||||
kfree(value);
|
kfree(value);
|
||||||
if (!error)
|
if (!error)
|
||||||
|
@ -238,7 +238,8 @@ ext4_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
retry:
|
retry:
|
||||||
error = ext4_xattr_set_credits(inode, acl_size, &credits);
|
error = ext4_xattr_set_credits(inode, acl_size, false /* is_create */,
|
||||||
|
&credits);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
@ -246,7 +247,7 @@ ext4_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
||||||
if (IS_ERR(handle))
|
if (IS_ERR(handle))
|
||||||
return PTR_ERR(handle);
|
return PTR_ERR(handle);
|
||||||
|
|
||||||
error = __ext4_set_acl(handle, inode, type, acl);
|
error = __ext4_set_acl(handle, inode, type, acl, 0 /* xattr_flags */);
|
||||||
ext4_journal_stop(handle);
|
ext4_journal_stop(handle);
|
||||||
if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
|
if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
|
||||||
goto retry;
|
goto retry;
|
||||||
|
@ -271,13 +272,13 @@ ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
|
||||||
|
|
||||||
if (default_acl) {
|
if (default_acl) {
|
||||||
error = __ext4_set_acl(handle, inode, ACL_TYPE_DEFAULT,
|
error = __ext4_set_acl(handle, inode, ACL_TYPE_DEFAULT,
|
||||||
default_acl);
|
default_acl, XATTR_CREATE);
|
||||||
posix_acl_release(default_acl);
|
posix_acl_release(default_acl);
|
||||||
}
|
}
|
||||||
if (acl) {
|
if (acl) {
|
||||||
if (!error)
|
if (!error)
|
||||||
error = __ext4_set_acl(handle, inode, ACL_TYPE_ACCESS,
|
error = __ext4_set_acl(handle, inode, ACL_TYPE_ACCESS,
|
||||||
acl);
|
acl, XATTR_CREATE);
|
||||||
posix_acl_release(acl);
|
posix_acl_release(acl);
|
||||||
}
|
}
|
||||||
return error;
|
return error;
|
||||||
|
|
|
@ -766,11 +766,13 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
|
||||||
if (!dir || !dir->i_nlink)
|
if (!dir || !dir->i_nlink)
|
||||||
return ERR_PTR(-EPERM);
|
return ERR_PTR(-EPERM);
|
||||||
|
|
||||||
if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb))))
|
sb = dir->i_sb;
|
||||||
|
sbi = EXT4_SB(sb);
|
||||||
|
|
||||||
|
if (unlikely(ext4_forced_shutdown(sbi)))
|
||||||
return ERR_PTR(-EIO);
|
return ERR_PTR(-EIO);
|
||||||
|
|
||||||
if ((ext4_encrypted_inode(dir) ||
|
if ((ext4_encrypted_inode(dir) || DUMMY_ENCRYPTION_ENABLED(sbi)) &&
|
||||||
DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb))) &&
|
|
||||||
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) &&
|
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) &&
|
||||||
!(i_flags & EXT4_EA_INODE_FL)) {
|
!(i_flags & EXT4_EA_INODE_FL)) {
|
||||||
err = fscrypt_get_encryption_info(dir);
|
err = fscrypt_get_encryption_info(dir);
|
||||||
|
@ -778,19 +780,55 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
if (!fscrypt_has_encryption_key(dir))
|
if (!fscrypt_has_encryption_key(dir))
|
||||||
return ERR_PTR(-ENOKEY);
|
return ERR_PTR(-ENOKEY);
|
||||||
if (!handle)
|
|
||||||
nblocks += EXT4_DATA_TRANS_BLOCKS(dir->i_sb);
|
|
||||||
encrypt = 1;
|
encrypt = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sb = dir->i_sb;
|
if (!handle && sbi->s_journal && !(i_flags & EXT4_EA_INODE_FL)) {
|
||||||
|
#ifdef CONFIG_EXT4_FS_POSIX_ACL
|
||||||
|
struct posix_acl *p = get_acl(dir, ACL_TYPE_DEFAULT);
|
||||||
|
|
||||||
|
if (p) {
|
||||||
|
int acl_size = p->a_count * sizeof(ext4_acl_entry);
|
||||||
|
|
||||||
|
nblocks += (S_ISDIR(mode) ? 2 : 1) *
|
||||||
|
__ext4_xattr_set_credits(sb, NULL /* inode */,
|
||||||
|
NULL /* block_bh */, acl_size,
|
||||||
|
true /* is_create */);
|
||||||
|
posix_acl_release(p);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_SECURITY
|
||||||
|
{
|
||||||
|
int num_security_xattrs = 1;
|
||||||
|
|
||||||
|
#ifdef CONFIG_INTEGRITY
|
||||||
|
num_security_xattrs++;
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
* We assume that security xattrs are never
|
||||||
|
* more than 1k. In practice they are under
|
||||||
|
* 128 bytes.
|
||||||
|
*/
|
||||||
|
nblocks += num_security_xattrs *
|
||||||
|
__ext4_xattr_set_credits(sb, NULL /* inode */,
|
||||||
|
NULL /* block_bh */, 1024,
|
||||||
|
true /* is_create */);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (encrypt)
|
||||||
|
nblocks += __ext4_xattr_set_credits(sb,
|
||||||
|
NULL /* inode */, NULL /* block_bh */,
|
||||||
|
FSCRYPT_SET_CONTEXT_MAX_SIZE,
|
||||||
|
true /* is_create */);
|
||||||
|
}
|
||||||
|
|
||||||
ngroups = ext4_get_groups_count(sb);
|
ngroups = ext4_get_groups_count(sb);
|
||||||
trace_ext4_request_inode(dir, mode);
|
trace_ext4_request_inode(dir, mode);
|
||||||
inode = new_inode(sb);
|
inode = new_inode(sb);
|
||||||
if (!inode)
|
if (!inode)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
ei = EXT4_I(inode);
|
ei = EXT4_I(inode);
|
||||||
sbi = EXT4_SB(sb);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize owners and quota early so that we don't have to account
|
* Initialize owners and quota early so that we don't have to account
|
||||||
|
|
|
@ -1194,7 +1194,8 @@ static int ext4_set_context(struct inode *inode, const void *ctx, size_t len,
|
||||||
if (res)
|
if (res)
|
||||||
return res;
|
return res;
|
||||||
retry:
|
retry:
|
||||||
res = ext4_xattr_set_credits(inode, len, &credits);
|
res = ext4_xattr_set_credits(inode, len, false /* is_create */,
|
||||||
|
&credits);
|
||||||
if (res)
|
if (res)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
|
|
|
@ -830,11 +830,10 @@ static void ext4_xattr_inode_free_quota(struct inode *inode, size_t len)
|
||||||
dquot_free_inode(inode);
|
dquot_free_inode(inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __ext4_xattr_set_credits(struct inode *inode,
|
int __ext4_xattr_set_credits(struct super_block *sb, struct inode *inode,
|
||||||
struct buffer_head *block_bh,
|
struct buffer_head *block_bh, size_t value_len,
|
||||||
size_t value_len)
|
bool is_create)
|
||||||
{
|
{
|
||||||
struct super_block *sb = inode->i_sb;
|
|
||||||
int credits;
|
int credits;
|
||||||
int blocks;
|
int blocks;
|
||||||
|
|
||||||
|
@ -860,7 +859,7 @@ static int __ext4_xattr_set_credits(struct inode *inode,
|
||||||
* In case of inline data, we may push out the data to a block,
|
* In case of inline data, we may push out the data to a block,
|
||||||
* so we need to reserve credits for this eventuality
|
* so we need to reserve credits for this eventuality
|
||||||
*/
|
*/
|
||||||
if (ext4_has_inline_data(inode))
|
if (inode && ext4_has_inline_data(inode))
|
||||||
credits += ext4_writepage_trans_blocks(inode) + 1;
|
credits += ext4_writepage_trans_blocks(inode) + 1;
|
||||||
|
|
||||||
/* We are done if ea_inode feature is not enabled. */
|
/* We are done if ea_inode feature is not enabled. */
|
||||||
|
@ -882,19 +881,23 @@ static int __ext4_xattr_set_credits(struct inode *inode,
|
||||||
/* Blocks themselves. */
|
/* Blocks themselves. */
|
||||||
credits += blocks;
|
credits += blocks;
|
||||||
|
|
||||||
/* Dereference ea_inode holding old xattr value.
|
if (!is_create) {
|
||||||
* Old ea_inode, inode map, block bitmap, group descriptor.
|
/* Dereference ea_inode holding old xattr value.
|
||||||
*/
|
* Old ea_inode, inode map, block bitmap, group descriptor.
|
||||||
credits += 4;
|
*/
|
||||||
|
credits += 4;
|
||||||
|
|
||||||
/* Data blocks for old ea_inode. */
|
/* Data blocks for old ea_inode. */
|
||||||
blocks = XATTR_SIZE_MAX >> sb->s_blocksize_bits;
|
blocks = XATTR_SIZE_MAX >> sb->s_blocksize_bits;
|
||||||
|
|
||||||
/* Indirection block or one level of extent tree for old ea_inode. */
|
/* Indirection block or one level of extent tree for old
|
||||||
blocks += 1;
|
* ea_inode.
|
||||||
|
*/
|
||||||
|
blocks += 1;
|
||||||
|
|
||||||
/* Block bitmap and group descriptor updates for each block. */
|
/* Block bitmap and group descriptor updates for each block. */
|
||||||
credits += blocks * 2;
|
credits += blocks * 2;
|
||||||
|
}
|
||||||
|
|
||||||
/* We may need to clone the existing xattr block in which case we need
|
/* We may need to clone the existing xattr block in which case we need
|
||||||
* to increment ref counts for existing ea_inodes referenced by it.
|
* to increment ref counts for existing ea_inodes referenced by it.
|
||||||
|
@ -2263,7 +2266,9 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
credits = __ext4_xattr_set_credits(inode, bh, value_len);
|
credits = __ext4_xattr_set_credits(inode->i_sb, inode, bh,
|
||||||
|
value_len,
|
||||||
|
flags & XATTR_CREATE);
|
||||||
brelse(bh);
|
brelse(bh);
|
||||||
|
|
||||||
if (!ext4_handle_has_enough_credits(handle, credits)) {
|
if (!ext4_handle_has_enough_credits(handle, credits)) {
|
||||||
|
@ -2370,7 +2375,8 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ext4_xattr_set_credits(struct inode *inode, size_t value_len, int *credits)
|
int ext4_xattr_set_credits(struct inode *inode, size_t value_len,
|
||||||
|
bool is_create, int *credits)
|
||||||
{
|
{
|
||||||
struct buffer_head *bh;
|
struct buffer_head *bh;
|
||||||
int err;
|
int err;
|
||||||
|
@ -2386,7 +2392,8 @@ int ext4_xattr_set_credits(struct inode *inode, size_t value_len, int *credits)
|
||||||
if (IS_ERR(bh)) {
|
if (IS_ERR(bh)) {
|
||||||
err = PTR_ERR(bh);
|
err = PTR_ERR(bh);
|
||||||
} else {
|
} else {
|
||||||
*credits = __ext4_xattr_set_credits(inode, bh, value_len);
|
*credits = __ext4_xattr_set_credits(inode->i_sb, inode, bh,
|
||||||
|
value_len, is_create);
|
||||||
brelse(bh);
|
brelse(bh);
|
||||||
err = 0;
|
err = 0;
|
||||||
}
|
}
|
||||||
|
@ -2417,7 +2424,8 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
error = ext4_xattr_set_credits(inode, value_len, &credits);
|
error = ext4_xattr_set_credits(inode, value_len, flags & XATTR_CREATE,
|
||||||
|
&credits);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
|
|
@ -153,7 +153,10 @@ extern int ext4_xattr_get(struct inode *, int, const char *, void *, size_t);
|
||||||
extern int ext4_xattr_set(struct inode *, int, const char *, const void *, size_t, int);
|
extern int ext4_xattr_set(struct inode *, int, const char *, const void *, size_t, int);
|
||||||
extern int ext4_xattr_set_handle(handle_t *, struct inode *, int, const char *, const void *, size_t, int);
|
extern int ext4_xattr_set_handle(handle_t *, struct inode *, int, const char *, const void *, size_t, int);
|
||||||
extern int ext4_xattr_set_credits(struct inode *inode, size_t value_len,
|
extern int ext4_xattr_set_credits(struct inode *inode, size_t value_len,
|
||||||
int *credits);
|
bool is_create, int *credits);
|
||||||
|
extern int __ext4_xattr_set_credits(struct super_block *sb, struct inode *inode,
|
||||||
|
struct buffer_head *block_bh, size_t value_len,
|
||||||
|
bool is_create);
|
||||||
|
|
||||||
extern int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,
|
extern int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,
|
||||||
struct ext4_xattr_inode_array **array,
|
struct ext4_xattr_inode_array **array,
|
||||||
|
|
|
@ -83,6 +83,9 @@ struct fscrypt_operations {
|
||||||
unsigned (*max_namelen)(struct inode *);
|
unsigned (*max_namelen)(struct inode *);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Maximum value for the third parameter of fscrypt_operations.set_context(). */
|
||||||
|
#define FSCRYPT_SET_CONTEXT_MAX_SIZE 28
|
||||||
|
|
||||||
static inline bool fscrypt_dummy_context_enabled(struct inode *inode)
|
static inline bool fscrypt_dummy_context_enabled(struct inode *inode)
|
||||||
{
|
{
|
||||||
if (inode->i_sb->s_cop->dummy_context &&
|
if (inode->i_sb->s_cop->dummy_context &&
|
||||||
|
|
Loading…
Reference in New Issue