btrfs: teach __process_pages_contig about PAGE_LOCK operation
authorLiu Bo <bo.li.liu@oracle.com>
Fri, 10 Feb 2017 15:41:05 +0000 (16:41 +0100)
committerDavid Sterba <dsterba@suse.com>
Fri, 17 Feb 2017 11:03:35 +0000 (12:03 +0100)
Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
Reviewed-by: David Sterba <dsterba@suse.com>
[ changes to the helper separated from the following patch ]
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/extent_io.c
fs/btrfs/extent_io.h

index 664f52c1c58aae0a82e99bb3376728c1b03bf040..5bb7ced20695022653dbab42da77cd9b9ef93339 100644 (file)
@@ -1554,6 +1554,11 @@ out:
        return found;
 }
 
+static int __process_pages_contig(struct address_space *mapping,
+                                 struct page *locked_page,
+                                 pgoff_t start_index, pgoff_t end_index,
+                                 unsigned long page_ops, pgoff_t *index_ret);
+
 static noinline void __unlock_for_delalloc(struct inode *inode,
                                           struct page *locked_page,
                                           u64 start, u64 end)
@@ -1731,17 +1736,24 @@ out_failed:
        return found;
 }
 
-static void __process_pages_contig(struct address_space *mapping,
-                                  struct page *locked_page,
-                                  pgoff_t start_index, pgoff_t end_index,
-                                  unsigned long page_ops)
+static int __process_pages_contig(struct address_space *mapping,
+                                 struct page *locked_page,
+                                 pgoff_t start_index, pgoff_t end_index,
+                                 unsigned long page_ops, pgoff_t *index_ret)
 {
        unsigned long nr_pages = end_index - start_index + 1;
+       unsigned long pages_locked = 0;
        pgoff_t index = start_index;
        struct page *pages[16];
        unsigned ret;
+       int err = 0;
        int i;
 
+       if (page_ops & PAGE_LOCK) {
+               ASSERT(page_ops == PAGE_LOCK);
+               ASSERT(index_ret && *index_ret == start_index);
+       }
+
        if ((page_ops & PAGE_SET_ERROR) && nr_pages > 0)
                mapping_set_error(mapping, -EIO);
 
@@ -1749,13 +1761,22 @@ static void __process_pages_contig(struct address_space *mapping,
                ret = find_get_pages_contig(mapping, index,
                                     min_t(unsigned long,
                                     nr_pages, ARRAY_SIZE(pages)), pages);
-               for (i = 0; i < ret; i++) {
+               if (ret == 0) {
+                       /*
+                        * Only if we're going to lock these pages,
+                        * can we find nothing at @index.
+                        */
+                       ASSERT(page_ops & PAGE_LOCK);
+                       return ret;
+               }
 
+               for (i = 0; i < ret; i++) {
                        if (page_ops & PAGE_SET_PRIVATE2)
                                SetPagePrivate2(pages[i]);
 
                        if (pages[i] == locked_page) {
                                put_page(pages[i]);
+                               pages_locked++;
                                continue;
                        }
                        if (page_ops & PAGE_CLEAR_DIRTY)
@@ -1768,12 +1789,27 @@ static void __process_pages_contig(struct address_space *mapping,
                                end_page_writeback(pages[i]);
                        if (page_ops & PAGE_UNLOCK)
                                unlock_page(pages[i]);
+                       if (page_ops & PAGE_LOCK) {
+                               lock_page(pages[i]);
+                               if (!PageDirty(pages[i]) ||
+                                   pages[i]->mapping != mapping) {
+                                       unlock_page(pages[i]);
+                                       put_page(pages[i]);
+                                       err = -EAGAIN;
+                                       goto out;
+                               }
+                       }
                        put_page(pages[i]);
+                       pages_locked++;
                }
                nr_pages -= ret;
                index += ret;
                cond_resched();
        }
+out:
+       if (err && index_ret)
+               *index_ret = start_index + pages_locked - 1;
+       return err;
 }
 
 void extent_clear_unlock_delalloc(struct inode *inode, u64 start, u64 end,
@@ -1786,7 +1822,7 @@ void extent_clear_unlock_delalloc(struct inode *inode, u64 start, u64 end,
 
        __process_pages_contig(inode->i_mapping, locked_page,
                               start >> PAGE_SHIFT, end >> PAGE_SHIFT,
-                              page_ops);
+                              page_ops, NULL);
 }
 
 /*
index 17f9ce479ed7fe12fb84f3bbfb0e4decbe29fcde..4551a5b4b8f59e904287d7501badcf63075505f3 100644 (file)
 #define EXTENT_BUFFER_IN_TREE 10
 #define EXTENT_BUFFER_WRITE_ERR 11    /* write IO error */
 
-/* these are flags for extent_clear_unlock_delalloc */
+/* these are flags for __process_pages_contig */
 #define PAGE_UNLOCK            (1 << 0)
 #define PAGE_CLEAR_DIRTY       (1 << 1)
 #define PAGE_SET_WRITEBACK     (1 << 2)
 #define PAGE_END_WRITEBACK     (1 << 3)
 #define PAGE_SET_PRIVATE2      (1 << 4)
 #define PAGE_SET_ERROR         (1 << 5)
+#define PAGE_LOCK              (1 << 6)
 
 /*
  * page->private values.  Every page that is controlled by the extent