Message ID | 20210602021528.68617-7-wqu@suse.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | btrfs: defrag: rework to support sector perfect defrag | expand |
On 2021/6/2 上午10:15, Qu Wenruo wrote: > A new helper, defrag_one_range(), is introduced to defrag one range. > > This function will mostly prepare the needed pages and extent status for > defrag_one_locked_target(). > > As we can only have a consistent view of extent map with page and > extent bits locked, we need to re-check the range passed in to get a > real target list for defrag_one_locked_target(). > > Signed-off-by: Qu Wenruo <wqu@suse.com> > --- > fs/btrfs/ioctl.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 71 insertions(+) > > diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c > index 26957cd91ea6..a5ca752bcda8 100644 > --- a/fs/btrfs/ioctl.c > +++ b/fs/btrfs/ioctl.c > @@ -1548,6 +1548,77 @@ static int defrag_one_locked_target(struct btrfs_inode *inode, > return ret; > } > > +static int defrag_one_range(struct btrfs_inode *inode, > + u64 start, u32 len, > + u32 extent_thresh, u64 newer_than, > + bool do_compress) > +{ > + struct extent_state *cached_state = NULL; > + struct defrag_target_range *entry; > + struct defrag_target_range *tmp; > + LIST_HEAD(target_list); > + struct page **pages; > + const u32 sectorsize = inode->root->fs_info->sectorsize; > + unsigned long last_index = (start + len - 1) >> PAGE_SHIFT; > + unsigned long start_index = start >> PAGE_SHIFT; > + unsigned int nr_pages = last_index - start_index + 1; > + int ret = 0; > + int i; > + > + ASSERT(nr_pages <= CLUSTER_SIZE / PAGE_SIZE); > + ASSERT(IS_ALIGNED(start, sectorsize) && IS_ALIGNED(len, sectorsize)); > + > + pages = kzalloc(sizeof(struct page *) * nr_pages, GFP_NOFS); > + if (!pages) > + return -ENOMEM; > + > + /* Prepare all pages */ > + for (i = 0; i < nr_pages; i++) { > + pages[i] = defrag_prepare_one_page(inode, start_index + i); > + if (IS_ERR(pages[i])) { > + ret = PTR_ERR(pages[i]); > + pages[i] = NULL; > + goto free_pages; > + } > + } > + /* Also lock the pages range */ > + lock_extent_bits(&inode->io_tree, start_index << PAGE_SHIFT, > + (last_index << PAGE_SHIFT) + PAGE_SIZE - 1, > + &cached_state); > + /* > + * Now we have a consistent view about the extent map, re-check > + * which range really need to be defraged. > + */ > + ret = defrag_collect_targets(inode, start, len, extent_thresh, > + newer_than, do_compress, &target_list); And this defrag_collect_targets() call can cause random hang for tests like btrfs/061, which issues both defrag and fsstress. defrag_collect_targets() call defrag_lookup_extent(), which will try to lock the extent range. If the desired range mismatch from the locked range, we will wait for the extent bit to be unlocked. But in this case, we have already locked the larger range, thus we wait for ourselvies, and cause a dead lock. In next update, I'll pass a new parameter to teach defrag_lookup_extent() to skip the lock for this call site. Thanks, Qu > + if (ret < 0) > + goto unlock_extent; > + > + list_for_each_entry(entry, &target_list, list) { > + ret = defrag_one_locked_target(inode, entry, pages, nr_pages); > + if (ret < 0) > + break; > + } > + > + list_for_each_entry_safe(entry, tmp, &target_list, list) { > + list_del_init(&entry->list); > + kfree(entry); > + } > +unlock_extent: > + unlock_extent_cached(&inode->io_tree, start_index << PAGE_SHIFT, > + (last_index << PAGE_SHIFT) + PAGE_SIZE - 1, > + &cached_state); > +free_pages: > + for (i = 0; i < nr_pages; i++) { > + if (pages[i]) { > + unlock_page(pages[i]); > + put_page(pages[i]); > + } > + } > + kfree(pages); > + return ret; > +} > + > /* > * Btrfs entrace for defrag. > * >
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 26957cd91ea6..a5ca752bcda8 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1548,6 +1548,77 @@ static int defrag_one_locked_target(struct btrfs_inode *inode, return ret; } +static int defrag_one_range(struct btrfs_inode *inode, + u64 start, u32 len, + u32 extent_thresh, u64 newer_than, + bool do_compress) +{ + struct extent_state *cached_state = NULL; + struct defrag_target_range *entry; + struct defrag_target_range *tmp; + LIST_HEAD(target_list); + struct page **pages; + const u32 sectorsize = inode->root->fs_info->sectorsize; + unsigned long last_index = (start + len - 1) >> PAGE_SHIFT; + unsigned long start_index = start >> PAGE_SHIFT; + unsigned int nr_pages = last_index - start_index + 1; + int ret = 0; + int i; + + ASSERT(nr_pages <= CLUSTER_SIZE / PAGE_SIZE); + ASSERT(IS_ALIGNED(start, sectorsize) && IS_ALIGNED(len, sectorsize)); + + pages = kzalloc(sizeof(struct page *) * nr_pages, GFP_NOFS); + if (!pages) + return -ENOMEM; + + /* Prepare all pages */ + for (i = 0; i < nr_pages; i++) { + pages[i] = defrag_prepare_one_page(inode, start_index + i); + if (IS_ERR(pages[i])) { + ret = PTR_ERR(pages[i]); + pages[i] = NULL; + goto free_pages; + } + } + /* Also lock the pages range */ + lock_extent_bits(&inode->io_tree, start_index << PAGE_SHIFT, + (last_index << PAGE_SHIFT) + PAGE_SIZE - 1, + &cached_state); + /* + * Now we have a consistent view about the extent map, re-check + * which range really need to be defraged. + */ + ret = defrag_collect_targets(inode, start, len, extent_thresh, + newer_than, do_compress, &target_list); + if (ret < 0) + goto unlock_extent; + + list_for_each_entry(entry, &target_list, list) { + ret = defrag_one_locked_target(inode, entry, pages, nr_pages); + if (ret < 0) + break; + } + + list_for_each_entry_safe(entry, tmp, &target_list, list) { + list_del_init(&entry->list); + kfree(entry); + } +unlock_extent: + unlock_extent_cached(&inode->io_tree, start_index << PAGE_SHIFT, + (last_index << PAGE_SHIFT) + PAGE_SIZE - 1, + &cached_state); +free_pages: + for (i = 0; i < nr_pages; i++) { + if (pages[i]) { + unlock_page(pages[i]); + put_page(pages[i]); + } + } + kfree(pages); + return ret; +} + /* * Btrfs entrace for defrag. *
A new helper, defrag_one_range(), is introduced to defrag one range. This function will mostly prepare the needed pages and extent status for defrag_one_locked_target(). As we can only have a consistent view of extent map with page and extent bits locked, we need to re-check the range passed in to get a real target list for defrag_one_locked_target(). Signed-off-by: Qu Wenruo <wqu@suse.com> --- fs/btrfs/ioctl.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+)