Commit 09114eb8 authored by Evgeniy Dushistov's avatar Evgeniy Dushistov Committed by Linus Torvalds

[PATCH] ufs: fix hang during `rm'

This fixes the code like this:

	bh = sb_find_get_block (sb, tmp + j);
	if ((bh && DATA_BUFFER_USED(bh)) || tmp != fs32_to_cpu(sb, *p)) {
		retry = 1;
		brelse (bh);
		goto next1;
	}
	bforget (bh);

sb_find_get_block() ordinarily returns a buffer_head with b_count>=2, and
this code assume that in case if "b_count>1" buffer is used, so this caused
infinite loop.

(akpm: that is-the-buffer-busy code is incomprehensible.  Good riddance.  Use
of block_truncate_page() seems sane).
Signed-off-by: default avatarEvgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent e295cfcb
...@@ -376,7 +376,7 @@ static struct buffer_head * ufs_block_getfrag (struct inode *inode, ...@@ -376,7 +376,7 @@ static struct buffer_head * ufs_block_getfrag (struct inode *inode,
* This function gets the block which contains the fragment. * This function gets the block which contains the fragment.
*/ */
static int ufs_getfrag_block (struct inode *inode, sector_t fragment, struct buffer_head *bh_result, int create) int ufs_getfrag_block (struct inode *inode, sector_t fragment, struct buffer_head *bh_result, int create)
{ {
struct super_block * sb = inode->i_sb; struct super_block * sb = inode->i_sb;
struct ufs_sb_private_info * uspi = UFS_SB(sb)->s_uspi; struct ufs_sb_private_info * uspi = UFS_SB(sb)->s_uspi;
......
...@@ -29,6 +29,11 @@ ...@@ -29,6 +29,11 @@
* Idea from Pierre del Perugia <delperug@gla.ecoledoc.ibp.fr> * Idea from Pierre del Perugia <delperug@gla.ecoledoc.ibp.fr>
*/ */
/*
* Modified to avoid infinite loop on 2006 by
* Evgeniy Dushistov <dushistov@mail.ru>
*/
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/ufs_fs.h> #include <linux/ufs_fs.h>
...@@ -65,19 +70,16 @@ ...@@ -65,19 +70,16 @@
#define DIRECT_BLOCK ((inode->i_size + uspi->s_bsize - 1) >> uspi->s_bshift) #define DIRECT_BLOCK ((inode->i_size + uspi->s_bsize - 1) >> uspi->s_bshift)
#define DIRECT_FRAGMENT ((inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift) #define DIRECT_FRAGMENT ((inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift)
#define DATA_BUFFER_USED(bh) \
(atomic_read(&bh->b_count)>1 || buffer_locked(bh))
static int ufs_trunc_direct (struct inode * inode) static int ufs_trunc_direct (struct inode * inode)
{ {
struct ufs_inode_info *ufsi = UFS_I(inode); struct ufs_inode_info *ufsi = UFS_I(inode);
struct super_block * sb; struct super_block * sb;
struct ufs_sb_private_info * uspi; struct ufs_sb_private_info * uspi;
struct buffer_head * bh;
__fs32 * p; __fs32 * p;
unsigned frag1, frag2, frag3, frag4, block1, block2; unsigned frag1, frag2, frag3, frag4, block1, block2;
unsigned frag_to_free, free_count; unsigned frag_to_free, free_count;
unsigned i, j, tmp; unsigned i, tmp;
int retry; int retry;
UFSD(("ENTER\n")) UFSD(("ENTER\n"))
...@@ -117,15 +119,7 @@ static int ufs_trunc_direct (struct inode * inode) ...@@ -117,15 +119,7 @@ static int ufs_trunc_direct (struct inode * inode)
ufs_panic (sb, "ufs_trunc_direct", "internal error"); ufs_panic (sb, "ufs_trunc_direct", "internal error");
frag1 = ufs_fragnum (frag1); frag1 = ufs_fragnum (frag1);
frag2 = ufs_fragnum (frag2); frag2 = ufs_fragnum (frag2);
for (j = frag1; j < frag2; j++) {
bh = sb_find_get_block (sb, tmp + j);
if ((bh && DATA_BUFFER_USED(bh)) || tmp != fs32_to_cpu(sb, *p)) {
retry = 1;
brelse (bh);
goto next1;
}
bforget (bh);
}
inode->i_blocks -= (frag2-frag1) << uspi->s_nspfshift; inode->i_blocks -= (frag2-frag1) << uspi->s_nspfshift;
mark_inode_dirty(inode); mark_inode_dirty(inode);
ufs_free_fragments (inode, tmp + frag1, frag2 - frag1); ufs_free_fragments (inode, tmp + frag1, frag2 - frag1);
...@@ -140,15 +134,7 @@ static int ufs_trunc_direct (struct inode * inode) ...@@ -140,15 +134,7 @@ static int ufs_trunc_direct (struct inode * inode)
tmp = fs32_to_cpu(sb, *p); tmp = fs32_to_cpu(sb, *p);
if (!tmp) if (!tmp)
continue; continue;
for (j = 0; j < uspi->s_fpb; j++) {
bh = sb_find_get_block(sb, tmp + j);
if ((bh && DATA_BUFFER_USED(bh)) || tmp != fs32_to_cpu(sb, *p)) {
retry = 1;
brelse (bh);
goto next2;
}
bforget (bh);
}
*p = 0; *p = 0;
inode->i_blocks -= uspi->s_nspb; inode->i_blocks -= uspi->s_nspb;
mark_inode_dirty(inode); mark_inode_dirty(inode);
...@@ -162,7 +148,6 @@ static int ufs_trunc_direct (struct inode * inode) ...@@ -162,7 +148,6 @@ static int ufs_trunc_direct (struct inode * inode)
frag_to_free = tmp; frag_to_free = tmp;
free_count = uspi->s_fpb; free_count = uspi->s_fpb;
} }
next2:;
} }
if (free_count > 0) if (free_count > 0)
...@@ -179,15 +164,7 @@ next2:; ...@@ -179,15 +164,7 @@ next2:;
if (!tmp ) if (!tmp )
ufs_panic(sb, "ufs_truncate_direct", "internal error"); ufs_panic(sb, "ufs_truncate_direct", "internal error");
frag4 = ufs_fragnum (frag4); frag4 = ufs_fragnum (frag4);
for (j = 0; j < frag4; j++) {
bh = sb_find_get_block (sb, tmp + j);
if ((bh && DATA_BUFFER_USED(bh)) || tmp != fs32_to_cpu(sb, *p)) {
retry = 1;
brelse (bh);
goto next1;
}
bforget (bh);
}
*p = 0; *p = 0;
inode->i_blocks -= frag4 << uspi->s_nspfshift; inode->i_blocks -= frag4 << uspi->s_nspfshift;
mark_inode_dirty(inode); mark_inode_dirty(inode);
...@@ -204,9 +181,8 @@ static int ufs_trunc_indirect (struct inode * inode, unsigned offset, __fs32 *p) ...@@ -204,9 +181,8 @@ static int ufs_trunc_indirect (struct inode * inode, unsigned offset, __fs32 *p)
struct super_block * sb; struct super_block * sb;
struct ufs_sb_private_info * uspi; struct ufs_sb_private_info * uspi;
struct ufs_buffer_head * ind_ubh; struct ufs_buffer_head * ind_ubh;
struct buffer_head * bh;
__fs32 * ind; __fs32 * ind;
unsigned indirect_block, i, j, tmp; unsigned indirect_block, i, tmp;
unsigned frag_to_free, free_count; unsigned frag_to_free, free_count;
int retry; int retry;
...@@ -238,15 +214,7 @@ static int ufs_trunc_indirect (struct inode * inode, unsigned offset, __fs32 *p) ...@@ -238,15 +214,7 @@ static int ufs_trunc_indirect (struct inode * inode, unsigned offset, __fs32 *p)
tmp = fs32_to_cpu(sb, *ind); tmp = fs32_to_cpu(sb, *ind);
if (!tmp) if (!tmp)
continue; continue;
for (j = 0; j < uspi->s_fpb; j++) {
bh = sb_find_get_block(sb, tmp + j);
if ((bh && DATA_BUFFER_USED(bh)) || tmp != fs32_to_cpu(sb, *ind)) {
retry = 1;
brelse (bh);
goto next;
}
bforget (bh);
}
*ind = 0; *ind = 0;
ubh_mark_buffer_dirty(ind_ubh); ubh_mark_buffer_dirty(ind_ubh);
if (free_count == 0) { if (free_count == 0) {
...@@ -261,7 +229,6 @@ static int ufs_trunc_indirect (struct inode * inode, unsigned offset, __fs32 *p) ...@@ -261,7 +229,6 @@ static int ufs_trunc_indirect (struct inode * inode, unsigned offset, __fs32 *p)
} }
inode->i_blocks -= uspi->s_nspb; inode->i_blocks -= uspi->s_nspb;
mark_inode_dirty(inode); mark_inode_dirty(inode);
next:;
} }
if (free_count > 0) { if (free_count > 0) {
...@@ -430,9 +397,7 @@ void ufs_truncate (struct inode * inode) ...@@ -430,9 +397,7 @@ void ufs_truncate (struct inode * inode)
struct ufs_inode_info *ufsi = UFS_I(inode); struct ufs_inode_info *ufsi = UFS_I(inode);
struct super_block * sb; struct super_block * sb;
struct ufs_sb_private_info * uspi; struct ufs_sb_private_info * uspi;
struct buffer_head * bh; int retry;
unsigned offset;
int err, retry;
UFSD(("ENTER\n")) UFSD(("ENTER\n"))
sb = inode->i_sb; sb = inode->i_sb;
...@@ -442,6 +407,9 @@ void ufs_truncate (struct inode * inode) ...@@ -442,6 +407,9 @@ void ufs_truncate (struct inode * inode)
return; return;
if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
return; return;
block_truncate_page(inode->i_mapping, inode->i_size, ufs_getfrag_block);
lock_kernel(); lock_kernel();
while (1) { while (1) {
retry = ufs_trunc_direct(inode); retry = ufs_trunc_direct(inode);
...@@ -457,15 +425,7 @@ void ufs_truncate (struct inode * inode) ...@@ -457,15 +425,7 @@ void ufs_truncate (struct inode * inode)
blk_run_address_space(inode->i_mapping); blk_run_address_space(inode->i_mapping);
yield(); yield();
} }
offset = inode->i_size & uspi->s_fshift;
if (offset) {
bh = ufs_bread (inode, inode->i_size >> uspi->s_fshift, 0, &err);
if (bh) {
memset (bh->b_data + offset, 0, uspi->s_fsize - offset);
mark_buffer_dirty (bh);
brelse (bh);
}
}
inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
ufsi->i_lastfrag = DIRECT_FRAGMENT; ufsi->i_lastfrag = DIRECT_FRAGMENT;
unlock_kernel(); unlock_kernel();
......
...@@ -912,6 +912,7 @@ extern int ufs_sync_inode (struct inode *); ...@@ -912,6 +912,7 @@ extern int ufs_sync_inode (struct inode *);
extern void ufs_delete_inode (struct inode *); extern void ufs_delete_inode (struct inode *);
extern struct buffer_head * ufs_getfrag (struct inode *, unsigned, int, int *); extern struct buffer_head * ufs_getfrag (struct inode *, unsigned, int, int *);
extern struct buffer_head * ufs_bread (struct inode *, unsigned, int, int *); extern struct buffer_head * ufs_bread (struct inode *, unsigned, int, int *);
extern int ufs_getfrag_block (struct inode *inode, sector_t fragment, struct buffer_head *bh_result, int create);
/* namei.c */ /* namei.c */
extern struct file_operations ufs_dir_operations; extern struct file_operations ufs_dir_operations;
......
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