aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/ext4/inline.c48
-rw-r--r--fs/ext4/xattr.c54
-rw-r--r--fs/ext4/xattr.h9
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:
207/* 207/*
208 * write the buffer to the inline inode. 208 * write the buffer to the inline inode.
209 * If 'create' is set, we don't need to do the extra copy in the xattr 209 * If 'create' is set, we don't need to do the extra copy in the xattr
210 * value since it is already handled by ext4_xattr_ibody_set. That saves 210 * value since it is already handled by ext4_xattr_ibody_inline_set.
211 * us one memcpy. 211 * That saves us one memcpy.
212 */ 212 */
213void ext4_write_inline_data(struct inode *inode, struct ext4_iloc *iloc, 213void ext4_write_inline_data(struct inode *inode, struct ext4_iloc *iloc,
214 void *buffer, loff_t pos, unsigned int len) 214 void *buffer, loff_t pos, unsigned int len)
@@ -285,7 +285,7 @@ static int ext4_create_inline_data(handle_t *handle,
285 285
286 BUG_ON(!is.s.not_found); 286 BUG_ON(!is.s.not_found);
287 287
288 error = ext4_xattr_ibody_set(handle, inode, &i, &is); 288 error = ext4_xattr_ibody_inline_set(handle, inode, &i, &is);
289 if (error) { 289 if (error) {
290 if (error == -ENOSPC) 290 if (error == -ENOSPC)
291 ext4_clear_inode_state(inode, 291 ext4_clear_inode_state(inode,
@@ -354,7 +354,7 @@ static int ext4_update_inline_data(handle_t *handle, struct inode *inode,
354 i.value = value; 354 i.value = value;
355 i.value_len = len; 355 i.value_len = len;
356 356
357 error = ext4_xattr_ibody_set(handle, inode, &i, &is); 357 error = ext4_xattr_ibody_inline_set(handle, inode, &i, &is);
358 if (error) 358 if (error)
359 goto out; 359 goto out;
360 360
@@ -427,7 +427,7 @@ static int ext4_destroy_inline_data_nolock(handle_t *handle,
427 if (error) 427 if (error)
428 goto out; 428 goto out;
429 429
430 error = ext4_xattr_ibody_set(handle, inode, &i, &is); 430 error = ext4_xattr_ibody_inline_set(handle, inode, &i, &is);
431 if (error) 431 if (error)
432 goto out; 432 goto out;
433 433
@@ -1715,3 +1715,41 @@ out:
1715 up_read(&EXT4_I(inode)->xattr_sem); 1715 up_read(&EXT4_I(inode)->xattr_sem);
1716 return (error < 0 ? error : 0); 1716 return (error < 0 ? error : 0);
1717} 1717}
1718
1719/*
1720 * Called during xattr set, and if we can sparse space 'needed',
1721 * just create the extent tree evict the data to the outer block.
1722 *
1723 * We use jbd2 instead of page cache to move data to the 1st block
1724 * so that the whole transaction can be committed as a whole and
1725 * the data isn't lost because of the delayed page cache write.
1726 */
1727int ext4_try_to_evict_inline_data(handle_t *handle,
1728 struct inode *inode,
1729 int needed)
1730{
1731 int error;
1732 struct ext4_xattr_entry *entry;
1733 struct ext4_xattr_ibody_header *header;
1734 struct ext4_inode *raw_inode;
1735 struct ext4_iloc iloc;
1736
1737 error = ext4_get_inode_loc(inode, &iloc);
1738 if (error)
1739 return error;
1740
1741 raw_inode = ext4_raw_inode(&iloc);
1742 header = IHDR(inode, raw_inode);
1743 entry = (struct ext4_xattr_entry *)((void *)raw_inode +
1744 EXT4_I(inode)->i_inline_off);
1745 if (EXT4_XATTR_LEN(entry->e_name_len) +
1746 EXT4_XATTR_SIZE(le32_to_cpu(entry->e_value_size)) < needed) {
1747 error = -ENOSPC;
1748 goto out;
1749 }
1750
1751 error = ext4_convert_inline_data_nolock(handle, inode, &iloc);
1752out:
1753 brelse(iloc.bh);
1754 return error;
1755}
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,
958 return 0; 958 return 0;
959} 959}
960 960
961int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode, 961int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,
962 struct ext4_xattr_info *i, 962 struct ext4_xattr_info *i,
963 struct ext4_xattr_ibody_find *is) 963 struct ext4_xattr_ibody_find *is)
964{
965 struct ext4_xattr_ibody_header *header;
966 struct ext4_xattr_search *s = &is->s;
967 int error;
968
969 if (EXT4_I(inode)->i_extra_isize == 0)
970 return -ENOSPC;
971 error = ext4_xattr_set_entry(i, s);
972 if (error) {
973 if (error == -ENOSPC &&
974 ext4_has_inline_data(inode)) {
975 error = ext4_try_to_evict_inline_data(handle, inode,
976 EXT4_XATTR_LEN(strlen(i->name) +
977 EXT4_XATTR_SIZE(i->value_len)));
978 if (error)
979 return error;
980 error = ext4_xattr_ibody_find(inode, i, is);
981 if (error)
982 return error;
983 error = ext4_xattr_set_entry(i, s);
984 }
985 if (error)
986 return error;
987 }
988 header = IHDR(inode, ext4_raw_inode(&is->iloc));
989 if (!IS_LAST_ENTRY(s->first)) {
990 header->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC);
991 ext4_set_inode_state(inode, EXT4_STATE_XATTR);
992 } else {
993 header->h_magic = cpu_to_le32(0);
994 ext4_clear_inode_state(inode, EXT4_STATE_XATTR);
995 }
996 return 0;
997}
998
999static int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
1000 struct ext4_xattr_info *i,
1001 struct ext4_xattr_ibody_find *is)
964{ 1002{
965 struct ext4_xattr_ibody_header *header; 1003 struct ext4_xattr_ibody_header *header;
966 struct ext4_xattr_search *s = &is->s; 1004 struct ext4_xattr_search *s = &is->s;
@@ -1116,9 +1154,17 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,
1116{ 1154{
1117 handle_t *handle; 1155 handle_t *handle;
1118 int error, retries = 0; 1156 int error, retries = 0;
1157 int credits = EXT4_DATA_TRANS_BLOCKS(inode->i_sb);
1119 1158
1120retry: 1159retry:
1121 handle = ext4_journal_start(inode, EXT4_DATA_TRANS_BLOCKS(inode->i_sb)); 1160 /*
1161 * In case of inline data, we may push out the data to a block,
1162 * So reserve the journal space first.
1163 */
1164 if (ext4_has_inline_data(inode))
1165 credits += ext4_writepage_trans_blocks(inode) + 1;
1166
1167 handle = ext4_journal_start(inode, credits);
1122 if (IS_ERR(handle)) { 1168 if (IS_ERR(handle)) {
1123 error = PTR_ERR(handle); 1169 error = PTR_ERR(handle);
1124 } else { 1170 } 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,
122extern int ext4_xattr_ibody_get(struct inode *inode, int name_index, 122extern int ext4_xattr_ibody_get(struct inode *inode, int name_index,
123 const char *name, 123 const char *name,
124 void *buffer, size_t buffer_size); 124 void *buffer, size_t buffer_size);
125extern int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode, 125extern int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,
126 struct ext4_xattr_info *i, 126 struct ext4_xattr_info *i,
127 struct ext4_xattr_ibody_find *is); 127 struct ext4_xattr_ibody_find *is);
128 128
129extern int ext4_has_inline_data(struct inode *inode); 129extern int ext4_has_inline_data(struct inode *inode);
130extern int ext4_get_inline_size(struct inode *inode); 130extern int ext4_get_inline_size(struct inode *inode);
@@ -187,6 +187,9 @@ extern struct buffer_head *ext4_get_first_inline_block(struct inode *inode,
187extern int ext4_inline_data_fiemap(struct inode *inode, 187extern int ext4_inline_data_fiemap(struct inode *inode,
188 struct fiemap_extent_info *fieinfo, 188 struct fiemap_extent_info *fieinfo,
189 int *has_inline); 189 int *has_inline);
190extern int ext4_try_to_evict_inline_data(handle_t *handle,
191 struct inode *inode,
192 int needed);
190# else /* CONFIG_EXT4_FS_XATTR */ 193# else /* CONFIG_EXT4_FS_XATTR */
191 194
192static inline int 195static inline int