ring-buffer: Add set/clear_current_oom_origin() during allocations
authorSteven Rostedt (VMware) <rostedt@goodmis.org>
Wed, 4 Apr 2018 15:29:57 +0000 (11:29 -0400)
committerSteven Rostedt (VMware) <rostedt@goodmis.org>
Fri, 6 Apr 2018 12:56:52 +0000 (08:56 -0400)
As si_mem_available() can say there is enough memory even though the memory
available is not useable by the ring buffer, it is best to not kill innocent
applications because the ring buffer is taking up all the memory while it is
trying to allocate a great deal of memory.

If the allocator is user space (because kernel threads can also increase the
size of the kernel ring buffer on boot up), then after si_mem_available()
says there is enough memory, set the OOM killer to kill the current task if
an OOM triggers during the allocation.

Link: http://lkml.kernel.org/r/20180404062340.GD6312@dhcp22.suse.cz
Suggested-by: Michal Hocko <mhocko@kernel.org>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
kernel/trace/ring_buffer.c

index 966128f02121743ee6524bbf49dc39dcd8f6389e..c9cb9767d49b915e28d6eb50187d4d6805cea564 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/hash.h>
 #include <linux/list.h>
 #include <linux/cpu.h>
+#include <linux/oom.h>
 
 #include <asm/local.h>
 
@@ -1162,35 +1163,60 @@ static int rb_check_pages(struct ring_buffer_per_cpu *cpu_buffer)
 static int __rb_allocate_pages(long nr_pages, struct list_head *pages, int cpu)
 {
        struct buffer_page *bpage, *tmp;
+       bool user_thread = current->mm != NULL;
+       gfp_t mflags;
        long i;
 
-       /* Check if the available memory is there first */
+       /*
+        * Check if the available memory is there first.
+        * Note, si_mem_available() only gives us a rough estimate of available
+        * memory. It may not be accurate. But we don't care, we just want
+        * to prevent doing any allocation when it is obvious that it is
+        * not going to succeed.
+        */
        i = si_mem_available();
        if (i < nr_pages)
                return -ENOMEM;
 
+       /*
+        * __GFP_RETRY_MAYFAIL flag makes sure that the allocation fails
+        * gracefully without invoking oom-killer and the system is not
+        * destabilized.
+        */
+       mflags = GFP_KERNEL | __GFP_RETRY_MAYFAIL;
+
+       /*
+        * If a user thread allocates too much, and si_mem_available()
+        * reports there's enough memory, even though there is not.
+        * Make sure the OOM killer kills this thread. This can happen
+        * even with RETRY_MAYFAIL because another task may be doing
+        * an allocation after this task has taken all memory.
+        * This is the task the OOM killer needs to take out during this
+        * loop, even if it was triggered by an allocation somewhere else.
+        */
+       if (user_thread)
+               set_current_oom_origin();
        for (i = 0; i < nr_pages; i++) {
                struct page *page;
-               /*
-                * __GFP_RETRY_MAYFAIL flag makes sure that the allocation fails
-                * gracefully without invoking oom-killer and the system is not
-                * destabilized.
-                */
+
                bpage = kzalloc_node(ALIGN(sizeof(*bpage), cache_line_size()),
-                                   GFP_KERNEL | __GFP_RETRY_MAYFAIL,
-                                   cpu_to_node(cpu));
+                                   mflags, cpu_to_node(cpu));
                if (!bpage)
                        goto free_pages;
 
                list_add(&bpage->list, pages);
 
-               page = alloc_pages_node(cpu_to_node(cpu),
-                                       GFP_KERNEL | __GFP_RETRY_MAYFAIL, 0);
+               page = alloc_pages_node(cpu_to_node(cpu), mflags, 0);
                if (!page)
                        goto free_pages;
                bpage->page = page_address(page);
                rb_init_page(bpage->page);
+
+               if (user_thread && fatal_signal_pending(current))
+                       goto free_pages;
        }
+       if (user_thread)
+               clear_current_oom_origin();
 
        return 0;
 
@@ -1199,6 +1225,8 @@ free_pages:
                list_del_init(&bpage->list);
                free_buffer_page(bpage);
        }
+       if (user_thread)
+               clear_current_oom_origin();
 
        return -ENOMEM;
 }