Commit 23f9e0f8 authored by Alexander Zarochentzev's avatar Alexander Zarochentzev Committed by Linus Torvalds

[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: default avatarVladimir 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: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent bdfc3266
...@@ -981,6 +981,8 @@ static inline int prepare_for_direntry_item(struct path *path, ...@@ -981,6 +981,8 @@ static inline int prepare_for_direntry_item(struct path *path,
return M_CUT; return M_CUT;
} }
#define JOURNAL_FOR_FREE_BLOCK_AND_UPDATE_SD (2 * JOURNAL_PER_BALANCE_CNT + 1)
/* If the path points to a directory or direct item, calculate mode and the size cut, for balance. /* If the path points to a directory or direct item, calculate mode and the size cut, for balance.
If the path points to an indirect item, remove some number of its unformatted nodes. If the path points to an indirect item, remove some number of its unformatted nodes.
In case of file truncate calculate whether this item must be deleted/truncated or last 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,148 +1022,79 @@ static char prepare_for_delete_or_cut(struct reiserfs_transaction_handle *th, st
/* Case of an indirect item. */ /* Case of an indirect item. */
{ {
int n_unfm_number, /* Number of the item unformatted nodes. */ int blk_size = p_s_sb->s_blocksize;
n_counter, n_blk_size; struct item_head s_ih;
__le32 *p_n_unfm_pointer; /* Pointer to the unformatted node number. */ int need_re_search;
__u32 tmp; int delete = 0;
struct item_head s_ih; /* Item header. */ int result = M_CUT;
char c_mode; /* Returned mode of the balance. */ int pos = 0;
int need_research;
if ( n_new_file_length == max_reiserfs_offset (inode) ) {
n_blk_size = p_s_sb->s_blocksize; /* prepare_for_delete_or_cut() is called by
* reiserfs_delete_item() */
/* Search for the needed object indirect item until there are no unformatted nodes to be removed. */ n_new_file_length = 0;
do { delete = 1;
need_research = 0; }
p_s_bh = PATH_PLAST_BUFFER(p_s_path);
/* Copy indirect item header to a temp variable. */ do {
copy_item_head(&s_ih, PATH_PITEM_HEAD(p_s_path)); need_re_search = 0;
/* Calculate number of unformatted nodes in this item. */ *p_n_cut_size = 0;
n_unfm_number = I_UNFM_NUM(&s_ih); p_s_bh = PATH_PLAST_BUFFER(p_s_path);
copy_item_head(&s_ih, PATH_PITEM_HEAD(p_s_path));
RFALSE(!is_indirect_le_ih(&s_ih) || !n_unfm_number || pos = I_UNFM_NUM(&s_ih);
pos_in_item(p_s_path) + 1 != n_unfm_number,
"PAP-5240: invalid item %h "
"n_unfm_number = %d *p_n_pos_in_item = %d",
&s_ih, n_unfm_number, pos_in_item(p_s_path));
/* Calculate balance mode and position in the item to remove unformatted nodes. */
if (n_new_file_length == max_reiserfs_offset(inode)) { /* Case of delete. */
pos_in_item(p_s_path) = 0;
*p_n_cut_size = -(IH_SIZE + ih_item_len(&s_ih));
c_mode = M_DELETE;
} else { /* Case of truncate. */
if (n_new_file_length < le_ih_k_offset(&s_ih)) {
pos_in_item(p_s_path) = 0;
*p_n_cut_size =
-(IH_SIZE + ih_item_len(&s_ih));
c_mode = M_DELETE; /* Delete this item. */
} else {
/* indirect item must be truncated starting from *p_n_pos_in_item-th position */
pos_in_item(p_s_path) =
(n_new_file_length + n_blk_size -
le_ih_k_offset(&s_ih)) >> p_s_sb->
s_blocksize_bits;
RFALSE(pos_in_item(p_s_path) >
n_unfm_number,
"PAP-5250: invalid position in the item");
/* Either convert last unformatted node of indirect item to direct item or increase
its free space. */
if (pos_in_item(p_s_path) ==
n_unfm_number) {
*p_n_cut_size = 0; /* Nothing to cut. */
return M_CONVERT; /* Maybe convert last unformatted node to the direct item. */
}
/* Calculate size to cut. */
*p_n_cut_size =
-(ih_item_len(&s_ih) -
pos_in_item(p_s_path) *
UNFM_P_SIZE);
c_mode = M_CUT; /* Cut from this indirect item. */
}
}
RFALSE(n_unfm_number <= pos_in_item(p_s_path), while (le_ih_k_offset (&s_ih) + (pos - 1) * blk_size > n_new_file_length) {
"PAP-5260: invalid position in the indirect item"); __u32 *unfm, block;
/* pointers to be cut */
n_unfm_number -= pos_in_item(p_s_path);
/* Set pointer to the last unformatted node pointer that is to be cut. */
p_n_unfm_pointer =
(__le32 *) B_I_PITEM(p_s_bh,
&s_ih) + I_UNFM_NUM(&s_ih) -
1 - *p_n_removed;
/* We go through the unformatted nodes pointers of the indirect
item and look for the unformatted nodes in the cache. If we
found some of them we free it, zero corresponding indirect item
entry and log buffer containing that indirect item. For this we
need to prepare last path element for logging. If some
unformatted node has b_count > 1 we must not free this
unformatted node since it is in use. */
reiserfs_prepare_for_journal(p_s_sb, p_s_bh, 1);
// note: path could be changed, first line in for loop takes care
// of it
for (n_counter = *p_n_removed; /* Each unformatted block deletion may involve one additional
n_counter < n_unfm_number; * bitmap block into the transaction, thereby the initial
n_counter++, p_n_unfm_pointer--) { * journal space reservation might not be enough. */
if (!delete && (*p_n_cut_size) != 0 &&
reiserfs_transaction_free_space(th) < JOURNAL_FOR_FREE_BLOCK_AND_UPDATE_SD) {
break;
}
cond_resched(); unfm = (__u32 *)B_I_PITEM(p_s_bh, &s_ih) + pos - 1;
if (item_moved(&s_ih, p_s_path)) { block = get_block_num(unfm, 0);
need_research = 1;
break;
}
RFALSE(p_n_unfm_pointer <
(__le32 *) B_I_PITEM(p_s_bh, &s_ih)
|| p_n_unfm_pointer >
(__le32 *) B_I_PITEM(p_s_bh,
&s_ih) +
I_UNFM_NUM(&s_ih) - 1,
"vs-5265: pointer out of range");
/* Hole, nothing to remove. */
if (!get_block_num(p_n_unfm_pointer, 0)) {
(*p_n_removed)++;
continue;
}
(*p_n_removed)++; if (block != 0) {
reiserfs_prepare_for_journal(p_s_sb, p_s_bh, 1);
put_block_num(unfm, 0, 0);
journal_mark_dirty (th, p_s_sb, p_s_bh);
reiserfs_free_block(th, inode, block, 1);
}
tmp = get_block_num(p_n_unfm_pointer, 0); cond_resched();
put_block_num(p_n_unfm_pointer, 0, 0);
journal_mark_dirty(th, p_s_sb, p_s_bh);
reiserfs_free_block(th, inode, tmp, 1);
if (item_moved(&s_ih, p_s_path)) {
need_research = 1;
break;
}
}
/* a trick. If the buffer has been logged, this if (item_moved (&s_ih, p_s_path)) {
** will do nothing. If we've broken the loop without need_re_search = 1;
** logging it, it will restore the buffer break;
** }
*/
reiserfs_restore_prepared_buffer(p_s_sb, p_s_bh); pos --;
(*p_n_removed) ++;
/* This loop can be optimized. */ (*p_n_cut_size) -= UNFM_P_SIZE;
} while ((*p_n_removed < n_unfm_number || need_research) &&
search_for_position_by_key(p_s_sb, p_s_item_key, if (pos == 0) {
p_s_path) == (*p_n_cut_size) -= IH_SIZE;
POSITION_FOUND); result = M_DELETE;
break;
RFALSE(*p_n_removed < n_unfm_number, }
"PAP-5310: indirect item is not found"); }
RFALSE(item_moved(&s_ih, p_s_path), /* a trick. If the buffer has been logged, this will do nothing. If
"after while, comp failed, retry"); ** we've broken the loop without logging it, it will restore the
** buffer */
if (c_mode == M_CUT) reiserfs_restore_prepared_buffer(p_s_sb, p_s_bh);
pos_in_item(p_s_path) *= UNFM_P_SIZE; } while (need_re_search &&
return c_mode; search_for_position_by_key(p_s_sb, p_s_item_key, p_s_path) == POSITION_FOUND);
pos_in_item(p_s_path) = pos * UNFM_P_SIZE;
if (*p_n_cut_size == 0) {
/* Nothing were cut. maybe convert last unformatted node to the
* direct item? */
result = M_CONVERT;
}
return result;
} }
} }
...@@ -1948,7 +1881,8 @@ int reiserfs_do_truncate(struct reiserfs_transaction_handle *th, struct inode *p ...@@ -1948,7 +1881,8 @@ int reiserfs_do_truncate(struct reiserfs_transaction_handle *th, struct inode *p
** sure the file is consistent before ending the current trans ** sure the file is consistent before ending the current trans
** and starting a new one ** and starting a new one
*/ */
if (journal_transaction_should_end(th, th->t_blocks_allocated)) { if (journal_transaction_should_end(th, 0) ||
reiserfs_transaction_free_space(th) <= JOURNAL_FOR_FREE_BLOCK_AND_UPDATE_SD) {
int orig_len_alloc = th->t_blocks_allocated; int orig_len_alloc = th->t_blocks_allocated;
decrement_counters_in_path(&s_search_path); decrement_counters_in_path(&s_search_path);
...@@ -1962,7 +1896,7 @@ int reiserfs_do_truncate(struct reiserfs_transaction_handle *th, struct inode *p ...@@ -1962,7 +1896,7 @@ int reiserfs_do_truncate(struct reiserfs_transaction_handle *th, struct inode *p
if (err) if (err)
goto out; goto out;
err = journal_begin(th, p_s_inode->i_sb, err = journal_begin(th, p_s_inode->i_sb,
JOURNAL_PER_BALANCE_CNT * 6); JOURNAL_FOR_FREE_BLOCK_AND_UPDATE_SD + JOURNAL_PER_BALANCE_CNT * 4) ;
if (err) if (err)
goto out; goto out;
reiserfs_update_inode_transaction(p_s_inode); reiserfs_update_inode_transaction(p_s_inode);
......
...@@ -1704,6 +1704,11 @@ static inline int reiserfs_transaction_running(struct super_block *s) ...@@ -1704,6 +1704,11 @@ static inline int reiserfs_transaction_running(struct super_block *s)
return 0; return 0;
} }
static inline int reiserfs_transaction_free_space(struct reiserfs_transaction_handle *th)
{
return th->t_blocks_allocated - th->t_blocks_logged;
}
int reiserfs_async_progress_wait(struct super_block *s); int reiserfs_async_progress_wait(struct super_block *s);
struct reiserfs_transaction_handle *reiserfs_persistent_transaction(struct struct reiserfs_transaction_handle *reiserfs_persistent_transaction(struct
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment