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 |
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 --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));
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(-)