mirror of
https://github.com/torvalds/linux
synced 2024-11-02 18:48:59 +00:00
kfence: allow use of a deferrable timer
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>
This commit is contained in:
parent
3cb1c9620e
commit
737b6a10ac
3 changed files with 37 additions and 2 deletions
|
@ -41,6 +41,18 @@ guarded by KFENCE. The default is configurable via the Kconfig option
|
||||||
``CONFIG_KFENCE_SAMPLE_INTERVAL``. Setting ``kfence.sample_interval=0``
|
``CONFIG_KFENCE_SAMPLE_INTERVAL``. Setting ``kfence.sample_interval=0``
|
||||||
disables KFENCE.
|
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
|
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
|
further KFENCE allocations occur. With ``CONFIG_KFENCE_NUM_OBJECTS`` (default
|
||||||
255), the number of available guarded objects can be controlled. Each object
|
255), the number of available guarded objects can be controlled. Each object
|
||||||
|
|
|
@ -45,6 +45,18 @@ config KFENCE_NUM_OBJECTS
|
||||||
pages are required; with one containing the object and two adjacent
|
pages are required; with one containing the object and two adjacent
|
||||||
ones used as guard pages.
|
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
|
config KFENCE_STATIC_KEYS
|
||||||
bool "Use static keys to set up allocations" if EXPERT
|
bool "Use static keys to set up allocations" if EXPERT
|
||||||
depends on JUMP_LABEL
|
depends on JUMP_LABEL
|
||||||
|
|
|
@ -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;
|
static unsigned long kfence_skip_covered_thresh __read_mostly = 75;
|
||||||
module_param_named(skip_covered_thresh, kfence_skip_covered_thresh, ulong, 0644);
|
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. */
|
/* The pool of pages used for guard pages and objects. */
|
||||||
char *__kfence_pool __read_mostly;
|
char *__kfence_pool __read_mostly;
|
||||||
EXPORT_SYMBOL(__kfence_pool); /* Export for test modules. */
|
EXPORT_SYMBOL(__kfence_pool); /* Export for test modules. */
|
||||||
|
@ -740,6 +744,8 @@ late_initcall(kfence_debugfs_init);
|
||||||
|
|
||||||
/* === Allocation Gate Timer ================================================ */
|
/* === Allocation Gate Timer ================================================ */
|
||||||
|
|
||||||
|
static struct delayed_work kfence_timer;
|
||||||
|
|
||||||
#ifdef CONFIG_KFENCE_STATIC_KEYS
|
#ifdef CONFIG_KFENCE_STATIC_KEYS
|
||||||
/* Wait queue to wake up allocation-gate timer task. */
|
/* Wait queue to wake up allocation-gate timer task. */
|
||||||
static DECLARE_WAIT_QUEUE_HEAD(allocation_wait);
|
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
|
* avoids IPIs, at the cost of not immediately capturing allocations if the
|
||||||
* instructions remain cached.
|
* instructions remain cached.
|
||||||
*/
|
*/
|
||||||
static struct delayed_work kfence_timer;
|
|
||||||
static void toggle_allocation_gate(struct work_struct *work)
|
static void toggle_allocation_gate(struct work_struct *work)
|
||||||
{
|
{
|
||||||
if (!READ_ONCE(kfence_enabled))
|
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,
|
queue_delayed_work(system_unbound_wq, &kfence_timer,
|
||||||
msecs_to_jiffies(kfence_sample_interval));
|
msecs_to_jiffies(kfence_sample_interval));
|
||||||
}
|
}
|
||||||
static DECLARE_DELAYED_WORK(kfence_timer, toggle_allocation_gate);
|
|
||||||
|
|
||||||
/* === Public interface ===================================================== */
|
/* === Public interface ===================================================== */
|
||||||
|
|
||||||
|
@ -809,8 +813,15 @@ static void kfence_init_enable(void)
|
||||||
{
|
{
|
||||||
if (!IS_ENABLED(CONFIG_KFENCE_STATIC_KEYS))
|
if (!IS_ENABLED(CONFIG_KFENCE_STATIC_KEYS))
|
||||||
static_branch_enable(&kfence_allocation_key);
|
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);
|
WRITE_ONCE(kfence_enabled, true);
|
||||||
queue_delayed_work(system_unbound_wq, &kfence_timer, 0);
|
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,
|
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,
|
CONFIG_KFENCE_NUM_OBJECTS, (void *)__kfence_pool,
|
||||||
(void *)(__kfence_pool + KFENCE_POOL_SIZE));
|
(void *)(__kfence_pool + KFENCE_POOL_SIZE));
|
||||||
|
|
Loading…
Reference in a new issue