From patchwork Tue Jul 13 08:55:55 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miao Xie X-Patchwork-Id: 111729 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.4/8.14.3) with ESMTP id o6D8wlLQ013093 for ; Tue, 13 Jul 2010 08:58:48 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753599Ab0GMI6p (ORCPT ); Tue, 13 Jul 2010 04:58:45 -0400 Received: from cn.fujitsu.com ([222.73.24.84]:62689 "EHLO song.cn.fujitsu.com" rhost-flags-OK-FAIL-OK-OK) by vger.kernel.org with ESMTP id S1751426Ab0GMI6o (ORCPT ); Tue, 13 Jul 2010 04:58:44 -0400 Received: from tang.cn.fujitsu.com (tang.cn.fujitsu.com [10.167.250.3]) by song.cn.fujitsu.com (Postfix) with ESMTP id ED30D17010C; Tue, 13 Jul 2010 16:58:40 +0800 (CST) Received: from fnst.cn.fujitsu.com (tang.cn.fujitsu.com [127.0.0.1]) by tang.cn.fujitsu.com (8.14.3/8.13.1) with ESMTP id o6D8tutZ018614; Tue, 13 Jul 2010 16:55:56 +0800 Received: from [10.167.141.211] (unknown [10.167.141.211]) by fnst.cn.fujitsu.com (Postfix) with ESMTPA id 9AFEA10C1B9; Tue, 13 Jul 2010 16:59:01 +0800 (CST) Message-ID: <4C3C2A1B.2020608@cn.fujitsu.com> Date: Tue, 13 Jul 2010 16:55:55 +0800 From: Miao Xie Reply-To: miaox@cn.fujitsu.com User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.9) Gecko/20100413 Fedora/3.0.4-2.fc13 Thunderbird/3.0.4 MIME-Version: 1.0 To: Chris Mason CC: Linux Btrfs Subject: [PATCH 2/2] btrfs: fix oops when leafsize is greator than nodesize Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Tue, 13 Jul 2010 08:59:26 +0000 (UTC) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 70b7cc5..b4f1c42 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -3130,6 +3130,45 @@ static inline void btrfs_release_extent_buffer(struct extent_buffer *eb) __free_extent_buffer(eb); } +/* + * we may reuse a extent buffer whose device space has been released, if the len + * of the extent buffer is smaller than we expect, we must enlarge the extent + * buffer, and before doing that, we must release the extent buffer that + * intersects it. + * + * Don't worry about the state of the extent buffer that is going to be release. + * because it is just an image left in the memory, and its device space has been + * released, or the btrfs can't allocate its device space for other extent + * buffer. + * + * Note: Must hold io_tree->buffer_lock + */ +static int btrfs_enlarge_extent_buffer(struct extent_io_tree *tree, + struct extent_buffer *eb, + unsigned long newlen) +{ + struct rb_node *next; + struct extent_buffer *next_eb; + + eb->len = newlen; + set_page_extent_head(eb->first_page, newlen); + + next = rb_next(&eb->rb_node); + while (next) { + next_eb = rb_entry(next, struct extent_buffer, rb_node); + if (next_eb->start >= eb->start + eb->len) + break; + + if (atomic_read(&next_eb->refs) > 1) + return 1; + + next = rb_next(next); + rb_erase(&next_eb->rb_node, &tree->buffer); + btrfs_release_extent_buffer(next_eb); + } + return 0; +} + struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree, u64 start, unsigned long len, struct page *page0, @@ -3147,10 +3186,49 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree, spin_lock(&tree->buffer_lock); eb = buffer_search(tree, start); if (eb) { - atomic_inc(&eb->refs); - spin_unlock(&tree->buffer_lock); - mark_page_accessed(eb->first_page); - return eb; + /* + * If this extent buffer's device space has been released some + * time ago, and is reallocated again to store other metadata, + * but it hasn't been release, we may get the old entent buffer + * and reuse it. + * + * But, we must change it according the new len. + */ + if (eb->len >= len) { + if (eb->len > len) { + btrfs_release_extent_buffer_page(eb, num_pages); + + eb->len = len; + set_page_extent_head(eb->first_page, len); + } + + atomic_inc(&eb->refs); + spin_unlock(&tree->buffer_lock); + mark_page_accessed(eb->first_page); + return eb; + } else { + int ret; + + /* + * if eb->len != len, it means this extent buffer + * is reused as a new extent buffer. + */ + BUG_ON(atomic_read(&eb->refs) != 1); + + i = num_extent_pages(eb->start, eb->len); + index += i; + + ret = btrfs_enlarge_extent_buffer(tree, eb, len); + if (ret) { + spin_unlock(&tree->buffer_lock); + return NULL; + } else { + rb_erase(&eb->rb_node, &tree->buffer); + spin_unlock(&tree->buffer_lock); + mark_page_accessed(eb->first_page); + goto eb_alloc_pages; + } + } } spin_unlock(&tree->buffer_lock); @@ -3170,6 +3248,8 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree, } else { i = 0; } + +eb_alloc_pages: for (; i < num_pages; i++, index++) { p = find_or_create_page(mapping, index, mask | __GFP_HIGHMEM); if (!p) { @@ -3197,6 +3277,8 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree, /* add one reference for the caller */ atomic_inc(&exists->refs); spin_unlock(&tree->buffer_lock); + + BUG_ON(exists->len != eb->len); goto free_eb; } /* add one reference for the tree */