]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - fs/btrfs/extent_io.c
Btrfs: reduce CPU usage in the extent_state tree
[net-next-2.6.git] / fs / btrfs / extent_io.c
index 05a1c42e25bf6ec5d12c34e32b6a6188e88b2df4..7c70613eb72c10547ac94fde0b9ea600ef1f2cc6 100644 (file)
 #include "ctree.h"
 #include "btrfs_inode.h"
 
-/* temporary define until extent_map moves out of btrfs */
-struct kmem_cache *btrfs_cache_create(const char *name, size_t size,
-                                      unsigned long extra_flags,
-                                      void (*ctor)(void *, struct kmem_cache *,
-                                                   unsigned long));
-
 static struct kmem_cache *extent_state_cache;
 static struct kmem_cache *extent_buffer_cache;
 
@@ -58,15 +52,15 @@ struct extent_page_data {
 
 int __init extent_io_init(void)
 {
-       extent_state_cache = btrfs_cache_create("extent_state",
-                                           sizeof(struct extent_state), 0,
-                                           NULL);
+       extent_state_cache = kmem_cache_create("extent_state",
+                       sizeof(struct extent_state), 0,
+                       SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL);
        if (!extent_state_cache)
                return -ENOMEM;
 
-       extent_buffer_cache = btrfs_cache_create("extent_buffers",
-                                           sizeof(struct extent_buffer), 0,
-                                           NULL);
+       extent_buffer_cache = kmem_cache_create("extent_buffers",
+                       sizeof(struct extent_buffer), 0,
+                       SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL);
        if (!extent_buffer_cache)
                goto free_state_cache;
        return 0;
@@ -373,10 +367,10 @@ static int insert_state(struct extent_io_tree *tree,
        }
        if (bits & EXTENT_DIRTY)
                tree->dirty_bytes += end - start + 1;
-       set_state_cb(tree, state, bits);
-       state->state |= bits;
        state->start = start;
        state->end = end;
+       set_state_cb(tree, state, bits);
+       state->state |= bits;
        node = tree_insert(&tree->state, end, &state->rb_node);
        if (node) {
                struct extent_state *found;
@@ -482,6 +476,7 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
        struct extent_state *state;
        struct extent_state *prealloc = NULL;
        struct rb_node *node;
+       u64 last_end;
        int err;
        int set = 0;
 
@@ -504,6 +499,7 @@ again:
        if (state->start > end)
                goto out;
        WARN_ON(state->end < start);
+       last_end = state->end;
 
        /*
         *     | ---- desired range ---- |
@@ -530,9 +526,11 @@ again:
                if (err)
                        goto out;
                if (state->end <= end) {
-                       start = state->end + 1;
                        set |= clear_state_bit(tree, state, bits,
                                        wake, delete);
+                       if (last_end == (u64)-1)
+                               goto out;
+                       start = last_end + 1;
                } else {
                        start = state->start;
                }
@@ -558,8 +556,10 @@ again:
                goto out;
        }
 
-       start = state->end + 1;
        set |= clear_state_bit(tree, state, bits, wake, delete);
+       if (last_end == (u64)-1)
+               goto out;
+       start = last_end + 1;
        goto search_again;
 
 out:
@@ -654,25 +654,24 @@ static void set_state_bits(struct extent_io_tree *tree,
 }
 
 /*
- * set some bits on a range in the tree.  This may require allocations
- * or sleeping, so the gfp mask is used to indicate what is allowed.
+ * set some bits on a range in the tree.  This may require allocations or
+ * sleeping, so the gfp mask is used to indicate what is allowed.
  *
- * If 'exclusive' == 1, this will fail with -EEXIST if some part of the
- * range already has the desired bits set.  The start of the existing
- * range is returned in failed_start in this case.
+ * If any of the exclusive bits are set, this will fail with -EEXIST if some
+ * part of the range already has the desired bits set.  The start of the
+ * existing range is returned in failed_start in this case.
  *
- * [start, end] is inclusive
- * This takes the tree lock.
+ * [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, u64 *failed_start,
+                         int bits, int exclusive_bits, u64 *failed_start,
                          gfp_t mask)
 {
        struct extent_state *state;
        struct extent_state *prealloc = NULL;
        struct rb_node *node;
        int err = 0;
-       int set;
        u64 last_start;
        u64 last_end;
 again:
@@ -694,8 +693,8 @@ again:
                BUG_ON(err == -EEXIST);
                goto out;
        }
-
        state = rb_entry(node, struct extent_state, rb_node);
+hit_next:
        last_start = state->start;
        last_end = state->end;
 
@@ -706,15 +705,27 @@ again:
         * Just lock what we found and keep going
         */
        if (state->start == start && state->end <= end) {
-               set = state->state & bits;
-               if (set && exclusive) {
+               struct rb_node *next_node;
+               if (state->state & exclusive_bits) {
                        *failed_start = state->start;
                        err = -EEXIST;
                        goto out;
                }
                set_state_bits(tree, state, bits);
-               start = state->end + 1;
                merge_state(tree, state);
+               if (last_end == (u64)-1)
+                       goto out;
+
+               start = last_end + 1;
+               if (start < end && prealloc && !need_resched()) {
+                       next_node = rb_next(node);
+                       if (next_node) {
+                               state = rb_entry(next_node, struct extent_state,
+                                                rb_node);
+                               if (state->start == start)
+                                       goto hit_next;
+                       }
+               }
                goto search_again;
        }
 
@@ -735,8 +746,7 @@ again:
         * desired bit on it.
         */
        if (state->start < start) {
-               set = state->state & bits;
-               if (exclusive && set) {
+               if (state->state & exclusive_bits) {
                        *failed_start = start;
                        err = -EEXIST;
                        goto out;
@@ -748,8 +758,10 @@ again:
                        goto out;
                if (state->end <= end) {
                        set_state_bits(tree, state, bits);
-                       start = state->end + 1;
                        merge_state(tree, state);
+                       if (last_end == (u64)-1)
+                               goto out;
+                       start = last_end + 1;
                } else {
                        start = state->start;
                }
@@ -784,8 +796,7 @@ again:
         * on the first half
         */
        if (state->start <= end && state->end > end) {
-               set = state->state & bits;
-               if (exclusive && set) {
+               if (state->state & exclusive_bits) {
                        *failed_start = start;
                        err = -EEXIST;
                        goto out;
@@ -848,7 +859,7 @@ int set_extent_delalloc(struct extent_io_tree *tree, u64 start, u64 end,
                     gfp_t mask)
 {
        return set_extent_bit(tree, start, end,
-                             EXTENT_DELALLOC | EXTENT_DIRTY,
+                             EXTENT_DELALLOC | EXTENT_DIRTY | EXTENT_UPTODATE,
                              0, NULL, mask);
 }
 
@@ -891,19 +902,6 @@ static int clear_extent_uptodate(struct extent_io_tree *tree, u64 start,
        return clear_extent_bit(tree, start, end, EXTENT_UPTODATE, 0, 0, mask);
 }
 
-static int set_extent_writeback(struct extent_io_tree *tree, u64 start, u64 end,
-                        gfp_t mask)
-{
-       return set_extent_bit(tree, start, end, EXTENT_WRITEBACK,
-                             0, NULL, mask);
-}
-
-static int clear_extent_writeback(struct extent_io_tree *tree, u64 start,
-                                 u64 end, gfp_t mask)
-{
-       return clear_extent_bit(tree, start, end, EXTENT_WRITEBACK, 1, 0, mask);
-}
-
 int wait_on_extent_writeback(struct extent_io_tree *tree, u64 start, u64 end)
 {
        return wait_extent_bit(tree, start, end, EXTENT_WRITEBACK);
@@ -913,13 +911,14 @@ int wait_on_extent_writeback(struct extent_io_tree *tree, u64 start, u64 end)
  * either insert or lock state struct between start and end use mask to tell
  * us if waiting is desired.
  */
-int lock_extent(struct extent_io_tree *tree, u64 start, u64 end, gfp_t mask)
+int lock_extent_bits(struct extent_io_tree *tree, u64 start, u64 end,
+                    int bits, gfp_t mask)
 {
        int err;
        u64 failed_start;
        while (1) {
-               err = set_extent_bit(tree, start, end, EXTENT_LOCKED, 1,
-                                    &failed_start, mask);
+               err = set_extent_bit(tree, start, end, EXTENT_LOCKED | bits,
+                                    EXTENT_LOCKED, &failed_start, mask);
                if (err == -EEXIST && (mask & __GFP_WAIT)) {
                        wait_extent_bit(tree, failed_start, end, EXTENT_LOCKED);
                        start = failed_start;
@@ -931,6 +930,11 @@ int lock_extent(struct extent_io_tree *tree, u64 start, u64 end, gfp_t mask)
        return err;
 }
 
+int lock_extent(struct extent_io_tree *tree, u64 start, u64 end, gfp_t mask)
+{
+       return lock_extent_bits(tree, start, end, 0, mask);
+}
+
 int try_lock_extent(struct extent_io_tree *tree, u64 start, u64 end,
                    gfp_t mask)
 {
@@ -970,7 +974,6 @@ int set_range_dirty(struct extent_io_tree *tree, u64 start, u64 end)
                page_cache_release(page);
                index++;
        }
-       set_extent_dirty(tree, start, end, GFP_NOFS);
        return 0;
 }
 
@@ -990,7 +993,6 @@ static int set_range_writeback(struct extent_io_tree *tree, u64 start, u64 end)
                page_cache_release(page);
                index++;
        }
-       set_extent_writeback(tree, start, end, GFP_NOFS);
        return 0;
 }
 
@@ -1407,69 +1409,6 @@ out:
        return total_bytes;
 }
 
-#if 0
-/*
- * helper function to lock both pages and extents in the tree.
- * pages must be locked first.
- */
-static int lock_range(struct extent_io_tree *tree, u64 start, u64 end)
-{
-       unsigned long index = start >> PAGE_CACHE_SHIFT;
-       unsigned long end_index = end >> PAGE_CACHE_SHIFT;
-       struct page *page;
-       int err;
-
-       while (index <= end_index) {
-               page = grab_cache_page(tree->mapping, index);
-               if (!page) {
-                       err = -ENOMEM;
-                       goto failed;
-               }
-               if (IS_ERR(page)) {
-                       err = PTR_ERR(page);
-                       goto failed;
-               }
-               index++;
-       }
-       lock_extent(tree, start, end, GFP_NOFS);
-       return 0;
-
-failed:
-       /*
-        * we failed above in getting the page at 'index', so we undo here
-        * up to but not including the page at 'index'
-        */
-       end_index = index;
-       index = start >> PAGE_CACHE_SHIFT;
-       while (index < end_index) {
-               page = find_get_page(tree->mapping, index);
-               unlock_page(page);
-               page_cache_release(page);
-               index++;
-       }
-       return err;
-}
-
-/*
- * helper function to unlock both pages and extents in the tree.
- */
-static int unlock_range(struct extent_io_tree *tree, u64 start, u64 end)
-{
-       unsigned long index = start >> PAGE_CACHE_SHIFT;
-       unsigned long end_index = end >> PAGE_CACHE_SHIFT;
-       struct page *page;
-
-       while (index <= end_index) {
-               page = find_get_page(tree->mapping, index);
-               unlock_page(page);
-               page_cache_release(page);
-               index++;
-       }
-       unlock_extent(tree, start, end, GFP_NOFS);
-       return 0;
-}
-#endif
-
 /*
  * set the private field for a given byte offset in the tree.  If there isn't
  * an extent_state there already, this does nothing.
@@ -1611,10 +1550,7 @@ static int check_page_locked(struct extent_io_tree *tree,
 static int check_page_writeback(struct extent_io_tree *tree,
                             struct page *page)
 {
-       u64 start = (u64)page->index << PAGE_CACHE_SHIFT;
-       u64 end = start + PAGE_CACHE_SIZE - 1;
-       if (!test_range_bit(tree, start, end, EXTENT_WRITEBACK, 0))
-               end_page_writeback(page);
+       end_page_writeback(page);
        return 0;
 }
 
@@ -1672,13 +1608,11 @@ static void end_bio_extent_writepage(struct bio *bio, int err)
                }
 
                if (!uptodate) {
-                       clear_extent_uptodate(tree, start, end, GFP_ATOMIC);
+                       clear_extent_uptodate(tree, start, end, GFP_NOFS);
                        ClearPageUptodate(page);
                        SetPageError(page);
                }
 
-               clear_extent_writeback(tree, start, end, GFP_ATOMIC);
-
                if (whole_page)
                        end_page_writeback(page);
                else
@@ -2183,6 +2117,7 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
        delalloc_end = 0;
        page_started = 0;
        if (!epd->extent_locked) {
+               u64 delalloc_to_write;
                /*
                 * make sure the wbc mapping index is at least updated
                 * to this page.
@@ -2202,6 +2137,14 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
                        tree->ops->fill_delalloc(inode, page, delalloc_start,
                                                 delalloc_end, &page_started,
                                                 &nr_written);
+                       delalloc_to_write = (delalloc_end -
+                                       max_t(u64, page_offset(page),
+                                             delalloc_start) + 1) >>
+                                       PAGE_CACHE_SHIFT;
+                       if (wbc->nr_to_write < delalloc_to_write) {
+                               wbc->nr_to_write = min_t(long, 8192,
+                                                delalloc_to_write);
+                       }
                        delalloc_start = delalloc_end + 1;
                }
 
@@ -2247,8 +2190,9 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
                printk(KERN_ERR "btrfs delalloc bits after lock_extent\n");
 
        if (last_byte <= start) {
-               clear_extent_dirty(tree, start, page_end, GFP_NOFS);
-               unlock_extent(tree, start, page_end, GFP_NOFS);
+               clear_extent_bit(tree, start, page_end,
+                                EXTENT_LOCKED | EXTENT_DIRTY,
+                                1, 0, GFP_NOFS);
                if (tree->ops && tree->ops->writepage_end_io_hook)
                        tree->ops->writepage_end_io_hook(page, start,
                                                         page_end, NULL, 1);
@@ -2256,12 +2200,10 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
                goto done;
        }
 
-       set_extent_uptodate(tree, start, page_end, GFP_NOFS);
        blocksize = inode->i_sb->s_blocksize;
 
        while (cur <= end) {
                if (cur >= last_byte) {
-                       clear_extent_dirty(tree, cur, page_end, GFP_NOFS);
                        unlock_extent(tree, unlock_start, page_end, GFP_NOFS);
                        if (tree->ops && tree->ops->writepage_end_io_hook)
                                tree->ops->writepage_end_io_hook(page, cur,
@@ -2294,9 +2236,6 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
                 */
                if (compressed || block_start == EXTENT_MAP_HOLE ||
                    block_start == EXTENT_MAP_INLINE) {
-                       clear_extent_dirty(tree, cur,
-                                          cur + iosize - 1, GFP_NOFS);
-
                        unlock_extent(tree, unlock_start, cur + iosize - 1,
                                      GFP_NOFS);
 
@@ -2330,7 +2269,6 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
                        continue;
                }
 
-               clear_extent_dirty(tree, cur, cur + iosize - 1, GFP_NOFS);
                if (tree->ops && tree->ops->writepage_io_hook) {
                        ret = tree->ops->writepage_io_hook(page, cur,
                                                cur + iosize - 1);
@@ -2398,7 +2336,6 @@ static int extent_write_cache_pages(struct extent_io_tree *tree,
                             writepage_t writepage, void *data,
                             void (*flush_fn)(void *))
 {
-       struct backing_dev_info *bdi = mapping->backing_dev_info;
        int ret = 0;
        int done = 0;
        struct pagevec pvec;
@@ -2473,10 +2410,6 @@ retry:
                        }
                        if (ret || wbc->nr_to_write <= 0)
                                done = 1;
-                       if (wbc->nonblocking && bdi_write_congested(bdi)) {
-                               wbc->encountered_congestion = 1;
-                               done = 1;
-                       }
                }
                pagevec_release(&pvec);
                cond_resched();
@@ -2663,7 +2596,7 @@ int extent_invalidatepage(struct extent_io_tree *tree,
                return 0;
 
        lock_extent(tree, start, end, GFP_NOFS);
-       wait_on_extent_writeback(tree, start, end);
+       wait_on_page_writeback(page);
        clear_extent_bit(tree, start, end,
                         EXTENT_LOCKED | EXTENT_DIRTY | EXTENT_DELALLOC,
                         1, 1, GFP_NOFS);
@@ -2830,15 +2763,15 @@ int try_release_extent_mapping(struct extent_map_tree *map,
                u64 len;
                while (start <= end) {
                        len = end - start + 1;
-                       spin_lock(&map->lock);
+                       write_lock(&map->lock);
                        em = lookup_extent_mapping(map, start, len);
                        if (!em || IS_ERR(em)) {
-                               spin_unlock(&map->lock);
+                               write_unlock(&map->lock);
                                break;
                        }
                        if (test_bit(EXTENT_FLAG_PINNED, &em->flags) ||
                            em->start != start) {
-                               spin_unlock(&map->lock);
+                               write_unlock(&map->lock);
                                free_extent_map(em);
                                break;
                        }
@@ -2852,7 +2785,7 @@ int try_release_extent_mapping(struct extent_map_tree *map,
                                free_extent_map(em);
                        }
                        start = extent_map_end(em);
-                       spin_unlock(&map->lock);
+                       write_unlock(&map->lock);
 
                        /* once for us */
                        free_extent_map(em);