]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
kfence: allow use of a deferrable timer
authorMarco Elver <elver@google.com>
Tue, 22 Mar 2022 21:48:25 +0000 (14:48 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 13 Mar 2025 11:50:49 +0000 (12:50 +0100)
[ Upstream commit 737b6a10ac19e41877aa1911bf6b361a72a88ad9 ]

Allow the use of a deferrable timer, which does not force CPU wake-ups
when the system is idle.  A consequence is that the sample interval
becomes very unpredictable, to the point that it is not guaranteed that
the KFENCE KUnit test still passes.

Nevertheless, on power-constrained systems this may be preferable, so
let's give the user the option should they accept the above trade-off.

Link: https://lkml.kernel.org/r/20220308141415.3168078-1-elver@google.com
Signed-off-by: Marco Elver <elver@google.com>
Reviewed-by: Alexander Potapenko <glider@google.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Stable-dep-of: e64f81946adf ("kfence: skip __GFP_THISNODE allocations on NUMA systems")
Signed-off-by: Sasha Levin <sashal@kernel.org>
Documentation/dev-tools/kfence.rst
lib/Kconfig.kfence
mm/kfence/core.c

index 48244d32780f60c90b2fa599824f6d2f7b3a1fd0..b6671d907836ab93518e52b6a2aaf03e363b63a2 100644 (file)
@@ -41,6 +41,18 @@ guarded by KFENCE. The default is configurable via the Kconfig option
 ``CONFIG_KFENCE_SAMPLE_INTERVAL``. Setting ``kfence.sample_interval=0``
 disables KFENCE.
 
+The sample interval controls a timer that sets up KFENCE allocations. By
+default, to keep the real sample interval predictable, the normal timer also
+causes CPU wake-ups when the system is completely idle. This may be undesirable
+on power-constrained systems. The boot parameter ``kfence.deferrable=1``
+instead switches to a "deferrable" timer which does not force CPU wake-ups on
+idle systems, at the risk of unpredictable sample intervals. The default is
+configurable via the Kconfig option ``CONFIG_KFENCE_DEFERRABLE``.
+
+.. warning::
+   The KUnit test suite is very likely to fail when using a deferrable timer
+   since it currently causes very unpredictable sample intervals.
+
 The KFENCE memory pool is of fixed size, and if the pool is exhausted, no
 further KFENCE allocations occur. With ``CONFIG_KFENCE_NUM_OBJECTS`` (default
 255), the number of available guarded objects can be controlled. Each object
index 912f252a41fc63744b168ddb43dcd42aec07433e..459dda9ef619b872f8bdca63131a311a7ca601bd 100644 (file)
@@ -45,6 +45,18 @@ config KFENCE_NUM_OBJECTS
          pages are required; with one containing the object and two adjacent
          ones used as guard pages.
 
+config KFENCE_DEFERRABLE
+       bool "Use a deferrable timer to trigger allocations"
+       help
+         Use a deferrable timer to trigger allocations. This avoids forcing
+         CPU wake-ups if the system is idle, at the risk of a less predictable
+         sample interval.
+
+         Warning: The KUnit test suite fails with this option enabled - due to
+         the unpredictability of the sample interval!
+
+         Say N if you are unsure.
+
 config KFENCE_STATIC_KEYS
        bool "Use static keys to set up allocations" if EXPERT
        depends on JUMP_LABEL
index 3eab72fb3d8c9a7627da7d916f74c12b9ce1f0d2..1a2dc187a72f9c2ecd8654fc54cda0380b9c05f0 100644 (file)
@@ -89,6 +89,10 @@ module_param_cb(sample_interval, &sample_interval_param_ops, &kfence_sample_inte
 static unsigned long kfence_skip_covered_thresh __read_mostly = 75;
 module_param_named(skip_covered_thresh, kfence_skip_covered_thresh, ulong, 0644);
 
+/* If true, use a deferrable timer. */
+static bool kfence_deferrable __read_mostly = IS_ENABLED(CONFIG_KFENCE_DEFERRABLE);
+module_param_named(deferrable, kfence_deferrable, bool, 0444);
+
 /* The pool of pages used for guard pages and objects. */
 char *__kfence_pool __ro_after_init;
 EXPORT_SYMBOL(__kfence_pool); /* Export for test modules. */
@@ -695,6 +699,8 @@ late_initcall(kfence_debugfs_init);
 
 /* === Allocation Gate Timer ================================================ */
 
+static struct delayed_work kfence_timer;
+
 #ifdef CONFIG_KFENCE_STATIC_KEYS
 /* Wait queue to wake up allocation-gate timer task. */
 static DECLARE_WAIT_QUEUE_HEAD(allocation_wait);
@@ -717,7 +723,6 @@ static DEFINE_IRQ_WORK(wake_up_kfence_timer_work, wake_up_kfence_timer);
  * avoids IPIs, at the cost of not immediately capturing allocations if the
  * instructions remain cached.
  */
-static struct delayed_work kfence_timer;
 static void toggle_allocation_gate(struct work_struct *work)
 {
        if (!READ_ONCE(kfence_enabled))
@@ -745,7 +750,6 @@ static void toggle_allocation_gate(struct work_struct *work)
        queue_delayed_work(system_unbound_wq, &kfence_timer,
                           msecs_to_jiffies(kfence_sample_interval));
 }
-static DECLARE_DELAYED_WORK(kfence_timer, toggle_allocation_gate);
 
 /* === Public interface ===================================================== */
 
@@ -774,8 +778,15 @@ void __init kfence_init(void)
 
        if (!IS_ENABLED(CONFIG_KFENCE_STATIC_KEYS))
                static_branch_enable(&kfence_allocation_key);
+
+       if (kfence_deferrable)
+               INIT_DEFERRABLE_WORK(&kfence_timer, toggle_allocation_gate);
+       else
+               INIT_DELAYED_WORK(&kfence_timer, toggle_allocation_gate);
+
        WRITE_ONCE(kfence_enabled, true);
        queue_delayed_work(system_unbound_wq, &kfence_timer, 0);
+
        pr_info("initialized - using %lu bytes for %d objects at 0x%p-0x%p\n", KFENCE_POOL_SIZE,
                CONFIG_KFENCE_NUM_OBJECTS, (void *)__kfence_pool,
                (void *)(__kfence_pool + KFENCE_POOL_SIZE));