Commit 0d812f77 authored by Tao Ma's avatar Tao Ma Committed by Theodore Ts'o

ext4: evict inline data out if we need to strore xattr in inode

Now we that store data in the inode, in case we need to store some
xattrs and inode doesn't have enough space, Andreas suggested that we
should keep the xattr(metadata) in and data should be pushed out.  So
this patch does the work.
Signed-off-by: default avatarTao Ma <boyu.mt@taobao.com>
Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
parent 94191985
...@@ -207,8 +207,8 @@ static int ext4_read_inline_data(struct inode *inode, void *buffer, ...@@ -207,8 +207,8 @@ static int ext4_read_inline_data(struct inode *inode, void *buffer,
/* /*
* write the buffer to the inline inode. * write the buffer to the inline inode.
* If 'create' is set, we don't need to do the extra copy in the xattr * If 'create' is set, we don't need to do the extra copy in the xattr
* value since it is already handled by ext4_xattr_ibody_set. That saves * value since it is already handled by ext4_xattr_ibody_inline_set.
* us one memcpy. * That saves us one memcpy.
*/ */
void ext4_write_inline_data(struct inode *inode, struct ext4_iloc *iloc, void ext4_write_inline_data(struct inode *inode, struct ext4_iloc *iloc,
void *buffer, loff_t pos, unsigned int len) void *buffer, loff_t pos, unsigned int len)
...@@ -285,7 +285,7 @@ static int ext4_create_inline_data(handle_t *handle, ...@@ -285,7 +285,7 @@ static int ext4_create_inline_data(handle_t *handle,
BUG_ON(!is.s.not_found); BUG_ON(!is.s.not_found);
error = ext4_xattr_ibody_set(handle, inode, &i, &is); error = ext4_xattr_ibody_inline_set(handle, inode, &i, &is);
if (error) { if (error) {
if (error == -ENOSPC) if (error == -ENOSPC)
ext4_clear_inode_state(inode, ext4_clear_inode_state(inode,
...@@ -354,7 +354,7 @@ static int ext4_update_inline_data(handle_t *handle, struct inode *inode, ...@@ -354,7 +354,7 @@ static int ext4_update_inline_data(handle_t *handle, struct inode *inode,
i.value = value; i.value = value;
i.value_len = len; i.value_len = len;
error = ext4_xattr_ibody_set(handle, inode, &i, &is); error = ext4_xattr_ibody_inline_set(handle, inode, &i, &is);
if (error) if (error)
goto out; goto out;
...@@ -427,7 +427,7 @@ static int ext4_destroy_inline_data_nolock(handle_t *handle, ...@@ -427,7 +427,7 @@ static int ext4_destroy_inline_data_nolock(handle_t *handle,
if (error) if (error)
goto out; goto out;
error = ext4_xattr_ibody_set(handle, inode, &i, &is); error = ext4_xattr_ibody_inline_set(handle, inode, &i, &is);
if (error) if (error)
goto out; goto out;
...@@ -1715,3 +1715,41 @@ int ext4_inline_data_fiemap(struct inode *inode, ...@@ -1715,3 +1715,41 @@ int ext4_inline_data_fiemap(struct inode *inode,
up_read(&EXT4_I(inode)->xattr_sem); up_read(&EXT4_I(inode)->xattr_sem);
return (error < 0 ? error : 0); return (error < 0 ? error : 0);
} }
/*
* Called during xattr set, and if we can sparse space 'needed',
* just create the extent tree evict the data to the outer block.
*
* We use jbd2 instead of page cache to move data to the 1st block
* so that the whole transaction can be committed as a whole and
* the data isn't lost because of the delayed page cache write.
*/
int ext4_try_to_evict_inline_data(handle_t *handle,
struct inode *inode,
int needed)
{
int error;
struct ext4_xattr_entry *entry;
struct ext4_xattr_ibody_header *header;
struct ext4_inode *raw_inode;
struct ext4_iloc iloc;
error = ext4_get_inode_loc(inode, &iloc);
if (error)
return error;
raw_inode = ext4_raw_inode(&iloc);
header = IHDR(inode, raw_inode);
entry = (struct ext4_xattr_entry *)((void *)raw_inode +
EXT4_I(inode)->i_inline_off);
if (EXT4_XATTR_LEN(entry->e_name_len) +
EXT4_XATTR_SIZE(le32_to_cpu(entry->e_value_size)) < needed) {
error = -ENOSPC;
goto out;
}
error = ext4_convert_inline_data_nolock(handle, inode, &iloc);
out:
brelse(iloc.bh);
return error;
}
...@@ -958,7 +958,45 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i, ...@@ -958,7 +958,45 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
return 0; return 0;
} }
int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode, int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,
struct ext4_xattr_info *i,
struct ext4_xattr_ibody_find *is)
{
struct ext4_xattr_ibody_header *header;
struct ext4_xattr_search *s = &is->s;
int error;
if (EXT4_I(inode)->i_extra_isize == 0)
return -ENOSPC;
error = ext4_xattr_set_entry(i, s);
if (error) {
if (error == -ENOSPC &&
ext4_has_inline_data(inode)) {
error = ext4_try_to_evict_inline_data(handle, inode,
EXT4_XATTR_LEN(strlen(i->name) +
EXT4_XATTR_SIZE(i->value_len)));
if (error)
return error;
error = ext4_xattr_ibody_find(inode, i, is);
if (error)
return error;
error = ext4_xattr_set_entry(i, s);
}
if (error)
return error;
}
header = IHDR(inode, ext4_raw_inode(&is->iloc));
if (!IS_LAST_ENTRY(s->first)) {
header->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC);
ext4_set_inode_state(inode, EXT4_STATE_XATTR);
} else {
header->h_magic = cpu_to_le32(0);
ext4_clear_inode_state(inode, EXT4_STATE_XATTR);
}
return 0;
}
static int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
struct ext4_xattr_info *i, struct ext4_xattr_info *i,
struct ext4_xattr_ibody_find *is) struct ext4_xattr_ibody_find *is)
{ {
...@@ -1116,9 +1154,17 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name, ...@@ -1116,9 +1154,17 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,
{ {
handle_t *handle; handle_t *handle;
int error, retries = 0; int error, retries = 0;
int credits = EXT4_DATA_TRANS_BLOCKS(inode->i_sb);
retry: retry:
handle = ext4_journal_start(inode, EXT4_DATA_TRANS_BLOCKS(inode->i_sb)); /*
* In case of inline data, we may push out the data to a block,
* So reserve the journal space first.
*/
if (ext4_has_inline_data(inode))
credits += ext4_writepage_trans_blocks(inode) + 1;
handle = ext4_journal_start(inode, credits);
if (IS_ERR(handle)) { if (IS_ERR(handle)) {
error = PTR_ERR(handle); error = PTR_ERR(handle);
} else { } else {
......
...@@ -122,7 +122,7 @@ extern int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i, ...@@ -122,7 +122,7 @@ extern int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
extern int ext4_xattr_ibody_get(struct inode *inode, int name_index, extern int ext4_xattr_ibody_get(struct inode *inode, int name_index,
const char *name, const char *name,
void *buffer, size_t buffer_size); void *buffer, size_t buffer_size);
extern int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode, extern int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,
struct ext4_xattr_info *i, struct ext4_xattr_info *i,
struct ext4_xattr_ibody_find *is); struct ext4_xattr_ibody_find *is);
...@@ -187,6 +187,9 @@ extern struct buffer_head *ext4_get_first_inline_block(struct inode *inode, ...@@ -187,6 +187,9 @@ extern struct buffer_head *ext4_get_first_inline_block(struct inode *inode,
extern int ext4_inline_data_fiemap(struct inode *inode, extern int ext4_inline_data_fiemap(struct inode *inode,
struct fiemap_extent_info *fieinfo, struct fiemap_extent_info *fieinfo,
int *has_inline); int *has_inline);
extern int ext4_try_to_evict_inline_data(handle_t *handle,
struct inode *inode,
int needed);
# else /* CONFIG_EXT4_FS_XATTR */ # else /* CONFIG_EXT4_FS_XATTR */
static inline int static inline int
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment