diff options
author | Alexander Zarochentzev <zam@namesys.com> | 2006-03-25 06:06:57 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-03-25 11:22:51 -0500 |
commit | 23f9e0f891c9b159a199629d4426f6ae0c383508 (patch) | |
tree | bc34ab7abe6de4d7b3a7741c1ab5d97ddc29f133 | |
parent | bdfc326614b90e7bc47ee4a8fed05988555f0169 (diff) |
[PATCH] reiserfs: fix transaction overflowing
This patch fixes a bug in reiserfs truncate. A transaction might overflow
when truncating long highly fragmented file. The fix is to split
truncation into several transactions to avoid overflowing.
Signed-off-by: Vladimir V. Saveliev <vs@namesys.com>
Cc; Charles McColgan <cm@chuck.net>
Cc: Alexander Zarochentsev <zam@namesys.com>
Cc: Hans Reiser <reiser@namesys.com>
Cc: Chris Mason <mason@suse.com>
Cc: Jeff Mahoney <jeffm@suse.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | fs/reiserfs/stree.c | 210 | ||||
-rw-r--r-- | include/linux/reiserfs_fs.h | 5 |
2 files changed, 77 insertions, 138 deletions
diff --git a/fs/reiserfs/stree.c b/fs/reiserfs/stree.c index e2d08d7bcffc..d2b25e1ba6e9 100644 --- a/fs/reiserfs/stree.c +++ b/fs/reiserfs/stree.c | |||
@@ -981,6 +981,8 @@ static inline int prepare_for_direntry_item(struct path *path, | |||
981 | return M_CUT; | 981 | return M_CUT; |
982 | } | 982 | } |
983 | 983 | ||
984 | #define JOURNAL_FOR_FREE_BLOCK_AND_UPDATE_SD (2 * JOURNAL_PER_BALANCE_CNT + 1) | ||
985 | |||
984 | /* If the path points to a directory or direct item, calculate mode and the size cut, for balance. | 986 | /* If the path points to a directory or direct item, calculate mode and the size cut, for balance. |
985 | If the path points to an indirect item, remove some number of its unformatted nodes. | 987 | If the path points to an indirect item, remove some number of its unformatted nodes. |
986 | In case of file truncate calculate whether this item must be deleted/truncated or last | 988 | In case of file truncate calculate whether this item must be deleted/truncated or last |
@@ -1020,148 +1022,79 @@ static char prepare_for_delete_or_cut(struct reiserfs_transaction_handle *th, st | |||
1020 | 1022 | ||
1021 | /* Case of an indirect item. */ | 1023 | /* Case of an indirect item. */ |
1022 | { | 1024 | { |
1023 | int n_unfm_number, /* Number of the item unformatted nodes. */ | 1025 | int blk_size = p_s_sb->s_blocksize; |
1024 | n_counter, n_blk_size; | 1026 | struct item_head s_ih; |
1025 | __le32 *p_n_unfm_pointer; /* Pointer to the unformatted node number. */ | 1027 | int need_re_search; |
1026 | __u32 tmp; | 1028 | int delete = 0; |
1027 | struct item_head s_ih; /* Item header. */ | 1029 | int result = M_CUT; |
1028 | char c_mode; /* Returned mode of the balance. */ | 1030 | int pos = 0; |
1029 | int need_research; | 1031 | |
1030 | 1032 | if ( n_new_file_length == max_reiserfs_offset (inode) ) { | |
1031 | n_blk_size = p_s_sb->s_blocksize; | 1033 | /* prepare_for_delete_or_cut() is called by |
1032 | 1034 | * reiserfs_delete_item() */ | |
1033 | /* Search for the needed object indirect item until there are no unformatted nodes to be removed. */ | 1035 | n_new_file_length = 0; |
1034 | do { | 1036 | delete = 1; |
1035 | need_research = 0; | 1037 | } |
1036 | p_s_bh = PATH_PLAST_BUFFER(p_s_path); | 1038 | |
1037 | /* Copy indirect item header to a temp variable. */ | 1039 | do { |
1038 | copy_item_head(&s_ih, PATH_PITEM_HEAD(p_s_path)); | 1040 | need_re_search = 0; |
1039 | /* Calculate number of unformatted nodes in this item. */ | 1041 | *p_n_cut_size = 0; |
1040 | n_unfm_number = I_UNFM_NUM(&s_ih); | 1042 | p_s_bh = PATH_PLAST_BUFFER(p_s_path); |
1041 | 1043 | copy_item_head(&s_ih, PATH_PITEM_HEAD(p_s_path)); | |
1042 | RFALSE(!is_indirect_le_ih(&s_ih) || !n_unfm_number || | 1044 | pos = I_UNFM_NUM(&s_ih); |
1043 | pos_in_item(p_s_path) + 1 != n_unfm_number, | ||
1044 | "PAP-5240: invalid item %h " | ||
1045 | "n_unfm_number = %d *p_n_pos_in_item = %d", | ||
1046 | &s_ih, n_unfm_number, pos_in_item(p_s_path)); | ||
1047 | |||
1048 | /* Calculate balance mode and position in the item to remove unformatted nodes. */ | ||
1049 | if (n_new_file_length == max_reiserfs_offset(inode)) { /* Case of delete. */ | ||
1050 | pos_in_item(p_s_path) = 0; | ||
1051 | *p_n_cut_size = -(IH_SIZE + ih_item_len(&s_ih)); | ||
1052 | c_mode = M_DELETE; | ||
1053 | } else { /* Case of truncate. */ | ||
1054 | if (n_new_file_length < le_ih_k_offset(&s_ih)) { | ||
1055 | pos_in_item(p_s_path) = 0; | ||
1056 | *p_n_cut_size = | ||
1057 | -(IH_SIZE + ih_item_len(&s_ih)); | ||
1058 | c_mode = M_DELETE; /* Delete this item. */ | ||
1059 | } else { | ||
1060 | /* indirect item must be truncated starting from *p_n_pos_in_item-th position */ | ||
1061 | pos_in_item(p_s_path) = | ||
1062 | (n_new_file_length + n_blk_size - | ||
1063 | le_ih_k_offset(&s_ih)) >> p_s_sb-> | ||
1064 | s_blocksize_bits; | ||
1065 | |||
1066 | RFALSE(pos_in_item(p_s_path) > | ||
1067 | n_unfm_number, | ||
1068 | "PAP-5250: invalid position in the item"); | ||
1069 | |||
1070 | /* Either convert last unformatted node of indirect item to direct item or increase | ||
1071 | its free space. */ | ||
1072 | if (pos_in_item(p_s_path) == | ||
1073 | n_unfm_number) { | ||
1074 | *p_n_cut_size = 0; /* Nothing to cut. */ | ||
1075 | return M_CONVERT; /* Maybe convert last unformatted node to the direct item. */ | ||
1076 | } | ||
1077 | /* Calculate size to cut. */ | ||
1078 | *p_n_cut_size = | ||
1079 | -(ih_item_len(&s_ih) - | ||
1080 | pos_in_item(p_s_path) * | ||
1081 | UNFM_P_SIZE); | ||
1082 | |||
1083 | c_mode = M_CUT; /* Cut from this indirect item. */ | ||
1084 | } | ||
1085 | } | ||
1086 | 1045 | ||
1087 | RFALSE(n_unfm_number <= pos_in_item(p_s_path), | 1046 | while (le_ih_k_offset (&s_ih) + (pos - 1) * blk_size > n_new_file_length) { |
1088 | "PAP-5260: invalid position in the indirect item"); | 1047 | __u32 *unfm, block; |
1089 | |||
1090 | /* pointers to be cut */ | ||
1091 | n_unfm_number -= pos_in_item(p_s_path); | ||
1092 | /* Set pointer to the last unformatted node pointer that is to be cut. */ | ||
1093 | p_n_unfm_pointer = | ||
1094 | (__le32 *) B_I_PITEM(p_s_bh, | ||
1095 | &s_ih) + I_UNFM_NUM(&s_ih) - | ||
1096 | 1 - *p_n_removed; | ||
1097 | |||
1098 | /* We go through the unformatted nodes pointers of the indirect | ||
1099 | item and look for the unformatted nodes in the cache. If we | ||
1100 | found some of them we free it, zero corresponding indirect item | ||
1101 | entry and log buffer containing that indirect item. For this we | ||
1102 | need to prepare last path element for logging. If some | ||
1103 | unformatted node has b_count > 1 we must not free this | ||
1104 | unformatted node since it is in use. */ | ||
1105 | reiserfs_prepare_for_journal(p_s_sb, p_s_bh, 1); | ||
1106 | // note: path could be changed, first line in for loop takes care | ||
1107 | // of it | ||
1108 | 1048 | ||
1109 | for (n_counter = *p_n_removed; | 1049 | /* Each unformatted block deletion may involve one additional |
1110 | n_counter < n_unfm_number; | 1050 | * bitmap block into the transaction, thereby the initial |
1111 | n_counter++, p_n_unfm_pointer--) { | 1051 | * journal space reservation might not be enough. */ |
1052 | if (!delete && (*p_n_cut_size) != 0 && | ||
1053 | reiserfs_transaction_free_space(th) < JOURNAL_FOR_FREE_BLOCK_AND_UPDATE_SD) { | ||
1054 | break; | ||
1055 | } | ||
1112 | 1056 | ||
1113 | cond_resched(); | 1057 | unfm = (__u32 *)B_I_PITEM(p_s_bh, &s_ih) + pos - 1; |
1114 | if (item_moved(&s_ih, p_s_path)) { | 1058 | block = get_block_num(unfm, 0); |
1115 | need_research = 1; | ||
1116 | break; | ||
1117 | } | ||
1118 | RFALSE(p_n_unfm_pointer < | ||
1119 | (__le32 *) B_I_PITEM(p_s_bh, &s_ih) | ||
1120 | || p_n_unfm_pointer > | ||
1121 | (__le32 *) B_I_PITEM(p_s_bh, | ||
1122 | &s_ih) + | ||
1123 | I_UNFM_NUM(&s_ih) - 1, | ||
1124 | "vs-5265: pointer out of range"); | ||
1125 | |||
1126 | /* Hole, nothing to remove. */ | ||
1127 | if (!get_block_num(p_n_unfm_pointer, 0)) { | ||
1128 | (*p_n_removed)++; | ||
1129 | continue; | ||
1130 | } | ||
1131 | 1059 | ||
1132 | (*p_n_removed)++; | 1060 | if (block != 0) { |
1061 | reiserfs_prepare_for_journal(p_s_sb, p_s_bh, 1); | ||
1062 | put_block_num(unfm, 0, 0); | ||
1063 | journal_mark_dirty (th, p_s_sb, p_s_bh); | ||
1064 | reiserfs_free_block(th, inode, block, 1); | ||
1065 | } | ||
1133 | 1066 | ||
1134 | tmp = get_block_num(p_n_unfm_pointer, 0); | 1067 | cond_resched(); |
1135 | put_block_num(p_n_unfm_pointer, 0, 0); | ||
1136 | journal_mark_dirty(th, p_s_sb, p_s_bh); | ||
1137 | reiserfs_free_block(th, inode, tmp, 1); | ||
1138 | if (item_moved(&s_ih, p_s_path)) { | ||
1139 | need_research = 1; | ||
1140 | break; | ||
1141 | } | ||
1142 | } | ||
1143 | 1068 | ||
1144 | /* a trick. If the buffer has been logged, this | 1069 | if (item_moved (&s_ih, p_s_path)) { |
1145 | ** will do nothing. If we've broken the loop without | 1070 | need_re_search = 1; |
1146 | ** logging it, it will restore the buffer | 1071 | break; |
1147 | ** | 1072 | } |
1148 | */ | 1073 | |
1149 | reiserfs_restore_prepared_buffer(p_s_sb, p_s_bh); | 1074 | pos --; |
1150 | 1075 | (*p_n_removed) ++; | |
1151 | /* This loop can be optimized. */ | 1076 | (*p_n_cut_size) -= UNFM_P_SIZE; |
1152 | } while ((*p_n_removed < n_unfm_number || need_research) && | 1077 | |
1153 | search_for_position_by_key(p_s_sb, p_s_item_key, | 1078 | if (pos == 0) { |
1154 | p_s_path) == | 1079 | (*p_n_cut_size) -= IH_SIZE; |
1155 | POSITION_FOUND); | 1080 | result = M_DELETE; |
1156 | 1081 | break; | |
1157 | RFALSE(*p_n_removed < n_unfm_number, | 1082 | } |
1158 | "PAP-5310: indirect item is not found"); | 1083 | } |
1159 | RFALSE(item_moved(&s_ih, p_s_path), | 1084 | /* a trick. If the buffer has been logged, this will do nothing. If |
1160 | "after while, comp failed, retry"); | 1085 | ** we've broken the loop without logging it, it will restore the |
1161 | 1086 | ** buffer */ | |
1162 | if (c_mode == M_CUT) | 1087 | reiserfs_restore_prepared_buffer(p_s_sb, p_s_bh); |
1163 | pos_in_item(p_s_path) *= UNFM_P_SIZE; | 1088 | } while (need_re_search && |
1164 | return c_mode; | 1089 | search_for_position_by_key(p_s_sb, p_s_item_key, p_s_path) == POSITION_FOUND); |
1090 | pos_in_item(p_s_path) = pos * UNFM_P_SIZE; | ||
1091 | |||
1092 | if (*p_n_cut_size == 0) { | ||
1093 | /* Nothing were cut. maybe convert last unformatted node to the | ||
1094 | * direct item? */ | ||
1095 | result = M_CONVERT; | ||
1096 | } | ||
1097 | return result; | ||
1165 | } | 1098 | } |
1166 | } | 1099 | } |
1167 | 1100 | ||
@@ -1948,7 +1881,8 @@ int reiserfs_do_truncate(struct reiserfs_transaction_handle *th, struct inode *p | |||
1948 | ** sure the file is consistent before ending the current trans | 1881 | ** sure the file is consistent before ending the current trans |
1949 | ** and starting a new one | 1882 | ** and starting a new one |
1950 | */ | 1883 | */ |
1951 | if (journal_transaction_should_end(th, th->t_blocks_allocated)) { | 1884 | if (journal_transaction_should_end(th, 0) || |
1885 | reiserfs_transaction_free_space(th) <= JOURNAL_FOR_FREE_BLOCK_AND_UPDATE_SD) { | ||
1952 | int orig_len_alloc = th->t_blocks_allocated; | 1886 | int orig_len_alloc = th->t_blocks_allocated; |
1953 | decrement_counters_in_path(&s_search_path); | 1887 | decrement_counters_in_path(&s_search_path); |
1954 | 1888 | ||
@@ -1962,7 +1896,7 @@ int reiserfs_do_truncate(struct reiserfs_transaction_handle *th, struct inode *p | |||
1962 | if (err) | 1896 | if (err) |
1963 | goto out; | 1897 | goto out; |
1964 | err = journal_begin(th, p_s_inode->i_sb, | 1898 | err = journal_begin(th, p_s_inode->i_sb, |
1965 | JOURNAL_PER_BALANCE_CNT * 6); | 1899 | JOURNAL_FOR_FREE_BLOCK_AND_UPDATE_SD + JOURNAL_PER_BALANCE_CNT * 4) ; |
1966 | if (err) | 1900 | if (err) |
1967 | goto out; | 1901 | goto out; |
1968 | reiserfs_update_inode_transaction(p_s_inode); | 1902 | reiserfs_update_inode_transaction(p_s_inode); |
diff --git a/include/linux/reiserfs_fs.h b/include/linux/reiserfs_fs.h index dad78cecfd20..912f1b7cb18f 100644 --- a/include/linux/reiserfs_fs.h +++ b/include/linux/reiserfs_fs.h | |||
@@ -1704,6 +1704,11 @@ static inline int reiserfs_transaction_running(struct super_block *s) | |||
1704 | return 0; | 1704 | return 0; |
1705 | } | 1705 | } |
1706 | 1706 | ||
1707 | static inline int reiserfs_transaction_free_space(struct reiserfs_transaction_handle *th) | ||
1708 | { | ||
1709 | return th->t_blocks_allocated - th->t_blocks_logged; | ||
1710 | } | ||
1711 | |||
1707 | int reiserfs_async_progress_wait(struct super_block *s); | 1712 | int reiserfs_async_progress_wait(struct super_block *s); |
1708 | 1713 | ||
1709 | struct reiserfs_transaction_handle *reiserfs_persistent_transaction(struct | 1714 | struct reiserfs_transaction_handle *reiserfs_persistent_transaction(struct |