Btrfs: rework O_DIRECT enospc handling
authorChris Mason <chris.mason@oracle.com>
Wed, 26 May 2010 00:56:50 +0000 (20:56 -0400)
committerChris Mason <chris.mason@oracle.com>
Wed, 26 May 2010 01:52:08 +0000 (21:52 -0400)
This changes O_DIRECT write code to mark extents as delalloc
while it is processing them.  Yan Zheng has reworked the
enospc accounting based on tracking delalloc extents and
this makes it much easier to track enospc in the O_DIRECT code.

There are a few space cases with the O_DIRECT code though,
it only sets the EXTENT_DELALLOC bits, instead of doing
EXTENT_DELALLOC | EXTENT_DIRTY | EXTENT_UPTODATE, because
we don't want to mess with clearing the dirty and uptodate
bits when things go wrong.  This is important because there
are no pages in the page cache, so any extent state structs
that we put in the tree won't get freed by releasepage.  We have
to clear them ourselves as the DIO ends.

With this commit, we reserve space at in btrfs_file_aio_write,
and then as each btrfs_direct_IO call progresses it sets
EXTENT_DELALLOC on the range.

btrfs_get_blocks_direct is responsible for clearing the delalloc
at the same time it drops the extent lock.

Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/extent_io.c
fs/btrfs/extent_io.h
fs/btrfs/file.c
fs/btrfs/inode.c

index 15392af21bfb0f499eee2de70cf754981bbab3b6..a4080c21ec55377b4e5e60fc5bc8fa59e032a1ee 100644 (file)
@@ -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;
index 86c7b341d0706ccaf387c9b7ab2a207c293529bf..5691c7b590dae86844ccbeccecdb5d8a2b3e2554 100644 (file)
@@ -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,
index 233aea2e5ef2e69281296cc29e6930c351a94e14..54556cae449755ffa4c57d53ec9704a14c280f8e 100644 (file)
@@ -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
index 13a4aa222861239d897edd2d92745617be622e4e..00aefbdcc2dfabca958a56a34a3b80ae432e7499 100644 (file)
@@ -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;
 }