[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>
This commit is contained in:
parent
bdfc326614
commit
23f9e0f891
2 changed files with 71 additions and 132 deletions
|
@ -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
|
||||||
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
n_blk_size = p_s_sb->s_blocksize;
|
if ( n_new_file_length == max_reiserfs_offset (inode) ) {
|
||||||
|
/* prepare_for_delete_or_cut() is called by
|
||||||
|
* reiserfs_delete_item() */
|
||||||
|
n_new_file_length = 0;
|
||||||
|
delete = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Search for the needed object indirect item until there are no unformatted nodes to be removed. */
|
do {
|
||||||
do {
|
need_re_search = 0;
|
||||||
need_research = 0;
|
*p_n_cut_size = 0;
|
||||||
p_s_bh = PATH_PLAST_BUFFER(p_s_path);
|
p_s_bh = PATH_PLAST_BUFFER(p_s_path);
|
||||||
/* Copy indirect item header to a temp variable. */
|
copy_item_head(&s_ih, PATH_PITEM_HEAD(p_s_path));
|
||||||
copy_item_head(&s_ih, PATH_PITEM_HEAD(p_s_path));
|
pos = I_UNFM_NUM(&s_ih);
|
||||||
/* Calculate number of unformatted nodes in this item. */
|
|
||||||
n_unfm_number = I_UNFM_NUM(&s_ih);
|
|
||||||
|
|
||||||
RFALSE(!is_indirect_le_ih(&s_ih) || !n_unfm_number ||
|
while (le_ih_k_offset (&s_ih) + (pos - 1) * blk_size > n_new_file_length) {
|
||||||
pos_in_item(p_s_path) + 1 != n_unfm_number,
|
__u32 *unfm, block;
|
||||||
"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. */
|
/* Each unformatted block deletion may involve one additional
|
||||||
if (n_new_file_length == max_reiserfs_offset(inode)) { /* Case of delete. */
|
* bitmap block into the transaction, thereby the initial
|
||||||
pos_in_item(p_s_path) = 0;
|
* journal space reservation might not be enough. */
|
||||||
*p_n_cut_size = -(IH_SIZE + ih_item_len(&s_ih));
|
if (!delete && (*p_n_cut_size) != 0 &&
|
||||||
c_mode = M_DELETE;
|
reiserfs_transaction_free_space(th) < JOURNAL_FOR_FREE_BLOCK_AND_UPDATE_SD) {
|
||||||
} else { /* Case of truncate. */
|
break;
|
||||||
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) >
|
unfm = (__u32 *)B_I_PITEM(p_s_bh, &s_ih) + pos - 1;
|
||||||
n_unfm_number,
|
block = get_block_num(unfm, 0);
|
||||||
"PAP-5250: invalid position in the item");
|
|
||||||
|
|
||||||
/* Either convert last unformatted node of indirect item to direct item or increase
|
if (block != 0) {
|
||||||
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),
|
|
||||||
"PAP-5260: invalid position in the indirect item");
|
|
||||||
|
|
||||||
/* 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);
|
reiserfs_prepare_for_journal(p_s_sb, p_s_bh, 1);
|
||||||
// note: path could be changed, first line in for loop takes care
|
put_block_num(unfm, 0, 0);
|
||||||
// of it
|
journal_mark_dirty (th, p_s_sb, p_s_bh);
|
||||||
|
reiserfs_free_block(th, inode, block, 1);
|
||||||
|
}
|
||||||
|
|
||||||
for (n_counter = *p_n_removed;
|
cond_resched();
|
||||||
n_counter < n_unfm_number;
|
|
||||||
n_counter++, p_n_unfm_pointer--) {
|
|
||||||
|
|
||||||
cond_resched();
|
if (item_moved (&s_ih, p_s_path)) {
|
||||||
if (item_moved(&s_ih, p_s_path)) {
|
need_re_search = 1;
|
||||||
need_research = 1;
|
break;
|
||||||
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. */
|
pos --;
|
||||||
if (!get_block_num(p_n_unfm_pointer, 0)) {
|
(*p_n_removed) ++;
|
||||||
(*p_n_removed)++;
|
(*p_n_cut_size) -= UNFM_P_SIZE;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*p_n_removed)++;
|
if (pos == 0) {
|
||||||
|
(*p_n_cut_size) -= IH_SIZE;
|
||||||
|
result = M_DELETE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* a trick. If the buffer has been logged, this will do nothing. If
|
||||||
|
** we've broken the loop without logging it, it will restore the
|
||||||
|
** buffer */
|
||||||
|
reiserfs_restore_prepared_buffer(p_s_sb, p_s_bh);
|
||||||
|
} while (need_re_search &&
|
||||||
|
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;
|
||||||
|
|
||||||
tmp = get_block_num(p_n_unfm_pointer, 0);
|
if (*p_n_cut_size == 0) {
|
||||||
put_block_num(p_n_unfm_pointer, 0, 0);
|
/* Nothing were cut. maybe convert last unformatted node to the
|
||||||
journal_mark_dirty(th, p_s_sb, p_s_bh);
|
* direct item? */
|
||||||
reiserfs_free_block(th, inode, tmp, 1);
|
result = M_CONVERT;
|
||||||
if (item_moved(&s_ih, p_s_path)) {
|
}
|
||||||
need_research = 1;
|
return result;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* a trick. If the buffer has been logged, this
|
|
||||||
** will do nothing. If we've broken the loop without
|
|
||||||
** logging it, it will restore the buffer
|
|
||||||
**
|
|
||||||
*/
|
|
||||||
reiserfs_restore_prepared_buffer(p_s_sb, p_s_bh);
|
|
||||||
|
|
||||||
/* This loop can be optimized. */
|
|
||||||
} while ((*p_n_removed < n_unfm_number || need_research) &&
|
|
||||||
search_for_position_by_key(p_s_sb, p_s_item_key,
|
|
||||||
p_s_path) ==
|
|
||||||
POSITION_FOUND);
|
|
||||||
|
|
||||||
RFALSE(*p_n_removed < n_unfm_number,
|
|
||||||
"PAP-5310: indirect item is not found");
|
|
||||||
RFALSE(item_moved(&s_ih, p_s_path),
|
|
||||||
"after while, comp failed, retry");
|
|
||||||
|
|
||||||
if (c_mode == M_CUT)
|
|
||||||
pos_in_item(p_s_path) *= UNFM_P_SIZE;
|
|
||||||
return c_mode;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
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)
|
||||||
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
|
||||||
|
|
Loading…
Add table
Reference in a new issue