Add a write-only module parameter 'flush' that triggers immediate
page reporting.  The value specifies a page budget: at least
this many pages (at page_reporting_order) will be reported,
or all unreported pages if fewer remain.  The actual number
reported may exceed the budget since each reporting pass
processes a full cycle across all zones.

This is helpful when there is a lot of memory freed quickly,
and a single cycle may not process all free pages due to
internal budget limits.

  echo 512 > /sys/module/page_reporting/parameters/flush

Note: the set callback runs under kernel param_lock,
so writing this parameter blocks other built-in parameter
writes until the flush loop completes.  This is acceptable
for a privileged debug/test parameter.
Signed-off-by: Michael S. Tsirkin <[email protected]>
Assisted-by: Claude:claude-opus-4-6
Assisted-by: cursor-agent:GPT-5.4-xhigh
---
 mm/page_reporting.c | 54 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/mm/page_reporting.c b/mm/page_reporting.c
index 691a9e66aa5b..8b278a494ea5 100644
--- a/mm/page_reporting.c
+++ b/mm/page_reporting.c
@@ -358,6 +358,60 @@ static void page_reporting_process(struct work_struct 
*work)
 static DEFINE_MUTEX(page_reporting_mutex);
 DEFINE_STATIC_KEY_FALSE(page_reporting_enabled);
 
+static int page_reporting_flush_set(const char *val,
+                                   const struct kernel_param *kp)
+{
+       struct page_reporting_dev_info *prdev;
+       unsigned int budget;
+       int err;
+
+       err = kstrtouint(val, 0, &budget);
+       if (err)
+               return err;
+       if (!budget)
+               return 0;
+
+       mutex_lock(&page_reporting_mutex);
+       prdev = rcu_dereference_protected(pr_dev_info,
+                               lockdep_is_held(&page_reporting_mutex));
+       if (prdev) {
+               unsigned int reported;
+               bool interrupted = false;
+
+               for (reported = 0; reported < budget;
+                    reported += min(prdev->capacity, budget - reported)) {
+                       /*
+                        * First flush completes any previously scheduled
+                        * reporting work.  Then request a new reporting
+                        * cycle and flush again to execute it.
+                        */
+                       flush_delayed_work(&prdev->work);
+                       __page_reporting_request(prdev);
+                       flush_delayed_work(&prdev->work);
+                       if (atomic_read(&prdev->state) == PAGE_REPORTING_IDLE)
+                               break;
+                       if (signal_pending(current)) {
+                               interrupted = true;
+                               break;
+                       }
+               }
+               if (interrupted) {
+                       mutex_unlock(&page_reporting_mutex);
+                       return -EINTR;
+               }
+       }
+       mutex_unlock(&page_reporting_mutex);
+       return 0;
+}
+
+static const struct kernel_param_ops flush_ops = {
+       .set = page_reporting_flush_set,
+       .get = param_get_uint,
+};
+static unsigned int page_reporting_flush;
+module_param_cb(flush, &flush_ops, &page_reporting_flush, 0200);
+MODULE_PARM_DESC(flush, "Report at least N pages at page_reporting_order, or 
until all reported");
+
 int page_reporting_register(struct page_reporting_dev_info *prdev)
 {
        int err = 0;
-- 
MST


Reply via email to