kernel: Fix massive cpufreq stats memory leaks
authorSultan Alsawaf <sultanxda@gmail.com>
Sun, 3 Jun 2018 17:47:51 +0000 (10:47 -0700)
committerDanny Wood <danwood76@gmail.com>
Mon, 8 Apr 2019 09:21:25 +0000 (10:21 +0100)
Every time _cpu_up() is called for a CPU, idle_thread_get() is called which
then re-initializes a CPU's idle thread that was already previously created
and cached in a global variable in smpboot.c. idle_thread_get() calls
init_idle() which then calls __sched_fork(). __sched_fork() is where
cpufreq_task_stats_init() is, and cpufreq_task_stats_init() allocates
512 bytes of memory to a pointer in the task struct.

Since idle_thread_get() reuses a task struct instance that was already
previously created, this means that every time it calls init_idle(),
cpufreq_task_stats_init() allocates 512 bytes again and overwrites the
existing 512-byte allocation that the idle thread already had.

This causes 512 bytes to be leaked every time a CPU is onlined. This is
significant when non-boot CPUs are enabled during resume from suspend; this
means that (NR_CPUS - 1) * 512 bytes are leaked every time the device exits
suspend (this turned out to be ~500 kiB leaked in 20 minutes with the
device left on a desk with the screen off).

In order to fix this, don't initialize cpufreq stats at all for the idle
threads. The cpufreq stats interface is intended to be used for tracking
userspace tasks, so we can safely remove it from the kernel's idle threads
without killing any functionality.

Change-Id: I12fe7611fc88eb7f6c39f8f7629ad27b6ec4722c
Signed-off-by: Sultan Alsawaf <sultanxda@gmail.com>
kernel/sched/core.c

index 517bdbf02d110ff759942ad4e35bcdc9015a8821..7bf72e0c3c13fda08202df28a8ff0ea1df023bd1 100644 (file)
@@ -1738,10 +1738,6 @@ static void __sched_fork(struct task_struct *p)
        memset(&p->se.statistics, 0, sizeof(p->se.statistics));
 #endif
 
-#ifdef CONFIG_CPU_FREQ_STAT
-       cpufreq_task_stats_init(p);
-#endif
-
        INIT_LIST_HEAD(&p->rt.run_list);
 
 #ifdef CONFIG_PREEMPT_NOTIFIERS
@@ -1790,6 +1786,10 @@ void sched_fork(struct task_struct *p)
        unsigned long flags;
        int cpu = get_cpu();
 
+#ifdef CONFIG_CPU_FREQ_STAT
+       cpufreq_task_stats_init(p);
+#endif
+
        __sched_fork(p);
        /*
         * We mark the process as running here. This guarantees that