diff mbox series

[v2] kfence: allow use of a deferrable timer

Message ID 20220308141415.3168078-1-elver@google.com (mailing list archive)
State New
Headers show
Series [v2] kfence: allow use of a deferrable timer | expand

Commit Message

Marco Elver March 8, 2022, 2:14 p.m. UTC
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.

Signed-off-by: Marco Elver <elver@google.com>
---
v2:
* Add more documentation.
* Remove 'if EXPERT' from Kconfig option since it's configurable via
  kernel boot param anyway.
---
 Documentation/dev-tools/kfence.rst | 12 ++++++++++++
 lib/Kconfig.kfence                 | 12 ++++++++++++
 mm/kfence/core.c                   | 15 +++++++++++++--
 3 files changed, 37 insertions(+), 2 deletions(-)

Comments

Alexander Potapenko March 8, 2022, 2:26 p.m. UTC | #1
On Tue, Mar 8, 2022 at 3:14 PM Marco Elver <elver@google.com> wrote:

> 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.
>
> Signed-off-by: Marco Elver <elver@google.com>
>
Reviewed-by: Alexander Potapenko <glider@google.com>

> ---
> v2:
> * Add more documentation.
> * Remove 'if EXPERT' from Kconfig option since it's configurable via
>   kernel boot param anyway.
> ---
>  Documentation/dev-tools/kfence.rst | 12 ++++++++++++
>  lib/Kconfig.kfence                 | 12 ++++++++++++
>  mm/kfence/core.c                   | 15 +++++++++++++--
>  3 files changed, 37 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/dev-tools/kfence.rst
> b/Documentation/dev-tools/kfence.rst
> index ac6b89d1a8c3..936f6aaa75c8 100644
> --- a/Documentation/dev-tools/kfence.rst
> +++ b/Documentation/dev-tools/kfence.rst
> @@ -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
> diff --git a/lib/Kconfig.kfence b/lib/Kconfig.kfence
> index 912f252a41fc..459dda9ef619 100644
> --- a/lib/Kconfig.kfence
> +++ b/lib/Kconfig.kfence
> @@ -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
> diff --git a/mm/kfence/core.c b/mm/kfence/core.c
> index f126b53b9b85..2f9fdfde1941 100644
> --- a/mm/kfence/core.c
> +++ b/mm/kfence/core.c
> @@ -95,6 +95,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 __read_mostly;
>  EXPORT_SYMBOL(__kfence_pool); /* Export for test modules. */
> @@ -740,6 +744,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);
> @@ -762,7 +768,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))
> @@ -790,7 +795,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
> ===================================================== */
>
> @@ -809,8 +813,15 @@ static void kfence_init_enable(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));
> --
> 2.35.1.616.g0bdcbb4464-goog
>
>
diff mbox series

Patch

diff --git a/Documentation/dev-tools/kfence.rst b/Documentation/dev-tools/kfence.rst
index ac6b89d1a8c3..936f6aaa75c8 100644
--- a/Documentation/dev-tools/kfence.rst
+++ b/Documentation/dev-tools/kfence.rst
@@ -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
diff --git a/lib/Kconfig.kfence b/lib/Kconfig.kfence
index 912f252a41fc..459dda9ef619 100644
--- a/lib/Kconfig.kfence
+++ b/lib/Kconfig.kfence
@@ -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
diff --git a/mm/kfence/core.c b/mm/kfence/core.c
index f126b53b9b85..2f9fdfde1941 100644
--- a/mm/kfence/core.c
+++ b/mm/kfence/core.c
@@ -95,6 +95,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 __read_mostly;
 EXPORT_SYMBOL(__kfence_pool); /* Export for test modules. */
@@ -740,6 +744,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);
@@ -762,7 +768,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))
@@ -790,7 +795,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 ===================================================== */
 
@@ -809,8 +813,15 @@  static void kfence_init_enable(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));