Btrfs: set page and mapping error on compressed write failure
authorFilipe Manana <fdmanana@suse.com>
Mon, 6 Oct 2014 21:14:22 +0000 (22:14 +0100)
committerChris Mason <clm@fb.com>
Fri, 21 Nov 2014 01:14:25 +0000 (17:14 -0800)
If we fail in submit_compressed_extents() before calling btrfs_submit_compressed_write(),
we start and end the writeback for the pages (clear their dirty flag, unlock them, etc)
but we don't tag the pages, nor the inode's mapping, with an error. This makes it
impossible for a caller of filemap_fdatawait_range() (fsync, or transaction commit
for e.g.) know that there was an error.

Note that the return value of submit_compressed_extents() is useless, as that function
is executed by a workqueue task and not directly by the fill_delalloc callback. This
means the writepage/s callbacks of the inode's address space operations don't get that
return value.

Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Chris Mason <clm@fb.com>
fs/btrfs/extent_io.c
fs/btrfs/extent_io.h
fs/btrfs/inode.c

index bf3f424e0013c17d3a47047e5c7301abba93bfac..420fe26d32d56b60f57454f9daa8356b9318ed50 100644 (file)
@@ -1746,6 +1746,9 @@ int extent_clear_unlock_delalloc(struct inode *inode, u64 start, u64 end,
        if (page_ops == 0)
                return 0;
 
+       if ((page_ops & PAGE_SET_ERROR) && nr_pages > 0)
+               mapping_set_error(inode->i_mapping, -EIO);
+
        while (nr_pages > 0) {
                ret = find_get_pages_contig(inode->i_mapping, index,
                                     min_t(unsigned long,
@@ -1763,6 +1766,8 @@ int extent_clear_unlock_delalloc(struct inode *inode, u64 start, u64 end,
                                clear_page_dirty_for_io(pages[i]);
                        if (page_ops & PAGE_SET_WRITEBACK)
                                set_page_writeback(pages[i]);
+                       if (page_ops & PAGE_SET_ERROR)
+                               SetPageError(pages[i]);
                        if (page_ops & PAGE_END_WRITEBACK)
                                end_page_writeback(pages[i]);
                        if (page_ops & PAGE_UNLOCK)
index 6d4b938be98678660cc51fb55bd51a5688279ed8..ece9ce87edff521fa0a54f38bca0cb47059c638b 100644 (file)
@@ -49,6 +49,7 @@
 #define PAGE_SET_WRITEBACK     (1 << 2)
 #define PAGE_END_WRITEBACK     (1 << 3)
 #define PAGE_SET_PRIVATE2      (1 << 4)
+#define PAGE_SET_ERROR         (1 << 5)
 
 /*
  * page->private values.  Every page that is controlled by the extent
index d23362f4464e9b7091654ed570e726dd9b7507b3..fcb9a38fc9d35ff62df6e16161d39779d0833d3f 100644 (file)
@@ -832,7 +832,8 @@ out_free:
                                     NULL, EXTENT_LOCKED | EXTENT_DELALLOC |
                                     EXTENT_DEFRAG | EXTENT_DO_ACCOUNTING,
                                     PAGE_UNLOCK | PAGE_CLEAR_DIRTY |
-                                    PAGE_SET_WRITEBACK | PAGE_END_WRITEBACK);
+                                    PAGE_SET_WRITEBACK | PAGE_END_WRITEBACK |
+                                    PAGE_SET_ERROR);
        kfree(async_extent);
        goto again;
 }