diff options
| author | 2015-07-09 16:05:24 -0700 | |
|---|---|---|
| committer | 2015-07-09 16:23:01 -0700 | |
| commit | ceeccaa160042f10adb23970a2bd94510df76186 (patch) | |
| tree | 71a5f9ab64e5286cc726888ee6bbf52774390a2d | |
| parent | 973b91cf6a3aca1273a42ab9e335d0b1aa9626c6 (diff) | |
| download | linux-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.c | 40 |
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; |
