diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 15392af21bfb0f499eee2de70cf754981bbab3b6..a4080c21ec55377b4e5e60fc5bc8fa59e032a1ee 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -135,7 +135,7 @@ static struct extent_state *alloc_extent_state(gfp_t mask) return state; } -static void free_extent_state(struct extent_state *state) +void free_extent_state(struct extent_state *state) { if (!state) return; @@ -745,10 +745,9 @@ static void cache_state(struct extent_state *state, * [start, end] is inclusive This takes the tree lock. */ -static int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, - int bits, int exclusive_bits, u64 *failed_start, - struct extent_state **cached_state, - gfp_t mask) +int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, + int bits, int exclusive_bits, u64 *failed_start, + struct extent_state **cached_state, gfp_t mask) { struct extent_state *state; struct extent_state *prealloc = NULL; diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index 86c7b341d0706ccaf387c9b7ab2a207c293529bf..5691c7b590dae86844ccbeccecdb5d8a2b3e2554 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -178,6 +178,7 @@ u64 count_range_bits(struct extent_io_tree *tree, u64 *start, u64 search_end, u64 max_bytes, unsigned long bits); +void free_extent_state(struct extent_state *state); int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end, int bits, int filled, struct extent_state *cached_state); int clear_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, @@ -187,6 +188,9 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, gfp_t mask); int set_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int bits, gfp_t mask); +int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, + int bits, int exclusive_bits, u64 *failed_start, + struct extent_state **cached_state, gfp_t mask); int set_extent_uptodate(struct extent_io_tree *tree, u64 start, u64 end, gfp_t mask); int set_extent_new(struct extent_io_tree *tree, u64 start, u64 end, diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 233aea2e5ef2e69281296cc29e6930c351a94e14..54556cae449755ffa4c57d53ec9704a14c280f8e 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -909,13 +909,6 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb, } if (num_written < 0) { - if (num_written != -EIOCBQUEUED) { - /* - * aio land will take care of releasing the - * delalloc - */ - btrfs_delalloc_release_space(inode, count); - } ret = num_written; num_written = 0; goto out; @@ -924,13 +917,6 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb, pos = *ppos; goto out; } - - /* - * the buffered IO will reserve bytes for the rest of the - * range, don't double count them here - */ - btrfs_delalloc_release_space(inode, count - num_written); - /* * We are going to do buffered for the rest of the range, so we * need to make sure to invalidate the buffered pages when we're diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 13a4aa222861239d897edd2d92745617be622e4e..00aefbdcc2dfabca958a56a34a3b80ae432e7499 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5327,8 +5327,9 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock, return PTR_ERR(em); len = min(len, em->block_len); } - unlock_extent(&BTRFS_I(inode)->io_tree, start, start + len - 1, - GFP_NOFS); + clear_extent_bit(&BTRFS_I(inode)->io_tree, start, start + len - 1, + EXTENT_LOCKED | EXTENT_DELALLOC | EXTENT_DIRTY, 1, + 0, NULL, GFP_NOFS); map: bh_result->b_blocknr = (em->block_start + (start - em->start)) >> inode->i_blkbits; @@ -5596,14 +5597,18 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; struct btrfs_ordered_extent *ordered; + struct extent_state *cached_state = NULL; u64 lockstart, lockend; ssize_t ret; + int writing = rw & WRITE; + int write_bits = 0; lockstart = offset; lockend = offset + iov_length(iov, nr_segs) - 1; + while (1) { - lock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend, - GFP_NOFS); + lock_extent_bits(&BTRFS_I(inode)->io_tree, lockstart, lockend, + 0, &cached_state, GFP_NOFS); /* * We're concerned with the entire range that we're going to be * doing DIO to, so we need to make sure theres no ordered @@ -5613,29 +5618,54 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, lockend - lockstart + 1); if (!ordered) break; - unlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend, - GFP_NOFS); + unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend, + &cached_state, GFP_NOFS); btrfs_start_ordered_extent(inode, ordered, 1); btrfs_put_ordered_extent(ordered); cond_resched(); } + /* + * we don't use btrfs_set_extent_delalloc because we don't want + * the dirty or uptodate bits + */ + if (writing) { + write_bits = EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING; + ret = set_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, lockend, + EXTENT_DELALLOC, 0, NULL, &cached_state, + GFP_NOFS); + if (ret) { + clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, + lockend, EXTENT_LOCKED | write_bits, + 1, 0, &cached_state, GFP_NOFS); + goto out; + } + } + + free_extent_state(cached_state); + cached_state = NULL; + ret = __blockdev_direct_IO(rw, iocb, inode, NULL, iov, offset, nr_segs, btrfs_get_blocks_direct, NULL, btrfs_submit_direct, 0); if (ret < 0 && ret != -EIOCBQUEUED) { - unlock_extent(&BTRFS_I(inode)->io_tree, offset, - offset + iov_length(iov, nr_segs) - 1, GFP_NOFS); + clear_extent_bit(&BTRFS_I(inode)->io_tree, offset, + offset + iov_length(iov, nr_segs) - 1, + EXTENT_LOCKED | write_bits, 1, 0, + &cached_state, GFP_NOFS); } else if (ret >= 0 && ret < iov_length(iov, nr_segs)) { /* * We're falling back to buffered, unlock the section we didn't * do IO on. */ - unlock_extent(&BTRFS_I(inode)->io_tree, offset + ret, - offset + iov_length(iov, nr_segs) - 1, GFP_NOFS); + clear_extent_bit(&BTRFS_I(inode)->io_tree, offset + ret, + offset + iov_length(iov, nr_segs) - 1, + EXTENT_LOCKED | write_bits, 1, 0, + &cached_state, GFP_NOFS); } - +out: + free_extent_state(cached_state); return ret; }