diff options
| -rw-r--r-- | fs/ext4/inline.c | 48 | ||||
| -rw-r--r-- | fs/ext4/xattr.c | 54 | ||||
| -rw-r--r-- | fs/ext4/xattr.h | 9 | 
3 files changed, 99 insertions, 12 deletions
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index bf5f7780388..cec651e2646 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -207,8 +207,8 @@ out:  /*   * write the buffer to the inline inode.   * 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 - * us one memcpy. + * value since it is already handled by ext4_xattr_ibody_inline_set. + * That saves us one memcpy.   */  void ext4_write_inline_data(struct inode *inode, struct ext4_iloc *iloc,  			    void *buffer, loff_t pos, unsigned int len) @@ -285,7 +285,7 @@ static int ext4_create_inline_data(handle_t *handle,  	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 == -ENOSPC)  			ext4_clear_inode_state(inode, @@ -354,7 +354,7 @@ static int ext4_update_inline_data(handle_t *handle, struct inode *inode,  	i.value = value;  	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)  		goto out; @@ -427,7 +427,7 @@ static int ext4_destroy_inline_data_nolock(handle_t *handle,  	if (error)  		goto out; -	error = ext4_xattr_ibody_set(handle, inode, &i, &is); +	error = ext4_xattr_ibody_inline_set(handle, inode, &i, &is);  	if (error)  		goto out; @@ -1715,3 +1715,41 @@ out:  	up_read(&EXT4_I(inode)->xattr_sem);  	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; +} diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index a47dc3883a2..2251769a3c5 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -958,9 +958,47 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,  	return 0;  } -int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode, -			 struct ext4_xattr_info *i, -			 struct ext4_xattr_ibody_find *is) +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_ibody_find *is)  {  	struct ext4_xattr_ibody_header *header;  	struct ext4_xattr_search *s = &is->s; @@ -1116,9 +1154,17 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,  {  	handle_t *handle;  	int error, retries = 0; +	int credits = EXT4_DATA_TRANS_BLOCKS(inode->i_sb);  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)) {  		error = PTR_ERR(handle);  	} else { diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h index 5c7e55edfe6..1be243aab01 100644 --- a/fs/ext4/xattr.h +++ b/fs/ext4/xattr.h @@ -122,9 +122,9 @@ 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,  				const char *name,  				void *buffer, size_t buffer_size); -extern int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode, -				struct ext4_xattr_info *i, -				struct ext4_xattr_ibody_find *is); +extern int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode, +				       struct ext4_xattr_info *i, +				       struct ext4_xattr_ibody_find *is);  extern int ext4_has_inline_data(struct inode *inode);  extern int ext4_get_inline_size(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,  				   struct fiemap_extent_info *fieinfo,  				   int *has_inline); +extern int ext4_try_to_evict_inline_data(handle_t *handle, +					 struct inode *inode, +					 int needed);  # else  /* CONFIG_EXT4_FS_XATTR */  static inline int  |