Skip to content
This repository was archived by the owner on Oct 5, 2018. It is now read-only.

Commit 50745b0

Browse files
chandanmasoncl
authored andcommitted
Btrfs: Direct I/O: Fix space accounting
The following call trace is seen when generic/095 test is executed, WARNING: CPU: 3 PID: 2769 at /home/chandan/code/repos/linux/fs/btrfs/inode.c:8967 btrfs_destroy_inode+0x284/0x2a0() Modules linked in: CPU: 3 PID: 2769 Comm: umount Not tainted 4.2.0-rc5+ #31 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.7.5-20150306_163512-brownie 04/01/2014 ffffffff81c08150 ffff8802ec9cbce8 ffffffff81984058 ffff8802ffd8feb0 0000000000000000 ffff8802ec9cbd28 ffffffff81050385 ffff8802ec9cbd38 ffff8802d12f8588 ffff8802d12f8588 ffff8802f15ab000 ffff8800bb96c0b0 Call Trace: [<ffffffff81984058>] dump_stack+0x45/0x57 [<ffffffff81050385>] warn_slowpath_common+0x85/0xc0 [<ffffffff81050465>] warn_slowpath_null+0x15/0x20 [<ffffffff81340294>] btrfs_destroy_inode+0x284/0x2a0 [<ffffffff8117ce07>] destroy_inode+0x37/0x60 [<ffffffff8117cf39>] evict+0x109/0x170 [<ffffffff8117cfd5>] dispose_list+0x35/0x50 [<ffffffff8117dd3a>] evict_inodes+0xaa/0x100 [<ffffffff81165667>] generic_shutdown_super+0x47/0xf0 [<ffffffff81165951>] kill_anon_super+0x11/0x20 [<ffffffff81302093>] btrfs_kill_super+0x13/0x110 [<ffffffff81165c99>] deactivate_locked_super+0x39/0x70 [<ffffffff811660cf>] deactivate_super+0x5f/0x70 [<ffffffff81180e1e>] cleanup_mnt+0x3e/0x90 [<ffffffff81180ebd>] __cleanup_mnt+0xd/0x10 [<ffffffff81069c06>] task_work_run+0x96/0xb0 [<ffffffff81003a3d>] do_notify_resume+0x3d/0x50 [<ffffffff8198cbc2>] int_signal+0x12/0x17 This means that the inode had non-zero "outstanding extents" during eviction. This occurs because, during direct I/O a task which successfully used up its reserved data space would set BTRFS_INODE_DIO_READY bit and does not clear the bit after finishing the DIO write. A future DIO write could actually fail and the unused reserve space won't be freed because of the previously set BTRFS_INODE_DIO_READY bit. Clearing the BTRFS_INODE_DIO_READY bit in btrfs_direct_IO() caused the following issue, |-----------------------------------+-------------------------------------| | Task A | Task B | |-----------------------------------+-------------------------------------| | Start direct i/o write on inode X.| | | reserve space | | | Allocate ordered extent | | | release reserved space | | | Set BTRFS_INODE_DIO_READY bit. | | | | splice() | | | Transfer data from pipe buffer to | | | destination file. | | | - kmap(pipe buffer page) | | | - Start direct i/o write on | | | inode X. | | | - reserve space | | | - dio_refill_pages() | | | - sdio->blocks_available == 0 | | | - Since a kernel address is | | | being passed instead of a | | | user space address, | | | iov_iter_get_pages() returns | | | -EFAULT. | | | - Since BTRFS_INODE_DIO_READY is | | | set, we don't release reserved | | | space. | | | - Clear BTRFS_INODE_DIO_READY bit.| | -EIOCBQUEUED is returned. | | |-----------------------------------+-------------------------------------| Hence this commit introduces "struct btrfs_dio_data" to track the usage of reserved data space. The remaining unused "reserve space" can now be freed reliably. Signed-off-by: Chandan Rajendra <[email protected]> Reviewed-by: Liu Bo <[email protected]> Signed-off-by: Chris Mason <[email protected]>
1 parent a30e577 commit 50745b0

File tree

2 files changed

+21
-23
lines changed

2 files changed

+21
-23
lines changed

fs/btrfs/btrfs_inode.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@
4444
#define BTRFS_INODE_IN_DELALLOC_LIST 9
4545
#define BTRFS_INODE_READDIO_NEED_LOCK 10
4646
#define BTRFS_INODE_HAS_PROPS 11
47-
/* DIO is ready to submit */
48-
#define BTRFS_INODE_DIO_READY 12
4947
/*
5048
* The following 3 bits are meant only for the btree inode.
5149
* When any of them is set, it means an error happened while writing an

fs/btrfs/inode.c

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7405,17 +7405,21 @@ static struct extent_map *create_pinned_em(struct inode *inode, u64 start,
74057405
return em;
74067406
}
74077407

7408+
struct btrfs_dio_data {
7409+
u64 outstanding_extents;
7410+
u64 reserve;
7411+
};
74087412

74097413
static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
74107414
struct buffer_head *bh_result, int create)
74117415
{
74127416
struct extent_map *em;
74137417
struct btrfs_root *root = BTRFS_I(inode)->root;
74147418
struct extent_state *cached_state = NULL;
7419+
struct btrfs_dio_data *dio_data = NULL;
74157420
u64 start = iblock << inode->i_blkbits;
74167421
u64 lockstart, lockend;
74177422
u64 len = bh_result->b_size;
7418-
u64 *outstanding_extents = NULL;
74197423
int unlock_bits = EXTENT_LOCKED;
74207424
int ret = 0;
74217425

@@ -7433,7 +7437,7 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
74337437
* that anything that needs to check if there's a transction doesn't get
74347438
* confused.
74357439
*/
7436-
outstanding_extents = current->journal_info;
7440+
dio_data = current->journal_info;
74377441
current->journal_info = NULL;
74387442
}
74397443

@@ -7565,17 +7569,18 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
75657569
* within our reservation, otherwise we need to adjust our inode
75667570
* counter appropriately.
75677571
*/
7568-
if (*outstanding_extents) {
7569-
(*outstanding_extents)--;
7572+
if (dio_data->outstanding_extents) {
7573+
(dio_data->outstanding_extents)--;
75707574
} else {
75717575
spin_lock(&BTRFS_I(inode)->lock);
75727576
BTRFS_I(inode)->outstanding_extents++;
75737577
spin_unlock(&BTRFS_I(inode)->lock);
75747578
}
75757579

7576-
current->journal_info = outstanding_extents;
75777580
btrfs_free_reserved_data_space(inode, len);
7578-
set_bit(BTRFS_INODE_DIO_READY, &BTRFS_I(inode)->runtime_flags);
7581+
WARN_ON(dio_data->reserve < len);
7582+
dio_data->reserve -= len;
7583+
current->journal_info = dio_data;
75797584
}
75807585

75817586
/*
@@ -7598,8 +7603,8 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
75987603
unlock_err:
75997604
clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, lockend,
76007605
unlock_bits, 1, 0, &cached_state, GFP_NOFS);
7601-
if (outstanding_extents)
7602-
current->journal_info = outstanding_extents;
7606+
if (dio_data)
7607+
current->journal_info = dio_data;
76037608
return ret;
76047609
}
76057610

@@ -8329,7 +8334,8 @@ static ssize_t btrfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
83298334
{
83308335
struct file *file = iocb->ki_filp;
83318336
struct inode *inode = file->f_mapping->host;
8332-
u64 outstanding_extents = 0;
8337+
struct btrfs_root *root = BTRFS_I(inode)->root;
8338+
struct btrfs_dio_data dio_data = { 0 };
83338339
size_t count = 0;
83348340
int flags = 0;
83358341
bool wakeup = true;
@@ -8367,7 +8373,7 @@ static ssize_t btrfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
83678373
ret = btrfs_delalloc_reserve_space(inode, count);
83688374
if (ret)
83698375
goto out;
8370-
outstanding_extents = div64_u64(count +
8376+
dio_data.outstanding_extents = div64_u64(count +
83718377
BTRFS_MAX_EXTENT_SIZE - 1,
83728378
BTRFS_MAX_EXTENT_SIZE);
83738379

@@ -8376,7 +8382,8 @@ static ssize_t btrfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
83768382
* do the accounting properly if we go over the number we
83778383
* originally calculated. Abuse current->journal_info for this.
83788384
*/
8379-
current->journal_info = &outstanding_extents;
8385+
dio_data.reserve = round_up(count, root->sectorsize);
8386+
current->journal_info = &dio_data;
83808387
} else if (test_bit(BTRFS_INODE_READDIO_NEED_LOCK,
83818388
&BTRFS_I(inode)->runtime_flags)) {
83828389
inode_dio_end(inode);
@@ -8391,16 +8398,9 @@ static ssize_t btrfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
83918398
if (iov_iter_rw(iter) == WRITE) {
83928399
current->journal_info = NULL;
83938400
if (ret < 0 && ret != -EIOCBQUEUED) {
8394-
/*
8395-
* If the error comes from submitting stage,
8396-
* btrfs_get_blocsk_direct() has free'd data space,
8397-
* and metadata space will be handled by
8398-
* finish_ordered_fn, don't do that again to make
8399-
* sure bytes_may_use is correct.
8400-
*/
8401-
if (!test_and_clear_bit(BTRFS_INODE_DIO_READY,
8402-
&BTRFS_I(inode)->runtime_flags))
8403-
btrfs_delalloc_release_space(inode, count);
8401+
if (dio_data.reserve)
8402+
btrfs_delalloc_release_space(inode,
8403+
dio_data.reserve);
84048404
} else if (ret >= 0 && (size_t)ret < count)
84058405
btrfs_delalloc_release_space(inode,
84068406
count - (size_t)ret);

0 commit comments

Comments
 (0)