writeback: remove unnecessary wait in throttle_vm_writeout()
[safe/jmp/linux-2.6] / mm / page-writeback.c
index 1d2fc89..7845462 100644 (file)
@@ -2,6 +2,7 @@
  * mm/page-writeback.c
  *
  * Copyright (C) 2002, Linus Torvalds.
+ * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
  *
  * Contains functions related to writing back dirty pages at the
  * address_space level.
@@ -36,7 +37,7 @@
 
 /*
  * The maximum number of pages to writeout in a single bdflush/kupdate
- * operation.  We do this so we don't hold I_LOCK against an inode for
+ * operation.  We do this so we don't hold I_SYNC against an inode for
  * enormous amounts of time, which would block a userspace task which has
  * been forced to throttle against that inode.  Also, the code reevaluates
  * the dirty each time it has written this many pages.
@@ -49,8 +50,6 @@
  */
 static long ratelimit_pages = 32;
 
-static int dirty_exceeded __cacheline_aligned_in_smp;  /* Dirty mem may be over limit */
-
 /*
  * When balance_dirty_pages decides that the caller needs to perform some
  * non-background writeback, this is how many pages it will attempt to write.
@@ -67,12 +66,12 @@ static inline long sync_writeback_pages(void)
 /*
  * Start background writeback (via pdflush) at this percentage
  */
-int dirty_background_ratio = 10;
+int dirty_background_ratio = 5;
 
 /*
  * The generator of dirty data starts writeback at this percentage
  */
-int vm_dirty_ratio = 40;
+int vm_dirty_ratio = 10;
 
 /*
  * The interval between `kupdate'-style writebacks, in jiffies
@@ -103,6 +102,141 @@ EXPORT_SYMBOL(laptop_mode);
 static void background_writeout(unsigned long _min_pages);
 
 /*
+ * Scale the writeback cache size proportional to the relative writeout speeds.
+ *
+ * We do this by keeping a floating proportion between BDIs, based on page
+ * writeback completions [end_page_writeback()]. Those devices that write out
+ * pages fastest will get the larger share, while the slower will get a smaller
+ * share.
+ *
+ * We use page writeout completions because we are interested in getting rid of
+ * dirty pages. Having them written out is the primary goal.
+ *
+ * We introduce a concept of time, a period over which we measure these events,
+ * because demand can/will vary over time. The length of this period itself is
+ * measured in page writeback completions.
+ *
+ */
+static struct prop_descriptor vm_completions;
+static struct prop_descriptor vm_dirties;
+
+static unsigned long determine_dirtyable_memory(void);
+
+/*
+ * couple the period to the dirty_ratio:
+ *
+ *   period/2 ~ roundup_pow_of_two(dirty limit)
+ */
+static int calc_period_shift(void)
+{
+       unsigned long dirty_total;
+
+       dirty_total = (vm_dirty_ratio * determine_dirtyable_memory()) / 100;
+       return 2 + ilog2(dirty_total - 1);
+}
+
+/*
+ * update the period when the dirty ratio changes.
+ */
+int dirty_ratio_handler(struct ctl_table *table, int write,
+               struct file *filp, void __user *buffer, size_t *lenp,
+               loff_t *ppos)
+{
+       int old_ratio = vm_dirty_ratio;
+       int ret = proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos);
+       if (ret == 0 && write && vm_dirty_ratio != old_ratio) {
+               int shift = calc_period_shift();
+               prop_change_shift(&vm_completions, shift);
+               prop_change_shift(&vm_dirties, shift);
+       }
+       return ret;
+}
+
+/*
+ * Increment the BDI's writeout completion count and the global writeout
+ * completion count. Called from test_clear_page_writeback().
+ */
+static inline void __bdi_writeout_inc(struct backing_dev_info *bdi)
+{
+       __prop_inc_percpu(&vm_completions, &bdi->completions);
+}
+
+static inline void task_dirty_inc(struct task_struct *tsk)
+{
+       prop_inc_single(&vm_dirties, &tsk->dirties);
+}
+
+/*
+ * Obtain an accurate fraction of the BDI's portion.
+ */
+static void bdi_writeout_fraction(struct backing_dev_info *bdi,
+               long *numerator, long *denominator)
+{
+       if (bdi_cap_writeback_dirty(bdi)) {
+               prop_fraction_percpu(&vm_completions, &bdi->completions,
+                               numerator, denominator);
+       } else {
+               *numerator = 0;
+               *denominator = 1;
+       }
+}
+
+/*
+ * Clip the earned share of dirty pages to that which is actually available.
+ * This avoids exceeding the total dirty_limit when the floating averages
+ * fluctuate too quickly.
+ */
+static void
+clip_bdi_dirty_limit(struct backing_dev_info *bdi, long dirty, long *pbdi_dirty)
+{
+       long avail_dirty;
+
+       avail_dirty = dirty -
+               (global_page_state(NR_FILE_DIRTY) +
+                global_page_state(NR_WRITEBACK) +
+                global_page_state(NR_UNSTABLE_NFS));
+
+       if (avail_dirty < 0)
+               avail_dirty = 0;
+
+       avail_dirty += bdi_stat(bdi, BDI_RECLAIMABLE) +
+               bdi_stat(bdi, BDI_WRITEBACK);
+
+       *pbdi_dirty = min(*pbdi_dirty, avail_dirty);
+}
+
+static inline void task_dirties_fraction(struct task_struct *tsk,
+               long *numerator, long *denominator)
+{
+       prop_fraction_single(&vm_dirties, &tsk->dirties,
+                               numerator, denominator);
+}
+
+/*
+ * scale the dirty limit
+ *
+ * task specific dirty limit:
+ *
+ *   dirty -= (dirty/8) * p_{t}
+ */
+void task_dirty_limit(struct task_struct *tsk, long *pdirty)
+{
+       long numerator, denominator;
+       long dirty = *pdirty;
+       u64 inv = dirty >> 3;
+
+       task_dirties_fraction(tsk, &numerator, &denominator);
+       inv *= numerator;
+       do_div(inv, denominator);
+
+       dirty -= inv;
+       if (dirty < *pdirty/2)
+               dirty = *pdirty/2;
+
+       *pdirty = dirty;
+}
+
+/*
  * Work out the current dirty-memory clamping and background writeout
  * thresholds.
  *
@@ -119,31 +253,59 @@ static void background_writeout(unsigned long _min_pages);
  * We make sure that the background writeout level is below the adjusted
  * clamping level.
  */
+
+static unsigned long highmem_dirtyable_memory(unsigned long total)
+{
+#ifdef CONFIG_HIGHMEM
+       int node;
+       unsigned long x = 0;
+
+       for_each_node_state(node, N_HIGH_MEMORY) {
+               struct zone *z =
+                       &NODE_DATA(node)->node_zones[ZONE_HIGHMEM];
+
+               x += zone_page_state(z, NR_FREE_PAGES)
+                       + zone_page_state(z, NR_INACTIVE)
+                       + zone_page_state(z, NR_ACTIVE);
+       }
+       /*
+        * Make sure that the number of highmem pages is never larger
+        * than the number of the total dirtyable memory. This can only
+        * occur in very strange VM situations but we want to make sure
+        * that this does not occur.
+        */
+       return min(x, total);
+#else
+       return 0;
+#endif
+}
+
+static unsigned long determine_dirtyable_memory(void)
+{
+       unsigned long x;
+
+       x = global_page_state(NR_FREE_PAGES)
+               + global_page_state(NR_INACTIVE)
+               + global_page_state(NR_ACTIVE);
+       x -= highmem_dirtyable_memory(x);
+       return x + 1;   /* Ensure that we never return 0 */
+}
+
 static void
-get_dirty_limits(long *pbackground, long *pdirty,
-                                       struct address_space *mapping)
+get_dirty_limits(long *pbackground, long *pdirty, long *pbdi_dirty,
+                struct backing_dev_info *bdi)
 {
        int background_ratio;           /* Percentages */
        int dirty_ratio;
        int unmapped_ratio;
        long background;
        long dirty;
-       unsigned long available_memory = vm_total_pages;
+       unsigned long available_memory = determine_dirtyable_memory();
        struct task_struct *tsk;
 
-#ifdef CONFIG_HIGHMEM
-       /*
-        * If this mapping can only allocate from low memory,
-        * we exclude high memory from our count.
-        */
-       if (mapping && !(mapping_gfp_mask(mapping) & __GFP_HIGHMEM))
-               available_memory -= totalhigh_pages;
-#endif
-
-
        unmapped_ratio = 100 - ((global_page_state(NR_FILE_MAPPED) +
                                global_page_state(NR_ANON_PAGES)) * 100) /
-                                       vm_total_pages;
+                                       available_memory;
 
        dirty_ratio = vm_dirty_ratio;
        if (dirty_ratio > unmapped_ratio / 2)
@@ -165,6 +327,23 @@ get_dirty_limits(long *pbackground, long *pdirty,
        }
        *pbackground = background;
        *pdirty = dirty;
+
+       if (bdi) {
+               u64 bdi_dirty = dirty;
+               long numerator, denominator;
+
+               /*
+                * Calculate this BDI's share of the dirty ratio.
+                */
+               bdi_writeout_fraction(bdi, &numerator, &denominator);
+
+               bdi_dirty *= numerator;
+               do_div(bdi_dirty, denominator);
+
+               *pbdi_dirty = bdi_dirty;
+               clip_bdi_dirty_limit(bdi, dirty, pbdi_dirty);
+               task_dirty_limit(current, pbdi_dirty);
+       }
 }
 
 /*
@@ -176,9 +355,11 @@ get_dirty_limits(long *pbackground, long *pdirty,
  */
 static void balance_dirty_pages(struct address_space *mapping)
 {
-       long nr_reclaimable;
+       long bdi_nr_reclaimable;
+       long bdi_nr_writeback;
        long background_thresh;
        long dirty_thresh;
+       long bdi_thresh;
        unsigned long pages_written = 0;
        unsigned long write_chunk = sync_writeback_pages();
 
@@ -193,15 +374,15 @@ static void balance_dirty_pages(struct address_space *mapping)
                        .range_cyclic   = 1,
                };
 
-               get_dirty_limits(&background_thresh, &dirty_thresh, mapping);
-               nr_reclaimable = global_page_state(NR_FILE_DIRTY) +
-                                       global_page_state(NR_UNSTABLE_NFS);
-               if (nr_reclaimable + global_page_state(NR_WRITEBACK) <=
-                       dirty_thresh)
-                               break;
+               get_dirty_limits(&background_thresh, &dirty_thresh,
+                               &bdi_thresh, bdi);
+               bdi_nr_reclaimable = bdi_stat(bdi, BDI_RECLAIMABLE);
+               bdi_nr_writeback = bdi_stat(bdi, BDI_WRITEBACK);
+               if (bdi_nr_reclaimable + bdi_nr_writeback <= bdi_thresh)
+                       break;
 
-               if (!dirty_exceeded)
-                       dirty_exceeded = 1;
+               if (!bdi->dirty_exceeded)
+                       bdi->dirty_exceeded = 1;
 
                /* Note: nr_reclaimable denotes nr_dirty + nr_unstable.
                 * Unstable writes are a feature of certain networked
@@ -209,26 +390,42 @@ static void balance_dirty_pages(struct address_space *mapping)
                 * written to the server's write cache, but has not yet
                 * been flushed to permanent storage.
                 */
-               if (nr_reclaimable) {
+               if (bdi_nr_reclaimable) {
                        writeback_inodes(&wbc);
-                       get_dirty_limits(&background_thresh,
-                                               &dirty_thresh, mapping);
-                       nr_reclaimable = global_page_state(NR_FILE_DIRTY) +
-                                       global_page_state(NR_UNSTABLE_NFS);
-                       if (nr_reclaimable +
-                               global_page_state(NR_WRITEBACK)
-                                       <= dirty_thresh)
-                                               break;
                        pages_written += write_chunk - wbc.nr_to_write;
-                       if (pages_written >= write_chunk)
-                               break;          /* We've done our duty */
+                       get_dirty_limits(&background_thresh, &dirty_thresh,
+                                      &bdi_thresh, bdi);
                }
+
+               /*
+                * In order to avoid the stacked BDI deadlock we need
+                * to ensure we accurately count the 'dirty' pages when
+                * the threshold is low.
+                *
+                * Otherwise it would be possible to get thresh+n pages
+                * reported dirty, even though there are thresh-m pages
+                * actually dirty; with m+n sitting in the percpu
+                * deltas.
+                */
+               if (bdi_thresh < 2*bdi_stat_error(bdi)) {
+                       bdi_nr_reclaimable = bdi_stat_sum(bdi, BDI_RECLAIMABLE);
+                       bdi_nr_writeback = bdi_stat_sum(bdi, BDI_WRITEBACK);
+               } else if (bdi_nr_reclaimable) {
+                       bdi_nr_reclaimable = bdi_stat(bdi, BDI_RECLAIMABLE);
+                       bdi_nr_writeback = bdi_stat(bdi, BDI_WRITEBACK);
+               }
+
+               if (bdi_nr_reclaimable + bdi_nr_writeback <= bdi_thresh)
+                       break;
+               if (pages_written >= write_chunk)
+                       break;          /* We've done our duty */
+
                congestion_wait(WRITE, HZ/10);
        }
 
-       if (nr_reclaimable + global_page_state(NR_WRITEBACK)
-               <= dirty_thresh && dirty_exceeded)
-                       dirty_exceeded = 0;
+       if (bdi_nr_reclaimable + bdi_nr_writeback < bdi_thresh &&
+                       bdi->dirty_exceeded)
+               bdi->dirty_exceeded = 0;
 
        if (writeback_in_progress(bdi))
                return;         /* pdflush is already working this queue */
@@ -242,13 +439,15 @@ static void balance_dirty_pages(struct address_space *mapping)
         * background_thresh, to keep the amount of dirty memory low.
         */
        if ((laptop_mode && pages_written) ||
-            (!laptop_mode && (nr_reclaimable > background_thresh)))
+                       (!laptop_mode && (global_page_state(NR_FILE_DIRTY)
+                                         + global_page_state(NR_UNSTABLE_NFS)
+                                         > background_thresh)))
                pdflush_operation(background_writeout, 0);
 }
 
-void set_page_dirty_balance(struct page *page)
+void set_page_dirty_balance(struct page *page, int page_mkwrite)
 {
-       if (set_page_dirty(page)) {
+       if (set_page_dirty(page) || page_mkwrite) {
                struct address_space *mapping = page_mapping(page);
 
                if (mapping)
@@ -278,7 +477,7 @@ void balance_dirty_pages_ratelimited_nr(struct address_space *mapping,
        unsigned long *p;
 
        ratelimit = ratelimit_pages;
-       if (dirty_exceeded)
+       if (mapping->backing_dev_info->dirty_exceeded)
                ratelimit = 8;
 
        /*
@@ -298,13 +497,13 @@ void balance_dirty_pages_ratelimited_nr(struct address_space *mapping,
 }
 EXPORT_SYMBOL(balance_dirty_pages_ratelimited_nr);
 
-void throttle_vm_writeout(void)
+void throttle_vm_writeout(gfp_t gfp_mask)
 {
        long background_thresh;
        long dirty_thresh;
 
         for ( ; ; ) {
-               get_dirty_limits(&background_thresh, &dirty_thresh, NULL);
+               get_dirty_limits(&background_thresh, &dirty_thresh, NULL, NULL);
 
                 /*
                  * Boost the allowable dirty threshold a bit for page
@@ -316,10 +515,17 @@ void throttle_vm_writeout(void)
                        global_page_state(NR_WRITEBACK) <= dirty_thresh)
                                break;
                 congestion_wait(WRITE, HZ/10);
+
+               /*
+                * The caller might hold locks which can prevent IO completion
+                * or progress in the filesystem.  So we cannot just sit here
+                * waiting for IO to complete.
+                */
+               if ((gfp_mask & (__GFP_FS|__GFP_IO)) != (__GFP_FS|__GFP_IO))
+                       break;
         }
 }
 
-
 /*
  * writeback at least _min_pages, and keep writing until the amount of dirty
  * memory is less than the background threshold, or until we're all clean.
@@ -340,11 +546,12 @@ static void background_writeout(unsigned long _min_pages)
                long background_thresh;
                long dirty_thresh;
 
-               get_dirty_limits(&background_thresh, &dirty_thresh, NULL);
+               get_dirty_limits(&background_thresh, &dirty_thresh, NULL, NULL);
                if (global_page_state(NR_FILE_DIRTY) +
                        global_page_state(NR_UNSTABLE_NFS) < background_thresh
                                && min_pages <= 0)
                        break;
+               wbc.more_io = 0;
                wbc.encountered_congestion = 0;
                wbc.nr_to_write = MAX_WRITEBACK_PAGES;
                wbc.pages_skipped = 0;
@@ -352,8 +559,9 @@ static void background_writeout(unsigned long _min_pages)
                min_pages -= MAX_WRITEBACK_PAGES - wbc.nr_to_write;
                if (wbc.nr_to_write > 0 || wbc.pages_skipped > 0) {
                        /* Wrote less than expected */
-                       congestion_wait(WRITE, HZ/10);
-                       if (!wbc.encountered_congestion)
+                       if (wbc.encountered_congestion || wbc.more_io)
+                               congestion_wait(WRITE, HZ/10);
+                       else
                                break;
                }
        }
@@ -418,11 +626,12 @@ static void wb_kupdate(unsigned long arg)
                        global_page_state(NR_UNSTABLE_NFS) +
                        (inodes_stat.nr_inodes - inodes_stat.nr_unused);
        while (nr_to_write > 0) {
+               wbc.more_io = 0;
                wbc.encountered_congestion = 0;
                wbc.nr_to_write = MAX_WRITEBACK_PAGES;
                writeback_inodes(&wbc);
                if (wbc.nr_to_write > 0) {
-                       if (wbc.encountered_congestion)
+                       if (wbc.encountered_congestion || wbc.more_io)
                                congestion_wait(WRITE, HZ/10);
                        else
                                break;  /* All the old data is written */
@@ -439,15 +648,13 @@ static void wb_kupdate(unsigned long arg)
  * sysctl handler for /proc/sys/vm/dirty_writeback_centisecs
  */
 int dirty_writeback_centisecs_handler(ctl_table *table, int write,
-               struct file *file, void __user *buffer, size_t *length, loff_t *ppos)
+       struct file *file, void __user *buffer, size_t *length, loff_t *ppos)
 {
        proc_dointvec_userhz_jiffies(table, write, file, buffer, length, ppos);
-       if (dirty_writeback_interval) {
-               mod_timer(&wb_timer,
-                       jiffies + dirty_writeback_interval);
-               } else {
+       if (dirty_writeback_interval)
+               mod_timer(&wb_timer, jiffies + dirty_writeback_interval);
+       else
                del_timer(&wb_timer);
-       }
        return 0;
 }
 
@@ -517,7 +724,7 @@ static int __cpuinit
 ratelimit_handler(struct notifier_block *self, unsigned long u, void *v)
 {
        writeback_set_ratelimit();
-       return 0;
+       return NOTIFY_DONE;
 }
 
 static struct notifier_block __cpuinitdata ratelimit_nb = {
@@ -526,61 +733,58 @@ static struct notifier_block __cpuinitdata ratelimit_nb = {
 };
 
 /*
- * If the machine has a large highmem:lowmem ratio then scale back the default
- * dirty memory thresholds: allowing too much dirty highmem pins an excessive
- * number of buffer_heads.
+ * Called early on to tune the page writeback dirty limits.
+ *
+ * We used to scale dirty pages according to how total memory
+ * related to pages that could be allocated for buffers (by
+ * comparing nr_free_buffer_pages() to vm_total_pages.
+ *
+ * However, that was when we used "dirty_ratio" to scale with
+ * all memory, and we don't do that any more. "dirty_ratio"
+ * is now applied to total non-HIGHPAGE memory (by subtracting
+ * totalhigh_pages from vm_total_pages), and as such we can't
+ * get into the old insane situation any more where we had
+ * large amounts of dirty pages compared to a small amount of
+ * non-HIGHMEM memory.
+ *
+ * But we might still want to scale the dirty_ratio by how
+ * much memory the box has..
  */
 void __init page_writeback_init(void)
 {
-       long buffer_pages = nr_free_buffer_pages();
-       long correction;
-
-       correction = (100 * 4 * buffer_pages) / vm_total_pages;
+       int shift;
 
-       if (correction < 100) {
-               dirty_background_ratio *= correction;
-               dirty_background_ratio /= 100;
-               vm_dirty_ratio *= correction;
-               vm_dirty_ratio /= 100;
-
-               if (dirty_background_ratio <= 0)
-                       dirty_background_ratio = 1;
-               if (vm_dirty_ratio <= 0)
-                       vm_dirty_ratio = 1;
-       }
        mod_timer(&wb_timer, jiffies + dirty_writeback_interval);
        writeback_set_ratelimit();
        register_cpu_notifier(&ratelimit_nb);
+
+       shift = calc_period_shift();
+       prop_descriptor_init(&vm_completions, shift);
+       prop_descriptor_init(&vm_dirties, shift);
 }
 
 /**
- * generic_writepages - walk the list of dirty pages of the given
- *                      address space and writepage() all of them.
- *
+ * write_cache_pages - walk the list of dirty pages of the given address space and write all of them.
  * @mapping: address space structure to write
  * @wbc: subtract the number of written pages from *@wbc->nr_to_write
+ * @writepage: function called for each page
+ * @data: data passed to writepage function
  *
- * This is a library function, which implements the writepages()
- * address_space_operation.
- *
- * If a page is already under I/O, generic_writepages() skips it, even
+ * If a page is already under I/O, write_cache_pages() skips it, even
  * if it's dirty.  This is desirable behaviour for memory-cleaning writeback,
  * but it is INCORRECT for data-integrity system calls such as fsync().  fsync()
  * and msync() need to guarantee that all the data which was dirty at the time
  * the call was made get new I/O started against them.  If wbc->sync_mode is
  * WB_SYNC_ALL then we were called for data integrity and we must wait for
  * existing IO to complete.
- *
- * Derived from mpage_writepages() - if you fix this you should check that
- * also!
  */
-int generic_writepages(struct address_space *mapping,
-                      struct writeback_control *wbc)
+int write_cache_pages(struct address_space *mapping,
+                     struct writeback_control *wbc, writepage_t writepage,
+                     void *data)
 {
        struct backing_dev_info *bdi = mapping->backing_dev_info;
        int ret = 0;
        int done = 0;
-       int (*writepage)(struct page *page, struct writeback_control *wbc);
        struct pagevec pvec;
        int nr_pages;
        pgoff_t index;
@@ -593,12 +797,6 @@ int generic_writepages(struct address_space *mapping,
                return 0;
        }
 
-       writepage = mapping->a_ops->writepage;
-
-       /* deal with chardevs and other special file */
-       if (!writepage)
-               return 0;
-
        pagevec_init(&pvec, 0);
        if (wbc->range_cyclic) {
                index = mapping->writeback_index; /* Start from prev offset */
@@ -650,16 +848,12 @@ retry:
                                continue;
                        }
 
-                       ret = (*writepage)(page, wbc);
-                       if (ret) {
-                               if (ret == -ENOSPC)
-                                       set_bit(AS_ENOSPC, &mapping->flags);
-                               else
-                                       set_bit(AS_EIO, &mapping->flags);
-                       }
+                       ret = (*writepage)(page, wbc, data);
 
-                       if (unlikely(ret == AOP_WRITEPAGE_ACTIVATE))
+                       if (unlikely(ret == AOP_WRITEPAGE_ACTIVATE)) {
                                unlock_page(page);
+                               ret = 0;
+                       }
                        if (ret || (--(wbc->nr_to_write) <= 0))
                                done = 1;
                        if (wbc->nonblocking && bdi_write_congested(bdi)) {
@@ -683,6 +877,38 @@ retry:
                mapping->writeback_index = index;
        return ret;
 }
+EXPORT_SYMBOL(write_cache_pages);
+
+/*
+ * Function used by generic_writepages to call the real writepage
+ * function and set the mapping flags on error
+ */
+static int __writepage(struct page *page, struct writeback_control *wbc,
+                      void *data)
+{
+       struct address_space *mapping = data;
+       int ret = mapping->a_ops->writepage(page, wbc);
+       mapping_set_error(mapping, ret);
+       return ret;
+}
+
+/**
+ * generic_writepages - walk the list of dirty pages of the given address space and writepage() all of them.
+ * @mapping: address space structure to write
+ * @wbc: subtract the number of written pages from *@wbc->nr_to_write
+ *
+ * This is a library function, which implements the writepages()
+ * address_space_operation.
+ */
+int generic_writepages(struct address_space *mapping,
+                      struct writeback_control *wbc)
+{
+       /* deal with chardevs and other special file */
+       if (!mapping->a_ops->writepage)
+               return 0;
+
+       return write_cache_pages(mapping, wbc, __writepage, mapping);
+}
 
 EXPORT_SYMBOL(generic_writepages);
 
@@ -703,7 +929,6 @@ int do_writepages(struct address_space *mapping, struct writeback_control *wbc)
 
 /**
  * write_one_page - write out a single page and optionally wait on I/O
- *
  * @page: the page to write
  * @wait: if true, wait on writeout
  *
@@ -742,6 +967,16 @@ int write_one_page(struct page *page, int wait)
 EXPORT_SYMBOL(write_one_page);
 
 /*
+ * For address_spaces which do not use buffers nor write back.
+ */
+int __set_page_dirty_no_writeback(struct page *page)
+{
+       if (!PageDirty(page))
+               SetPageDirty(page);
+       return 0;
+}
+
+/*
  * For address_spaces which do not use buffers.  Just tag the page as dirty in
  * its radix tree.
  *
@@ -769,8 +1004,11 @@ int __set_page_dirty_nobuffers(struct page *page)
                mapping2 = page_mapping(page);
                if (mapping2) { /* Race with truncate? */
                        BUG_ON(mapping2 != mapping);
+                       WARN_ON_ONCE(!PagePrivate(page) && !PageUptodate(page));
                        if (mapping_cap_account_dirty(mapping)) {
                                __inc_zone_page_state(page, NR_FILE_DIRTY);
+                               __inc_bdi_stat(mapping->backing_dev_info,
+                                               BDI_RECLAIMABLE);
                                task_io_account_write(PAGE_CACHE_SIZE);
                        }
                        radix_tree_tag_set(&mapping->page_tree,
@@ -803,7 +1041,7 @@ EXPORT_SYMBOL(redirty_page_for_writepage);
  * If the mapping doesn't provide a set_page_dirty a_op, then
  * just fall through and assume that it wants buffer_heads.
  */
-int fastcall set_page_dirty(struct page *page)
+static int __set_page_dirty(struct page *page)
 {
        struct address_space *mapping = page_mapping(page);
 
@@ -821,6 +1059,14 @@ int fastcall set_page_dirty(struct page *page)
        }
        return 0;
 }
+
+int fastcall set_page_dirty(struct page *page)
+{
+       int ret = __set_page_dirty(page);
+       if (ret)
+               task_dirty_inc(current);
+       return ret;
+}
 EXPORT_SYMBOL(set_page_dirty);
 
 /*
@@ -862,6 +1108,9 @@ int clear_page_dirty_for_io(struct page *page)
 {
        struct address_space *mapping = page_mapping(page);
 
+       BUG_ON(!PageLocked(page));
+
+       ClearPageReclaim(page);
        if (mapping && mapping_cap_account_dirty(mapping)) {
                /*
                 * Yes, Virginia, this is indeed insane.
@@ -887,16 +1136,23 @@ int clear_page_dirty_for_io(struct page *page)
                 * We basically use the page "master dirty bit"
                 * as a serialization point for all the different
                 * threads doing their things.
-                *
-                * FIXME! We still have a race here: if somebody
-                * adds the page back to the page tables in
-                * between the "page_mkclean()" and the "TestClearPageDirty()",
-                * we might have it mapped without the dirty bit set.
                 */
                if (page_mkclean(page))
                        set_page_dirty(page);
+               /*
+                * We carefully synchronise fault handlers against
+                * installing a dirty pte and marking the page dirty
+                * at this point. We do this by having them hold the
+                * page lock at some point after installing their
+                * pte, but before marking the page dirty.
+                * Pages are always locked coming in here, so we get
+                * the desired exclusion. See mm/memory.c:do_wp_page()
+                * for more comments.
+                */
                if (TestClearPageDirty(page)) {
                        dec_zone_page_state(page, NR_FILE_DIRTY);
+                       dec_bdi_stat(mapping->backing_dev_info,
+                                       BDI_RECLAIMABLE);
                        return 1;
                }
                return 0;
@@ -911,18 +1167,26 @@ int test_clear_page_writeback(struct page *page)
        int ret;
 
        if (mapping) {
+               struct backing_dev_info *bdi = mapping->backing_dev_info;
                unsigned long flags;
 
                write_lock_irqsave(&mapping->tree_lock, flags);
                ret = TestClearPageWriteback(page);
-               if (ret)
+               if (ret) {
                        radix_tree_tag_clear(&mapping->page_tree,
                                                page_index(page),
                                                PAGECACHE_TAG_WRITEBACK);
+                       if (bdi_cap_writeback_dirty(bdi)) {
+                               __dec_bdi_stat(bdi, BDI_WRITEBACK);
+                               __bdi_writeout_inc(bdi);
+                       }
+               }
                write_unlock_irqrestore(&mapping->tree_lock, flags);
        } else {
                ret = TestClearPageWriteback(page);
        }
+       if (ret)
+               dec_zone_page_state(page, NR_WRITEBACK);
        return ret;
 }
 
@@ -932,14 +1196,18 @@ int test_set_page_writeback(struct page *page)
        int ret;
 
        if (mapping) {
+               struct backing_dev_info *bdi = mapping->backing_dev_info;
                unsigned long flags;
 
                write_lock_irqsave(&mapping->tree_lock, flags);
                ret = TestSetPageWriteback(page);
-               if (!ret)
+               if (!ret) {
                        radix_tree_tag_set(&mapping->page_tree,
                                                page_index(page),
                                                PAGECACHE_TAG_WRITEBACK);
+                       if (bdi_cap_writeback_dirty(bdi))
+                               __inc_bdi_stat(bdi, BDI_WRITEBACK);
+               }
                if (!PageDirty(page))
                        radix_tree_tag_clear(&mapping->page_tree,
                                                page_index(page),
@@ -948,23 +1216,23 @@ int test_set_page_writeback(struct page *page)
        } else {
                ret = TestSetPageWriteback(page);
        }
+       if (!ret)
+               inc_zone_page_state(page, NR_WRITEBACK);
        return ret;
 
 }
 EXPORT_SYMBOL(test_set_page_writeback);
 
 /*
- * Return true if any of the pages in the mapping are marged with the
+ * Return true if any of the pages in the mapping are marked with the
  * passed tag.
  */
 int mapping_tagged(struct address_space *mapping, int tag)
 {
-       unsigned long flags;
        int ret;
-
-       read_lock_irqsave(&mapping->tree_lock, flags);
+       rcu_read_lock();
        ret = radix_tree_tagged(&mapping->page_tree, tag);
-       read_unlock_irqrestore(&mapping->tree_lock, flags);
+       rcu_read_unlock();
        return ret;
 }
 EXPORT_SYMBOL(mapping_tagged);