diff options
author | Tejun Heo <tj@kernel.org> | 2010-03-10 04:57:54 -0500 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2010-03-29 10:07:12 -0400 |
commit | 10fad5e46f6c7bdfb01b1a012380a38e3c6ab346 (patch) | |
tree | 9ec6e3955e7f879f64ea79812cf5ecd41baa6939 | |
parent | 259354deaaf03d49a02dbb9975d6ec2a54675672 (diff) |
percpu, module: implement and use is_kernel/module_percpu_address()
lockdep has custom code to check whether a pointer belongs to static
percpu area which is somewhat broken. Implement proper
is_kernel/module_percpu_address() and replace the custom code.
On UP, percpu variables are regular static variables and can't be
distinguished from them. Always return %false on UP.
Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: Ingo Molnar <mingo@redhat.com>
-rw-r--r-- | include/linux/module.h | 1 | ||||
-rw-r--r-- | include/linux/percpu.h | 7 | ||||
-rw-r--r-- | kernel/lockdep.c | 21 | ||||
-rw-r--r-- | kernel/module.c | 38 | ||||
-rw-r--r-- | mm/percpu.c | 26 |
5 files changed, 77 insertions, 16 deletions
diff --git a/include/linux/module.h b/include/linux/module.h index 87d247ac6761..f0e2659f4e3e 100644 --- a/include/linux/module.h +++ b/include/linux/module.h | |||
@@ -395,6 +395,7 @@ static inline int module_is_live(struct module *mod) | |||
395 | struct module *__module_text_address(unsigned long addr); | 395 | struct module *__module_text_address(unsigned long addr); |
396 | struct module *__module_address(unsigned long addr); | 396 | struct module *__module_address(unsigned long addr); |
397 | bool is_module_address(unsigned long addr); | 397 | bool is_module_address(unsigned long addr); |
398 | bool is_module_percpu_address(unsigned long addr); | ||
398 | bool is_module_text_address(unsigned long addr); | 399 | bool is_module_text_address(unsigned long addr); |
399 | 400 | ||
400 | static inline int within_module_core(unsigned long addr, struct module *mod) | 401 | static inline int within_module_core(unsigned long addr, struct module *mod) |
diff --git a/include/linux/percpu.h b/include/linux/percpu.h index a93e5bfdccb8..11d5f834b54a 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h | |||
@@ -137,6 +137,7 @@ extern int __init pcpu_page_first_chunk(size_t reserved_size, | |||
137 | extern void __percpu *__alloc_reserved_percpu(size_t size, size_t align); | 137 | extern void __percpu *__alloc_reserved_percpu(size_t size, size_t align); |
138 | extern void __percpu *__alloc_percpu(size_t size, size_t align); | 138 | extern void __percpu *__alloc_percpu(size_t size, size_t align); |
139 | extern void free_percpu(void __percpu *__pdata); | 139 | extern void free_percpu(void __percpu *__pdata); |
140 | extern bool is_kernel_percpu_address(unsigned long addr); | ||
140 | extern phys_addr_t per_cpu_ptr_to_phys(void *addr); | 141 | extern phys_addr_t per_cpu_ptr_to_phys(void *addr); |
141 | 142 | ||
142 | #ifndef CONFIG_HAVE_SETUP_PER_CPU_AREA | 143 | #ifndef CONFIG_HAVE_SETUP_PER_CPU_AREA |
@@ -163,6 +164,12 @@ static inline void free_percpu(void __percpu *p) | |||
163 | kfree(p); | 164 | kfree(p); |
164 | } | 165 | } |
165 | 166 | ||
167 | /* can't distinguish from other static vars, always false */ | ||
168 | static inline bool is_kernel_percpu_address(unsigned long addr) | ||
169 | { | ||
170 | return false; | ||
171 | } | ||
172 | |||
166 | static inline phys_addr_t per_cpu_ptr_to_phys(void *addr) | 173 | static inline phys_addr_t per_cpu_ptr_to_phys(void *addr) |
167 | { | 174 | { |
168 | return __pa(addr); | 175 | return __pa(addr); |
diff --git a/kernel/lockdep.c b/kernel/lockdep.c index c927a549db2c..9bbb9c841e48 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c | |||
@@ -582,9 +582,6 @@ static int static_obj(void *obj) | |||
582 | unsigned long start = (unsigned long) &_stext, | 582 | unsigned long start = (unsigned long) &_stext, |
583 | end = (unsigned long) &_end, | 583 | end = (unsigned long) &_end, |
584 | addr = (unsigned long) obj; | 584 | addr = (unsigned long) obj; |
585 | #ifdef CONFIG_SMP | ||
586 | int i; | ||
587 | #endif | ||
588 | 585 | ||
589 | /* | 586 | /* |
590 | * static variable? | 587 | * static variable? |
@@ -595,24 +592,16 @@ static int static_obj(void *obj) | |||
595 | if (arch_is_kernel_data(addr)) | 592 | if (arch_is_kernel_data(addr)) |
596 | return 1; | 593 | return 1; |
597 | 594 | ||
598 | #ifdef CONFIG_SMP | ||
599 | /* | 595 | /* |
600 | * percpu var? | 596 | * in-kernel percpu var? |
601 | */ | 597 | */ |
602 | for_each_possible_cpu(i) { | 598 | if (is_kernel_percpu_address(addr)) |
603 | start = (unsigned long) &__per_cpu_start + per_cpu_offset(i); | 599 | return 1; |
604 | end = (unsigned long) &__per_cpu_start + PERCPU_ENOUGH_ROOM | ||
605 | + per_cpu_offset(i); | ||
606 | |||
607 | if ((addr >= start) && (addr < end)) | ||
608 | return 1; | ||
609 | } | ||
610 | #endif | ||
611 | 600 | ||
612 | /* | 601 | /* |
613 | * module var? | 602 | * module static or percpu var? |
614 | */ | 603 | */ |
615 | return is_module_address(addr); | 604 | return is_module_address(addr) || is_module_percpu_address(addr); |
616 | } | 605 | } |
617 | 606 | ||
618 | /* | 607 | /* |
diff --git a/kernel/module.c b/kernel/module.c index e7a6e53fc73e..9f8d23d8b3a8 100644 --- a/kernel/module.c +++ b/kernel/module.c | |||
@@ -415,6 +415,40 @@ static void percpu_modcopy(struct module *mod, | |||
415 | memcpy(per_cpu_ptr(mod->percpu, cpu), from, size); | 415 | memcpy(per_cpu_ptr(mod->percpu, cpu), from, size); |
416 | } | 416 | } |
417 | 417 | ||
418 | /** | ||
419 | * is_module_percpu_address - test whether address is from module static percpu | ||
420 | * @addr: address to test | ||
421 | * | ||
422 | * Test whether @addr belongs to module static percpu area. | ||
423 | * | ||
424 | * RETURNS: | ||
425 | * %true if @addr is from module static percpu area | ||
426 | */ | ||
427 | bool is_module_percpu_address(unsigned long addr) | ||
428 | { | ||
429 | struct module *mod; | ||
430 | unsigned int cpu; | ||
431 | |||
432 | preempt_disable(); | ||
433 | |||
434 | list_for_each_entry_rcu(mod, &modules, list) { | ||
435 | if (!mod->percpu_size) | ||
436 | continue; | ||
437 | for_each_possible_cpu(cpu) { | ||
438 | void *start = per_cpu_ptr(mod->percpu, cpu); | ||
439 | |||
440 | if ((void *)addr >= start && | ||
441 | (void *)addr < start + mod->percpu_size) { | ||
442 | preempt_enable(); | ||
443 | return true; | ||
444 | } | ||
445 | } | ||
446 | } | ||
447 | |||
448 | preempt_enable(); | ||
449 | return false; | ||
450 | } | ||
451 | |||
418 | #else /* ... !CONFIG_SMP */ | 452 | #else /* ... !CONFIG_SMP */ |
419 | 453 | ||
420 | static inline void __percpu *mod_percpu(struct module *mod) | 454 | static inline void __percpu *mod_percpu(struct module *mod) |
@@ -441,6 +475,10 @@ static inline void percpu_modcopy(struct module *mod, | |||
441 | /* pcpusec should be 0, and size of that section should be 0. */ | 475 | /* pcpusec should be 0, and size of that section should be 0. */ |
442 | BUG_ON(size != 0); | 476 | BUG_ON(size != 0); |
443 | } | 477 | } |
478 | bool is_module_percpu_address(unsigned long addr) | ||
479 | { | ||
480 | return false; | ||
481 | } | ||
444 | 482 | ||
445 | #endif /* CONFIG_SMP */ | 483 | #endif /* CONFIG_SMP */ |
446 | 484 | ||
diff --git a/mm/percpu.c b/mm/percpu.c index 768419d44ad7..6e09741ddc62 100644 --- a/mm/percpu.c +++ b/mm/percpu.c | |||
@@ -1304,6 +1304,32 @@ void free_percpu(void __percpu *ptr) | |||
1304 | EXPORT_SYMBOL_GPL(free_percpu); | 1304 | EXPORT_SYMBOL_GPL(free_percpu); |
1305 | 1305 | ||
1306 | /** | 1306 | /** |
1307 | * is_kernel_percpu_address - test whether address is from static percpu area | ||
1308 | * @addr: address to test | ||
1309 | * | ||
1310 | * Test whether @addr belongs to in-kernel static percpu area. Module | ||
1311 | * static percpu areas are not considered. For those, use | ||
1312 | * is_module_percpu_address(). | ||
1313 | * | ||
1314 | * RETURNS: | ||
1315 | * %true if @addr is from in-kernel static percpu area, %false otherwise. | ||
1316 | */ | ||
1317 | bool is_kernel_percpu_address(unsigned long addr) | ||
1318 | { | ||
1319 | const size_t static_size = __per_cpu_end - __per_cpu_start; | ||
1320 | void __percpu *base = __addr_to_pcpu_ptr(pcpu_base_addr); | ||
1321 | unsigned int cpu; | ||
1322 | |||
1323 | for_each_possible_cpu(cpu) { | ||
1324 | void *start = per_cpu_ptr(base, cpu); | ||
1325 | |||
1326 | if ((void *)addr >= start && (void *)addr < start + static_size) | ||
1327 | return true; | ||
1328 | } | ||
1329 | return false; | ||
1330 | } | ||
1331 | |||
1332 | /** | ||
1307 | * per_cpu_ptr_to_phys - convert translated percpu address to physical address | 1333 | * per_cpu_ptr_to_phys - convert translated percpu address to physical address |
1308 | * @addr: the address to be converted to physical address | 1334 | * @addr: the address to be converted to physical address |
1309 | * | 1335 | * |