aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorTao Ma <boyu.mt@taobao.com>2012-12-10 14:06:02 -0500
committerTheodore Ts'o <tytso@mit.edu>2012-12-10 14:06:02 -0500
commit0d812f77b36c16dff692390508155de2c7f95ea3 (patch)
tree18bf309f2e7a9fb6637ae8385a07f86eb5c05afd /fs
parent941919856c11d4dd11d4fcabb4dab58bd2b146bf (diff)
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: Tao Ma <boyu.mt@taobao.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs')
-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