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: | |||
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 | */ |
213 | void ext4_write_inline_data(struct inode *inode, struct ext4_iloc *iloc, | 213 | void 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 | */ | ||
1727 | int 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); | ||
1752 | out: | ||
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 | ||
961 | int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode, | 961 | int 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 | |||
999 | static 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 | ||
1120 | retry: | 1159 | retry: |
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, | |||
122 | extern int ext4_xattr_ibody_get(struct inode *inode, int name_index, | 122 | extern 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); |
125 | extern int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode, | 125 | extern 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 | ||
129 | extern int ext4_has_inline_data(struct inode *inode); | 129 | extern int ext4_has_inline_data(struct inode *inode); |
130 | extern int ext4_get_inline_size(struct inode *inode); | 130 | 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, | |||
187 | extern int ext4_inline_data_fiemap(struct inode *inode, | 187 | extern 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); |
190 | extern 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 | ||
192 | static inline int | 195 | static inline int |