]> git.hungrycats.org Git - linux/commitdiff
Btrfs: fix hang on compressed write error
authorFilipe Manana <fdmanana@suse.com>
Mon, 6 Oct 2014 21:14:23 +0000 (22:14 +0100)
committerZygo Blaxell <zblaxell@serenity.furryterror.org>
Sun, 14 Dec 2014 03:33:50 +0000 (22:33 -0500)
In inode.c:submit_compressed_extents(), before calling btrfs_submit_compressed_write()
we start writeback for all pages, clear their dirty flag, unlock them, etc, but if
btrfs_submit_compressed_write() fails (at the moment it can only fail with -ENOMEM),
we never end the writeback on the pages, so any filemap_fdatawait_range() call will
hang forever. We were also not calling the writepage end io hook, which means the
corresponding ordered extent will never complete and all its waiters will block
forever, such as a full fsync (via btrfs_wait_ordered_range()).

Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Chris Mason <clm@fb.com>
(cherry picked from commit fce2a4e6b2da1bcc03837aff53b5bcb02bee4cee)

fs/btrfs/inode.c

index fcb9a38fc9d35ff62df6e16161d39779d0833d3f..ec68eaef05d7ed39bfc3c9a10065a91b0375bfe2 100644 (file)
@@ -814,6 +814,20 @@ retry:
                                    ins.objectid,
                                    ins.offset, async_extent->pages,
                                    async_extent->nr_pages);
+               if (ret) {
+                       struct extent_io_tree *tree = &BTRFS_I(inode)->io_tree;
+                       struct page *p = async_extent->pages[0];
+                       const u64 start = async_extent->start;
+                       const u64 end = start + async_extent->ram_size - 1;
+
+                       p->mapping = inode->i_mapping;
+                       tree->ops->writepage_end_io_hook(p, start, end,
+                                                        NULL, 0);
+                       p->mapping = NULL;
+                       extent_clear_unlock_delalloc(inode, start, end, NULL, 0,
+                                                    PAGE_END_WRITEBACK |
+                                                    PAGE_SET_ERROR);
+               }
                alloc_hint = ins.objectid + ins.offset;
                kfree(async_extent);
                if (ret)