| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| From: Joel Fernandes <joelaf@google.com> |
| Date: Mon, 18 Nov 2019 14:02:15 -0500 |
| Subject: UPSTREAM: rss_stat: Add support to detect RSS updates of external mm |
| |
| When a process updates the RSS of a different process, the rss_stat |
| tracepoint appears in the context of the process doing the update. This |
| can confuse userspace that the RSS of process doing the update is |
| updated, while in reality a different process's RSS was updated. |
| |
| This issue happens in reclaim paths such as with direct reclaim or |
| background reclaim. |
| |
| This patch adds more information to the tracepoint about whether the mm |
| being updated belongs to the current process's context (curr field). We |
| also include a hash of the mm pointer so that the process who the mm |
| belongs to can be uniquely identified (mm_id field). |
| |
| Also vsprintf.c is refactored a bit to allow reuse of hashing code. |
| |
| Change-Id: Ic87af93af608c83be0b08757aed99d2b9c2c01d8 |
| Reported-by: Ioannis Ilkos <ilkos@google.com> |
| Signed-off-by: Joel Fernandes (Google) <joel@joelfernandes.org> |
| Acked-by: Petr Mladek <pmladek@suse.com> # lib/vsprintf.c |
| Signed-off-by: Joel Fernandes <joelaf@google.com> |
| --- |
| include/linux/mm.h | 8 ++++---- |
| include/linux/string.h | 2 ++ |
| include/trace/events/kmem.h | 32 +++++++++++++++++++++++++++++--- |
| lib/vsprintf.c | 35 ++++++++++++++++++++++++----------- |
| mm/memory.c | 4 ++-- |
| 5 files changed, 61 insertions(+), 20 deletions(-) |
| |
| diff --git a/include/linux/mm.h b/include/linux/mm.h |
| index 7cf998f7b1bc..265ee4ac4d7a 100644 |
| --- a/include/linux/mm.h |
| +++ b/include/linux/mm.h |
| @@ -1643,27 +1643,27 @@ static inline unsigned long get_mm_counter(struct mm_struct *mm, int member) |
| return (unsigned long)val; |
| } |
| |
| -void mm_trace_rss_stat(int member, long count); |
| +void mm_trace_rss_stat(struct mm_struct *mm, int member, long count); |
| |
| static inline void add_mm_counter(struct mm_struct *mm, int member, long value) |
| { |
| long count = atomic_long_add_return(value, &mm->rss_stat.count[member]); |
| |
| - mm_trace_rss_stat(member, count); |
| + mm_trace_rss_stat(mm, member, count); |
| } |
| |
| static inline void inc_mm_counter(struct mm_struct *mm, int member) |
| { |
| long count = atomic_long_inc_return(&mm->rss_stat.count[member]); |
| |
| - mm_trace_rss_stat(member, count); |
| + mm_trace_rss_stat(mm, member, count); |
| } |
| |
| static inline void dec_mm_counter(struct mm_struct *mm, int member) |
| { |
| long count = atomic_long_dec_return(&mm->rss_stat.count[member]); |
| |
| - mm_trace_rss_stat(member, count); |
| + mm_trace_rss_stat(mm, member, count); |
| } |
| |
| /* Optimized variant when page is already known not to be PageAnon */ |
| diff --git a/include/linux/string.h b/include/linux/string.h |
| index b6ccdc2c7f02..02894e417565 100644 |
| --- a/include/linux/string.h |
| +++ b/include/linux/string.h |
| @@ -216,6 +216,8 @@ int bprintf(u32 *bin_buf, size_t size, const char *fmt, ...) __printf(3, 4); |
| extern ssize_t memory_read_from_buffer(void *to, size_t count, loff_t *ppos, |
| const void *from, size_t available); |
| |
| +int ptr_to_hashval(const void *ptr, unsigned long *hashval_out); |
| + |
| /** |
| * strstarts - does @str start with @prefix? |
| * @str: string to examine |
| diff --git a/include/trace/events/kmem.h b/include/trace/events/kmem.h |
| index 5a0666bfcf85..ad7e642bd497 100644 |
| --- a/include/trace/events/kmem.h |
| +++ b/include/trace/events/kmem.h |
| @@ -316,24 +316,50 @@ TRACE_EVENT(mm_page_alloc_extfrag, |
| __entry->change_ownership) |
| ); |
| |
| +/* |
| + * Required for uniquely and securely identifying mm in rss_stat tracepoint. |
| + */ |
| +#ifndef __PTR_TO_HASHVAL |
| +static unsigned int __maybe_unused mm_ptr_to_hash(const void *ptr) |
| +{ |
| + int ret; |
| + unsigned long hashval; |
| + |
| + ret = ptr_to_hashval(ptr, &hashval); |
| + if (ret) |
| + return 0; |
| + |
| + /* The hashed value is only 32-bit */ |
| + return (unsigned int)hashval; |
| +} |
| +#define __PTR_TO_HASHVAL |
| +#endif |
| + |
| TRACE_EVENT(rss_stat, |
| |
| - TP_PROTO(int member, |
| + TP_PROTO(struct mm_struct *mm, |
| + int member, |
| long count), |
| |
| - TP_ARGS(member, count), |
| + TP_ARGS(mm, member, count), |
| |
| TP_STRUCT__entry( |
| + __field(unsigned int, mm_id) |
| + __field(unsigned int, curr) |
| __field(int, member) |
| __field(long, size) |
| ), |
| |
| TP_fast_assign( |
| + __entry->mm_id = mm_ptr_to_hash(mm); |
| + __entry->curr = !!(current->mm == mm); |
| __entry->member = member; |
| __entry->size = (count << PAGE_SHIFT); |
| ), |
| |
| - TP_printk("member=%d size=%ldB", |
| + TP_printk("mm_id=%u curr=%d member=%d size=%ldB", |
| + __entry->mm_id, |
| + __entry->curr, |
| __entry->member, |
| __entry->size) |
| ); |
| diff --git a/lib/vsprintf.c b/lib/vsprintf.c |
| index e78017a3e1bd..465c5758d222 100644 |
| --- a/lib/vsprintf.c |
| +++ b/lib/vsprintf.c |
| @@ -738,6 +738,27 @@ static int __init initialize_ptr_random(void) |
| return ret; |
| } |
| early_initcall(initialize_ptr_random); |
| + |
| +int ptr_to_hashval(const void *ptr, unsigned long *hashval_out) |
| +{ |
| + unsigned long hashval; |
| + |
| + if (static_branch_unlikely(¬_filled_random_ptr_key)) |
| + return -EAGAIN; |
| + |
| +#ifdef CONFIG_64BIT |
| + hashval = (unsigned long)siphash_1u64((u64)ptr, &ptr_key); |
| + /* |
| + * Mask off the first 32 bits, this makes explicit that we have |
| + * modified the address (and 32 bits is plenty for a unique ID). |
| + */ |
| + hashval = hashval & 0xffffffff; |
| +#else |
| + hashval = (unsigned long)siphash_1u32((u32)ptr, &ptr_key); |
| +#endif |
| + *hashval_out = hashval; |
| + return 0; |
| +} |
| |
| /* Maps a pointer to a 32 bit unique identifier. */ |
| static char *ptr_to_id(char *buf, char *end, const void *ptr, |
| @@ -745,6 +766,7 @@ static char *ptr_to_id(char *buf, char *end, const void *ptr, |
| { |
| const char *str = sizeof(ptr) == 8 ? "(____ptrval____)" : "(ptrval)"; |
| unsigned long hashval; |
| + int ret; |
| |
| /* When debugging early boot use non-cryptographically secure hash. */ |
| if (unlikely(debug_boot_weak_hash)) { |
| @@ -752,22 +774,13 @@ static char *ptr_to_id(char *buf, char *end, const void *ptr, |
| return pointer_string(buf, end, (const void *)hashval, spec); |
| } |
| |
| - if (static_branch_unlikely(¬_filled_random_ptr_key)) { |
| + ret = ptr_to_hashval(ptr, &hashval); |
| + if (ret) { |
| spec.field_width = 2 * sizeof(ptr); |
| /* string length must be less than default_width */ |
| return error_string(buf, end, str, spec); |
| } |
| |
| -#ifdef CONFIG_64BIT |
| - hashval = (unsigned long)siphash_1u64((u64)ptr, &ptr_key); |
| - /* |
| - * Mask off the first 32 bits, this makes explicit that we have |
| - * modified the address (and 32 bits is plenty for a unique ID). |
| - */ |
| - hashval = hashval & 0xffffffff; |
| -#else |
| - hashval = (unsigned long)siphash_1u32((u32)ptr, &ptr_key); |
| -#endif |
| return pointer_string(buf, end, (const void *)hashval, spec); |
| } |
| |
| diff --git a/mm/memory.c b/mm/memory.c |
| index 499b1916a6b6..3fb5bad0b262 100644 |
| --- a/mm/memory.c |
| +++ b/mm/memory.c |
| @@ -142,9 +142,9 @@ static int __init init_zero_pfn(void) |
| } |
| core_initcall(init_zero_pfn); |
| |
| -void mm_trace_rss_stat(int member, long count) |
| +void mm_trace_rss_stat(struct mm_struct *mm, int member, long count) |
| { |
| - trace_rss_stat(member, count); |
| + trace_rss_stat(mm, member, count); |
| } |
| |
| #if defined(SPLIT_RSS_COUNTING) |