[PATCH] NFS: Fix race in nfs_release_page()
[firefly-linux-kernel-4.4.55.git] / mm / truncate.c
index 89a5c359b6e821ec00e0fd8e8d189f3d9152e45a..6c79ca4a1ca7cfe9226e46f651e752db78ef88c6 100644 (file)
@@ -60,11 +60,16 @@ void cancel_dirty_page(struct page *page, unsigned int account_size)
                WARN_ON(++warncount < 5);
        }
                
-       if (TestClearPageDirty(page) && account_size) {
-               dec_zone_page_state(page, NR_FILE_DIRTY);
-               task_io_account_cancelled_write(account_size);
+       if (TestClearPageDirty(page)) {
+               struct address_space *mapping = page->mapping;
+               if (mapping && mapping_cap_account_dirty(mapping)) {
+                       dec_zone_page_state(page, NR_FILE_DIRTY);
+                       if (account_size)
+                               task_io_account_cancelled_write(account_size);
+               }
        }
 }
+EXPORT_SYMBOL(cancel_dirty_page);
 
 /*
  * If truncate cannot remove the fs-private metadata from the page, the page
@@ -336,6 +341,15 @@ failed:
        return 0;
 }
 
+static int do_launder_page(struct address_space *mapping, struct page *page)
+{
+       if (!PageDirty(page))
+               return 0;
+       if (page->mapping != mapping || mapping->a_ops->launder_page == NULL)
+               return 0;
+       return mapping->a_ops->launder_page(page);
+}
+
 /**
  * invalidate_inode_pages2_range - remove range of pages from an address_space
  * @mapping: the address_space
@@ -400,7 +414,8 @@ int invalidate_inode_pages2_range(struct address_space *mapping,
                                          PAGE_CACHE_SIZE, 0);
                                }
                        }
-                       if (!invalidate_complete_page2(mapping, page))
+                       ret = do_launder_page(mapping, page);
+                       if (ret == 0 && !invalidate_complete_page2(mapping, page))
                                ret = -EIO;
                        unlock_page(page);
                }