| From 2a872fa4e9c8adc79c830e4009e1cc0c013a9d8a Mon Sep 17 00:00:00 2001 |
| From: "Steven Rostedt (VMware)" <rostedt@goodmis.org> |
| Date: Mon, 2 Apr 2018 10:33:56 -0400 |
| Subject: ring-buffer: Check if memory is available before allocation |
| |
| From: Steven Rostedt (VMware) <rostedt@goodmis.org> |
| |
| commit 2a872fa4e9c8adc79c830e4009e1cc0c013a9d8a upstream. |
| |
| The ring buffer is made up of a link list of pages. When making the ring |
| buffer bigger, it will allocate all the pages it needs before adding to the |
| ring buffer, and if it fails, it frees them and returns an error. This makes |
| increasing the ring buffer size an all or nothing action. When this was |
| first created, the pages were allocated with "NORETRY". This was to not |
| cause any Out-Of-Memory (OOM) actions from allocating the ring buffer. But |
| NORETRY was too strict, as the ring buffer would fail to expand even when |
| there's memory available, but was taken up in the page cache. |
| |
| Commit 848618857d253 ("tracing/ring_buffer: Try harder to allocate") changed |
| the allocating from NORETRY to RETRY_MAYFAIL. The RETRY_MAYFAIL would |
| allocate from the page cache, but if there was no memory available, it would |
| simple fail the allocation and not trigger an OOM. |
| |
| This worked fine, but had one problem. As the ring buffer would allocate one |
| page at a time, it could take up all memory in the system before it failed |
| to allocate and free that memory. If the allocation is happening and the |
| ring buffer allocates all memory and then tries to take more than available, |
| its allocation will not trigger an OOM, but if there's any allocation that |
| happens someplace else, that could trigger an OOM, even though once the ring |
| buffer's allocation fails, it would free up all the previous memory it tried |
| to allocate, and allow other memory allocations to succeed. |
| |
| Commit d02bd27bd33dd ("mm/page_alloc.c: calculate 'available' memory in a |
| separate function") separated out si_mem_availble() as a separate function |
| that could be used to see how much memory is available in the system. Using |
| this function to make sure that the ring buffer could be allocated before it |
| tries to allocate pages we can avoid allocating all memory in the system and |
| making it vulnerable to OOMs if other allocations are taking place. |
| |
| Link: http://lkml.kernel.org/r/1522320104-6573-1-git-send-email-zhaoyang.huang@spreadtrum.com |
| |
| CC: stable@vger.kernel.org |
| Cc: linux-mm@kvack.org |
| Fixes: 848618857d253 ("tracing/ring_buffer: Try harder to allocate") |
| Requires: d02bd27bd33dd ("mm/page_alloc.c: calculate 'available' memory in a separate function") |
| Reported-by: Zhaoyang Huang <huangzhaoyang@gmail.com> |
| Tested-by: Joel Fernandes <joelaf@google.com> |
| Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| kernel/trace/ring_buffer.c | 5 +++++ |
| 1 file changed, 5 insertions(+) |
| |
| --- a/kernel/trace/ring_buffer.c |
| +++ b/kernel/trace/ring_buffer.c |
| @@ -1136,6 +1136,11 @@ static int __rb_allocate_pages(long nr_p |
| struct buffer_page *bpage, *tmp; |
| long i; |
| |
| + /* Check if the available memory is there first */ |
| + i = si_mem_available(); |
| + if (i < nr_pages) |
| + return -ENOMEM; |
| + |
| for (i = 0; i < nr_pages; i++) { |
| struct page *page; |
| /* |