diff options
author | Larry Finger <Larry.Finger@lwfinger.net> | 2015-06-24 19:58:51 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-07-21 13:10:03 -0400 |
commit | 03445a4c2324f4adddd6b6c9b92879c1c754238a (patch) | |
tree | 691e794f2d8b6dd859b5c061735f13d5c12cde4e | |
parent | 3baf726f001b69454f3eb18a589c508992622be9 (diff) |
mm: kmemleak_alloc_percpu() should follow the gfp from per_alloc()
commit 8a8c35fadfaf55629a37ef1a8ead1b8fb32581d2 upstream.
Beginning at commit d52d3997f843 ("ipv6: Create percpu rt6_info"), the
following INFO splat is logged:
===============================
[ INFO: suspicious RCU usage. ]
4.1.0-rc7-next-20150612 #1 Not tainted
-------------------------------
kernel/sched/core.c:7318 Illegal context switch in RCU-bh read-side critical section!
other info that might help us debug this:
rcu_scheduler_active = 1, debug_locks = 0
3 locks held by systemd/1:
#0: (rtnl_mutex){+.+.+.}, at: [<ffffffff815f0c8f>] rtnetlink_rcv+0x1f/0x40
#1: (rcu_read_lock_bh){......}, at: [<ffffffff816a34e2>] ipv6_add_addr+0x62/0x540
#2: (addrconf_hash_lock){+...+.}, at: [<ffffffff816a3604>] ipv6_add_addr+0x184/0x540
stack backtrace:
CPU: 0 PID: 1 Comm: systemd Not tainted 4.1.0-rc7-next-20150612 #1
Hardware name: TOSHIBA TECRA A50-A/TECRA A50-A, BIOS Version 4.20 04/17/2014
Call Trace:
dump_stack+0x4c/0x6e
lockdep_rcu_suspicious+0xe7/0x120
___might_sleep+0x1d5/0x1f0
__might_sleep+0x4d/0x90
kmem_cache_alloc+0x47/0x250
create_object+0x39/0x2e0
kmemleak_alloc_percpu+0x61/0xe0
pcpu_alloc+0x370/0x630
Additional backtrace lines are truncated. In addition, the above splat
is followed by several "BUG: sleeping function called from invalid
context at mm/slub.c:1268" outputs. As suggested by Martin KaFai Lau,
these are the clue to the fix. Routine kmemleak_alloc_percpu() always
uses GFP_KERNEL for its allocations, whereas it should follow the gfp
from its callers.
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Reviewed-by: Kamalesh Babulal <kamalesh@linux.vnet.ibm.com>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Christoph Lameter <cl@linux-foundation.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | include/linux/kmemleak.h | 6 | ||||
-rw-r--r-- | mm/kmemleak.c | 9 | ||||
-rw-r--r-- | mm/percpu.c | 2 |
3 files changed, 10 insertions, 7 deletions
diff --git a/include/linux/kmemleak.h b/include/linux/kmemleak.h index e705467ddb47..d0a1f99e24e3 100644 --- a/include/linux/kmemleak.h +++ b/include/linux/kmemleak.h | |||
@@ -28,7 +28,8 @@ | |||
28 | extern void kmemleak_init(void) __ref; | 28 | extern void kmemleak_init(void) __ref; |
29 | extern void kmemleak_alloc(const void *ptr, size_t size, int min_count, | 29 | extern void kmemleak_alloc(const void *ptr, size_t size, int min_count, |
30 | gfp_t gfp) __ref; | 30 | gfp_t gfp) __ref; |
31 | extern void kmemleak_alloc_percpu(const void __percpu *ptr, size_t size) __ref; | 31 | extern void kmemleak_alloc_percpu(const void __percpu *ptr, size_t size, |
32 | gfp_t gfp) __ref; | ||
32 | extern void kmemleak_free(const void *ptr) __ref; | 33 | extern void kmemleak_free(const void *ptr) __ref; |
33 | extern void kmemleak_free_part(const void *ptr, size_t size) __ref; | 34 | extern void kmemleak_free_part(const void *ptr, size_t size) __ref; |
34 | extern void kmemleak_free_percpu(const void __percpu *ptr) __ref; | 35 | extern void kmemleak_free_percpu(const void __percpu *ptr) __ref; |
@@ -71,7 +72,8 @@ static inline void kmemleak_alloc_recursive(const void *ptr, size_t size, | |||
71 | gfp_t gfp) | 72 | gfp_t gfp) |
72 | { | 73 | { |
73 | } | 74 | } |
74 | static inline void kmemleak_alloc_percpu(const void __percpu *ptr, size_t size) | 75 | static inline void kmemleak_alloc_percpu(const void __percpu *ptr, size_t size, |
76 | gfp_t gfp) | ||
75 | { | 77 | { |
76 | } | 78 | } |
77 | static inline void kmemleak_free(const void *ptr) | 79 | static inline void kmemleak_free(const void *ptr) |
diff --git a/mm/kmemleak.c b/mm/kmemleak.c index 41df5b8efd25..3716cdb8ba42 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c | |||
@@ -909,12 +909,13 @@ EXPORT_SYMBOL_GPL(kmemleak_alloc); | |||
909 | * kmemleak_alloc_percpu - register a newly allocated __percpu object | 909 | * kmemleak_alloc_percpu - register a newly allocated __percpu object |
910 | * @ptr: __percpu pointer to beginning of the object | 910 | * @ptr: __percpu pointer to beginning of the object |
911 | * @size: size of the object | 911 | * @size: size of the object |
912 | * @gfp: flags used for kmemleak internal memory allocations | ||
912 | * | 913 | * |
913 | * This function is called from the kernel percpu allocator when a new object | 914 | * This function is called from the kernel percpu allocator when a new object |
914 | * (memory block) is allocated (alloc_percpu). It assumes GFP_KERNEL | 915 | * (memory block) is allocated (alloc_percpu). |
915 | * allocation. | ||
916 | */ | 916 | */ |
917 | void __ref kmemleak_alloc_percpu(const void __percpu *ptr, size_t size) | 917 | void __ref kmemleak_alloc_percpu(const void __percpu *ptr, size_t size, |
918 | gfp_t gfp) | ||
918 | { | 919 | { |
919 | unsigned int cpu; | 920 | unsigned int cpu; |
920 | 921 | ||
@@ -927,7 +928,7 @@ void __ref kmemleak_alloc_percpu(const void __percpu *ptr, size_t size) | |||
927 | if (kmemleak_enabled && ptr && !IS_ERR(ptr)) | 928 | if (kmemleak_enabled && ptr && !IS_ERR(ptr)) |
928 | for_each_possible_cpu(cpu) | 929 | for_each_possible_cpu(cpu) |
929 | create_object((unsigned long)per_cpu_ptr(ptr, cpu), | 930 | create_object((unsigned long)per_cpu_ptr(ptr, cpu), |
930 | size, 0, GFP_KERNEL); | 931 | size, 0, gfp); |
931 | else if (kmemleak_early_log) | 932 | else if (kmemleak_early_log) |
932 | log_early(KMEMLEAK_ALLOC_PERCPU, ptr, size, 0); | 933 | log_early(KMEMLEAK_ALLOC_PERCPU, ptr, size, 0); |
933 | } | 934 | } |
diff --git a/mm/percpu.c b/mm/percpu.c index dfd02484e8de..2dd74487a0af 100644 --- a/mm/percpu.c +++ b/mm/percpu.c | |||
@@ -1030,7 +1030,7 @@ area_found: | |||
1030 | memset((void *)pcpu_chunk_addr(chunk, cpu, 0) + off, 0, size); | 1030 | memset((void *)pcpu_chunk_addr(chunk, cpu, 0) + off, 0, size); |
1031 | 1031 | ||
1032 | ptr = __addr_to_pcpu_ptr(chunk->base_addr + off); | 1032 | ptr = __addr_to_pcpu_ptr(chunk->base_addr + off); |
1033 | kmemleak_alloc_percpu(ptr, size); | 1033 | kmemleak_alloc_percpu(ptr, size, gfp); |
1034 | return ptr; | 1034 | return ptr; |
1035 | 1035 | ||
1036 | fail_unlock: | 1036 | fail_unlock: |