* Re: zswap: Delete an error message for a failed memory allocation in zswap_dstmem_prepare()
From: SF Markus Elfring @ 2017-05-21 9:40 UTC (permalink / raw)
To: Wolfram Sang, linux-mm
Cc: Dan Streetman, Seth Jennings, LKML, kernel-janitors
In-Reply-To: <20170521084734.GB1456@katana>
> Markus, can you please stop CCing me on every of those patches?
Yes, of course.
>> Link: http://events.linuxfoundation.org/sites/events/files/slides/LCJ16-Refactor_Strings-WSang_0.pdf
Did I interpret any information from your presentation slides in an
inappropriate way?
> And why do you create a patch for every occasion in the same file?
This can occasionally happen when I am more unsure about the change acceptance
for a specific place.
> Do you want to increase your patch count?
This can also happen as a side effect if such a source code search pattern
will point hundreds of places out for further software development considerations.
How would you prefer to clarify the remaining update candidates there?
Regards,
Markus
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH 3/3] zswap: Delete an error message for a failed memory allocation in zswap_dstmem_prepare()
From: Wolfram Sang @ 2017-05-21 8:47 UTC (permalink / raw)
To: SF Markus Elfring
Cc: linux-mm, Dan Streetman, Seth Jennings, LKML, kernel-janitors
In-Reply-To: <bae25b04-2ce2-7137-a71c-50d7b4f06431@users.sourceforge.net>
[-- Attachment #1: Type: text/plain, Size: 497 bytes --]
Markus, can you please stop CCing me on every of those patches?
> Omit an extra message for a memory allocation failure in this function.
>
> This issue was detected by using the Coccinelle software.
>
> Link: http://events.linuxfoundation.org/sites/events/files/slides/LCJ16-Refactor_Strings-WSang_0.pdf
> Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
And why do you create a patch for every occasion in the same file? Do
you want to increase your patch count?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* [PATCH 3/3] zswap: Delete an error message for a failed memory allocation in zswap_dstmem_prepare()
From: SF Markus Elfring @ 2017-05-21 8:27 UTC (permalink / raw)
To: linux-mm, Dan Streetman, Seth Jennings
Cc: LKML, kernel-janitors, Wolfram Sang
In-Reply-To: <05101843-91f6-3243-18ea-acac8e8ef6af@users.sourceforge.net>
From: Markus Elfring <elfring@users.sourceforge.net>
Date: Sun, 21 May 2017 09:29:25 +0200
Omit an extra message for a memory allocation failure in this function.
This issue was detected by using the Coccinelle software.
Link: http://events.linuxfoundation.org/sites/events/files/slides/LCJ16-Refactor_Strings-WSang_0.pdf
Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
---
mm/zswap.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/mm/zswap.c b/mm/zswap.c
index 3f0a9a1daef4..ed7312291df9 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -374,7 +374,6 @@ static int zswap_dstmem_prepare(unsigned int cpu)
- if (!dst) {
- pr_err("can't allocate compressor buffer\n");
+ if (!dst)
return -ENOMEM;
- }
+
per_cpu(zswap_dstmem, cpu) = dst;
return 0;
}
--
2.13.0
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related
* [PATCH 2/3] zswap: Improve a size determination in zswap_frontswap_init()
From: SF Markus Elfring @ 2017-05-21 8:26 UTC (permalink / raw)
To: linux-mm, Dan Streetman, Seth Jennings; +Cc: LKML, kernel-janitors
In-Reply-To: <05101843-91f6-3243-18ea-acac8e8ef6af@users.sourceforge.net>
From: Markus Elfring <elfring@users.sourceforge.net>
Date: Sat, 20 May 2017 22:44:03 +0200
Replace the specification of a data structure by a pointer dereference
as the parameter for the operator "sizeof" to make the corresponding size
determination a bit safer according to the Linux coding style convention.
Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
---
mm/zswap.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mm/zswap.c b/mm/zswap.c
index 18d8e87119a6..a6e67633be03 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -1156,5 +1156,5 @@ static void zswap_frontswap_init(unsigned type)
{
struct zswap_tree *tree;
- tree = kzalloc(sizeof(struct zswap_tree), GFP_KERNEL);
+ tree = kzalloc(sizeof(*tree), GFP_KERNEL);
if (!tree) {
--
2.13.0
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related
* [PATCH 1/3] zswap: Delete an error message for a failed memory allocation in zswap_pool_create()
From: SF Markus Elfring @ 2017-05-21 8:25 UTC (permalink / raw)
To: linux-mm, Dan Streetman, Seth Jennings
Cc: LKML, kernel-janitors, Wolfram Sang
In-Reply-To: <05101843-91f6-3243-18ea-acac8e8ef6af@users.sourceforge.net>
From: Markus Elfring <elfring@users.sourceforge.net>
Date: Sat, 20 May 2017 22:33:21 +0200
Omit an extra message for a memory allocation failure in this function.
This issue was detected by using the Coccinelle software.
Link: http://events.linuxfoundation.org/sites/events/files/slides/LCJ16-Refactor_Strings-WSang_0.pdf
Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
---
mm/zswap.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/mm/zswap.c b/mm/zswap.c
index eedc27894b10..18d8e87119a6 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -518,7 +518,5 @@ static struct zswap_pool *zswap_pool_create(char *type, char *compressor)
- if (!pool) {
- pr_err("pool alloc failed\n");
+ if (!pool)
return NULL;
- }
/* unique name for each pool specifically required by zsmalloc */
snprintf(name, 38, "zswap%x", atomic_inc_return(&zswap_pools_count));
--
2.13.0
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related
* [PATCH 0/3] zswap: Adjustments for three function implementations
From: SF Markus Elfring @ 2017-05-21 8:23 UTC (permalink / raw)
To: linux-mm, Dan Streetman, Seth Jennings; +Cc: LKML, kernel-janitors
From: Markus Elfring <elfring@users.sourceforge.net>
Date: Sun, 21 May 2017 10:20:03 +0200
Three update suggestions were taken into account
from static source code analysis.
Markus Elfring (3):
Delete an error message for a failed memory allocation in zswap_pool_create()
Improve a size determination in zswap_frontswap_init()
Delete an error message for a failed memory allocation in zswap_dstmem_prepare()
mm/zswap.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
--
2.13.0
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH v5 17/32] x86/mm: Add support to access boot related data in the clear
From: Borislav Petkov @ 2017-05-21 7:16 UTC (permalink / raw)
To: Tom Lendacky
Cc: linux-arch, linux-efi, kvm, linux-doc, x86, kexec, linux-kernel,
kasan-dev, linux-mm, iommu, Rik van Riel,
Radim Krčmář, Toshimitsu Kani, Arnd Bergmann,
Jonathan Corbet, Matt Fleming, Michael S. Tsirkin, Joerg Roedel,
Konrad Rzeszutek Wilk, Paolo Bonzini, Larry Woodman,
Brijesh Singh, Ingo Molnar, Andy Lutomirski, H. Peter Anvin,
Andrey Ryabinin, Alexander Potapenko, Dave Young, Thomas Gleixner,
Dmitry Vyukov
In-Reply-To: <c0cb8a50-e860-169b-ee0c-7eb4db7c3fda@amd.com>
On Fri, May 19, 2017 at 03:50:32PM -0500, Tom Lendacky wrote:
> The "worker" function would be doing the loop through the setup data,
> but since the setup data is mapped inside the loop I can't do the __init
> calling the non-init function and still hope to consolidate the code.
> Maybe I'm missing something here...
Hmm, I see what you mean. But the below change ontop doesn't fire any
warnings here. Maybe your .config has something set which I don't...
---
diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c
index 55317ba3b6dc..199c983192ae 100644
--- a/arch/x86/mm/ioremap.c
+++ b/arch/x86/mm/ioremap.c
@@ -515,71 +515,50 @@ static bool memremap_is_efi_data(resource_size_t phys_addr,
* Examine the physical address to determine if it is boot data by checking
* it against the boot params setup_data chain.
*/
-static bool memremap_is_setup_data(resource_size_t phys_addr,
- unsigned long size)
+static bool
+__memremap_is_setup_data(resource_size_t phys_addr, unsigned long size, bool early)
{
struct setup_data *data;
u64 paddr, paddr_next;
+ u32 len;
paddr = boot_params.hdr.setup_data;
while (paddr) {
- bool is_setup_data = false;
if (phys_addr == paddr)
return true;
- data = memremap(paddr, sizeof(*data),
- MEMREMAP_WB | MEMREMAP_DEC);
+ if (early)
+ data = early_memremap_decrypted(paddr, sizeof(*data));
+ else
+ data = memremap(paddr, sizeof(*data), MEMREMAP_WB | MEMREMAP_DEC);
paddr_next = data->next;
+ len = data->len;
- if ((phys_addr > paddr) && (phys_addr < (paddr + data->len)))
- is_setup_data = true;
+ if (early)
+ early_memunmap(data, sizeof(*data));
+ else
+ memunmap(data);
- memunmap(data);
- if (is_setup_data)
+ if ((phys_addr > paddr) && (phys_addr < (paddr + data->len)))
return true;
paddr = paddr_next;
}
-
return false;
}
-/*
- * Examine the physical address to determine if it is boot data by checking
- * it against the boot params setup_data chain (early boot version).
- */
static bool __init early_memremap_is_setup_data(resource_size_t phys_addr,
unsigned long size)
{
- struct setup_data *data;
- u64 paddr, paddr_next;
-
- paddr = boot_params.hdr.setup_data;
- while (paddr) {
- bool is_setup_data = false;
-
- if (phys_addr == paddr)
- return true;
-
- data = early_memremap_decrypted(paddr, sizeof(*data));
-
- paddr_next = data->next;
-
- if ((phys_addr > paddr) && (phys_addr < (paddr + data->len)))
- is_setup_data = true;
-
- early_memunmap(data, sizeof(*data));
-
- if (is_setup_data)
- return true;
-
- paddr = paddr_next;
- }
+ return __memremap_is_setup_data(phys_addr, size, true);
+}
- return false;
+static bool memremap_is_setup_data(resource_size_t phys_addr, unsigned long size)
+{
+ return __memremap_is_setup_data(phys_addr, size, false);
}
/*
--
Regards/Gruss,
Boris.
Good mailing practices for 400: avoid top-posting and trim the reply.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related
* Re: [PATCH] ext4: handle the rest of ext4_mb_load_buddy() ENOMEM errors
From: Theodore Ts'o @ 2017-05-21 2:45 UTC (permalink / raw)
To: Konstantin Khlebnikov; +Cc: Andreas Dilger, linux-ext4, linux-kernel, linux-mm
In-Reply-To: <149517779388.33359.16474190951431954772.stgit@buzz>
On Fri, May 19, 2017 at 10:09:54AM +0300, Konstantin Khlebnikov wrote:
> I've got another report about breaking ext4 by ENOMEM error returned from
> ext4_mb_load_buddy() caused by memory shortage in memory cgroup.
> This time inside ext4_discard_preallocations().
>
> This patch replaces ext4_error() with ext4_warning() where errors returned
> from ext4_mb_load_buddy() are not fatal and handled by caller:
> * ext4_mb_discard_group_preallocations() - called before generating ENOSPC,
> we'll try to discard other group or return ENOSPC into user-space.
> * ext4_trim_all_free() - just stop trimming and return ENOMEM from ioctl.
>
> Some callers cannot handle errors, thus __GFP_NOFAIL is used for them:
> * ext4_discard_preallocations()
> * ext4_mb_discard_lg_preallocations()
>
> The only unclear case is ext4_group_add_blocks(), probably ext4_std_error()
> should handle ENOMEM as warning and don't break filesystem.
>
> Fixes: adb7ef600cc9 ("ext4: use __GFP_NOFAIL in ext4_free_blocks()")
> Signed-off-by: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
Thanks, applied.
- Ted
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [v4 1/1] mm: Adaptive hash table scaling
From: Andi Kleen @ 2017-05-21 2:07 UTC (permalink / raw)
To: Pavel Tatashin; +Cc: akpm, linux-kernel, linux-mm, mhocko
In-Reply-To: <1495300013-653283-2-git-send-email-pasha.tatashin@oracle.com>
Pavel Tatashin <pasha.tatashin@oracle.com> writes:
> Allow hash tables to scale with memory but at slower pace, when HASH_ADAPT
> is provided every time memory quadruples the sizes of hash tables will only
> double instead of quadrupling as well. This algorithm starts working only
> when memory size reaches a certain point, currently set to 64G.
>
> This is example of dentry hash table size, before and after four various
> memory configurations:
IMHO the scale is still too aggressive. I find it very unlikely
that a 1TB machine really needs 256MB of hash table because
number of used files are unlikely to directly scale with memory.
Perhaps should just cap it at some large size, e.g. 32M
-Andi
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* [PATCH v9] mm: Add memory allocation watchdog kernel thread.
From: Tetsuo Handa @ 2017-05-21 1:51 UTC (permalink / raw)
To: akpm; +Cc: linux-mm, linux-kernel, Tetsuo Handa
This patch adds a watchdog which periodically reports number of memory
allocating tasks, dying tasks and OOM victim tasks when some task is
spending too long time inside __alloc_pages_slowpath(). This patch also
serves as a hook for obtaining additional information using SystemTap
(e.g. examine other variables using printk(), capture a crash dump by
calling panic()) by triggering a callback only when a stall is detected.
Ability to take administrator-controlled actions based on some threshold
is a big advantage gained by introducing a state tracking.
Commit 63f53dea0c9866e9 ("mm: warn about allocations which stall for
too long") was a great step for reducing possibility of silent hang up
problem caused by memory allocation stalls [1]. However, there are
reports of long stalls (e.g. [2] is over 30 minutes!) and lockups (e.g.
[3] is an "unable to invoke the OOM killer due to !__GFP_FS allocation"
lockup problem) where this patch is more useful than that commit, for
this patch can report possibly related tasks even if allocating tasks
are unexpectedly blocked for so long. Regarding premature OOM killer
invocation, tracepoints which can accumulate samples in short interval
would be useful. But regarding too late to report allocation stalls,
this patch which can capture all tasks (for reporting overall situation)
in longer interval and act as a trigger (for accumulating short interval
samples) would be useful.
Thanks to the OOM reaper which can guarantee forward progress (by selecting
next OOM victim) as long as the OOM killer can be invoked, we can start
testing low memory situations which are previously too difficult to test.
And we are now aware that there are still corner cases remaining where
the system hangs without invoking the OOM killer.
This patch is aimed for help bisecting whether unexpected hung cases are
related to memory allocation. By merging this patch (and enabling this
watchdog in enterprise systems via kernels supported by distributors),
we can identify patterns/cases of problems (if related to memory
allocation) and improve quality of Linux kernels by fixing problems
related to memory allocation.
As a nature of hang up problems caused by memory allocation, it is very
hard for administrators to collect information for analysis. As a result,
such problems are left unrecognized/unsolved at the support center, and
are seldom reported to distributors/developers in order to ask for fixes.
Therefore, nobody can prove that this patch will not find any problems
which occur in production systems. By merging this patch, we can start
focusing on real problems which occurred in production systems.
This patch remained out-of-tree for a year and a half due to a question
whether amount of changes, runtime cost and maintenance burden caused
by this patch can be justified. But after all there is no real objection.
Regarding amount of changes, I consider it is needed for making the
watchdog safe/robust (e.g. no duplicated/skipped reports and no lockup
warnings even if hundreds of threads entered into direct reclaim for
memory allocation) and useful (e.g. trigger additional actions only when
needed).
Regarding runtime cost of allocating threads, this watchdog involves
only slowpath where __GFP_DIRECT_RECLAIM is evaluated (in other words,
direct reclaim for memory allocation is needed). Therefore, systems
with adequate memory pressure will not notice.
Regarding runtime cost of the watchdog kernel thread side, I tried to
minimize it by checking per CPU in-flight counters before traversing
the tasklist.
Regarding maintenance burden, I consider this patch is least invasive
because it does not make __GFP_NOWARN flag's semantic confusing while
providing administrators some hints [4]. Also, this patch will remain
useful because we might overlook something that can cause infinite
loop (or significant delay) in future changes, and we can remove this
patch when we achieve safe and robust memory management subsystem.
Changes from v1 [5]:
(1) Use per a "struct task_struct" variables. This allows vmcore to
remember information about last memory allocation request, which
is useful for understanding last-minute behavior of the kernel.
(2) Report using accurate timeout. This increases possibility of
successfully reporting before watchdog timers reset the machine.
(3) Show memory information (SysRq-m). This makes it easier to know
the reason of stalling.
(4) Show both $state_of_allocation and $state_of_task in the same
line. This makes it easier to grep the output.
(5) Minimize duration of spinlock held by the kernel thread.
Changes from v2 [6]:
(1) Print sequence number. This makes it easier to know whether
memory allocation is succeeding (looks like a livelock but making
forward progress) or not.
(2) Replace spinlock with cheaper seqlock_t like sequence number based
method. The caller no longer contend on lock, and major overhead
for caller side will be two smp_wmb() instead for
read_lock()/read_unlock().
(3) Print "exiting" instead for "dying" if an OOM victim is stalling
at do_exit(), for SIGKILL is removed before arriving at do_exit().
(4) Moved explanation to Documentation/malloc-watchdog.txt .
Changes from v3 [7]:
(1) Avoid stalls even if there are so many tasks to report.
Changes from v4 [8]:
(1) Use per CPU in-flight counter by reverting "Report using accurate
timeout." in v2, in order to avoid walking the process list which
is costly when there are extremely so many tasks in the system.
(2) Updated Documentation/malloc-watchdog.txt to add explanation for
serving as a hook for dynamic probes.
Changes from v5 [9]:
(1) Disable commit 63f53dea0c9866e9 ("mm: warn about allocations which
stall for too long") when CONFIG_DETECT_MEMALLOC_STALL_TASK is
enabled.
(2) Updated Documentation/malloc-watchdog.txt to reflect OOM related
improvements up to Linux 4.9.
Changes from v6 [10]:
(1) Check __GFP_DIRECT_RECLAIM allocation requests rather than
__GFP_RECLAIM == (__GFP_KSWAPD_RECLAIM|__GFP_DIRECT_RECLAIM)
allocation requests.
(2) Rename /proc/sys/kernel/memalloc_task_timeout_secs to
/proc/sys/kernel/memalloc_task_warning_secs , for the name of
"timeout" is associated with "give up retrying, and OOM kill or fail".
This variable is for emitting warning messages rather than giving up.
Changes from v7 [11]:
(1) Reflect review comments from Andrew Morton. (Convert "u8 type" to
"bool report", use CPUHP_PAGE_ALLOC_DEAD event and replace
for_each_possible_cpu() with for_each_online_cpu(), reuse existing
rcu_lock_break() and hung_timeout_jiffies() for now, update comments).
Changes from v8 [12]:
(1) Check mempool_alloc() as well, for mempool_alloc() is a sort of
open-coded __GFP_NOFAIL allocation request where warn_alloc() cannot
detect stalls due to __GFP_NORETRY, and actually I found a case where
kswapd was unable to get memory for bio from mempool at
bio_alloc_bioset(GFP_NOFS) [13], and I believe there are similar bugs.
[1] http://lkml.kernel.org/r/201610182004.AEF87559.FOOHVLJOQFFtSM@I-love.SAKURA.ne.jp
[2] https://bugzilla.kernel.org/show_bug.cgi?id=192981
[3] http://lkml.kernel.org/r/201703031948.CHJ81278.VOHSFFFOOLJQMt@I-love.SAKURA.ne.jp
[4] http://lkml.kernel.org/r/1484132120-35288-1-git-send-email-penguin-kernel@I-love.SAKURA.ne.jp
[5] http://lkml.kernel.org/r/201510182105.AGA00839.FHVFFStLQOMOOJ@I-love.SAKURA.ne.jp
[6] http://lkml.kernel.org/r/201511222346.JBH48464.VFFtOLOOQJMFHS@I-love.SAKURA.ne.jp
[7] http://lkml.kernel.org/r/201511250024.AAE78692.QVOtFFOSFOMLJH@I-love.SAKURA.ne.jp
[8] http://lkml.kernel.org/r/201512130033.ABH90650.FtFOMOFLVOJHQS@I-love.SAKURA.ne.jp
[9] http://lkml.kernel.org/r/1462630604-23410-1-git-send-email-penguin-kernel@I-love.SAKURA.ne.jp
[10] http://lkml.kernel.org/r/1478416501-10104-1-git-send-email-penguin-kernel@I-love.SAKURA.ne.jp
[11] http://lkml.kernel.org/r/1488244908-57586-1-git-send-email-penguin-kernel@I-love.SAKURA.ne.jp
[12] http://lkml.kernel.org/r/1489578541-81526-1-git-send-email-penguin-kernel@I-love.SAKURA.ne.jp
[13] http://lkml.kernel.org/r/201704272019.JEH26057.SHFOtMLJOOVFQF@I-love.SAKURA.ne.jp
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
Documentation/malloc-watchdog.txt | 515 ++++++++++++++++++++++++++++++++++++++
include/linux/gfp.h | 8 +
include/linux/oom.h | 4 +
include/linux/sched.h | 19 ++
kernel/fork.c | 4 +
kernel/hung_task.c | 216 +++++++++++++++-
kernel/sysctl.c | 10 +
lib/Kconfig.debug | 24 ++
mm/mempool.c | 9 +-
mm/oom_kill.c | 3 +
mm/page_alloc.c | 82 ++++++
11 files changed, 892 insertions(+), 2 deletions(-)
create mode 100644 Documentation/malloc-watchdog.txt
diff --git a/Documentation/malloc-watchdog.txt b/Documentation/malloc-watchdog.txt
new file mode 100644
index 0000000..218c094
--- /dev/null
+++ b/Documentation/malloc-watchdog.txt
@@ -0,0 +1,515 @@
+=================================
+Memory allocation stall watchdog.
+=================================
+
+
+- What is it?
+
+This is an extension to khungtaskd kernel thread, which is for warning
+that memory allocation requests are stalling, in order to catch unexplained
+hangups/reboots caused by memory allocation stalls.
+
+
+- Why need to use it?
+
+Currently, when something went wrong inside memory allocation request,
+the system might stall without any kernel messages.
+
+Although there is khungtaskd kernel thread as an asynchronous monitoring
+approach, khungtaskd kernel thread is not always helpful because memory
+allocating tasks unlikely sleep in uninterruptible state for
+/proc/sys/kernel/hung_task_timeout_secs seconds.
+
+Although there is warn_alloc() as a synchronous monitoring approach
+which emits
+
+ "%s: page allocation stalls for %ums, order:%u, mode:%#x(%pGg), nodemask=%*pbl\n"
+
+line, warn_alloc() is not bullet proof because allocating tasks can take
+too long to wait (e.g. 30+ minutes, or possibly forever) before calling
+warn_alloc() and/or such lines are suppressed by ratelimiting and/or
+such lines are corrupted due to collisions.
+
+Unless we use asynchronous monitoring approach, we can fail to figure out
+that something went wrong inside memory allocation requests.
+
+People are reporting hang up problems and/or slowdown problem inside memory
+allocation request. But we are forcing people to use kernels without means
+to find out what was happening. The means are expected to work without
+knowledge to use trace points functionality, are expected to run without
+memory allocation, are expected to dump output without administrator's
+operation, are expected to work before watchdog timers reset the machine.
+
+This extension adds a state tracking mechanism for memory allocation requests
+to khungtaskd kernel thread, allowing administrators to figure out that the
+system hung up due to memory allocation stalls and/or to take administrator-
+controlled actions when memory allocation requests are stalling.
+
+
+- How to configure it?
+
+Build kernels with CONFIG_DETECT_HUNG_TASK=y and
+CONFIG_DETECT_MEMALLOC_STALL_TASK=y.
+
+Default scan interval is configured by CONFIG_DEFAULT_MEMALLOC_TASK_TIMEOUT.
+Scan interval can be changed at run time by writing timeout in seconds to
+/proc/sys/kernel/memalloc_task_warning_secs. Writing 0 disables this scan.
+
+Even if you disable this scan, information about last memory allocation
+request is kept. That is, you will get some hint for understanding
+last-minute behavior of the kernel when you analyze vmcore (or memory
+snapshot of a virtualized machine).
+
+
+- How memory allocation stalls are reported?
+
+This extension will report allocation stalls by printing
+
+ MemAlloc-Info: stalling=$X dying=$Y1 exiting=$Y2 victim=$Z oom_count=$O
+
+line where $X > 0, followed by
+
+ MemAlloc: $name($pid) flags=$flags switches=$switches $state_of_allocation $state_of_task
+
+lines and corresponding stack traces.
+
+$O is number of times the OOM killer is invoked. If $O does not increase
+over time, allocation requests got stuck before calling the OOM killer.
+
+$name is that task's comm name string ("struct task_struct"->comm).
+
+$pid is that task's pid value ("struct task_struct"->pid).
+
+$flags is that task's flags value ("struct task_struct"->flags).
+
+$switches is that task's context switch counter ("struct task_struct"->nvcsw +
+"struct task_struct"->nivcsw) which is also checked by
+/proc/sys/kernel/hung_task_warnings for finding hung tasks.
+
+$state_of_allocation is reported only when that task is stalling inside
+__alloc_pages_slowpath(), in seq=$seq gfp=$gfp order=$order delay=$delay
+format where $seq is the sequence number for allocation request, $gfp is
+the gfp flags used for that allocation request, $order is the order,
+delay is jiffies elapsed since entering into __alloc_pages_slowpath().
+
+You can check for seq=$seq field for each reported process. If $seq is
+increasing over time, it will be simply overloaded (not a livelock but
+progress is too slow to wait) unless the caller is doing open-coded
+__GFP_NOFAIL allocation requests (effectively a livelock).
+
+$state_of_task is reported only when that task is dying, in combination
+of "uninterruptible" (where that task is in uninterruptible sleep,
+likely due to uninterruptible lock), "exiting" (where that task arrived
+at do_exit() function), "dying" (where that task has pending SIGKILL)
+and "victim" (where that task received TIF_MEMDIE, likely be only 1 task).
+
+
+- How the messages look like?
+
+An example of MemAlloc lines (grep of dmesg output) is shown below.
+You can use serial console and/or netconsole to save these messages
+when the system is stalling.
+
+ [ 100.503284] MemAlloc-Info: stalling=8 dying=1 exiting=0 victim=1 oom_count=101421
+ [ 100.505674] MemAlloc: kswapd0(54) flags=0xa40840 switches=84685
+ [ 100.546645] MemAlloc: kworker/3:1(70) flags=0x4208060 switches=9462 seq=5 gfp=0x2400000(GFP_NOIO) order=0 delay=8207 uninterruptible
+ [ 100.606034] MemAlloc: systemd-journal(469) flags=0x400100 switches=8380 seq=212 gfp=0x24201ca(GFP_HIGHUSER_MOVABLE|__GFP_COLD) order=0 delay=10620 uninterruptible
+ [ 100.651766] MemAlloc: irqbalance(998) flags=0x400100 switches=4366 seq=5 gfp=0x24201ca(GFP_HIGHUSER_MOVABLE|__GFP_COLD) order=0 delay=5819
+ [ 100.697590] MemAlloc: vmtoolsd(1928) flags=0x400100 switches=8542 seq=82 gfp=0x24201ca(GFP_HIGHUSER_MOVABLE|__GFP_COLD) order=0 delay=10620 uninterruptible
+ [ 100.743312] MemAlloc: tuned(3737) flags=0x400040 switches=8220 seq=44 gfp=0x24201ca(GFP_HIGHUSER_MOVABLE|__GFP_COLD) order=0 delay=10620 uninterruptible
+ [ 100.792038] MemAlloc: nmbd(3759) flags=0x400140 switches=8079 seq=198 gfp=0x24201ca(GFP_HIGHUSER_MOVABLE|__GFP_COLD) order=0 delay=10620 uninterruptible
+ [ 100.839428] MemAlloc: oom-write(3814) flags=0x400000 switches=8126 seq=223446 gfp=0x24280ca(GFP_HIGHUSER_MOVABLE|__GFP_ZERO) order=0 delay=10620 uninterruptible
+ [ 100.878846] MemAlloc: write(3816) flags=0x400000 switches=7440 uninterruptible dying victim
+ [ 100.917971] MemAlloc: write(3820) flags=0x400000 switches=16130 seq=8714 gfp=0x342004a(GFP_NOFS|__GFP_HIGHMEM|__GFP_HARDWALL|__GFP_MOVABLE|__GFP_WRITE) order=0 delay=10620 uninterruptible
+ [ 101.190979] MemAlloc-Info: stalling=8 dying=1 exiting=0 victim=1 oom_count=107514
+ [ 111.194055] MemAlloc-Info: stalling=9 dying=1 exiting=0 victim=1 oom_count=199825
+ [ 111.196624] MemAlloc: kswapd0(54) flags=0xa40840 switches=168410
+ [ 111.238096] MemAlloc: kworker/3:1(70) flags=0x4208060 switches=18592 seq=5 gfp=0x2400000(GFP_NOIO) order=0 delay=18898
+ [ 111.296920] MemAlloc: systemd-journal(469) flags=0x400100 switches=15918 seq=212 gfp=0x24201ca(GFP_HIGHUSER_MOVABLE|__GFP_COLD) order=0 delay=21311
+ [ 111.343129] MemAlloc: systemd-logind(973) flags=0x400100 switches=7786 seq=3 gfp=0x24201ca(GFP_HIGHUSER_MOVABLE|__GFP_COLD) order=0 delay=10476
+ [ 111.390142] MemAlloc: irqbalance(998) flags=0x400100 switches=11965 seq=5 gfp=0x24201ca(GFP_HIGHUSER_MOVABLE|__GFP_COLD) order=0 delay=16510
+ [ 111.435170] MemAlloc: vmtoolsd(1928) flags=0x400100 switches=16230 seq=82 gfp=0x24201ca(GFP_HIGHUSER_MOVABLE|__GFP_COLD) order=0 delay=21311 uninterruptible
+ [ 111.479089] MemAlloc: tuned(3737) flags=0x400040 switches=15850 seq=44 gfp=0x24201ca(GFP_HIGHUSER_MOVABLE|__GFP_COLD) order=0 delay=21311 uninterruptible
+ [ 111.528294] MemAlloc: nmbd(3759) flags=0x400140 switches=15682 seq=198 gfp=0x24201ca(GFP_HIGHUSER_MOVABLE|__GFP_COLD) order=0 delay=21311
+ [ 111.576371] MemAlloc: oom-write(3814) flags=0x400000 switches=15378 seq=223446 gfp=0x24280ca(GFP_HIGHUSER_MOVABLE|__GFP_ZERO) order=0 delay=21311 uninterruptible
+ [ 111.617562] MemAlloc: write(3816) flags=0x400000 switches=7440 uninterruptible dying victim
+ [ 111.661662] MemAlloc: write(3820) flags=0x400000 switches=24334 seq=8714 gfp=0x342004a(GFP_NOFS|__GFP_HIGHMEM|__GFP_HARDWALL|__GFP_MOVABLE|__GFP_WRITE) order=0 delay=21311 uninterruptible
+ [ 111.956964] MemAlloc-Info: stalling=9 dying=1 exiting=0 victim=1 oom_count=206663
+
+You can check whether memory allocations are making forward progress.
+You can check where memory allocations are stalling using stack trace
+of reported task which follows each MemAlloc: line. You can check memory
+information (SysRq-m) and stuck workqueues information which follow the
+end of MemAlloc: lines. You can also check locks held (SysRq-d) if built
+with CONFIG_PROVE_LOCKING=y and lockdep is still active.
+
+Two more examples from stress tests are shown below.
+
+This extension found a lockup situation where all __GFP_DIRECT_RECLAIM allocation
+requests fall into an infinite loop (and therefore cannot call warn_alloc()) inside
+shrink_inactive_list() from output shown below.
+
+ (1) kswapd cannot make progress waiting for lock.
+
+----------
+[ 1209.790966] MemAlloc: kswapd0(67) flags=0xa60840 switches=51139 uninterruptible
+[ 1209.799726] kswapd0 D10936 67 2 0x00000000
+[ 1209.807326] Call Trace:
+[ 1209.812581] __schedule+0x336/0xe00
+[ 1209.818599] schedule+0x3d/0x90
+[ 1209.823907] schedule_timeout+0x26a/0x510
+[ 1209.827218] ? trace_hardirqs_on+0xd/0x10
+[ 1209.830535] __down_common+0xfb/0x131
+[ 1209.833801] ? _xfs_buf_find+0x2cb/0xc10 [xfs]
+[ 1209.837372] __down+0x1d/0x1f
+[ 1209.840331] down+0x41/0x50
+[ 1209.843243] xfs_buf_lock+0x64/0x370 [xfs]
+[ 1209.846597] _xfs_buf_find+0x2cb/0xc10 [xfs]
+[ 1209.850031] ? _xfs_buf_find+0xa4/0xc10 [xfs]
+[ 1209.853514] xfs_buf_get_map+0x2a/0x480 [xfs]
+[ 1209.855831] xfs_buf_read_map+0x2c/0x400 [xfs]
+[ 1209.857388] ? free_debug_processing+0x27d/0x2af
+[ 1209.859037] xfs_trans_read_buf_map+0x186/0x830 [xfs]
+[ 1209.860707] xfs_read_agf+0xc8/0x2b0 [xfs]
+[ 1209.862184] xfs_alloc_read_agf+0x7a/0x300 [xfs]
+[ 1209.863728] ? xfs_alloc_space_available+0x7b/0x120 [xfs]
+[ 1209.865385] xfs_alloc_fix_freelist+0x3bc/0x490 [xfs]
+[ 1209.866974] ? __radix_tree_lookup+0x84/0xf0
+[ 1209.868374] ? xfs_perag_get+0x1a0/0x310 [xfs]
+[ 1209.869798] ? xfs_perag_get+0x5/0x310 [xfs]
+[ 1209.871288] xfs_alloc_vextent+0x161/0xda0 [xfs]
+[ 1209.872757] xfs_bmap_btalloc+0x46c/0x8b0 [xfs]
+[ 1209.874182] ? save_stack_trace+0x1b/0x20
+[ 1209.875542] xfs_bmap_alloc+0x17/0x30 [xfs]
+[ 1209.876847] xfs_bmapi_write+0x74e/0x11d0 [xfs]
+[ 1209.878190] xfs_iomap_write_allocate+0x199/0x3a0 [xfs]
+[ 1209.879632] xfs_map_blocks+0x2cc/0x5a0 [xfs]
+[ 1209.880909] xfs_do_writepage+0x215/0x920 [xfs]
+[ 1209.882255] ? clear_page_dirty_for_io+0xb4/0x310
+[ 1209.883598] xfs_vm_writepage+0x3b/0x70 [xfs]
+[ 1209.884841] pageout.isra.54+0x1a4/0x460
+[ 1209.886210] shrink_page_list+0xa86/0xcf0
+[ 1209.887441] shrink_inactive_list+0x1c5/0x660
+[ 1209.888682] shrink_node_memcg+0x535/0x7f0
+[ 1209.889975] ? mem_cgroup_iter+0x14d/0x720
+[ 1209.891197] shrink_node+0xe1/0x310
+[ 1209.892288] kswapd+0x362/0x9b0
+[ 1209.893308] kthread+0x10f/0x150
+[ 1209.894383] ? mem_cgroup_shrink_node+0x3b0/0x3b0
+[ 1209.895703] ? kthread_create_on_node+0x70/0x70
+[ 1209.896956] ret_from_fork+0x31/0x40
+----------
+
+ (2) Both GFP_IO and GFP_KERNEL allocations are stuck at
+ too_many_isolated() loop.
+
+----------
+[ 1209.898117] MemAlloc: systemd-journal(526) flags=0x400900 switches=33248 seq=121659 gfp=0x14201ca(GFP_HIGHUSER_MOVABLE|__GFP_COLD) order=0 delay=52772 uninterruptible
+[ 1209.902154] systemd-journal D11240 526 1 0x00000000
+[ 1209.903642] Call Trace:
+[ 1209.904574] __schedule+0x336/0xe00
+[ 1209.905734] schedule+0x3d/0x90
+[ 1209.906817] schedule_timeout+0x20d/0x510
+[ 1209.908025] ? prepare_to_wait+0x2b/0xc0
+[ 1209.909268] ? lock_timer_base+0xa0/0xa0
+[ 1209.910460] io_schedule_timeout+0x1e/0x50
+[ 1209.911681] congestion_wait+0x86/0x260
+[ 1209.912853] ? remove_wait_queue+0x60/0x60
+[ 1209.914115] shrink_inactive_list+0x5b4/0x660
+[ 1209.915385] ? __list_lru_count_one.isra.2+0x22/0x80
+[ 1209.916768] shrink_node_memcg+0x535/0x7f0
+[ 1209.918173] shrink_node+0xe1/0x310
+[ 1209.919288] do_try_to_free_pages+0xe1/0x300
+[ 1209.920548] try_to_free_pages+0x131/0x3f0
+[ 1209.921827] __alloc_pages_slowpath+0x3ec/0xd95
+[ 1209.923137] __alloc_pages_nodemask+0x3e4/0x460
+[ 1209.924454] ? __radix_tree_lookup+0x84/0xf0
+[ 1209.925790] alloc_pages_current+0x97/0x1b0
+[ 1209.927021] ? find_get_entry+0x5/0x300
+[ 1209.928189] __page_cache_alloc+0x15d/0x1a0
+[ 1209.929471] ? pagecache_get_page+0x2c/0x2b0
+[ 1209.930716] filemap_fault+0x4df/0x8b0
+[ 1209.931867] ? filemap_fault+0x373/0x8b0
+[ 1209.933111] ? xfs_ilock+0x22c/0x360 [xfs]
+[ 1209.934510] ? xfs_filemap_fault+0x64/0x1e0 [xfs]
+[ 1209.935857] ? down_read_nested+0x7b/0xc0
+[ 1209.937123] ? xfs_ilock+0x22c/0x360 [xfs]
+[ 1209.938373] xfs_filemap_fault+0x6c/0x1e0 [xfs]
+[ 1209.939691] __do_fault+0x1e/0xa0
+[ 1209.940807] ? _raw_spin_unlock+0x27/0x40
+[ 1209.942002] __handle_mm_fault+0xbb1/0xf40
+[ 1209.943228] ? mutex_unlock+0x12/0x20
+[ 1209.944410] ? devkmsg_read+0x15c/0x330
+[ 1209.945912] handle_mm_fault+0x16b/0x390
+[ 1209.947297] ? handle_mm_fault+0x49/0x390
+[ 1209.948868] __do_page_fault+0x24a/0x530
+[ 1209.950351] do_page_fault+0x30/0x80
+[ 1209.951615] page_fault+0x28/0x30
+
+[ 1210.538496] MemAlloc: kworker/3:0(6345) flags=0x4208860 switches=10134 seq=22 gfp=0x1400000(GFP_NOIO) order=0 delay=45953 uninterruptible
+[ 1210.541487] kworker/3:0 D12560 6345 2 0x00000080
+[ 1210.542991] Workqueue: events_freezable_power_ disk_events_workfn
+[ 1210.544577] Call Trace:
+[ 1210.545468] __schedule+0x336/0xe00
+[ 1210.546606] schedule+0x3d/0x90
+[ 1210.547616] schedule_timeout+0x20d/0x510
+[ 1210.548778] ? prepare_to_wait+0x2b/0xc0
+[ 1210.550013] ? lock_timer_base+0xa0/0xa0
+[ 1210.551208] io_schedule_timeout+0x1e/0x50
+[ 1210.552519] congestion_wait+0x86/0x260
+[ 1210.553650] ? remove_wait_queue+0x60/0x60
+[ 1210.554900] shrink_inactive_list+0x5b4/0x660
+[ 1210.556119] ? __list_lru_count_one.isra.2+0x22/0x80
+[ 1210.557447] shrink_node_memcg+0x535/0x7f0
+[ 1210.558714] shrink_node+0xe1/0x310
+[ 1210.559803] do_try_to_free_pages+0xe1/0x300
+[ 1210.561009] try_to_free_pages+0x131/0x3f0
+[ 1210.562250] __alloc_pages_slowpath+0x3ec/0xd95
+[ 1210.563506] __alloc_pages_nodemask+0x3e4/0x460
+[ 1210.564777] alloc_pages_current+0x97/0x1b0
+[ 1210.566017] bio_copy_kern+0xc9/0x180
+[ 1210.567116] blk_rq_map_kern+0x70/0x140
+[ 1210.568356] __scsi_execute.isra.22+0x13a/0x1e0
+[ 1210.569839] scsi_execute_req_flags+0x94/0x100
+[ 1210.571218] sr_check_events+0xbf/0x2b0 [sr_mod]
+[ 1210.572500] cdrom_check_events+0x18/0x30 [cdrom]
+[ 1210.573934] sr_block_check_events+0x2a/0x30 [sr_mod]
+[ 1210.575335] disk_check_events+0x60/0x170
+[ 1210.576509] disk_events_workfn+0x1c/0x20
+[ 1210.577744] process_one_work+0x22b/0x760
+[ 1210.578934] ? process_one_work+0x194/0x760
+[ 1210.580147] worker_thread+0x137/0x4b0
+[ 1210.581336] kthread+0x10f/0x150
+[ 1210.582365] ? process_one_work+0x760/0x760
+[ 1210.583603] ? kthread_create_on_node+0x70/0x70
+[ 1210.584961] ? do_syscall_64+0x6c/0x200
+[ 1210.586343] ret_from_fork+0x31/0x40
+----------
+
+ (3) Number of stalling threads does not decrease over time and
+ number of out_of_memory() calls does not increase over time.
+
+----------
+[ 1209.781787] MemAlloc-Info: stalling=32 dying=1 exiting=0 victim=1 oom_count=45896
+[ 1212.195351] MemAlloc-Info: stalling=32 dying=1 exiting=0 victim=1 oom_count=45896
+[ 1242.551629] MemAlloc-Info: stalling=36 dying=1 exiting=0 victim=1 oom_count=45896
+[ 1245.149165] MemAlloc-Info: stalling=36 dying=1 exiting=0 victim=1 oom_count=45896
+[ 1275.319189] MemAlloc-Info: stalling=40 dying=1 exiting=0 victim=1 oom_count=45896
+[ 1278.241813] MemAlloc-Info: stalling=40 dying=1 exiting=0 victim=1 oom_count=45896
+----------
+
+This extension also found a lockup situation where memory reclaim activity got stuck
+waiting for memory allocation (and therefore the system hung without invoking the
+OOM killer) from output shown below.
+
+ (1) kswapd cannot make progress waiting for lock.
+
+----------
+[ 518.900038] MemAlloc: kswapd0(69) flags=0xa40840 switches=23883 uninterruptible
+[ 518.902095] kswapd0 D10776 69 2 0x00000000
+[ 518.903784] Call Trace:
+[ 518.904849] __schedule+0x336/0xe00
+[ 518.906118] schedule+0x3d/0x90
+[ 518.907314] io_schedule+0x16/0x40
+[ 518.908622] __xfs_iflock+0x129/0x140 [xfs]
+[ 518.910027] ? autoremove_wake_function+0x60/0x60
+[ 518.911559] xfs_reclaim_inode+0x162/0x440 [xfs]
+[ 518.913068] xfs_reclaim_inodes_ag+0x2cf/0x4f0 [xfs]
+[ 518.914611] ? xfs_reclaim_inodes_ag+0xf2/0x4f0 [xfs]
+[ 518.916148] ? trace_hardirqs_on+0xd/0x10
+[ 518.917465] ? try_to_wake_up+0x59/0x7a0
+[ 518.918758] ? wake_up_process+0x15/0x20
+[ 518.920067] xfs_reclaim_inodes_nr+0x33/0x40 [xfs]
+[ 518.921560] xfs_fs_free_cached_objects+0x19/0x20 [xfs]
+[ 518.923114] super_cache_scan+0x181/0x190
+[ 518.924435] shrink_slab+0x29f/0x6d0
+[ 518.925683] shrink_node+0x2fa/0x310
+[ 518.926909] kswapd+0x362/0x9b0
+[ 518.928061] kthread+0x10f/0x150
+[ 518.929218] ? mem_cgroup_shrink_node+0x3b0/0x3b0
+[ 518.930953] ? kthread_create_on_node+0x70/0x70
+[ 518.932380] ret_from_fork+0x31/0x40
+
+[ 1095.632984] MemAlloc: kswapd0(69) flags=0xa40840 switches=23883 uninterruptible
+[ 1095.632985] kswapd0 D10776 69 2 0x00000000
+[ 1095.632988] Call Trace:
+[ 1095.632991] __schedule+0x336/0xe00
+[ 1095.632994] schedule+0x3d/0x90
+[ 1095.632996] io_schedule+0x16/0x40
+[ 1095.633017] __xfs_iflock+0x129/0x140 [xfs]
+[ 1095.633021] ? autoremove_wake_function+0x60/0x60
+[ 1095.633051] xfs_reclaim_inode+0x162/0x440 [xfs]
+[ 1095.633072] xfs_reclaim_inodes_ag+0x2cf/0x4f0 [xfs]
+[ 1095.633106] ? xfs_reclaim_inodes_ag+0xf2/0x4f0 [xfs]
+[ 1095.633114] ? trace_hardirqs_on+0xd/0x10
+[ 1095.633116] ? try_to_wake_up+0x59/0x7a0
+[ 1095.633120] ? wake_up_process+0x15/0x20
+[ 1095.633156] xfs_reclaim_inodes_nr+0x33/0x40 [xfs]
+[ 1095.633178] xfs_fs_free_cached_objects+0x19/0x20 [xfs]
+[ 1095.633180] super_cache_scan+0x181/0x190
+[ 1095.633183] shrink_slab+0x29f/0x6d0
+[ 1095.633189] shrink_node+0x2fa/0x310
+[ 1095.633193] kswapd+0x362/0x9b0
+[ 1095.633200] kthread+0x10f/0x150
+[ 1095.633201] ? mem_cgroup_shrink_node+0x3b0/0x3b0
+[ 1095.633202] ? kthread_create_on_node+0x70/0x70
+[ 1095.633205] ret_from_fork+0x31/0x40
+----------
+
+ (2) All WQ_MEM_RECLAIM threads shown by show_workqueue_state()
+ cannot make progress waiting for memory allocation.
+
+----------
+[ 1095.633625] MemAlloc: xfs-data/sda1(451) flags=0x4228060 switches=45509 seq=1 gfp=0x1604240(GFP_NOFS|__GFP_NOWARN|__GFP_COMP|__GFP_NOTRACK) order=0 delay=652073
+[ 1095.633626] xfs-data/sda1 R running task 12696 451 2 0x00000000
+[ 1095.633663] Workqueue: xfs-data/sda1 xfs_end_io [xfs]
+[ 1095.633665] Call Trace:
+[ 1095.633668] __schedule+0x336/0xe00
+[ 1095.633671] schedule+0x3d/0x90
+[ 1095.633672] schedule_timeout+0x20d/0x510
+[ 1095.633675] ? lock_timer_base+0xa0/0xa0
+[ 1095.633678] schedule_timeout_uninterruptible+0x2a/0x30
+[ 1095.633680] __alloc_pages_slowpath+0x2b5/0xd95
+[ 1095.633687] __alloc_pages_nodemask+0x3e4/0x460
+[ 1095.633699] alloc_pages_current+0x97/0x1b0
+[ 1095.633702] new_slab+0x4cb/0x6b0
+[ 1095.633706] ___slab_alloc+0x3a3/0x620
+[ 1095.633728] ? kmem_alloc+0x96/0x120 [xfs]
+[ 1095.633730] ? ___slab_alloc+0x5c6/0x620
+[ 1095.633732] ? cpuacct_charge+0x38/0x1e0
+[ 1095.633767] ? kmem_alloc+0x96/0x120 [xfs]
+[ 1095.633770] __slab_alloc+0x46/0x7d
+[ 1095.633773] __kmalloc+0x301/0x3b0
+[ 1095.633802] kmem_alloc+0x96/0x120 [xfs]
+[ 1095.633804] ? kfree+0x1fa/0x330
+[ 1095.633842] xfs_log_commit_cil+0x489/0x710 [xfs]
+[ 1095.633864] __xfs_trans_commit+0x83/0x260 [xfs]
+[ 1095.633883] xfs_trans_commit+0x10/0x20 [xfs]
+[ 1095.633901] __xfs_setfilesize+0xdb/0x240 [xfs]
+[ 1095.633936] xfs_setfilesize_ioend+0x89/0xb0 [xfs]
+[ 1095.633954] ? xfs_setfilesize_ioend+0x5/0xb0 [xfs]
+[ 1095.633971] xfs_end_io+0x81/0x110 [xfs]
+[ 1095.633973] process_one_work+0x22b/0x760
+[ 1095.633975] ? process_one_work+0x194/0x760
+[ 1095.633997] rescuer_thread+0x1f2/0x3d0
+[ 1095.634002] kthread+0x10f/0x150
+[ 1095.634003] ? worker_thread+0x4b0/0x4b0
+[ 1095.634004] ? kthread_create_on_node+0x70/0x70
+[ 1095.634007] ret_from_fork+0x31/0x40
+[ 1095.634013] MemAlloc: xfs-eofblocks/s(456) flags=0x4228860 switches=15435 seq=1 gfp=0x1400240(GFP_NOFS|__GFP_NOWARN) order=0 delay=293074
+[ 1095.634014] xfs-eofblocks/s R running task 12032 456 2 0x00000000
+[ 1095.634037] Workqueue: xfs-eofblocks/sda1 xfs_eofblocks_worker [xfs]
+[ 1095.634038] Call Trace:
+[ 1095.634040] ? _raw_spin_lock+0x3d/0x80
+[ 1095.634042] ? vmpressure+0xd0/0x120
+[ 1095.634044] ? vmpressure+0xd0/0x120
+[ 1095.634047] ? vmpressure_prio+0x21/0x30
+[ 1095.634049] ? do_try_to_free_pages+0x70/0x300
+[ 1095.634052] ? try_to_free_pages+0x131/0x3f0
+[ 1095.634058] ? __alloc_pages_slowpath+0x3ec/0xd95
+[ 1095.634065] ? __alloc_pages_nodemask+0x3e4/0x460
+[ 1095.634069] ? alloc_pages_current+0x97/0x1b0
+[ 1095.634111] ? xfs_buf_allocate_memory+0x160/0x2a3 [xfs]
+[ 1095.634133] ? xfs_buf_get_map+0x2be/0x480 [xfs]
+[ 1095.634169] ? xfs_buf_read_map+0x2c/0x400 [xfs]
+[ 1095.634204] ? xfs_trans_read_buf_map+0x186/0x830 [xfs]
+[ 1095.634222] ? xfs_btree_read_buf_block.constprop.34+0x78/0xc0 [xfs]
+[ 1095.634239] ? xfs_btree_lookup_get_block+0x8a/0x180 [xfs]
+[ 1095.634257] ? xfs_btree_lookup+0xd0/0x3f0 [xfs]
+[ 1095.634296] ? kmem_zone_alloc+0x96/0x120 [xfs]
+[ 1095.634299] ? _raw_spin_unlock+0x27/0x40
+[ 1095.634315] ? xfs_bmbt_lookup_eq+0x1f/0x30 [xfs]
+[ 1095.634348] ? xfs_bmap_del_extent+0x1b2/0x1610 [xfs]
+[ 1095.634380] ? kmem_zone_alloc+0x96/0x120 [xfs]
+[ 1095.634400] ? __xfs_bunmapi+0x4db/0xda0 [xfs]
+[ 1095.634421] ? xfs_bunmapi+0x2b/0x40 [xfs]
+[ 1095.634459] ? xfs_itruncate_extents+0x1df/0x780 [xfs]
+[ 1095.634502] ? xfs_rename+0xc70/0x1080 [xfs]
+[ 1095.634525] ? xfs_free_eofblocks+0x1c4/0x230 [xfs]
+[ 1095.634546] ? xfs_inode_free_eofblocks+0x18d/0x280 [xfs]
+[ 1095.634565] ? xfs_inode_ag_walk.isra.13+0x2b5/0x620 [xfs]
+[ 1095.634582] ? xfs_inode_ag_walk.isra.13+0x91/0x620 [xfs]
+[ 1095.634618] ? xfs_inode_clear_eofblocks_tag+0x1a0/0x1a0 [xfs]
+[ 1095.634630] ? radix_tree_next_chunk+0x10b/0x2d0
+[ 1095.634635] ? radix_tree_gang_lookup_tag+0xd7/0x150
+[ 1095.634672] ? xfs_perag_get_tag+0x11d/0x370 [xfs]
+[ 1095.634690] ? xfs_perag_get_tag+0x5/0x370 [xfs]
+[ 1095.634709] ? xfs_inode_ag_iterator_tag+0x71/0xa0 [xfs]
+[ 1095.634726] ? xfs_inode_clear_eofblocks_tag+0x1a0/0x1a0 [xfs]
+[ 1095.634744] ? __xfs_icache_free_eofblocks+0x3b/0x40 [xfs]
+[ 1095.634759] ? xfs_eofblocks_worker+0x27/0x40 [xfs]
+[ 1095.634762] ? process_one_work+0x22b/0x760
+[ 1095.634763] ? process_one_work+0x194/0x760
+[ 1095.634784] ? rescuer_thread+0x1f2/0x3d0
+[ 1095.634788] ? kthread+0x10f/0x150
+[ 1095.634789] ? worker_thread+0x4b0/0x4b0
+[ 1095.634790] ? kthread_create_on_node+0x70/0x70
+[ 1095.634793] ? ret_from_fork+0x31/0x40
+----------
+
+ (3) order-0 GFP_NOIO allocation request cannot make progress
+ waiting for memory allocation.
+
+----------
+[ 1095.631687] MemAlloc: kworker/2:0(27) flags=0x4208860 switches=38727 seq=21 gfp=0x1400000(GFP_NOIO) order=0 delay=652160
+[ 1095.631688] kworker/2:0 R running task 12680 27 2 0x00000000
+[ 1095.631739] Workqueue: events_freezable_power_ disk_events_workfn
+[ 1095.631740] Call Trace:
+[ 1095.631743] __schedule+0x336/0xe00
+[ 1095.631746] preempt_schedule_common+0x1f/0x31
+[ 1095.631747] _cond_resched+0x1c/0x30
+[ 1095.631749] shrink_slab+0x339/0x6d0
+[ 1095.631754] shrink_node+0x2fa/0x310
+[ 1095.631758] do_try_to_free_pages+0xe1/0x300
+[ 1095.631761] try_to_free_pages+0x131/0x3f0
+[ 1095.631765] __alloc_pages_slowpath+0x3ec/0xd95
+[ 1095.631771] __alloc_pages_nodemask+0x3e4/0x460
+[ 1095.631775] alloc_pages_current+0x97/0x1b0
+[ 1095.631779] bio_copy_kern+0xc9/0x180
+[ 1095.631830] blk_rq_map_kern+0x70/0x140
+[ 1095.631835] __scsi_execute.isra.22+0x13a/0x1e0
+[ 1095.631838] scsi_execute_req_flags+0x94/0x100
+[ 1095.631844] sr_check_events+0xbf/0x2b0 [sr_mod]
+[ 1095.631852] cdrom_check_events+0x18/0x30 [cdrom]
+[ 1095.631854] sr_block_check_events+0x2a/0x30 [sr_mod]
+[ 1095.631856] disk_check_events+0x60/0x170
+[ 1095.631859] disk_events_workfn+0x1c/0x20
+[ 1095.631862] process_one_work+0x22b/0x760
+[ 1095.631863] ? process_one_work+0x194/0x760
+[ 1095.631867] worker_thread+0x137/0x4b0
+[ 1095.631887] kthread+0x10f/0x150
+[ 1095.631889] ? process_one_work+0x760/0x760
+[ 1095.631890] ? kthread_create_on_node+0x70/0x70
+[ 1095.631893] ret_from_fork+0x31/0x40
+----------
+
+ (4) Number of stalling threads does not decrease over time while
+ number of out_of_memory() calls increases over time.
+
+----------
+[ 518.090012] MemAlloc-Info: stalling=184 dying=1 exiting=0 victim=1 oom_count=8441307
+[ 553.070829] MemAlloc-Info: stalling=184 dying=1 exiting=0 victim=1 oom_count=10318507
+[ 616.394649] MemAlloc-Info: stalling=186 dying=1 exiting=0 victim=1 oom_count=13908219
+[ 642.266252] MemAlloc-Info: stalling=186 dying=1 exiting=0 victim=1 oom_count=15180673
+[ 702.412189] MemAlloc-Info: stalling=187 dying=1 exiting=0 victim=1 oom_count=18732529
+[ 736.787879] MemAlloc-Info: stalling=187 dying=1 exiting=0 victim=1 oom_count=20565244
+[ 800.715759] MemAlloc-Info: stalling=188 dying=1 exiting=0 victim=1 oom_count=24411576
+[ 837.571405] MemAlloc-Info: stalling=188 dying=1 exiting=0 victim=1 oom_count=26463562
+[ 899.021495] MemAlloc-Info: stalling=189 dying=1 exiting=0 victim=1 oom_count=30144879
+[ 936.282709] MemAlloc-Info: stalling=189 dying=1 exiting=0 victim=1 oom_count=32129234
+[ 997.328119] MemAlloc-Info: stalling=190 dying=1 exiting=0 victim=1 oom_count=35657983
+[ 1033.977265] MemAlloc-Info: stalling=190 dying=1 exiting=0 victim=1 oom_count=37659912
+[ 1095.630961] MemAlloc-Info: stalling=190 dying=1 exiting=0 victim=1 oom_count=40639677
+[ 1095.821248] MemAlloc-Info: stalling=190 dying=1 exiting=0 victim=1 oom_count=40646791
+----------
+
+This extension also serves as a hook for triggering actions when timeout
+expired. If you want to obtain more information, you can utilize dynamic
+probes using e.g. SystemTap. For example,
+
+ # stap -F -g -e 'probe kernel.function("check_memalloc_stalling_tasks").return { if ($return > 0) panic("MemAlloc stall detected."); }'
+
+will allow you to obtain vmcore by triggering the kernel panic. Since
+variables used by this extension is associated with "struct task_struct",
+you can obtain accurate snapshot using "foreach task" command from crash
+utility.
diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index a89d37e..5a2240f 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -469,6 +469,14 @@ static inline struct page *alloc_pages_node(int nid, gfp_t gfp_mask,
return __alloc_pages_node(nid, gfp_mask, order);
}
+#ifdef CONFIG_DETECT_MEMALLOC_STALL_TASK
+extern void start_memalloc_timer(const gfp_t gfp_mask, const int order);
+extern void stop_memalloc_timer(const gfp_t gfp_mask);
+#else
+#define start_memalloc_timer(gfp_mask, order) do { } while (0)
+#define stop_memalloc_timer(gfp_mask) do { } while (0)
+#endif
+
#ifdef CONFIG_NUMA
extern struct page *alloc_pages_current(gfp_t gfp_mask, unsigned order);
diff --git a/include/linux/oom.h b/include/linux/oom.h
index 8a266e2..fd31dee 100644
--- a/include/linux/oom.h
+++ b/include/linux/oom.h
@@ -79,8 +79,12 @@ extern unsigned long oom_badness(struct task_struct *p,
extern struct task_struct *find_lock_task_mm(struct task_struct *p);
+extern unsigned int out_of_memory_count;
+extern bool memalloc_maybe_stalling(void);
+
/* sysctls */
extern int sysctl_oom_dump_tasks;
extern int sysctl_oom_kill_allocating_task;
extern int sysctl_panic_on_oom;
+extern unsigned long sysctl_memalloc_task_warning_secs;
#endif /* _INCLUDE_LINUX_OOM_H */
diff --git a/include/linux/sched.h b/include/linux/sched.h
index c533851..494be84 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -480,6 +480,22 @@ struct wake_q_node {
struct wake_q_node *next;
};
+struct memalloc_info {
+ /* Is current thread doing (nested) memory allocation? */
+ u8 in_flight;
+ /* Watchdog kernel thread is about to report this task? */
+ bool report;
+ /* Index used for memalloc_in_flight[] counter. */
+ u8 idx;
+ /* For progress monitoring. */
+ unsigned int sequence;
+ /* Started time in jiffies as of in_flight == 1. */
+ unsigned long start;
+ /* Requested order and gfp flags as of in_flight == 1. */
+ unsigned int order;
+ gfp_t gfp;
+};
+
struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
/*
@@ -1052,6 +1068,9 @@ struct task_struct {
/* Used by LSM modules for access restriction: */
void *security;
#endif
+#ifdef CONFIG_DETECT_MEMALLOC_STALL_TASK
+ struct memalloc_info memalloc;
+#endif
/* CPU-specific state of this task: */
struct thread_struct thread;
diff --git a/kernel/fork.c b/kernel/fork.c
index 158b769..e83b170f 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1689,6 +1689,10 @@ static __latent_entropy struct task_struct *copy_process(
p->sequential_io_avg = 0;
#endif
+#ifdef CONFIG_DETECT_MEMALLOC_STALL_TASK
+ p->memalloc.sequence = 0;
+#endif
+
/* Perform scheduler related setup. Assign this task to a CPU. */
retval = sched_fork(clone_flags, p);
if (retval)
diff --git a/kernel/hung_task.c b/kernel/hung_task.c
index 751593e..aed2c21 100644
--- a/kernel/hung_task.c
+++ b/kernel/hung_task.c
@@ -16,6 +16,8 @@
#include <linux/export.h>
#include <linux/sysctl.h>
#include <linux/utsname.h>
+#include <linux/oom.h>
+#include <linux/console.h>
#include <linux/sched/signal.h>
#include <linux/sched/debug.h>
@@ -148,6 +150,8 @@ static bool rcu_lock_break(struct task_struct *g, struct task_struct *t)
get_task_struct(g);
get_task_struct(t);
rcu_read_unlock();
+ if (console_trylock())
+ console_unlock();
cond_resched();
rcu_read_lock();
can_cont = pid_alive(g) && pid_alive(t);
@@ -203,6 +207,200 @@ static long hung_timeout_jiffies(unsigned long last_checked,
MAX_SCHEDULE_TIMEOUT;
}
+#ifdef CONFIG_DETECT_MEMALLOC_STALL_TASK
+/*
+ * Zero means infinite timeout - no checking done:
+ */
+unsigned long __read_mostly sysctl_memalloc_task_warning_secs =
+ CONFIG_DEFAULT_MEMALLOC_TASK_TIMEOUT;
+
+/* Filled by is_stalling_task(), used by only khungtaskd kernel thread. */
+static struct memalloc_info memalloc;
+
+/**
+ * is_stalling_task - Check and copy a task's memalloc variable.
+ *
+ * @task: A task to check.
+ * @expire: Timeout in jiffies.
+ *
+ * Returns true if a task is stalling, false otherwise.
+ */
+static bool is_stalling_task(const struct task_struct *task,
+ const unsigned long expire)
+{
+ const struct memalloc_info *m = &task->memalloc;
+
+ if (likely(!m->in_flight || !time_after_eq(expire, m->start)))
+ return false;
+ /*
+ * start_memalloc_timer() guarantees that ->in_flight is updated after
+ * ->start is stored.
+ */
+ smp_rmb();
+ memalloc.sequence = m->sequence;
+ memalloc.start = m->start;
+ memalloc.order = m->order;
+ memalloc.gfp = m->gfp;
+ return time_after_eq(expire, memalloc.start);
+}
+
+/*
+ * check_memalloc_stalling_tasks - Check for memory allocation stalls.
+ *
+ * @timeout: Timeout in jiffies.
+ *
+ * Returns number of stalling tasks.
+ *
+ * This function is marked as "noinline" in order to allow inserting dynamic
+ * probes (e.g. printing more information as needed using SystemTap, calling
+ * panic() if this function returned non 0 value).
+ */
+static noinline int check_memalloc_stalling_tasks(unsigned long timeout)
+{
+ enum {
+ MEMALLOC_TYPE_STALLING, /* Report as stalling task. */
+ MEMALLOC_TYPE_DYING, /* Report as dying task. */
+ MEMALLOC_TYPE_EXITING, /* Report as exiting task.*/
+ MEMALLOC_TYPE_OOM_VICTIM, /* Report as OOM victim. */
+ MEMALLOC_TYPE_UNCONDITIONAL, /* Report unconditionally. */
+ };
+ char buf[256];
+ struct task_struct *g, *p;
+ unsigned long now;
+ unsigned long expire;
+ unsigned int sigkill_pending = 0;
+ unsigned int exiting_tasks = 0;
+ unsigned int memdie_pending = 0;
+ unsigned int stalling_tasks = 0;
+
+ cond_resched();
+ now = jiffies;
+ /*
+ * Report tasks that stalled for more than half of timeout duration
+ * because such tasks might be correlated with tasks that already
+ * stalled for full timeout duration.
+ */
+ expire = now - timeout * (HZ / 2);
+ /* Count stalling tasks, dying and victim tasks. */
+ rcu_read_lock();
+ for_each_process_thread(g, p) {
+ bool report = false;
+
+ if (test_tsk_thread_flag(p, TIF_MEMDIE)) {
+ report = true;
+ memdie_pending++;
+ }
+ if (fatal_signal_pending(p)) {
+ report = true;
+ sigkill_pending++;
+ }
+ if ((p->flags & PF_EXITING) && p->state != TASK_DEAD) {
+ report = true;
+ exiting_tasks++;
+ }
+ if (is_stalling_task(p, expire)) {
+ report = true;
+ stalling_tasks++;
+ }
+ if (p->flags & PF_KSWAPD)
+ report = true;
+ p->memalloc.report = report;
+ }
+ rcu_read_unlock();
+ if (!stalling_tasks)
+ return 0;
+ cond_resched();
+ /* Report stalling tasks, dying and victim tasks. */
+ pr_warn("MemAlloc-Info: stalling=%u dying=%u exiting=%u victim=%u oom_count=%u\n",
+ stalling_tasks, sigkill_pending, exiting_tasks, memdie_pending,
+ out_of_memory_count);
+ cond_resched();
+ sigkill_pending = 0;
+ exiting_tasks = 0;
+ memdie_pending = 0;
+ stalling_tasks = 0;
+ rcu_read_lock();
+ restart_report:
+ for_each_process_thread(g, p) {
+ u8 type;
+
+ if (likely(!p->memalloc.report))
+ continue;
+ p->memalloc.report = false;
+ /* Recheck in case state changed meanwhile. */
+ type = 0;
+ if (test_tsk_thread_flag(p, TIF_MEMDIE)) {
+ type |= (1 << MEMALLOC_TYPE_OOM_VICTIM);
+ memdie_pending++;
+ }
+ if (fatal_signal_pending(p)) {
+ type |= (1 << MEMALLOC_TYPE_DYING);
+ sigkill_pending++;
+ }
+ if ((p->flags & PF_EXITING) && p->state != TASK_DEAD) {
+ type |= (1 << MEMALLOC_TYPE_EXITING);
+ exiting_tasks++;
+ }
+ if (is_stalling_task(p, expire)) {
+ type |= (1 << MEMALLOC_TYPE_STALLING);
+ stalling_tasks++;
+ snprintf(buf, sizeof(buf),
+ " seq=%u gfp=0x%x(%pGg) order=%u delay=%lu",
+ memalloc.sequence, memalloc.gfp,
+ &memalloc.gfp,
+ memalloc.order, now - memalloc.start);
+ } else {
+ buf[0] = '\0';
+ }
+ if (p->flags & PF_KSWAPD)
+ type |= (1 << MEMALLOC_TYPE_UNCONDITIONAL);
+ if (unlikely(!type))
+ continue;
+ /*
+ * Victim tasks get pending SIGKILL removed before arriving at
+ * do_exit(). Therefore, print " exiting" instead for " dying".
+ */
+ pr_warn("MemAlloc: %s(%u) flags=0x%x switches=%lu%s%s%s%s%s\n",
+ p->comm, p->pid, p->flags, p->nvcsw + p->nivcsw, buf,
+ (p->state & TASK_UNINTERRUPTIBLE) ?
+ " uninterruptible" : "",
+ (type & (1 << MEMALLOC_TYPE_EXITING)) ?
+ " exiting" : "",
+ (type & (1 << MEMALLOC_TYPE_DYING)) ? " dying" : "",
+ (type & (1 << MEMALLOC_TYPE_OOM_VICTIM)) ?
+ " victim" : "");
+ sched_show_task(p);
+ /*
+ * Since there could be thousands of tasks to report, we always
+ * call cond_resched() after each report, in order to avoid RCU
+ * stalls.
+ *
+ * Since not yet reported tasks are marked as
+ * p->memalloc.report == T, this loop can restart even if
+ * "g" or "p" went away.
+ *
+ * TODO: Try to wait for a while (e.g. sleep until usage of
+ * printk() buffer becomes less than 75%) in order to avoid
+ * dropping messages.
+ */
+ if (!rcu_lock_break(g, p))
+ goto restart_report;
+ }
+ rcu_read_unlock();
+ cond_resched();
+ /* Show memory information. (SysRq-m) */
+ show_mem(0, NULL);
+ /* Show workqueue state. */
+ show_workqueue_state();
+ /* Show lock information. (SysRq-d) */
+ debug_show_all_locks();
+ pr_warn("MemAlloc-Info: stalling=%u dying=%u exiting=%u victim=%u oom_count=%u\n",
+ stalling_tasks, sigkill_pending, exiting_tasks, memdie_pending,
+ out_of_memory_count);
+ return stalling_tasks;
+}
+#endif /* CONFIG_DETECT_MEMALLOC_STALL_TASK */
+
/*
* Process updating of timeout sysctl
*/
@@ -237,12 +435,28 @@ void reset_hung_task_detector(void)
static int watchdog(void *dummy)
{
unsigned long hung_last_checked = jiffies;
+#ifdef CONFIG_DETECT_MEMALLOC_STALL_TASK
+ unsigned long stall_last_checked = hung_last_checked;
+#endif
set_user_nice(current, 0);
for ( ; ; ) {
unsigned long timeout = sysctl_hung_task_timeout_secs;
long t = hung_timeout_jiffies(hung_last_checked, timeout);
+#ifdef CONFIG_DETECT_MEMALLOC_STALL_TASK
+ unsigned long timeout2 = sysctl_memalloc_task_warning_secs;
+ long t2 = hung_timeout_jiffies(stall_last_checked, timeout2);
+
+ if (t2 <= 0) {
+ if (memalloc_maybe_stalling())
+ check_memalloc_stalling_tasks(timeout2);
+ stall_last_checked = jiffies;
+ continue;
+ }
+#else
+ long t2 = t;
+#endif
if (t <= 0) {
if (!atomic_xchg(&reset_hung_task, 0))
@@ -250,7 +464,7 @@ static int watchdog(void *dummy)
hung_last_checked = jiffies;
continue;
}
- schedule_timeout_interruptible(t);
+ schedule_timeout_interruptible(min(t, t2));
}
return 0;
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 4dfba1a..ce73cb3 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1070,6 +1070,16 @@ static int sysrq_sysctl_handler(struct ctl_table *table, int write,
.proc_handler = proc_dointvec_minmax,
.extra1 = &neg_one,
},
+#ifdef CONFIG_DETECT_MEMALLOC_STALL_TASK
+ {
+ .procname = "memalloc_task_warning_secs",
+ .data = &sysctl_memalloc_task_warning_secs,
+ .maxlen = sizeof(unsigned long),
+ .mode = 0644,
+ .proc_handler = proc_dohung_task_timeout_secs,
+ .extra2 = &hung_task_timeout_max,
+ },
+#endif
#endif
#ifdef CONFIG_RT_MUTEXES
{
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index f6aece3..7635b2d 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -913,6 +913,30 @@ config WQ_WATCHDOG
state. This can be configured through kernel parameter
"workqueue.watchdog_thresh" and its sysfs counterpart.
+config DETECT_MEMALLOC_STALL_TASK
+ bool "Detect tasks stalling inside memory allocator"
+ default n
+ depends on DETECT_HUNG_TASK
+ help
+ This option emits warning messages and traces when memory
+ allocation requests are stalling, in order to catch unexplained
+ hangups/reboots caused by memory allocation stalls.
+
+config DEFAULT_MEMALLOC_TASK_TIMEOUT
+ int "Default timeout for stalling task detection (in seconds)"
+ depends on DETECT_MEMALLOC_STALL_TASK
+ default 60
+ help
+ This option controls the default timeout (in seconds) used
+ to determine when a task has become non-responsive and should
+ be considered stalling inside memory allocator.
+
+ It can be adjusted at runtime via the kernel.memalloc_task_warning_secs
+ sysctl or by writing a value to
+ /proc/sys/kernel/memalloc_task_warning_secs.
+
+ A timeout of 0 disables the check. The default is 60 seconds.
+
endmenu # "Debug lockups and hangs"
config PANIC_ON_OOPS
diff --git a/mm/mempool.c b/mm/mempool.c
index 47a659d..8b449af 100644
--- a/mm/mempool.c
+++ b/mm/mempool.c
@@ -324,11 +324,14 @@ void *mempool_alloc(mempool_t *pool, gfp_t gfp_mask)
gfp_temp = gfp_mask & ~(__GFP_DIRECT_RECLAIM|__GFP_IO);
+ start_memalloc_timer(gfp_temp, -1);
repeat_alloc:
element = pool->alloc(gfp_temp, pool->pool_data);
- if (likely(element != NULL))
+ if (likely(element != NULL)) {
+ stop_memalloc_timer(gfp_temp);
return element;
+ }
spin_lock_irqsave(&pool->lock, flags);
if (likely(pool->curr_nr)) {
@@ -341,6 +344,7 @@ void *mempool_alloc(mempool_t *pool, gfp_t gfp_mask)
* for debugging.
*/
kmemleak_update_trace(element);
+ stop_memalloc_timer(gfp_temp);
return element;
}
@@ -350,13 +354,16 @@ void *mempool_alloc(mempool_t *pool, gfp_t gfp_mask)
*/
if (gfp_temp != gfp_mask) {
spin_unlock_irqrestore(&pool->lock, flags);
+ stop_memalloc_timer(gfp_temp);
gfp_temp = gfp_mask;
+ start_memalloc_timer(gfp_temp, -1);
goto repeat_alloc;
}
/* We must not sleep if !__GFP_DIRECT_RECLAIM */
if (!(gfp_mask & __GFP_DIRECT_RECLAIM)) {
spin_unlock_irqrestore(&pool->lock, flags);
+ stop_memalloc_timer(gfp_temp);
return NULL;
}
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index 04c9143..3c652c6 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -47,6 +47,8 @@
#define CREATE_TRACE_POINTS
#include <trace/events/oom.h>
+unsigned int out_of_memory_count;
+
int sysctl_panic_on_oom;
int sysctl_oom_kill_allocating_task;
int sysctl_oom_dump_tasks = 1;
@@ -984,6 +986,7 @@ bool out_of_memory(struct oom_control *oc)
unsigned long freed = 0;
enum oom_constraint constraint = CONSTRAINT_NONE;
+ out_of_memory_count++;
if (oom_killer_disabled)
return false;
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index e7521ec..18f6018 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -3679,8 +3679,10 @@ bool gfp_pfmemalloc_allowed(gfp_t gfp_mask)
enum compact_result compact_result;
int compaction_retries;
int no_progress_loops;
+#ifndef CONFIG_DETECT_MEMALLOC_STALL_TASK
unsigned long alloc_start = jiffies;
unsigned int stall_timeout = 10 * HZ;
+#endif
unsigned int cpuset_mems_cookie;
/*
@@ -3810,6 +3812,7 @@ bool gfp_pfmemalloc_allowed(gfp_t gfp_mask)
if (!can_direct_reclaim)
goto nopage;
+#ifndef CONFIG_DETECT_MEMALLOC_STALL_TASK
/* Make sure we know about allocations which stall for too long */
if (time_after(jiffies, alloc_start + stall_timeout)) {
warn_alloc(gfp_mask & ~__GFP_NOWARN, ac->nodemask,
@@ -3817,6 +3820,7 @@ bool gfp_pfmemalloc_allowed(gfp_t gfp_mask)
jiffies_to_msecs(jiffies-alloc_start), order);
stall_timeout += 10 * HZ;
}
+#endif
/* Avoid recursion of direct reclaim */
if (current->flags & PF_MEMALLOC)
@@ -3988,6 +3992,76 @@ static inline void finalise_ac(gfp_t gfp_mask,
ac->high_zoneidx, ac->nodemask);
}
+#ifdef CONFIG_DETECT_MEMALLOC_STALL_TASK
+
+static DEFINE_PER_CPU_ALIGNED(int, memalloc_in_flight[2]);
+static u8 memalloc_active_index; /* Either 0 or 1. */
+
+/* Called periodically with sysctl_memalloc_task_warning_secs interval. */
+bool memalloc_maybe_stalling(void)
+{
+ int cpu;
+ int sum = 0;
+ const u8 idx = memalloc_active_index ^ 1;
+
+ for_each_online_cpu(cpu)
+ sum += per_cpu(memalloc_in_flight[idx], cpu);
+ if (sum)
+ return true;
+ memalloc_active_index ^= 1;
+ return false;
+}
+
+void start_memalloc_timer(const gfp_t gfp_mask, const int order)
+{
+ struct memalloc_info *m = ¤t->memalloc;
+
+ /* We don't check for stalls for !__GFP_DIRECT_RECLAIM allocations. */
+ if (!(gfp_mask & __GFP_DIRECT_RECLAIM))
+ return;
+ /* Record the beginning of memory allocation request. */
+ if (!m->in_flight) {
+ m->sequence++;
+ m->start = jiffies;
+ m->order = order;
+ m->gfp = gfp_mask;
+ m->idx = memalloc_active_index;
+ /*
+ * is_stalling_task() depends on ->in_flight being updated
+ * after ->start is stored.
+ */
+ smp_wmb();
+ this_cpu_inc(memalloc_in_flight[m->idx]);
+ }
+ m->in_flight++;
+}
+
+void stop_memalloc_timer(const gfp_t gfp_mask)
+{
+ struct memalloc_info *m = ¤t->memalloc;
+
+ if ((gfp_mask & __GFP_DIRECT_RECLAIM) && !--m->in_flight)
+ this_cpu_dec(memalloc_in_flight[m->idx]);
+}
+
+static void memalloc_counter_fold(int cpu)
+{
+ int counter;
+ u8 idx;
+
+ for (idx = 0; idx < 2; idx++) {
+ counter = per_cpu(memalloc_in_flight[idx], cpu);
+ if (!counter)
+ continue;
+ this_cpu_add(memalloc_in_flight[idx], counter);
+ per_cpu(memalloc_in_flight[idx], cpu) = 0;
+ }
+}
+
+#else
+#define memalloc_counter_fold(cpu) do { } while (0)
+#endif
+
/*
* This is the 'heart' of the zoned buddy allocator.
*/
@@ -4027,7 +4101,9 @@ struct page *
if (unlikely(ac.nodemask != nodemask))
ac.nodemask = nodemask;
+ start_memalloc_timer(alloc_mask, order);
page = __alloc_pages_slowpath(alloc_mask, order, &ac);
+ stop_memalloc_timer(alloc_mask);
out:
if (memcg_kmem_enabled() && (gfp_mask & __GFP_ACCOUNT) && page &&
@@ -6776,6 +6852,12 @@ static int page_alloc_cpu_dead(unsigned int cpu)
* race with what we are doing.
*/
cpu_vm_stats_fold(cpu);
+
+ /*
+ * Zero the in-flight counters of the dead processor so that
+ * memalloc_maybe_stalling() needs to check only online processors.
+ */
+ memalloc_counter_fold(cpu);
return 0;
}
--
1.8.3.1
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related
* Re: [PATCH] mm: per-cgroup memory reclaim stats
From: Vladimir Davydov @ 2017-05-20 19:15 UTC (permalink / raw)
To: Roman Gushchin
Cc: Johannes Weiner, Tejun Heo, Li Zefan, Michal Hocko, cgroups,
linux-doc, linux-kernel, linux-mm
In-Reply-To: <1494530183-30808-1-git-send-email-guro@fb.com>
On Thu, May 11, 2017 at 08:16:23PM +0100, Roman Gushchin wrote:
> Track the following reclaim counters for every memory cgroup:
> PGREFILL, PGSCAN, PGSTEAL, PGACTIVATE, PGDEACTIVATE, PGLAZYFREE and
> PGLAZYFREED.
>
> These values are exposed using the memory.stats interface of cgroup v2.
>
> The meaning of each value is the same as for global counters,
> available using /proc/vmstat.
>
> Also, for consistency, rename mem_cgroup_count_vm_event() to
> count_memcg_event_mm().
>
> Signed-off-by: Roman Gushchin <guro@fb.com>
> Suggested-by: Johannes Weiner <hannes@cmpxchg.org>
> Cc: Johannes Weiner <hannes@cmpxchg.org>
> Cc: Tejun Heo <tj@kernel.org>
> Cc: Li Zefan <lizefan@huawei.com>
> Cc: Michal Hocko <mhocko@kernel.org>
> Cc: Vladimir Davydov <vdavydov.dev@gmail.com>
> Cc: cgroups@vger.kernel.org
> Cc: linux-doc@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-mm@kvack.org
Acked-by: Vladimir Davydov <vdavydov.dev@gmail.com>
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [RFC PATCH] mm, oom: cgroup-aware OOM-killer
From: Vladimir Davydov @ 2017-05-20 18:37 UTC (permalink / raw)
To: Roman Gushchin
Cc: Johannes Weiner, Tejun Heo, Li Zefan, Michal Hocko, Tetsuo Handa,
kernel-team, cgroups, linux-doc, linux-kernel, linux-mm
In-Reply-To: <1495124884-28974-1-git-send-email-guro@fb.com>
Hello Roman,
On Thu, May 18, 2017 at 05:28:04PM +0100, Roman Gushchin wrote:
...
> +5-2-4. Cgroup-aware OOM Killer
> +
> +Cgroup v2 memory controller implements a cgroup-aware OOM killer.
> +It means that it treats memory cgroups as memory consumers
> +rather then individual processes. Under the OOM conditions it tries
> +to find an elegible leaf memory cgroup, and kill all processes
> +in this cgroup. If it's not possible (e.g. all processes belong
> +to the root cgroup), it falls back to the traditional per-process
> +behaviour.
I agree that the current OOM victim selection algorithm is totally
unfair in a system using containers and it has been crying for rework
for the last few years now, so it's great to see this finally coming.
However, I don't reckon that killing a whole leaf cgroup is always the
best practice. It does make sense when cgroups are used for
containerizing services or applications, because a service is unlikely
to remain operational after one of its processes is gone, but one can
also use cgroups to containerize processes started by a user. Kicking a
user out for one of her process has gone mad doesn't sound right to me.
Another example when the policy you're suggesting fails in my opinion is
in case a service (cgroup) consists of sub-services (sub-cgroups) that
run processes. The main service may stop working normally if one of its
sub-services is killed. So it might make sense to kill not just an
individual process or a leaf cgroup, but the whole main service with all
its sub-services.
And both kinds of workloads (services/applications and individual
processes run by users) can co-exist on the same host - consider the
default systemd setup, for instance.
IMHO it would be better to give users a choice regarding what they
really want for a particular cgroup in case of OOM - killing the whole
cgroup or one of its descendants. For example, we could introduce a
per-cgroup flag that would tell the kernel whether the cgroup can
tolerate killing a descendant or not. If it can, the kernel will pick
the fattest sub-cgroup or process and check it. If it cannot, it will
kill the whole cgroup and all its processes and sub-cgroups.
> +
> +The memory controller tries to make the best choise of a victim cgroup.
> +In general, it tries to select the largest cgroup, matching given
> +node/zone requirements, but the concrete algorithm is not defined,
> +and may be changed later.
> +
> +This affects both system- and cgroup-wide OOMs. For a cgroup-wide OOM
> +the memory controller considers only cgroups belonging to a sub-tree
> +of the OOM-ing cgroup, including itself.
...
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index c131f7e..8d07481 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -2625,6 +2625,75 @@ static inline bool memcg_has_children(struct mem_cgroup *memcg)
> return ret;
> }
>
> +bool mem_cgroup_select_oom_victim(struct oom_control *oc)
> +{
> + struct mem_cgroup *iter;
> + unsigned long chosen_memcg_points;
> +
> + oc->chosen_memcg = NULL;
> +
> + if (mem_cgroup_disabled())
> + return false;
> +
> + if (!cgroup_subsys_on_dfl(memory_cgrp_subsys))
> + return false;
> +
> + pr_info("Choosing a victim memcg because of %s",
> + oc->memcg ?
> + "memory limit reached of cgroup " :
> + "out of memory\n");
> + if (oc->memcg) {
> + pr_cont_cgroup_path(oc->memcg->css.cgroup);
> + pr_cont("\n");
> + }
> +
> + chosen_memcg_points = 0;
> +
> + for_each_mem_cgroup_tree(iter, oc->memcg) {
> + unsigned long points;
> + int nid;
> +
> + if (mem_cgroup_is_root(iter))
> + continue;
> +
> + if (memcg_has_children(iter))
> + continue;
> +
> + points = 0;
> + for_each_node_state(nid, N_MEMORY) {
> + if (oc->nodemask && !node_isset(nid, *oc->nodemask))
> + continue;
> + points += mem_cgroup_node_nr_lru_pages(iter, nid,
> + LRU_ALL_ANON | BIT(LRU_UNEVICTABLE));
> + }
> + points += mem_cgroup_get_nr_swap_pages(iter);
I guess we should also take into account kmem as well (unreclaimable
slabs, kernel stacks, socket buffers).
> +
> + pr_info("Memcg ");
> + pr_cont_cgroup_path(iter->css.cgroup);
> + pr_cont(": %lu\n", points);
> +
> + if (points > chosen_memcg_points) {
> + if (oc->chosen_memcg)
> + css_put(&oc->chosen_memcg->css);
> +
> + oc->chosen_memcg = iter;
> + css_get(&iter->css);
> +
> + chosen_memcg_points = points;
> + }
> + }
> +
> + if (oc->chosen_memcg) {
> + pr_info("Kill memcg ");
> + pr_cont_cgroup_path(oc->chosen_memcg->css.cgroup);
> + pr_cont(" (%lu)\n", chosen_memcg_points);
> + } else {
> + pr_info("No elegible memory cgroup found\n");
> + }
> +
> + return !!oc->chosen_memcg;
> +}
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* [v4 1/1] mm: Adaptive hash table scaling
From: Pavel Tatashin @ 2017-05-20 17:06 UTC (permalink / raw)
To: akpm, linux-kernel, linux-mm, mhocko
In-Reply-To: <1495300013-653283-1-git-send-email-pasha.tatashin@oracle.com>
Allow hash tables to scale with memory but at slower pace, when HASH_ADAPT
is provided every time memory quadruples the sizes of hash tables will only
double instead of quadrupling as well. This algorithm starts working only
when memory size reaches a certain point, currently set to 64G.
This is example of dentry hash table size, before and after four various
memory configurations:
MEMORY SCALE HASH_SIZE
old new old new
8G 13 13 8M 8M
16G 13 13 16M 16M
32G 13 13 32M 32M
64G 13 13 64M 64M
128G 13 14 128M 64M
256G 13 14 256M 128M
512G 13 15 512M 128M
1024G 13 15 1024M 256M
2048G 13 16 2048M 256M
4096G 13 16 4096M 512M
8192G 13 17 8192M 512M
16384G 13 17 16384M 1024M
32768G 13 18 32768M 1024M
65536G 13 18 65536M 2048M
Signed-off-by: Pavel Tatashin <pasha.tatashin@oracle.com>
---
mm/page_alloc.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 8afa63e81e73..15bba5c325a5 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -7169,6 +7169,17 @@ static unsigned long __init arch_reserved_kernel_pages(void)
#endif
/*
+ * Adaptive scale is meant to reduce sizes of hash tables on large memory
+ * machines. As memory size is increased the scale is also increased but at
+ * slower pace. Starting from ADAPT_SCALE_BASE (64G), every time memory
+ * quadruples the scale is increased by one, which means the size of hash table
+ * only doubles, instead of quadrupling as well.
+ */
+#define ADAPT_SCALE_BASE (64ull << 30)
+#define ADAPT_SCALE_SHIFT 2
+#define ADAPT_SCALE_NPAGES (ADAPT_SCALE_BASE >> PAGE_SHIFT)
+
+/*
* allocate a large system hash table from bootmem
* - it is assumed that the hash table must contain an exact power-of-2
* quantity of entries
@@ -7199,6 +7210,14 @@ void *__init alloc_large_system_hash(const char *tablename,
if (PAGE_SHIFT < 20)
numentries = round_up(numentries, (1<<20)/PAGE_SIZE);
+ if (!high_limit) {
+ unsigned long long adapt;
+
+ for (adapt = ADAPT_SCALE_NPAGES; adapt < numentries;
+ adapt <<= ADAPT_SCALE_SHIFT)
+ scale++;
+ }
+
/* limit to 1 bucket per 2^scale bytes of low memory */
if (scale > PAGE_SHIFT)
numentries >>= (scale - PAGE_SHIFT);
--
2.13.0
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related
* [v4 0/1] mm: Adaptive hash table scaling
From: Pavel Tatashin @ 2017-05-20 17:06 UTC (permalink / raw)
To: akpm, linux-kernel, linux-mm, mhocko
Changes from v3 - v4:
- Fixed an issue with 32-bit overflow (adapt is ull now instead ul)
- Added changes suggested by Michal Hocko: use high_limit instead of
a new flag to determine that we should use this new scaling.
Pavel Tatashin (1):
mm: Adaptive hash table scaling
mm/page_alloc.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
--
2.13.0
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH] slub/memcg: Cure the brainless abuse of sysfs attributes
From: Steven Rostedt @ 2017-05-20 14:25 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Thomas Gleixner, LKML, linux-mm, Johannes Weiner, Michal Hocko,
Christoph Lameter, Andrew Morton, Peter Zijlstra
In-Reply-To: <20170520131645.GA5058@infradead.org>
On Sat, 20 May 2017 06:16:45 -0700
Christoph Hellwig <hch@infradead.org> wrote:
> On Sat, May 20, 2017 at 12:52:03PM +0200, Thomas Gleixner wrote:
> > This should be rewritten proper by adding a propagate() callback to those
> > slub_attributes which must be propagated and avoid that insane conversion
> > to and from ASCII
>
> Exactly..
>
> >, but that's too large for a hot fix.
>
> What made this such a hot fix? Looks like this crap has been in
> for quite a while.
It can cause a deadlock with get_online_cpus() that has been uncovered
by recent cpu hotplug and lockdep changes that Thomas and Peter have
been doing.
[ 102.567308] Possible unsafe locking scenario:
[ 102.567308]
[ 102.574846] CPU0 CPU1
[ 102.580148] ---- ----
[ 102.585421] lock(cpu_hotplug.lock);
[ 102.589808] lock(slab_mutex);
[ 102.596166] lock(cpu_hotplug.lock);
[ 102.603028] lock(slab_mutex);
[ 102.606846]
[ 102.606846] *** DEADLOCK ***
-- Steve
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH] slub/memcg: Cure the brainless abuse of sysfs attributes
From: Christoph Hellwig @ 2017-05-20 13:16 UTC (permalink / raw)
To: Thomas Gleixner
Cc: LKML, linux-mm, Johannes Weiner, Michal Hocko, Christoph Lameter,
Andrew Morton, Steven Rostedt, Peter Zijlstra
In-Reply-To: <alpine.DEB.2.20.1705201244540.2255@nanos>
On Sat, May 20, 2017 at 12:52:03PM +0200, Thomas Gleixner wrote:
> This should be rewritten proper by adding a propagate() callback to those
> slub_attributes which must be propagated and avoid that insane conversion
> to and from ASCII
Exactly..
>, but that's too large for a hot fix.
What made this such a hot fix? Looks like this crap has been in
for quite a while.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* [PATCH] slub/memcg: Cure the brainless abuse of sysfs attributes
From: Thomas Gleixner @ 2017-05-20 10:52 UTC (permalink / raw)
To: LKML
Cc: linux-mm, Johannes Weiner, Michal Hocko, Christoph Lameter,
Andrew Morton, Steven Rostedt, Peter Zijlstra
memcg_propagate_slab_attrs() abuses the sysfs attribute file functions to
propagate settings from the root kmem_cache to a newly created
kmem_cache. It does that with:
attr->show(root, buf);
attr->store(new, buf, strlen(bug);
Aside of being a lazy and absurd hackery this is broken because it does not
check the return value of the show() function.
Some of the show() functions return 0 w/o touching the buffer. That means in
such a case the store function is called with the stale content of the
previous show(). That causes nonsense like invoking kmem_cache_shrink() on
a newly created kmem_cache. In the worst case it would cause handing in an
uninitialized buffer.
This should be rewritten proper by adding a propagate() callback to those
slub_attributes which must be propagated and avoid that insane conversion
to and from ASCII, but that's too large for a hot fix.
Check at least the return value of the show() function, so calling store()
with stale content is prevented.
Reported-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: stable@vger.kernel.org
---
mm/slub.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -5512,6 +5512,7 @@ static void memcg_propagate_slab_attrs(s
char mbuf[64];
char *buf;
struct slab_attribute *attr = to_slab_attr(slab_attrs[i]);
+ ssize_t len;
if (!attr || !attr->store || !attr->show)
continue;
@@ -5536,8 +5537,9 @@ static void memcg_propagate_slab_attrs(s
buf = buffer;
}
- attr->show(root_cache, buf);
- attr->store(s, buf, strlen(buf));
+ len = attr->show(root_cache, buf);
+ if (len > 0)
+ attr->store(s, buf, len);
}
if (buffer)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [kernel-hardening] [PATCH 1/1] Sealable memory support
From: Greg KH @ 2017-05-20 8:51 UTC (permalink / raw)
To: Igor Stoppa
Cc: mhocko, dave.hansen, labbott, linux-mm, kernel-hardening,
linux-kernel
In-Reply-To: <20170519103811.2183-2-igor.stoppa@huawei.com>
On Fri, May 19, 2017 at 01:38:11PM +0300, Igor Stoppa wrote:
> Dynamically allocated variables can be made read only,
> after they have been initialized, provided that they reside in memory
> pages devoid of any RW data.
>
> The implementation supplies means to create independent pools of memory,
> which can be individually created, sealed/unsealed and destroyed.
>
> A global pool is made available for those kernel modules that do not
> need to manage an independent pool.
>
> Signed-off-by: Igor Stoppa <igor.stoppa@huawei.com>
> ---
> mm/Makefile | 2 +-
> mm/smalloc.c | 200 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> mm/smalloc.h | 61 ++++++++++++++++++
> 3 files changed, 262 insertions(+), 1 deletion(-)
> create mode 100644 mm/smalloc.c
> create mode 100644 mm/smalloc.h
This is really nice, do you have a follow-on patch showing how any of
the kernel can be changed to use this new subsystem? Without that, it
might be hard to get this approved (we don't like adding new apis
without users.)
thanks,
greg k-h
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [patch 2/2] MM: allow per-cpu vmstat_threshold and vmstat_worker configuration
From: Marcelo Tosatti @ 2017-05-20 8:26 UTC (permalink / raw)
To: Christoph Lameter
Cc: Luiz Capitulino, linux-kernel, linux-mm, Rik van Riel,
Linux RT Users, cmetcalf
In-Reply-To: <alpine.DEB.2.20.1705191205580.19631@east.gentwo.org>
On Fri, May 19, 2017 at 12:13:26PM -0500, Christoph Lameter wrote:
> On Fri, 19 May 2017, Marcelo Tosatti wrote:
>
> > Use-case: realtime application on an isolated core which for some reason
> > updates vmstatistics.
>
> Ok that is already only happening every 2 seconds by default and that
> interval is configurable via the vmstat_interval proc setting.
>
> > > Just a measurement of vmstat_worker. Pointless.
> >
> > Shouldnt the focus be on general scenarios rather than particular
> > usecases, so that the solution covers a wider range of usecases?
>
> Yes indeed and as far as I can tell the wider usecases are covered. Not
> sure that there is anything required here.
>
> > The situation as i see is as follows:
> >
> > Your point of view is: an "isolated CPU" with a set of applications
> > cannot update vm statistics, otherwise they pay the vmstat_update cost:
> >
> > kworker/5:1-245 [005] ....1.. 673.454295: workqueue_execute_start: work struct ffffa0cf6e493e20: function vmstat_update
> > kworker/5:1-245 [005] ....1.. 673.454305: workqueue_execute_end: work struct ffffa0cf6e493e20
> >
> > Thats 10us for example.
>
> Well with a decent cpu that is 3 usec and it occurs infrequently on the
> order of once per multiple seconds.
>
> > So if want to customize a realtime setup whose code updates vmstatistic,
> > you are dead. You have to avoid any systemcall which possibly updates
> > vmstatistics (now and in the future kernel versions).
>
> You are already dead because you allow IPIs and other kernel processing
> which creates far more overhead. Still fail to see the point.
>
> > The point is that these vmstat updates are rare. From
> > http://www.7-cpu.com/cpu/Haswell.html:
> >
> > RAM Latency = 36 cycles + 57 ns (3.4 GHz i7-4770)
> > RAM Latency = 62 cycles + 100 ns (3.6 GHz E5-2699 dual)
> >
> > Lets round to 100ns = 0.1us.
>
> That depends on the kernel functionality used.
>
> > You need 100 vmstat updates (all misses to RAM, the worst possible case)
> > to have equivalent amount of time of the batching version.
>
> The batching version occurs every couple of seconds if at all.
>
> > But thats not the point. The point is the 10us interruption
> > to execution of the realtime app (which can either mean
> > your current deadline requirements are not met, or that
> > another application with lowest latency requirement can't
> > be used).
>
> Ok then you need to get rid of the IPIs and the other stuff that you have
> going on with the OS first I think.
I'll measure the cost of all IPIs in the system to confirm
vmstat_update's costs is larger than the cost of any IPI.
> > So why are you against integrating this simple, isolated patch which
> > does not affect how current logic works?
>
> Frankly the argument does not make sense. Vmstat updates occur very
> infrequently (probably even less than you IPIs and the other OS stuff that
> also causes additional latencies that you seem to be willing to tolerate).
>
> And you can configure the interval of vmstat updates freely.... Set
> the vmstat_interval to 60 seconds instead of 2 for a try? Is that rare
> enough?
Not rare enough. Never is rare enough.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH] mm: clarify why we want kmalloc before falling backto vmallock
From: Michal Hocko @ 2017-05-20 7:27 UTC (permalink / raw)
To: John Hubbard; +Cc: Andrew Morton, Chris Wilson, Vlastimil Babka, linux-mm, LKML
In-Reply-To: <d6121d8b-8d0f-88da-cd67-e9123bb96454@nvidia.com>
On Fri 19-05-17 17:46:58, John Hubbard wrote:
> On 05/17/2017 01:09 AM, Michal Hocko wrote:
> >From: Michal Hocko <mhocko@suse.com>
> >
> >While converting drm_[cm]alloc* helpers to kvmalloc* variants Chris
> >Wilson has wondered why we want to try kmalloc before vmalloc fallback
> >even for larger allocations requests. Let's clarify that one larger
> >physically contiguous block is less likely to fragment memory than many
> >scattered pages which can prevent more large blocks from being created.
> >
> >Suggested-by: Chris Wilson <chris@chris-wilson.co.uk>
> >Signed-off-by: Michal Hocko <mhocko@suse.com>
> >---
> > mm/util.c | 5 ++++-
> > 1 file changed, 4 insertions(+), 1 deletion(-)
> >
> >diff --git a/mm/util.c b/mm/util.c
> >index 464df3489903..87499f8119f2 100644
> >--- a/mm/util.c
> >+++ b/mm/util.c
> >@@ -357,7 +357,10 @@ void *kvmalloc_node(size_t size, gfp_t flags, int node)
> > WARN_ON_ONCE((flags & GFP_KERNEL) != GFP_KERNEL);
> > /*
> >- * Make sure that larger requests are not too disruptive - no OOM
> >+ * We want to attempt a large physically contiguous block first because
> >+ * it is less likely to fragment multiple larger blocks and therefore
> >+ * contribute to a long term fragmentation less than vmalloc fallback.
> >+ * However make sure that larger requests are not too disruptive - no OOM
> > * killer and no allocation failure warnings as we have a fallback
> > */
>
> Thanks for adding this, it's great to have. Here's a slightly polished
> version of your words, if you like:
>
> /*
> * We want to attempt a large physically contiguous block first because
> * it is less likely to fragment multiple larger blocks. This approach
> * therefore contributes less to long term fragmentation than a vmalloc
> * fallback would. However, make sure that larger requests are not too
> * disruptive: no OOM killer and no allocation failure warnings, as we
> * have a fallback.
> */
Looks ok to me.
--
Michal Hocko
SUSE Labs
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* [RFC PATCH] vmalloc: show more detail info in vmallocinfo for clarify
From: Yisheng Xie @ 2017-05-20 6:47 UTC (permalink / raw)
To: akpm, mhocko, zijun_hu, mingo, thgarnie, kirill.shutemov,
aryabinin, chris
Cc: linux-mm, linux-kernel, guohanjun
When ioremap a 67112960 bytes vm_area with the vmallocinfo:
[..]
0xec79b000-0xec7fa000 389120 ftl_add_mtd+0x4d0/0x754 pages=94 vmalloc
0xec800000-0xecbe1000 4067328 kbox_proc_mem_write+0x104/0x1c4 phys=8b520000 ioremap
we get result:
0xf1000000-0xf5001000 67112960 devm_ioremap+0x38/0x7c phys=40000000 ioremap
For the align for ioremap must be less than '1 << IOREMAP_MAX_ORDER':
if (flags & VM_IOREMAP)
align = 1ul << clamp_t(int, get_count_order_long(size),
PAGE_SHIFT, IOREMAP_MAX_ORDER);
So it makes idiot like me a litter puzzle why jump the vm_area from
0xec800000-0xecbe1000 to 0xf1000000-0xf5001000, and leave
0xed000000-0xf1000000 as a big hole.
This is to show all of vm_area, including which is freeing but still in
vmap_area_list, to make it more clear about why we will get
0xf1000000-0xf5001000 int the above case. And we will get the
vmallocinfo like:
[..]
0xec79b000-0xec7fa000 389120 ftl_add_mtd+0x4d0/0x754 pages=94 vmalloc
0xec800000-0xecbe1000 4067328 kbox_proc_mem_write+0x104/0x1c4 phys=8b520000 ioremap
[..]
0xece7c000-0xece7e000 8192 freeing vm_area
0xece7e000-0xece83000 20480 vm_map_ram
0xf0099000-0xf00aa000 69632 vm_map_ram
0xf1000000-0xf5001000 67112960 devm_ioremap+0x38/0x7c phys=40000000 ioremap
after apply this patch.
Signed-off-by: Yisheng Xie <xieyisheng1@huawei.com>
---
mm/vmalloc.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index b52aeed..dbb24fc 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -314,6 +314,7 @@ unsigned long vmalloc_to_pfn(const void *vmalloc_addr)
/*** Global kva allocator ***/
+#define VM_LAZY_FREE 0x02
#define VM_VM_AREA 0x04
static DEFINE_SPINLOCK(vmap_area_lock);
@@ -1486,6 +1487,7 @@ struct vm_struct *remove_vm_area(const void *addr)
spin_lock(&vmap_area_lock);
va->vm = NULL;
va->flags &= ~VM_VM_AREA;
+ va->flags |= VM_LAZY_FREE;
spin_unlock(&vmap_area_lock);
vmap_debug_free_range(va->va_start, va->va_end);
@@ -2684,8 +2686,14 @@ static int s_show(struct seq_file *m, void *p)
* s_show can encounter race with remove_vm_area, !VM_VM_AREA on
* behalf of vmap area is being tear down or vm_map_ram allocation.
*/
- if (!(va->flags & VM_VM_AREA))
+ if (!(va->flags & VM_VM_AREA)) {
+ seq_printf(m, "0x%pK-0x%pK %7ld %s\n",
+ (void *)va->va_start, (void *)va->va_end,
+ va->va_end - va->va_start,
+ va->flags & VM_LAZY_FREE ? "freeing vm_area" : "vm_map_ram");
+
return 0;
+ }
v = va->vm;
--
1.7.12.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related
* Re: mm, something wring in page_lock_anon_vma_read()?
From: zhong jiang @ 2017-05-20 3:01 UTC (permalink / raw)
To: Hugh Dickins
Cc: Xishi Qiu, Andrew Morton, Tejun Heo, Michal Hocko,
Johannes Weiner, Mel Gorman, Michal Hocko, Vlastimil Babka,
Minchan Kim, David Rientjes, Joonsoo Kim, aarcange,
sumeet.keswani, Rik van Riel, Linux MM, LKML
In-Reply-To: <alpine.LSU.2.11.1705191935220.11750@eggly.anvils>
On 2017/5/20 10:40, Hugh Dickins wrote:
> On Sat, 20 May 2017, Xishi Qiu wrote:
>> Here is a bug report form redhat: https://bugzilla.redhat.com/show_bug.cgi?id=1305620
>> And I meet the bug too. However it is hard to reproduce, and
>> 624483f3ea82598("mm: rmap: fix use-after-free in __put_anon_vma") is not help.
>>
>> From the vmcore, it seems that the page is still mapped(_mapcount=0 and _count=2),
>> and the value of mapping is a valid address(mapping = 0xffff8801b3e2a101),
>> but anon_vma has been corrupted.
>>
>> Any ideas?
> Sorry, no. I assume that _mapcount has been misaccounted, for example
> a pte mapped in on top of another pte; but cannot begin tell you where
> in Red Hat's kernel-3.10.0-229.4.2.el7 that might happen.
>
> Hugh
>
> .
>
Hi, Hugh
I find the following message from the dmesg.
[26068.316592] BUG: Bad rss-counter state mm:ffff8800a7de2d80 idx:1 val:1
I can prove that the __mapcount is misaccount. when task is exited. the rmap
still exist.
Thanks
zhongjiang
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: mm, something wring in page_lock_anon_vma_read()?
From: Hugh Dickins @ 2017-05-20 2:40 UTC (permalink / raw)
To: Xishi Qiu
Cc: Hugh Dickins, Andrew Morton, Tejun Heo, Michal Hocko,
Johannes Weiner, Mel Gorman, Michal Hocko, Vlastimil Babka,
Minchan Kim, David Rientjes, Joonsoo Kim, aarcange,
sumeet.keswani, Rik van Riel, Linux MM, LKML, zhong jiang
In-Reply-To: <591FA78E.9050307@huawei.com>
On Sat, 20 May 2017, Xishi Qiu wrote:
>
> Here is a bug report form redhat: https://bugzilla.redhat.com/show_bug.cgi?id=1305620
> And I meet the bug too. However it is hard to reproduce, and
> 624483f3ea82598("mm: rmap: fix use-after-free in __put_anon_vma") is not help.
>
> From the vmcore, it seems that the page is still mapped(_mapcount=0 and _count=2),
> and the value of mapping is a valid address(mapping = 0xffff8801b3e2a101),
> but anon_vma has been corrupted.
>
> Any ideas?
Sorry, no. I assume that _mapcount has been misaccounted, for example
a pte mapped in on top of another pte; but cannot begin tell you where
in Red Hat's kernel-3.10.0-229.4.2.el7 that might happen.
Hugh
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: mm, something wring in page_lock_anon_vma_read()?
From: Xishi Qiu @ 2017-05-20 2:18 UTC (permalink / raw)
To: Hugh Dickins
Cc: Andrew Morton, Tejun Heo, Michal Hocko, Johannes Weiner,
Mel Gorman, Michal Hocko, Vlastimil Babka, Minchan Kim,
David Rientjes, Joonsoo Kim, aarcange, sumeet.keswani,
Rik van Riel, Linux MM, LKML, zhong jiang
In-Reply-To: <alpine.LSU.2.11.1705191852360.11060@eggly.anvils>
On 2017/5/20 10:02, Hugh Dickins wrote:
> On Sat, 20 May 2017, Xishi Qiu wrote:
>> On 2017/5/20 6:00, Hugh Dickins wrote:
>>>
>>> You're ignoring the rcu_read_lock() on entry to page_lock_anon_vma_read(),
>>> and the SLAB_DESTROY_BY_RCU (recently renamed SLAB_TYPESAFE_BY_RCU) nature
>>> of the anon_vma_cachep kmem cache. It is not safe to muck with anon_vma->
>>> root in anon_vma_free(), others could still be looking at it.
>>>
>>> Hugh
>>>
>>
>> Hi Hugh,
>>
>> Thanks for your reply.
>>
>> SLAB_DESTROY_BY_RCU will let it call call_rcu() in free_slab(), but if the
>> anon_vma *reuse* by someone again, access root_anon_vma is not safe, right?
>
> That is safe, on reuse it is still a struct anon_vma; then the test for
> !page_mapped(page) will show that it's no longer a reliable anon_vma for
> this page, so page_lock_anon_vma_read() returns NULL.
>
> But of course, if page->_mapcount has been corrupted or misaccounted,
> it may think page_mapped(page) when actually page is not mapped,
> and the anon_vma is not good for it.
>
Hi Hugh,
Here is a bug report form redhat: https://bugzilla.redhat.com/show_bug.cgi?id=1305620
And I meet the bug too. However it is hard to reproduce, and
624483f3ea82598("mm: rmap: fix use-after-free in __put_anon_vma") is not help.
>From the vmcore, it seems that the page is still mapped(_mapcount=0 and _count=2),
and the value of mapping is a valid address(mapping = 0xffff8801b3e2a101),
but anon_vma has been corrupted.
Any ideas?
Thanks,
Xishi Qiu
>>
>> e.g. if I clean the root pointer before free it, then access root_anon_vma
>> in page_lock_anon_vma_read() is NULL pointer access, right?
>
> Yes, cleaning root pointer before free may result in NULL pointer access.
>
> Hugh
>
>>
>> anon_vma_free()
>> ...
>> anon_vma->root = NULL;
>> kmem_cache_free(anon_vma_cachep, anon_vma);
>> ...
>>
>> Thanks,
>> Xishi Qiu
>
> .
>
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [RFC PATCH v2 12/17] cgroup: Remove cgroup v2 no internal process constraint
From: Mike Galbraith @ 2017-05-20 2:10 UTC (permalink / raw)
To: Tejun Heo, Waiman Long
Cc: Li Zefan, Johannes Weiner, Peter Zijlstra, Ingo Molnar, cgroups,
linux-kernel, linux-doc, linux-mm, kernel-team, pjt, luto
In-Reply-To: <20170519203824.GC15279@wtj.duckdns.org>
On Fri, 2017-05-19 at 16:38 -0400, Tejun Heo wrote:
> Hello, Waiman.
>
> On Mon, May 15, 2017 at 09:34:11AM -0400, Waiman Long wrote:
> > The rationale behind the cgroup v2 no internal process constraint is
> > to avoid resouorce competition between internal processes and child
> > cgroups. However, not all controllers have problem with internal
> > process competiton. Enforcing this rule may lead to unnatural process
> > hierarchy and unneeded levels for those controllers.
>
> This isn't necessarily something we can determine by looking at the
> current state of controllers. It's true that some controllers - pid
> and perf - inherently only care about membership of each task but at
> the same time neither really suffers from the constraint either. CPU
> which is the problematic one here...
(+ cpuacct + cpuset)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox