aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMing Lin <mlin@kernel.org>2015-07-09 16:05:24 -0700
committerMing Lin <mlin@kernel.org>2015-07-09 16:23:01 -0700
commitceeccaa160042f10adb23970a2bd94510df76186 (patch)
tree71a5f9ab64e5286cc726888ee6bbf52774390a2d
parent973b91cf6a3aca1273a42ab9e335d0b1aa9626c6 (diff)
downloadlinux-ceeccaa16.tar.gz
xfstests/xfs/080 fixblock-dio-rewrite
Let's say there are 2 extents: one unwritten and the other written. rwtest.file: EXT: FILE-OFFSET BLOCK-RANGE AG AG-OFFSET TOTAL FLAGS 0: [0..15]: 160..175 0 (160..175) 16 10000 1: [16..63]: 176..223 0 (176..223) 48 00000 Then we do a direct io write to these 2 extends. root@block:/mnt/test# xfs_io xfs_io> open -d rwtest.file xfs_io> pwrite -b 32768 0 32768 EXT 0 should be converted to written extent. rwtest.file: EXT: FILE-OFFSET BLOCK-RANGE AG AG-OFFSET TOTAL FLAGS 0: [0..63]: 160..223 0 (160..223) 64 00000 There was a bug that caused dio->bi_privte set to NULL, so below code path was not executed, so the extent not converted from unwritten to written. [ 72.328096] Call Trace: [ 72.328786] [<ffffffff8169c2f3>] dump_stack+0x4f/0x7b [ 72.330002] [<ffffffff8127140f>] xfs_bmapi_convert_unwritten+0x73/0x176 [ 72.331565] [<ffffffff8126cd9d>] ? xfs_bmap_search_extents+0x60/0xd6 [ 72.333059] [<ffffffff812c96f1>] ? kmem_zone_alloc+0x6e/0xba [ 72.334412] [<ffffffff8127a233>] xfs_bmapi_write+0x2a0/0x7ee [ 72.335777] [<ffffffff812b9455>] xfs_iomap_write_unwritten+0x205/0x413 [ 72.337300] [<ffffffff8129d7e2>] xfs_end_io+0x50/0x75 [ 72.338495] [<ffffffff8129dab9>] xfs_end_io_direct_write+0x176/0x26c [ 72.339952] [<ffffffff810d20a4>] ? delayacct_end+0x55/0x5e [ 72.341238] [<ffffffff8118759d>] dio_complete+0x7c/0x134 [ 72.342488] [<ffffffff811882a7>] __blockdev_direct_IO+0x633/0x666 [ 72.343908] [<ffffffff8129fdbb>] ? xfs_get_blocks+0x13/0x13 [ 72.345263] [<ffffffff8129d890>] xfs_vm_direct_IO+0x89/0x90 [ 72.346598] [<ffffffff8129d943>] ? xfs_setfilesize_trans_alloc+0xac/0xac [ 72.348185] [<ffffffff8169ae5e>] xfs_file_dio_aio_write+0x2e0/0x449 [ 72.349652] [<ffffffff813e1028>] ? __clear_user+0x36/0x5b [ 72.350950] [<ffffffff812af9c6>] xfs_file_write_iter+0x75/0x105 [ 72.352369] [<ffffffff81151524>] __vfs_write+0x97/0xc0 [ 72.353607] [<ffffffff81151b1f>] vfs_write+0xb5/0x16f [ 72.354832] [<ffffffff811522c3>] SyS_write+0x4a/0x94 [ 72.356060] [<ffffffff816a4b17>] system_call_fastpath+0x12/0x6f The bug is the local "map_bh" in get_blocks(). For XFS: map_bh->b_private saves "ioend"(see xfs_map_direct()). We need to pass map_bh->b_private to next call of get_blocks(). Fixed it by moving "map_bh" to dio_alloc_bios(), then it's passed down to dio_send_bio() -> get_blocks().
-rw-r--r--fs/direct-io.c40
1 files changed, 21 insertions, 19 deletions
diff --git a/fs/direct-io.c b/fs/direct-io.c
index 3133bd5..b8e7622 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -258,9 +258,9 @@ static int dio_set_defer_completion(struct dio *dio)
}
static int get_blocks(struct dio *dio, loff_t offset, size_t size,
- struct dio_mapping *map, get_block_t *get_block)
+ struct buffer_head *map_bh, struct dio_mapping *map,
+ get_block_t *get_block)
{
- struct buffer_head map_bh = { 0, };
int ret, create;
unsigned i_mask = (1 << dio->i_blkbits) - 1;
unsigned fs_offset = offset & i_mask;
@@ -284,25 +284,26 @@ static int get_blocks(struct dio *dio, loff_t offset, size_t size,
}
/* fs expects units of fs_blocks */
- map_bh.b_size = size + fs_offset;
- map_bh.b_size = round_up(map_bh.b_size, 1 << dio->i_blkbits);
+ map_bh->b_state = 0;
+ map_bh->b_size = size + fs_offset;
+ map_bh->b_size = round_up(map_bh->b_size, 1 << dio->i_blkbits);
- ret = get_block(dio->inode, fs_blocknr, &map_bh, create);
+ ret = get_block(dio->inode, fs_blocknr, map_bh, create);
if (ret)
return ret;
/* Store for completion */
- dio->private = map_bh.b_private;
+ dio->private = map_bh->b_private;
- if (ret == 0 && buffer_defer_completion(&map_bh))
+ if (ret == 0 && buffer_defer_completion(map_bh))
ret = dio_set_defer_completion(dio);
- if (buffer_new(&map_bh))
- clean_blockdev_aliases(dio, &map_bh);
+ if (buffer_new(map_bh))
+ clean_blockdev_aliases(dio, map_bh);
- if (!buffer_mapped(&map_bh))
+ if (!buffer_mapped(map_bh))
map->state = MAP_UNMAPPED;
- else if (buffer_new(&map_bh))
+ else if (buffer_new(map_bh))
map->state = MAP_NEW;
else
map->state = MAP_MAPPED;
@@ -310,15 +311,15 @@ static int get_blocks(struct dio *dio, loff_t offset, size_t size,
#if 1
/* Previous DIO code only handled holes one block at a time */
if (map->state == MAP_UNMAPPED)
- map_bh.b_size = 1 << dio->i_blkbits;
+ map_bh->b_size = 1 << dio->i_blkbits;
#endif
- BUG_ON(map_bh.b_size <= fs_offset);
+ BUG_ON(map_bh->b_size <= fs_offset);
- map->bdev = map_bh.b_bdev;
- map->offset = (map_bh.b_blocknr << dio->i_blkbits) +
+ map->bdev = map_bh->b_bdev;
+ map->offset = (map_bh->b_blocknr << dio->i_blkbits) +
fs_offset;
- map->size = min(map_bh.b_size - fs_offset, size);
+ map->size = min(map_bh->b_size - fs_offset, size);
return ret;
}
@@ -440,7 +441,7 @@ static int dio_is_aligned(struct dio *dio, struct dio_mapping *map)
static int dio_send_bio(struct dio *dio, struct bio *bio, loff_t offset,
get_block_t *get_block, dio_submit_t *submit_io,
- struct dio_mapping *map)
+ struct buffer_head *map_bh, struct dio_mapping *map)
{
int ret = 0, rw = dio->rw & WRITE;
@@ -452,7 +453,7 @@ static int dio_send_bio(struct dio *dio, struct bio *bio, loff_t offset,
break;
ret = get_blocks(dio, offset, bio->bi_iter.bi_size,
- map, get_block);
+ map_bh, map, get_block);
if (ret)
break;
@@ -506,6 +507,7 @@ static int dio_alloc_bios(struct dio *dio, loff_t offset,
get_block_t *get_block, dio_submit_t *submit_io)
{
ssize_t ret;
+ struct buffer_head map_bh = { 0, };
struct dio_mapping map;
struct bio *bio;
@@ -533,7 +535,7 @@ start:
atomic_inc(&dio->refcount);
ret = dio_send_bio(dio, bio, offset + dio->result,
- get_block, submit_io, &map);
+ get_block, submit_io, &map_bh, &map);
if (ret)
return ret;