]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
mm: vmscan: take page buffers dirty and locked state into account
authorMel Gorman <mgorman@suse.de>
Fri, 7 Jun 2013 00:07:44 +0000 (10:07 +1000)
committerStephen Rothwell <sfr@canb.auug.org.au>
Fri, 7 Jun 2013 05:42:09 +0000 (15:42 +1000)
Page reclaim keeps track of dirty and under writeback pages and uses it to
determine if wait_iff_congested() should stall or if kswapd should begin
writing back pages.  This fails to account for buffer pages that can be
under writeback but not PageWriteback which is the case for filesystems
like ext3 ordered mode.  Furthermore, PageDirty buffer pages can have all
the buffers clean and writepage does no IO so it should not be accounted
as congested.

This patch adds an address_space operation that filesystems may optionally
use to check if a page is really dirty or really under writeback.  An
implementation is provided for for buffer_heads is added and used for
block operations and ext3 in ordered mode.  By default the page flags are
obeyed.

Credit goes to Jan Kara for identifying that the page flags alone are not
sufficient for ext3 and sanity checking a number of ideas on how the
problem could be addressed.

Signed-off-by: Mel Gorman <mgorman@suse.de>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Rik van Riel <riel@redhat.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Jiri Slaby <jslaby@suse.cz>
Cc: Valdis Kletnieks <Valdis.Kletnieks@vt.edu>
Cc: Zlatko Calusic <zcalusic@bitsync.net>
Cc: dormando <dormando@rydia.net>
Cc: Trond Myklebust <trond.myklebust@fys.uio.no>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
fs/block_dev.c
fs/buffer.c
fs/ext3/inode.c
include/linux/buffer_head.h
include/linux/fs.h
mm/vmscan.c

index 2091db8cdd783a2287ce9165a7223cfe59cf5a2b..9c8ebe4f678bcef08edba48fd0d7d388dde5be35 100644 (file)
@@ -1583,6 +1583,7 @@ static const struct address_space_operations def_blk_aops = {
        .writepages     = generic_writepages,
        .releasepage    = blkdev_releasepage,
        .direct_IO      = blkdev_direct_IO,
+       .is_dirty_writeback = buffer_check_dirty_writeback,
 };
 
 const struct file_operations def_blk_fops = {
index f93392e2df126fd5c17833b7105338fc6f65be5f..4d7433534f5cd77b7f9b240fba57ac7923df07e6 100644 (file)
@@ -82,6 +82,40 @@ void unlock_buffer(struct buffer_head *bh)
 }
 EXPORT_SYMBOL(unlock_buffer);
 
+/*
+ * Returns if the page has dirty or writeback buffers. If all the buffers
+ * are unlocked and clean then the PageDirty information is stale. If
+ * any of the pages are locked, it is assumed they are locked for IO.
+ */
+void buffer_check_dirty_writeback(struct page *page,
+                                    bool *dirty, bool *writeback)
+{
+       struct buffer_head *head, *bh;
+       *dirty = false;
+       *writeback = false;
+
+       BUG_ON(!PageLocked(page));
+
+       if (!page_has_buffers(page))
+               return;
+
+       if (PageWriteback(page))
+               *writeback = true;
+
+       head = page_buffers(page);
+       bh = head;
+       do {
+               if (buffer_locked(bh))
+                       *writeback = true;
+
+               if (buffer_dirty(bh))
+                       *dirty = true;
+
+               bh = bh->b_this_page;
+       } while (bh != head);
+}
+EXPORT_SYMBOL(buffer_check_dirty_writeback);
+
 /*
  * Block until a buffer comes unlocked.  This doesn't stop it
  * from becoming locked again - you have to lock it yourself
index f67668f724baaf4112e994ded2e55a4c62089c3a..2bd85486b87974b390892a2280676a49d8eb274e 100644 (file)
@@ -1985,6 +1985,7 @@ static const struct address_space_operations ext3_ordered_aops = {
        .direct_IO              = ext3_direct_IO,
        .migratepage            = buffer_migrate_page,
        .is_partially_uptodate  = block_is_partially_uptodate,
+       .is_dirty_writeback     = buffer_check_dirty_writeback,
        .error_remove_page      = generic_error_remove_page,
 };
 
index f5a3b838ddb00639aa1c6669ff60206e1f146507..91fa9a94ae9226f1bdb96864f9c768bd2ea3d3b0 100644 (file)
@@ -139,6 +139,9 @@ BUFFER_FNS(Prio, prio)
        })
 #define page_has_buffers(page) PagePrivate(page)
 
+void buffer_check_dirty_writeback(struct page *page,
+                                    bool *dirty, bool *writeback);
+
 /*
  * Declarations
  */
index 9f696014988d6973bafac32826fa9ce7544e8b48..a4c9fbe3c8d3b94771445176c93f4d91a65bd030 100644 (file)
@@ -380,6 +380,7 @@ struct address_space_operations {
        int (*launder_page) (struct page *);
        int (*is_partially_uptodate) (struct page *, read_descriptor_t *,
                                        unsigned long);
+       void (*is_dirty_writeback) (struct page *, bool *, bool *);
        int (*error_remove_page)(struct address_space *, struct page *);
 
        /* swapfile support */
index bf4778479e3a498b02da17379c59b1639a59ad6b..c85794399848df32b43194644422306bc262a71f 100644 (file)
@@ -673,6 +673,8 @@ static enum page_references page_check_references(struct page *page,
 static void page_check_dirty_writeback(struct page *page,
                                       bool *dirty, bool *writeback)
 {
+       struct address_space *mapping;
+
        /*
         * Anonymous pages are not handled by flushers and must be written
         * from reclaim context. Do not stall reclaim based on them
@@ -686,6 +688,14 @@ static void page_check_dirty_writeback(struct page *page,
        /* By default assume that the page flags are accurate */
        *dirty = PageDirty(page);
        *writeback = PageWriteback(page);
+
+       /* Verify dirty/writeback state if the filesystem supports it */
+       if (!page_has_private(page))
+               return;
+
+       mapping = page_mapping(page);
+       if (mapping && mapping->a_ops->is_dirty_writeback)
+               mapping->a_ops->is_dirty_writeback(page, dirty, writeback);
 }
 
 /*