]> git.ipfire.org Git - thirdparty/kernel/stable.git/blobdiff - mm/page-writeback.c
Merge tag 'mm-stable-2024-05-17-19-19' of git://git.kernel.org/pub/scm/linux/kernel...
[thirdparty/kernel/stable.git] / mm / page-writeback.c
index 06fc89d981e8b59cf9ff24727297ab2be46ac632..12c9297ed4a7fcf2b04e9683754a41330f979dc1 100644 (file)
@@ -838,13 +838,15 @@ static void mdtc_calc_avail(struct dirty_throttle_control *mdtc,
 }
 
 /**
- * __wb_calc_thresh - @wb's share of dirty throttling threshold
+ * __wb_calc_thresh - @wb's share of dirty threshold
  * @dtc: dirty_throttle_context of interest
+ * @thresh: dirty throttling or dirty background threshold of wb_domain in @dtc
  *
- * Note that balance_dirty_pages() will only seriously take it as a hard limit
- * when sleeping max_pause per page is not enough to keep the dirty pages under
- * control. For example, when the device is completely stalled due to some error
- * conditions, or when there are 1000 dd tasks writing to a slow 10MB/s USB key.
+ * Note that balance_dirty_pages() will only seriously take dirty throttling
+ * threshold as a hard limit when sleeping max_pause per page is not enough
+ * to keep the dirty pages under control. For example, when the device is
+ * completely stalled due to some error conditions, or when there are 1000
+ * dd tasks writing to a slow 10MB/s USB key.
  * In the other normal situations, it acts more gently by throttling the tasks
  * more (rather than completely block them) when the wb dirty pages go high.
  *
@@ -855,19 +857,20 @@ static void mdtc_calc_avail(struct dirty_throttle_control *mdtc,
  * The wb's share of dirty limit will be adapting to its throughput and
  * bounded by the bdi->min_ratio and/or bdi->max_ratio parameters, if set.
  *
- * Return: @wb's dirty limit in pages. The term "dirty" in the context of
- * dirty balancing includes all PG_dirty and PG_writeback pages.
+ * Return: @wb's dirty limit in pages. For dirty throttling limit, the term
+ * "dirty" in the context of dirty balancing includes all PG_dirty and
+ * PG_writeback pages.
  */
-static unsigned long __wb_calc_thresh(struct dirty_throttle_control *dtc)
+static unsigned long __wb_calc_thresh(struct dirty_throttle_control *dtc,
+                                     unsigned long thresh)
 {
        struct wb_domain *dom = dtc_dom(dtc);
-       unsigned long thresh = dtc->thresh;
        u64 wb_thresh;
        unsigned long numerator, denominator;
        unsigned long wb_min_ratio, wb_max_ratio;
 
        /*
-        * Calculate this BDI's share of the thresh ratio.
+        * Calculate this wb's share of the thresh ratio.
         */
        fprop_fraction_percpu(&dom->completions, dtc->wb_completions,
                              &numerator, &denominator);
@@ -887,9 +890,28 @@ static unsigned long __wb_calc_thresh(struct dirty_throttle_control *dtc)
 
 unsigned long wb_calc_thresh(struct bdi_writeback *wb, unsigned long thresh)
 {
-       struct dirty_throttle_control gdtc = { GDTC_INIT(wb),
-                                              .thresh = thresh };
-       return __wb_calc_thresh(&gdtc);
+       struct dirty_throttle_control gdtc = { GDTC_INIT(wb) };
+
+       return __wb_calc_thresh(&gdtc, thresh);
+}
+
+unsigned long cgwb_calc_thresh(struct bdi_writeback *wb)
+{
+       struct dirty_throttle_control gdtc = { GDTC_INIT_NO_WB };
+       struct dirty_throttle_control mdtc = { MDTC_INIT(wb, &gdtc) };
+       unsigned long filepages = 0, headroom = 0, writeback = 0;
+
+       gdtc.avail = global_dirtyable_memory();
+       gdtc.dirty = global_node_page_state(NR_FILE_DIRTY) +
+                    global_node_page_state(NR_WRITEBACK);
+
+       mem_cgroup_wb_stats(wb, &filepages, &headroom,
+                           &mdtc.dirty, &writeback);
+       mdtc.dirty += writeback;
+       mdtc_calc_avail(&mdtc, filepages, headroom);
+       domain_dirty_limits(&mdtc);
+
+       return __wb_calc_thresh(&mdtc, mdtc.thresh);
 }
 
 /*
@@ -1636,7 +1658,7 @@ static inline void wb_dirty_limits(struct dirty_throttle_control *dtc)
         *   wb_position_ratio() will let the dirtier task progress
         *   at some rate <= (write_bw / 2) for bringing down wb_dirty.
         */
-       dtc->wb_thresh = __wb_calc_thresh(dtc);
+       dtc->wb_thresh = __wb_calc_thresh(dtc, dtc->thresh);
        dtc->wb_bg_thresh = dtc->thresh ?
                div64_u64(dtc->wb_thresh * dtc->bg_thresh, dtc->thresh) : 0;
 
@@ -1675,7 +1697,7 @@ static int balance_dirty_pages(struct bdi_writeback *wb,
        struct dirty_throttle_control * const mdtc = mdtc_valid(&mdtc_stor) ?
                                                     &mdtc_stor : NULL;
        struct dirty_throttle_control *sdtc;
-       unsigned long nr_reclaimable;   /* = file_dirty */
+       unsigned long nr_dirty;
        long period;
        long pause;
        long max_pause;
@@ -1696,9 +1718,9 @@ static int balance_dirty_pages(struct bdi_writeback *wb,
                unsigned long m_thresh = 0;
                unsigned long m_bg_thresh = 0;
 
-               nr_reclaimable = global_node_page_state(NR_FILE_DIRTY);
+               nr_dirty = global_node_page_state(NR_FILE_DIRTY);
                gdtc->avail = global_dirtyable_memory();
-               gdtc->dirty = nr_reclaimable + global_node_page_state(NR_WRITEBACK);
+               gdtc->dirty = nr_dirty + global_node_page_state(NR_WRITEBACK);
 
                domain_dirty_limits(gdtc);
 
@@ -1749,7 +1771,7 @@ static int balance_dirty_pages(struct bdi_writeback *wb,
                 * In normal mode, we start background writeout at the lower
                 * background_thresh, to keep the amount of dirty memory low.
                 */
-               if (!laptop_mode && nr_reclaimable > gdtc->bg_thresh &&
+               if (!laptop_mode && nr_dirty > gdtc->bg_thresh &&
                    !writeback_in_progress(wb))
                        wb_start_background_writeback(wb);
 
@@ -2095,7 +2117,7 @@ bool wb_over_bg_thresh(struct bdi_writeback *wb)
        if (gdtc->dirty > gdtc->bg_thresh)
                return true;
 
-       thresh = wb_calc_thresh(gdtc->wb, gdtc->bg_thresh);
+       thresh = __wb_calc_thresh(gdtc, gdtc->bg_thresh);
        if (thresh < 2 * wb_stat_error())
                reclaimable = wb_stat_sum(wb, WB_RECLAIMABLE);
        else
@@ -2115,7 +2137,7 @@ bool wb_over_bg_thresh(struct bdi_writeback *wb)
                if (mdtc->dirty > mdtc->bg_thresh)
                        return true;
 
-               thresh = wb_calc_thresh(mdtc->wb, mdtc->bg_thresh);
+               thresh = __wb_calc_thresh(mdtc, mdtc->bg_thresh);
                if (thresh < 2 * wb_stat_error())
                        reclaimable = wb_stat_sum(wb, WB_RECLAIMABLE);
                else
@@ -2291,7 +2313,6 @@ static struct ctl_table vm_page_writeback_sysctls[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec_jiffies,
        },
-       {}
 };
 #endif
 
@@ -2701,17 +2722,20 @@ void folio_account_cleaned(struct folio *folio, struct bdi_writeback *wb)
 }
 
 /*
- * Mark the folio dirty, and set it dirty in the page cache, and mark
- * the inode dirty.
+ * Mark the folio dirty, and set it dirty in the page cache.
  *
  * If warn is true, then emit a warning if the folio is not uptodate and has
  * not been truncated.
  *
- * The caller must hold folio_memcg_lock().  Most callers have the folio
- * locked.  A few have the folio blocked from truncation through other
- * means (eg zap_vma_pages() has it mapped and is holding the page table
- * lock).  This can also be called from mark_buffer_dirty(), which I
- * cannot prove is always protected against truncate.
+ * The caller must hold folio_memcg_lock().  It is the caller's
+ * responsibility to prevent the folio from being truncated while
+ * this function is in progress, although it may have been truncated
+ * before this function is called.  Most callers have the folio locked.
+ * A few have the folio blocked from truncation through other means (e.g.
+ * zap_vma_pages() has it mapped and is holding the page table lock).
+ * When called from mark_buffer_dirty(), the filesystem should hold a
+ * reference to the buffer_head that is being marked dirty, which causes
+ * try_to_free_buffers() to fail.
  */
 void __folio_mark_dirty(struct folio *folio, struct address_space *mapping,
                             int warn)