Skip to content

Commit

Permalink
percpu, module: implement and use is_kernel/module_percpu_address()
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
htejun committed Mar 29, 2010
1 parent 259354d commit 10fad5e
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 16 deletions.
1 change: 1 addition & 0 deletions include/linux/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ static inline int module_is_live(struct module *mod)
struct module *__module_text_address(unsigned long addr);
struct module *__module_address(unsigned long addr);
bool is_module_address(unsigned long addr);
bool is_module_percpu_address(unsigned long addr);
bool is_module_text_address(unsigned long addr);

static inline int within_module_core(unsigned long addr, struct module *mod)
Expand Down
7 changes: 7 additions & 0 deletions include/linux/percpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ extern int __init pcpu_page_first_chunk(size_t reserved_size,
extern void __percpu *__alloc_reserved_percpu(size_t size, size_t align);
extern void __percpu *__alloc_percpu(size_t size, size_t align);
extern void free_percpu(void __percpu *__pdata);
extern bool is_kernel_percpu_address(unsigned long addr);
extern phys_addr_t per_cpu_ptr_to_phys(void *addr);

#ifndef CONFIG_HAVE_SETUP_PER_CPU_AREA
Expand All @@ -163,6 +164,12 @@ static inline void free_percpu(void __percpu *p)
kfree(p);
}

/* can't distinguish from other static vars, always false */
static inline bool is_kernel_percpu_address(unsigned long addr)
{
return false;
}

static inline phys_addr_t per_cpu_ptr_to_phys(void *addr)
{
return __pa(addr);
Expand Down
21 changes: 5 additions & 16 deletions kernel/lockdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -582,9 +582,6 @@ static int static_obj(void *obj)
unsigned long start = (unsigned long) &_stext,
end = (unsigned long) &_end,
addr = (unsigned long) obj;
#ifdef CONFIG_SMP
int i;
#endif

/*
* static variable?
Expand All @@ -595,24 +592,16 @@ static int static_obj(void *obj)
if (arch_is_kernel_data(addr))
return 1;

#ifdef CONFIG_SMP
/*
* percpu var?
* in-kernel percpu var?
*/
for_each_possible_cpu(i) {
start = (unsigned long) &__per_cpu_start + per_cpu_offset(i);
end = (unsigned long) &__per_cpu_start + PERCPU_ENOUGH_ROOM
+ per_cpu_offset(i);

if ((addr >= start) && (addr < end))
return 1;
}
#endif
if (is_kernel_percpu_address(addr))
return 1;

/*
* module var?
* module static or percpu var?
*/
return is_module_address(addr);
return is_module_address(addr) || is_module_percpu_address(addr);
}

/*
Expand Down
38 changes: 38 additions & 0 deletions kernel/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,40 @@ static void percpu_modcopy(struct module *mod,
memcpy(per_cpu_ptr(mod->percpu, cpu), from, size);
}

/**
* is_module_percpu_address - test whether address is from module static percpu
* @addr: address to test
*
* Test whether @addr belongs to module static percpu area.
*
* RETURNS:
* %true if @addr is from module static percpu area
*/
bool is_module_percpu_address(unsigned long addr)
{
struct module *mod;
unsigned int cpu;

preempt_disable();

list_for_each_entry_rcu(mod, &modules, list) {
if (!mod->percpu_size)
continue;
for_each_possible_cpu(cpu) {
void *start = per_cpu_ptr(mod->percpu, cpu);

if ((void *)addr >= start &&
(void *)addr < start + mod->percpu_size) {
preempt_enable();
return true;
}
}
}

preempt_enable();
return false;
}

#else /* ... !CONFIG_SMP */

static inline void __percpu *mod_percpu(struct module *mod)
Expand All @@ -441,6 +475,10 @@ static inline void percpu_modcopy(struct module *mod,
/* pcpusec should be 0, and size of that section should be 0. */
BUG_ON(size != 0);
}
bool is_module_percpu_address(unsigned long addr)
{
return false;
}

#endif /* CONFIG_SMP */

Expand Down
26 changes: 26 additions & 0 deletions mm/percpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -1303,6 +1303,32 @@ void free_percpu(void __percpu *ptr)
}
EXPORT_SYMBOL_GPL(free_percpu);

/**
* is_kernel_percpu_address - test whether address is from static percpu area
* @addr: address to test
*
* Test whether @addr belongs to in-kernel static percpu area. Module
* static percpu areas are not considered. For those, use
* is_module_percpu_address().
*
* RETURNS:
* %true if @addr is from in-kernel static percpu area, %false otherwise.
*/
bool is_kernel_percpu_address(unsigned long addr)
{
const size_t static_size = __per_cpu_end - __per_cpu_start;
void __percpu *base = __addr_to_pcpu_ptr(pcpu_base_addr);
unsigned int cpu;

for_each_possible_cpu(cpu) {
void *start = per_cpu_ptr(base, cpu);

if ((void *)addr >= start && (void *)addr < start + static_size)
return true;
}
return false;
}

/**
* per_cpu_ptr_to_phys - convert translated percpu address to physical address
* @addr: the address to be converted to physical address
Expand Down

0 comments on commit 10fad5e

Please sign in to comment.