2011-12-11 23:30:45

by Paul Mackerras

[permalink] [raw]
Subject: Crash in __brelse with recent kernels copying sparse file

Hi,

I'm seeing a repeatable crash in __brelse with recent kernels while
copying a sparse file with rsync. I have a file that is about 12GB in
length but which occupies about 5.5GB. I copy it with:

% rsync -avSP rhel6-root.img rhel6-root2.img

and at some point the system (a POWER7 12-core server) will crash like
this:

attempt to access beyond end of device
sda8: rw=0, want=776392648163376, limit=168558560
Unable to handle kernel paging request for data at address 0x6b6b6b6b6b6b6bcb
Faulting instruction address: 0xc0000000001f5f38
cpu 0x14: Vector: 300 (Data Access) at [c000001bd1aaecf0]
pc: c0000000001f5f38: .__brelse+0x18/0x60
lr: c0000000002e07a4: .ext4_ext_drop_refs+0x44/0x80
sp: c000001bd1aaef70
msr: 9000000000009032
dar: 6b6b6b6b6b6b6bcb
dsisr: 40000000
current = 0xc000001bd15b8010
paca = 0xc00000000ffe4600
pid = 19911, comm = flush-8:0
enter ? for help
[c000001bd1aaeff0] c0000000002e07a4 .ext4_ext_drop_refs+0x44/0x80
[c000001bd1aaf090] c0000000002e0c58 .ext4_ext_find_extent+0x408/0x4c0
[c000001bd1aaf180] c0000000002e145c .ext4_ext_insert_extent+0x2bc/0x14c0
[c000001bd1aaf2c0] c0000000002e3fb8 .ext4_ext_map_blocks+0x628/0x1710
[c000001bd1aaf420] c0000000002b2974 .ext4_map_blocks+0x224/0x310
[c000001bd1aaf4d0] c0000000002b7f2c .mpage_da_map_and_submit+0xbc/0x490
[c000001bd1aaf5a0] c0000000002b8688 .write_cache_pages_da+0x2c8/0x430
[c000001bd1aaf720] c0000000002b8b28 .ext4_da_writepages+0x338/0x670
[c000001bd1aaf8d0] c000000000157280 .do_writepages+0x40/0x90
[c000001bd1aaf940] c0000000001ea830 .writeback_single_inode+0xe0/0x530
[c000001bd1aafa00] c0000000001eb680 .writeback_sb_inodes+0x210/0x300
[c000001bd1aafb20] c0000000001ebc84 .__writeback_inodes_wb+0xd4/0x140
[c000001bd1aafbe0] c0000000001ebfec .wb_writeback+0x2fc/0x3e0
[c000001bd1aafce0] c0000000001ed770 .wb_do_writeback+0x2f0/0x300
[c000001bd1aafdf0] c0000000001ed848 .bdi_writeback_thread+0xc8/0x340
[c000001bd1aafed0] c0000000000c5494 .kthread+0xb4/0xc0
[c000001bd1aaff90] c000000000021f48 .kernel_thread+0x54/0x70

In other words, __brelse got passed a pointer that was obtained from a
freed object, which had been poisoned with the 0x6b6b... value (I have
SLAB configured with debugging enabled).

Digging a bit deeper, __brelse was called from ext4_ext_drop_refs, and
in that function we have path=0xc000001bba9761e8, depth=0x101, and i=5
at the time of the crash. So it looks like path on entry to that
function was 0xc000001bba9760c8, and if I dump out memory from there
it looks like this:

c000001bba9760c8 00000000001085d3 0101000000000000 |................|
c000001bba9760d8 0000000000000000 c000001b4518cc3c |............E..<|
c000001bba9760e8 c000001b4518cc30 0000000000000000 |....E..0........|

c000001bba9760f8 0000000000c69800 0100000000000000 |................|
c000001bba976108 0000000000000000 c000001b88063030 |..............00|
c000001bba976118 c000001b88063000 0000000000000000 |......0.........|

c000001bba976128 0000584400000005 00ff000000000000 |..XD............|
c000001bba976138 0000000000000000 c0000019395a0ff0 |............9Z..|
c000001bba976148 c0000019395a0000 0000000000000000 |....9Z..........|

c000001bba976158 0000000000000000 0000000000000000 |................|
c000001bba976168 0000000000000000 0000000000000000 |................|
c000001bba976178 0000000000000000 0000000000000000 |................|

c000001bba976188 0000000000000000 0000000000000000 |................|
c000001bba976198 0000000000000000 0000000000000000 |................|
c000001bba9761a8 0000000000000000 0000000000000000 |................|

c000001bba9761b8 0000000000000000 0000000000000000 |................|
c000001bba9761c8 d84156c5635688c0 c0000000002e0ca4 |.AV.cV..........|
c000001bba9761d8 09f911029d74e35b 6b6b6b6b6b6b6b6b |.....t.[kkkkkkkk|

c000001bba9761e8 6b6b6b6b6b6b6b6b 6b6b6b6b6b6b6b6b |kkkkkkkkkkkkkkkk|
c000001bba9761f8 6b6b6b6b6b6b6b6b 6b6b6b6b6b6b6b6b |kkkkkkkkkkkkkkkk|
c000001bba976208 6b6b6b6b6b6b6b6b 6b6b6b6b6b6b6b6b |kkkkkkkkkkkkkkkk|

So, the first three entries in this array have depth = 257, 256, 255,
but clearly there are only 5 entries allocated, and we've run off the
end of the array into a following freed object.

Has anyone seen anything like this? Any suggestions for patches to
try, or ways to debug this further?

Paul.


2011-12-12 02:44:02

by Theodore Ts'o

[permalink] [raw]
Subject: Re: Crash in __brelse with recent kernels copying sparse file

On Mon, Dec 12, 2011 at 10:29:51AM +1100, Paul Mackerras wrote:
> Hi,
>
> I'm seeing a repeatable crash in __brelse with recent kernels while
> copying a sparse file with rsync. I have a file that is about 12GB in
> length but which occupies about 5.5GB. I copy it with:
>
> % rsync -avSP rhel6-root.img rhel6-root2.img

Hmm, can you try this patch, and see if it triggers?

I've removed all of the static declarations so that the stack trace
will hopefully be more accurate. (I don't know if you have a reliable
way to map hex addresses to line numbers on PowerPC, but it's
critically important to do that given the huge number of static
functions that generally get inlined, making the stack trace hard to
decipher if you can't get the line numbers.)

- Ted

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 61fa9e1..2bda07a 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -45,14 +45,14 @@

#include <trace/events/ext4.h>

-static int ext4_split_extent(handle_t *handle,
+int ext4_split_extent(handle_t *handle,
struct inode *inode,
struct ext4_ext_path *path,
struct ext4_map_blocks *map,
int split_flag,
int flags);

-static int ext4_ext_truncate_extend_restart(handle_t *handle,
+int ext4_ext_truncate_extend_restart(handle_t *handle,
struct inode *inode,
int needed)
{
@@ -77,7 +77,7 @@ static int ext4_ext_truncate_extend_restart(handle_t *handle,
* - EROFS
* - ENOMEM
*/
-static int ext4_ext_get_access(handle_t *handle, struct inode *inode,
+int ext4_ext_get_access(handle_t *handle, struct inode *inode,
struct ext4_ext_path *path)
{
if (path->p_bh) {
@@ -97,7 +97,7 @@ static int ext4_ext_get_access(handle_t *handle, struct inode *inode,
*/
#define ext4_ext_dirty(handle, inode, path) \
__ext4_ext_dirty(__func__, __LINE__, (handle), (inode), (path))
-static int __ext4_ext_dirty(const char *where, unsigned int line,
+int __ext4_ext_dirty(const char *where, unsigned int line,
handle_t *handle, struct inode *inode,
struct ext4_ext_path *path)
{
@@ -113,7 +113,7 @@ static int __ext4_ext_dirty(const char *where, unsigned int line,
return err;
}

-static ext4_fsblk_t ext4_ext_find_goal(struct inode *inode,
+ext4_fsblk_t ext4_ext_find_goal(struct inode *inode,
struct ext4_ext_path *path,
ext4_lblk_t block)
{
@@ -162,7 +162,7 @@ static ext4_fsblk_t ext4_ext_find_goal(struct inode *inode,
/*
* Allocation for a meta data block
*/
-static ext4_fsblk_t
+ext4_fsblk_t
ext4_ext_new_meta_block(handle_t *handle, struct inode *inode,
struct ext4_ext_path *path,
struct ext4_extent *ex, int *err, unsigned int flags)
@@ -175,7 +175,7 @@ ext4_ext_new_meta_block(handle_t *handle, struct inode *inode,
return newblock;
}

-static inline int ext4_ext_space_block(struct inode *inode, int check)
+int ext4_ext_space_block(struct inode *inode, int check)
{
int size;

@@ -188,7 +188,7 @@ static inline int ext4_ext_space_block(struct inode *inode, int check)
return size;
}

-static inline int ext4_ext_space_block_idx(struct inode *inode, int check)
+int ext4_ext_space_block_idx(struct inode *inode, int check)
{
int size;

@@ -201,7 +201,7 @@ static inline int ext4_ext_space_block_idx(struct inode *inode, int check)
return size;
}

-static inline int ext4_ext_space_root(struct inode *inode, int check)
+int ext4_ext_space_root(struct inode *inode, int check)
{
int size;

@@ -215,7 +215,7 @@ static inline int ext4_ext_space_root(struct inode *inode, int check)
return size;
}

-static inline int ext4_ext_space_root_idx(struct inode *inode, int check)
+int ext4_ext_space_root_idx(struct inode *inode, int check)
{
int size;

@@ -276,7 +276,7 @@ int ext4_ext_calc_metadata_amount(struct inode *inode, ext4_lblk_t lblock)
return ext_depth(inode) + 1;
}

-static int
+int
ext4_ext_max_entries(struct inode *inode, int depth)
{
int max;
@@ -296,7 +296,7 @@ ext4_ext_max_entries(struct inode *inode, int depth)
return max;
}

-static int ext4_valid_extent(struct inode *inode, struct ext4_extent *ext)
+int ext4_valid_extent(struct inode *inode, struct ext4_extent *ext)
{
ext4_fsblk_t block = ext4_ext_pblock(ext);
int len = ext4_ext_get_actual_len(ext);
@@ -304,7 +304,7 @@ static int ext4_valid_extent(struct inode *inode, struct ext4_extent *ext)
return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, len);
}

-static int ext4_valid_extent_idx(struct inode *inode,
+int ext4_valid_extent_idx(struct inode *inode,
struct ext4_extent_idx *ext_idx)
{
ext4_fsblk_t block = ext4_idx_pblock(ext_idx);
@@ -312,7 +312,7 @@ static int ext4_valid_extent_idx(struct inode *inode,
return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, 1);
}

-static int ext4_valid_extent_entries(struct inode *inode,
+int ext4_valid_extent_entries(struct inode *inode,
struct ext4_extent_header *eh,
int depth)
{
@@ -343,7 +343,7 @@ static int ext4_valid_extent_entries(struct inode *inode,
return 1;
}

-static int __ext4_ext_check(const char *function, unsigned int line,
+int __ext4_ext_check(const char *function, unsigned int line,
struct inode *inode, struct ext4_extent_header *eh,
int depth)
{
@@ -397,7 +397,7 @@ int ext4_ext_check_inode(struct inode *inode)
}

#ifdef EXT_DEBUG
-static void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path)
+void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path)
{
int k, l = path->p_depth;

@@ -418,7 +418,7 @@ static void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path)
ext_debug("\n");
}

-static void ext4_ext_show_leaf(struct inode *inode, struct ext4_ext_path *path)
+void ext4_ext_show_leaf(struct inode *inode, struct ext4_ext_path *path)
{
int depth = ext_depth(inode);
struct ext4_extent_header *eh;
@@ -441,7 +441,7 @@ static void ext4_ext_show_leaf(struct inode *inode, struct ext4_ext_path *path)
ext_debug("\n");
}

-static void ext4_ext_show_move(struct inode *inode, struct ext4_ext_path *path,
+void ext4_ext_show_move(struct inode *inode, struct ext4_ext_path *path,
ext4_fsblk_t newblock, int level)
{
int depth = ext_depth(inode);
@@ -496,7 +496,7 @@ void ext4_ext_drop_refs(struct ext4_ext_path *path)
* binary search for the closest index of the given block
* the header must be checked before calling this
*/
-static void
+void
ext4_ext_binsearch_idx(struct inode *inode,
struct ext4_ext_path *path, ext4_lblk_t block)
{
@@ -556,7 +556,7 @@ ext4_ext_binsearch_idx(struct inode *inode,
* binary search for closest extent of the given block
* the header must be checked before calling this
*/
-static void
+void
ext4_ext_binsearch(struct inode *inode,
struct ext4_ext_path *path, ext4_lblk_t block)
{
@@ -638,6 +638,13 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block,
eh = ext_inode_hdr(inode);
depth = ext_depth(inode);

+ if (path && depth > (path->p_depth + 2)) {
+ ext4_msg(inode->i_sb, KERN_CRIT,
+ "%s: inode %lu, depth %d > path->p_depth %d", __func__,
+ inode->i_ino, depth, path->p_depth);
+ BUG_ON(1);
+ }
+
/* account possible depth increase */
if (!path) {
path = kzalloc(sizeof(struct ext4_ext_path) * (depth + 2),
@@ -717,7 +724,7 @@ err:
* insert new index [@logical;@ptr] into the block at @curp;
* check where to insert: before @curp or after @curp
*/
-static int ext4_ext_insert_index(handle_t *handle, struct inode *inode,
+int ext4_ext_insert_index(handle_t *handle, struct inode *inode,
struct ext4_ext_path *curp,
int logical, ext4_fsblk_t ptr)
{
@@ -793,7 +800,7 @@ static int ext4_ext_insert_index(handle_t *handle, struct inode *inode,
* into the newly allocated blocks
* - initializes subtree
*/
-static int ext4_ext_split(handle_t *handle, struct inode *inode,
+int ext4_ext_split(handle_t *handle, struct inode *inode,
unsigned int flags,
struct ext4_ext_path *path,
struct ext4_extent *newext, int at)
@@ -1032,7 +1039,7 @@ cleanup:
* - initializes new top-level, creating index that points to the
* just created block
*/
-static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode,
+int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode,
unsigned int flags,
struct ext4_extent *newext)
{
@@ -1108,7 +1115,7 @@ out:
* finds empty index and adds new leaf.
* if no free index is found, then it requests in-depth growing.
*/
-static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
+int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
unsigned int flags,
struct ext4_ext_path *path,
struct ext4_extent *newext)
@@ -1180,7 +1187,7 @@ out:
* returns 0 at @phys
* return value contains 0 (success) or error code
*/
-static int ext4_ext_search_left(struct inode *inode,
+int ext4_ext_search_left(struct inode *inode,
struct ext4_ext_path *path,
ext4_lblk_t *logical, ext4_fsblk_t *phys)
{
@@ -1245,7 +1252,7 @@ static int ext4_ext_search_left(struct inode *inode,
* returns 0 at @phys
* return value contains 0 (success) or error code
*/
-static int ext4_ext_search_right(struct inode *inode,
+int ext4_ext_search_right(struct inode *inode,
struct ext4_ext_path *path,
ext4_lblk_t *logical, ext4_fsblk_t *phys,
struct ext4_extent **ret_ex)
@@ -1362,7 +1369,7 @@ found_extent:
* allocated block. Thus, index entries have to be consistent
* with leaves.
*/
-static ext4_lblk_t
+ext4_lblk_t
ext4_ext_next_allocated_block(struct ext4_ext_path *path)
{
int depth;
@@ -1396,7 +1403,7 @@ ext4_ext_next_allocated_block(struct ext4_ext_path *path)
* ext4_ext_next_leaf_block:
* returns first allocated block from next leaf or EXT_MAX_BLOCKS
*/
-static ext4_lblk_t ext4_ext_next_leaf_block(struct ext4_ext_path *path)
+ext4_lblk_t ext4_ext_next_leaf_block(struct ext4_ext_path *path)
{
int depth;

@@ -1427,7 +1434,7 @@ static ext4_lblk_t ext4_ext_next_leaf_block(struct ext4_ext_path *path)
* then we have to correct all indexes above.
* TODO: do we need to correct tree in all cases?
*/
-static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode,
+int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode,
struct ext4_ext_path *path)
{
struct ext4_extent_header *eh;
@@ -1533,7 +1540,7 @@ ext4_can_extents_be_merged(struct inode *inode, struct ext4_extent *ex1,
* Returns 0 if the extents (ex and ex+1) were _not_ merged and returns
* 1 if they got merged.
*/
-static int ext4_ext_try_to_merge_right(struct inode *inode,
+int ext4_ext_try_to_merge_right(struct inode *inode,
struct ext4_ext_path *path,
struct ext4_extent *ex)
{
@@ -1576,7 +1583,7 @@ static int ext4_ext_try_to_merge_right(struct inode *inode,
* This function tries to merge the @ex extent to neighbours in the tree.
* return 1 if merge left else 0.
*/
-static int ext4_ext_try_to_merge(struct inode *inode,
+int ext4_ext_try_to_merge(struct inode *inode,
struct ext4_ext_path *path,
struct ext4_extent *ex) {
struct ext4_extent_header *eh;
@@ -1605,7 +1612,7 @@ static int ext4_ext_try_to_merge(struct inode *inode,
* such that there will be no overlap, and then returns 1.
* If there is no overlap found, it returns 0.
*/
-static unsigned int ext4_ext_check_overlap(struct ext4_sb_info *sbi,
+unsigned int ext4_ext_check_overlap(struct ext4_sb_info *sbi,
struct inode *inode,
struct ext4_extent *newext,
struct ext4_ext_path *path)
@@ -1830,7 +1837,7 @@ cleanup:
return err;
}

-static int ext4_ext_walk_space(struct inode *inode, ext4_lblk_t block,
+int ext4_ext_walk_space(struct inode *inode, ext4_lblk_t block,
ext4_lblk_t num, ext_prepare_callback func,
void *cbdata)
{
@@ -1945,7 +1952,7 @@ static int ext4_ext_walk_space(struct inode *inode, ext4_lblk_t block,
return err;
}

-static void
+void
ext4_ext_put_in_cache(struct inode *inode, ext4_lblk_t block,
__u32 len, ext4_fsblk_t start)
{
@@ -1965,7 +1972,7 @@ ext4_ext_put_in_cache(struct inode *inode, ext4_lblk_t block,
* calculate boundaries of the gap that the requested block fits into
* and cache this gap
*/
-static void
+void
ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path,
ext4_lblk_t block)
{
@@ -2025,7 +2032,7 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path,
*
* Return 0 if cache is invalid; 1 if the cache is valid
*/
-static int ext4_ext_check_cache(struct inode *inode, ext4_lblk_t block,
+int ext4_ext_check_cache(struct inode *inode, ext4_lblk_t block,
struct ext4_ext_cache *ex){
struct ext4_ext_cache *cex;
struct ext4_sb_info *sbi;
@@ -2072,7 +2079,7 @@ errout:
*
* Return 0 if cache is invalid; 1 if the cache is valid
*/
-static int
+int
ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block,
struct ext4_extent *ex)
{
@@ -2094,7 +2101,7 @@ ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block,
* ext4_ext_rm_idx:
* removes index from the index block.
*/
-static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
+int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
struct ext4_ext_path *path)
{
int err;
@@ -2188,7 +2195,7 @@ int ext4_ext_index_trans_blocks(struct inode *inode, int nrblocks, int chunk)
return index;
}

-static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
+int ext4_remove_blocks(handle_t *handle, struct inode *inode,
struct ext4_extent *ex,
ext4_fsblk_t *partial_cluster,
ext4_lblk_t from, ext4_lblk_t to)
@@ -2292,7 +2299,7 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
* @start: The first block to remove
* @end: The last block to remove
*/
-static int
+int
ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
struct ext4_ext_path *path, ext4_fsblk_t *partial_cluster,
ext4_lblk_t start, ext4_lblk_t end)
@@ -2466,7 +2473,7 @@ out:
* ext4_ext_more_to_rm:
* returns 1 if current index has to be freed (even partial)
*/
-static int
+int
ext4_ext_more_to_rm(struct ext4_ext_path *path)
{
BUG_ON(path->p_idx == NULL);
@@ -2483,7 +2490,7 @@ ext4_ext_more_to_rm(struct ext4_ext_path *path)
return 1;
}

-static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
+int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
{
struct super_block *sb = inode->i_sb;
int depth = ext_depth(inode);
@@ -2509,7 +2516,7 @@ again:
* after i_size and walking into the tree depth-wise.
*/
depth = ext_depth(inode);
- path = kzalloc(sizeof(struct ext4_ext_path) * (depth + 1), GFP_NOFS);
+ path = kzalloc(sizeof(struct ext4_ext_path) * (depth + 2), GFP_NOFS);
if (path == NULL) {
ext4_journal_stop(handle);
return -ENOMEM;
@@ -2693,7 +2700,7 @@ void ext4_ext_release(struct super_block *sb)
}

/* FIXME!! we need to try to merge to left or right after zero-out */
-static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex)
+int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex)
{
ext4_fsblk_t ee_pblock;
unsigned int ee_len;
@@ -2738,7 +2745,7 @@ static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex)
*
* return 0 on success.
*/
-static int ext4_split_extent_at(handle_t *handle,
+int ext4_split_extent_at(handle_t *handle,
struct inode *inode,
struct ext4_ext_path *path,
ext4_lblk_t split,
@@ -2842,7 +2849,7 @@ fix_extent_len:
* c> Splits in three extents: Somone is splitting in middle of the extent
*
*/
-static int ext4_split_extent(handle_t *handle,
+int ext4_split_extent(handle_t *handle,
struct inode *inode,
struct ext4_ext_path *path,
struct ext4_map_blocks *map,
@@ -2919,7 +2926,7 @@ out:
* that are allocated and initialized.
* It is guaranteed to be >= map->m_len.
*/
-static int ext4_ext_convert_to_initialized(handle_t *handle,
+int ext4_ext_convert_to_initialized(handle_t *handle,
struct inode *inode,
struct ext4_map_blocks *map,
struct ext4_ext_path *path)
@@ -3130,7 +3137,7 @@ out:
*
* Returns the size of uninitialized extent to be written on success.
*/
-static int ext4_split_unwritten_extents(handle_t *handle,
+int ext4_split_unwritten_extents(handle_t *handle,
struct inode *inode,
struct ext4_map_blocks *map,
struct ext4_ext_path *path,
@@ -3166,7 +3173,7 @@ static int ext4_split_unwritten_extents(handle_t *handle,
return ext4_split_extent(handle, inode, path, map, split_flag, flags);
}

-static int ext4_convert_unwritten_extents_endio(handle_t *handle,
+int ext4_convert_unwritten_extents_endio(handle_t *handle,
struct inode *inode,
struct ext4_ext_path *path)
{
@@ -3200,7 +3207,7 @@ out:
return err;
}

-static void unmap_underlying_metadata_blocks(struct block_device *bdev,
+void unmap_underlying_metadata_blocks(struct block_device *bdev,
sector_t block, int count)
{
int i;
@@ -3211,7 +3218,7 @@ static void unmap_underlying_metadata_blocks(struct block_device *bdev,
/*
* Handle EOFBLOCKS_FL flag, clearing it if necessary
*/
-static int check_eofblocks_fl(handle_t *handle, struct inode *inode,
+int check_eofblocks_fl(handle_t *handle, struct inode *inode,
ext4_lblk_t lblk,
struct ext4_ext_path *path,
unsigned int len)
@@ -3271,7 +3278,7 @@ static int check_eofblocks_fl(handle_t *handle, struct inode *inode,
* block sooner). This is useful when blocks are truncated sequentially from
* lblk_start towards lblk_end.
*/
-static int ext4_find_delalloc_range(struct inode *inode,
+int ext4_find_delalloc_range(struct inode *inode,
ext4_lblk_t lblk_start,
ext4_lblk_t lblk_end,
int search_hint_reverse)
@@ -3404,7 +3411,7 @@ int ext4_find_delalloc_cluster(struct inode *inode, ext4_lblk_t lblk,
* In the non-bigalloc case, this function will just end up returning num_blks
* without ever calling ext4_find_delalloc_range.
*/
-static unsigned int
+unsigned int
get_reserved_cluster_alloc(struct inode *inode, ext4_lblk_t lblk_start,
unsigned int num_blks)
{
@@ -3444,7 +3451,7 @@ get_reserved_cluster_alloc(struct inode *inode, ext4_lblk_t lblk_start,
return allocated_clusters;
}

-static int
+int
ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode,
struct ext4_map_blocks *map,
struct ext4_ext_path *path, int flags,
@@ -3618,7 +3625,7 @@ out2:
* ext4_ext_map_blocks() will then allocate one or more new clusters
* by calling ext4_mb_new_blocks().
*/
-static int get_implied_cluster_alloc(struct super_block *sb,
+int get_implied_cluster_alloc(struct super_block *sb,
struct ext4_map_blocks *map,
struct ext4_extent *ex,
struct ext4_ext_path *path)
@@ -4254,7 +4261,7 @@ out_stop:
ext4_journal_stop(handle);
}

-static void ext4_falloc_update_inode(struct inode *inode,
+void ext4_falloc_update_inode(struct inode *inode,
int mode, loff_t new_size, int update_ctime)
{
struct timespec now;
@@ -4452,7 +4459,7 @@ int ext4_convert_unwritten_extents(struct inode *inode, loff_t offset,
/*
* Callback function called for each extent to gather FIEMAP information.
*/
-static int ext4_ext_fiemap_cb(struct inode *inode, ext4_lblk_t next,
+int ext4_ext_fiemap_cb(struct inode *inode, ext4_lblk_t next,
struct ext4_ext_cache *newex, struct ext4_extent *ex,
void *data)
{
@@ -4654,7 +4661,7 @@ found_delayed_extent:
/* fiemap flags we can handle specified here */
#define EXT4_FIEMAP_FLAGS (FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR)

-static int ext4_xattr_fiemap(struct inode *inode,
+int ext4_xattr_fiemap(struct inode *inode,
struct fiemap_extent_info *fieinfo)
{
__u64 physical = 0;

2011-12-12 06:05:00

by Paul Mackerras

[permalink] [raw]
Subject: Re: Crash in __brelse with recent kernels copying sparse file

On Sun, Dec 11, 2011 at 09:43:59PM -0500, Ted Ts'o wrote:
> Hmm, can you try this patch, and see if it triggers?
>
> I've removed all of the static declarations so that the stack trace
> will hopefully be more accurate. (I don't know if you have a reliable
> way to map hex addresses to line numbers on PowerPC, but it's
> critically important to do that given the huge number of static
> functions that generally get inlined, making the stack trace hard to
> decipher if you can't get the line numbers.)

Yes, it triggers:

EXT4-fs (sda8): ext4_ext_find_extent: inode 264547, depth 257 > path->p_depth 1
------------[ cut here ]------------
kernel BUG at /home/paulus/kernel/kvm-merge/fs/ext4/extents.c:645!
cpu 0x1: Vector: 700 (Program Check) at [c000001bc8772dd0]
pc: c0000000002df3d4: .ext4_ext_find_extent+0x3d4/0x3e0
lr: c0000000002df3d0: .ext4_ext_find_extent+0x3d0/0x3e0
sp: c000001bc8773050
msr: 9000000000029032
current = 0xc000001bcc4b3b10
paca = 0xc00000000ffe0380
pid = 4211, comm = flush-8:0
kernel BUG at /home/paulus/kernel/kvm-merge/fs/ext4/extents.c:645!
enter ? for help
[c000001bc8773130] c0000000002e0324 .ext4_ext_create_new_leaf+0x144/0x230
[c000001bc87731e0] c0000000002e16ec .ext4_ext_insert_extent+0x15c/0x5a0
[c000001bc87732b0] c0000000002e5b78 .ext4_ext_map_blocks+0x5b8/0xea0
[c000001bc8773420] c0000000002b2974 .ext4_map_blocks+0x224/0x310
[c000001bc87734d0] c0000000002b7f2c .mpage_da_map_and_submit+0xbc/0x490
[c000001bc87735a0] c0000000002b8688 .write_cache_pages_da+0x2c8/0x430
[c000001bc8773720] c0000000002b8b28 .ext4_da_writepages+0x338/0x670
[c000001bc87738d0] c000000000157280 .do_writepages+0x40/0x90
[c000001bc8773940] c0000000001ea830 .writeback_single_inode+0xe0/0x530
[c000001bc8773a00] c0000000001eb680 .writeback_sb_inodes+0x210/0x300
[c000001bc8773b20] c0000000001ebc84 .__writeback_inodes_wb+0xd4/0x140
[c000001bc8773be0] c0000000001ebfec .wb_writeback+0x2fc/0x3e0
[c000001bc8773ce0] c0000000001ed55c .wb_do_writeback+0xdc/0x300
[c000001bc8773df0] c0000000001ed848 .bdi_writeback_thread+0xc8/0x340
[c000001bc8773ed0] c0000000000c5494 .kthread+0xb4/0xc0
[c000001bc8773f90] c000000000021f48 .kernel_thread+0x54/0x70

I'll try to dig out some line numbers for you.

Paul.