diff options
| -rw-r--r-- | include/linux/module.h | 11 | ||||
| -rw-r--r-- | include/linux/percpu.h | 7 | ||||
| -rw-r--r-- | kernel/lockdep.c | 21 | ||||
| -rw-r--r-- | kernel/module.c | 104 | ||||
| -rw-r--r-- | mm/percpu.c | 26 | ||||
| -rw-r--r-- | mm/rmap.c | 1 |
6 files changed, 122 insertions, 48 deletions
diff --git a/include/linux/module.h b/include/linux/module.h index 5e869ffd34aa..8bd399a00343 100644 --- a/include/linux/module.h +++ b/include/linux/module.h | |||
| @@ -330,8 +330,11 @@ struct module | |||
| 330 | struct module_notes_attrs *notes_attrs; | 330 | struct module_notes_attrs *notes_attrs; |
| 331 | #endif | 331 | #endif |
| 332 | 332 | ||
| 333 | #ifdef CONFIG_SMP | ||
| 333 | /* Per-cpu data. */ | 334 | /* Per-cpu data. */ |
| 334 | void *percpu; | 335 | void __percpu *percpu; |
| 336 | unsigned int percpu_size; | ||
| 337 | #endif | ||
| 335 | 338 | ||
| 336 | /* The command line arguments (may be mangled). People like | 339 | /* The command line arguments (may be mangled). People like |
| 337 | keeping pointers to this stuff */ | 340 | keeping pointers to this stuff */ |
| @@ -392,6 +395,7 @@ static inline int module_is_live(struct module *mod) | |||
| 392 | struct module *__module_text_address(unsigned long addr); | 395 | struct module *__module_text_address(unsigned long addr); |
| 393 | struct module *__module_address(unsigned long addr); | 396 | struct module *__module_address(unsigned long addr); |
| 394 | bool is_module_address(unsigned long addr); | 397 | bool is_module_address(unsigned long addr); |
| 398 | bool is_module_percpu_address(unsigned long addr); | ||
| 395 | bool is_module_text_address(unsigned long addr); | 399 | bool is_module_text_address(unsigned long addr); |
| 396 | 400 | ||
| 397 | 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) |
| @@ -563,6 +567,11 @@ static inline bool is_module_address(unsigned long addr) | |||
| 563 | return false; | 567 | return false; |
| 564 | } | 568 | } |
| 565 | 569 | ||
| 570 | static inline bool is_module_percpu_address(unsigned long addr) | ||
| 571 | { | ||
| 572 | return false; | ||
| 573 | } | ||
| 574 | |||
| 566 | static inline bool is_module_text_address(unsigned long addr) | 575 | static inline bool is_module_text_address(unsigned long addr) |
| 567 | { | 576 | { |
| 568 | return false; | 577 | return false; |
diff --git a/include/linux/percpu.h b/include/linux/percpu.h index c7845130bfdf..d3a38d687104 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h | |||
| @@ -135,6 +135,7 @@ extern int __init pcpu_page_first_chunk(size_t reserved_size, | |||
| 135 | #define per_cpu_ptr(ptr, cpu) SHIFT_PERCPU_PTR((ptr), per_cpu_offset((cpu))) | 135 | #define per_cpu_ptr(ptr, cpu) SHIFT_PERCPU_PTR((ptr), per_cpu_offset((cpu))) |
| 136 | 136 | ||
| 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 bool is_kernel_percpu_address(unsigned long addr); | ||
| 138 | 139 | ||
| 139 | #ifndef CONFIG_HAVE_SETUP_PER_CPU_AREA | 140 | #ifndef CONFIG_HAVE_SETUP_PER_CPU_AREA |
| 140 | extern void __init setup_per_cpu_areas(void); | 141 | extern void __init setup_per_cpu_areas(void); |
| @@ -144,6 +145,12 @@ extern void __init setup_per_cpu_areas(void); | |||
| 144 | 145 | ||
| 145 | #define per_cpu_ptr(ptr, cpu) ({ (void)(cpu); (ptr); }) | 146 | #define per_cpu_ptr(ptr, cpu) ({ (void)(cpu); (ptr); }) |
| 146 | 147 | ||
| 148 | /* can't distinguish from other static vars, always false */ | ||
| 149 | static inline bool is_kernel_percpu_address(unsigned long addr) | ||
| 150 | { | ||
| 151 | return false; | ||
| 152 | } | ||
| 153 | |||
| 147 | static inline void __init setup_per_cpu_areas(void) { } | 154 | static inline void __init setup_per_cpu_areas(void) { } |
| 148 | 155 | ||
| 149 | static inline void *pcpu_lpage_remapped(void *kaddr) | 156 | static inline void *pcpu_lpage_remapped(void *kaddr) |
diff --git a/kernel/lockdep.c b/kernel/lockdep.c index 367f72452ff4..2594e1ce41cb 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c | |||
| @@ -583,9 +583,6 @@ static int static_obj(void *obj) | |||
| 583 | unsigned long start = (unsigned long) &_stext, | 583 | unsigned long start = (unsigned long) &_stext, |
| 584 | end = (unsigned long) &_end, | 584 | end = (unsigned long) &_end, |
| 585 | addr = (unsigned long) obj; | 585 | addr = (unsigned long) obj; |
| 586 | #ifdef CONFIG_SMP | ||
| 587 | int i; | ||
| 588 | #endif | ||
| 589 | 586 | ||
| 590 | /* | 587 | /* |
| 591 | * static variable? | 588 | * static variable? |
| @@ -596,24 +593,16 @@ static int static_obj(void *obj) | |||
| 596 | if (arch_is_kernel_data(addr)) | 593 | if (arch_is_kernel_data(addr)) |
| 597 | return 1; | 594 | return 1; |
| 598 | 595 | ||
| 599 | #ifdef CONFIG_SMP | ||
| 600 | /* | 596 | /* |
| 601 | * percpu var? | 597 | * in-kernel percpu var? |
| 602 | */ | 598 | */ |
| 603 | for_each_possible_cpu(i) { | 599 | if (is_kernel_percpu_address(addr)) |
| 604 | start = (unsigned long) &__per_cpu_start + per_cpu_offset(i); | 600 | return 1; |
| 605 | end = (unsigned long) &__per_cpu_start + PERCPU_ENOUGH_ROOM | ||
| 606 | + per_cpu_offset(i); | ||
| 607 | |||
| 608 | if ((addr >= start) && (addr < end)) | ||
| 609 | return 1; | ||
| 610 | } | ||
| 611 | #endif | ||
| 612 | 601 | ||
| 613 | /* | 602 | /* |
| 614 | * module var? | 603 | * module static or percpu var? |
| 615 | */ | 604 | */ |
| 616 | return is_module_address(addr); | 605 | return is_module_address(addr) || is_module_percpu_address(addr); |
| 617 | } | 606 | } |
| 618 | 607 | ||
| 619 | /* | 608 | /* |
diff --git a/kernel/module.c b/kernel/module.c index c968d3606dca..9f8d23d8b3a8 100644 --- a/kernel/module.c +++ b/kernel/module.c | |||
| @@ -370,27 +370,33 @@ EXPORT_SYMBOL_GPL(find_module); | |||
| 370 | 370 | ||
| 371 | #ifdef CONFIG_SMP | 371 | #ifdef CONFIG_SMP |
| 372 | 372 | ||
| 373 | static void *percpu_modalloc(unsigned long size, unsigned long align, | 373 | static inline void __percpu *mod_percpu(struct module *mod) |
| 374 | const char *name) | ||
| 375 | { | 374 | { |
| 376 | void *ptr; | 375 | return mod->percpu; |
| 376 | } | ||
| 377 | 377 | ||
| 378 | static int percpu_modalloc(struct module *mod, | ||
| 379 | unsigned long size, unsigned long align) | ||
| 380 | { | ||
| 378 | if (align > PAGE_SIZE) { | 381 | if (align > PAGE_SIZE) { |
| 379 | printk(KERN_WARNING "%s: per-cpu alignment %li > %li\n", | 382 | printk(KERN_WARNING "%s: per-cpu alignment %li > %li\n", |
| 380 | name, align, PAGE_SIZE); | 383 | mod->name, align, PAGE_SIZE); |
| 381 | align = PAGE_SIZE; | 384 | align = PAGE_SIZE; |
| 382 | } | 385 | } |
| 383 | 386 | ||
| 384 | ptr = __alloc_reserved_percpu(size, align); | 387 | mod->percpu = __alloc_reserved_percpu(size, align); |
| 385 | if (!ptr) | 388 | if (!mod->percpu) { |
| 386 | printk(KERN_WARNING | 389 | printk(KERN_WARNING |
| 387 | "Could not allocate %lu bytes percpu data\n", size); | 390 | "Could not allocate %lu bytes percpu data\n", size); |
| 388 | return ptr; | 391 | return -ENOMEM; |
| 392 | } | ||
| 393 | mod->percpu_size = size; | ||
| 394 | return 0; | ||
| 389 | } | 395 | } |
| 390 | 396 | ||
| 391 | static void percpu_modfree(void *freeme) | 397 | static void percpu_modfree(struct module *mod) |
| 392 | { | 398 | { |
| 393 | free_percpu(freeme); | 399 | free_percpu(mod->percpu); |
| 394 | } | 400 | } |
| 395 | 401 | ||
| 396 | static unsigned int find_pcpusec(Elf_Ehdr *hdr, | 402 | static unsigned int find_pcpusec(Elf_Ehdr *hdr, |
| @@ -400,24 +406,62 @@ static unsigned int find_pcpusec(Elf_Ehdr *hdr, | |||
| 400 | return find_sec(hdr, sechdrs, secstrings, ".data.percpu"); | 406 | return find_sec(hdr, sechdrs, secstrings, ".data.percpu"); |
| 401 | } | 407 | } |
| 402 | 408 | ||
| 403 | static void percpu_modcopy(void *pcpudest, const void *from, unsigned long size) | 409 | static void percpu_modcopy(struct module *mod, |
| 410 | const void *from, unsigned long size) | ||
| 404 | { | 411 | { |
| 405 | int cpu; | 412 | int cpu; |
| 406 | 413 | ||
| 407 | for_each_possible_cpu(cpu) | 414 | for_each_possible_cpu(cpu) |
| 408 | memcpy(pcpudest + per_cpu_offset(cpu), from, size); | 415 | memcpy(per_cpu_ptr(mod->percpu, cpu), from, size); |
| 416 | } | ||
| 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; | ||
| 409 | } | 450 | } |
| 410 | 451 | ||
| 411 | #else /* ... !CONFIG_SMP */ | 452 | #else /* ... !CONFIG_SMP */ |
| 412 | 453 | ||
| 413 | static inline void *percpu_modalloc(unsigned long size, unsigned long align, | 454 | static inline void __percpu *mod_percpu(struct module *mod) |
| 414 | const char *name) | ||
| 415 | { | 455 | { |
| 416 | return NULL; | 456 | return NULL; |
| 417 | } | 457 | } |
| 418 | static inline void percpu_modfree(void *pcpuptr) | 458 | static inline int percpu_modalloc(struct module *mod, |
| 459 | unsigned long size, unsigned long align) | ||
| 460 | { | ||
| 461 | return -ENOMEM; | ||
| 462 | } | ||
| 463 | static inline void percpu_modfree(struct module *mod) | ||
| 419 | { | 464 | { |
| 420 | BUG(); | ||
| 421 | } | 465 | } |
| 422 | static inline unsigned int find_pcpusec(Elf_Ehdr *hdr, | 466 | static inline unsigned int find_pcpusec(Elf_Ehdr *hdr, |
| 423 | Elf_Shdr *sechdrs, | 467 | Elf_Shdr *sechdrs, |
| @@ -425,12 +469,16 @@ static inline unsigned int find_pcpusec(Elf_Ehdr *hdr, | |||
| 425 | { | 469 | { |
| 426 | return 0; | 470 | return 0; |
| 427 | } | 471 | } |
| 428 | static inline void percpu_modcopy(void *pcpudst, const void *src, | 472 | static inline void percpu_modcopy(struct module *mod, |
| 429 | unsigned long size) | 473 | const void *from, unsigned long size) |
| 430 | { | 474 | { |
| 431 | /* 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. */ |
| 432 | BUG_ON(size != 0); | 476 | BUG_ON(size != 0); |
| 433 | } | 477 | } |
| 478 | bool is_module_percpu_address(unsigned long addr) | ||
| 479 | { | ||
| 480 | return false; | ||
| 481 | } | ||
| 434 | 482 | ||
| 435 | #endif /* CONFIG_SMP */ | 483 | #endif /* CONFIG_SMP */ |
| 436 | 484 | ||
| @@ -1400,8 +1448,7 @@ static void free_module(struct module *mod) | |||
| 1400 | /* This may be NULL, but that's OK */ | 1448 | /* This may be NULL, but that's OK */ |
| 1401 | module_free(mod, mod->module_init); | 1449 | module_free(mod, mod->module_init); |
| 1402 | kfree(mod->args); | 1450 | kfree(mod->args); |
| 1403 | if (mod->percpu) | 1451 | percpu_modfree(mod); |
| 1404 | percpu_modfree(mod->percpu); | ||
| 1405 | #if defined(CONFIG_MODULE_UNLOAD) | 1452 | #if defined(CONFIG_MODULE_UNLOAD) |
| 1406 | if (mod->refptr) | 1453 | if (mod->refptr) |
| 1407 | free_percpu(mod->refptr); | 1454 | free_percpu(mod->refptr); |
| @@ -1520,7 +1567,7 @@ static int simplify_symbols(Elf_Shdr *sechdrs, | |||
| 1520 | default: | 1567 | default: |
| 1521 | /* Divert to percpu allocation if a percpu var. */ | 1568 | /* Divert to percpu allocation if a percpu var. */ |
| 1522 | if (sym[i].st_shndx == pcpuindex) | 1569 | if (sym[i].st_shndx == pcpuindex) |
| 1523 | secbase = (unsigned long)mod->percpu; | 1570 | secbase = (unsigned long)mod_percpu(mod); |
| 1524 | else | 1571 | else |
| 1525 | secbase = sechdrs[sym[i].st_shndx].sh_addr; | 1572 | secbase = sechdrs[sym[i].st_shndx].sh_addr; |
| 1526 | sym[i].st_value += secbase; | 1573 | sym[i].st_value += secbase; |
| @@ -1954,7 +2001,7 @@ static noinline struct module *load_module(void __user *umod, | |||
| 1954 | unsigned int modindex, versindex, infoindex, pcpuindex; | 2001 | unsigned int modindex, versindex, infoindex, pcpuindex; |
| 1955 | struct module *mod; | 2002 | struct module *mod; |
| 1956 | long err = 0; | 2003 | long err = 0; |
| 1957 | void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */ | 2004 | void *ptr = NULL; /* Stops spurious gcc warning */ |
| 1958 | unsigned long symoffs, stroffs, *strmap; | 2005 | unsigned long symoffs, stroffs, *strmap; |
| 1959 | 2006 | ||
| 1960 | mm_segment_t old_fs; | 2007 | mm_segment_t old_fs; |
| @@ -2094,15 +2141,11 @@ static noinline struct module *load_module(void __user *umod, | |||
| 2094 | 2141 | ||
| 2095 | if (pcpuindex) { | 2142 | if (pcpuindex) { |
| 2096 | /* We have a special allocation for this section. */ | 2143 | /* We have a special allocation for this section. */ |
| 2097 | percpu = percpu_modalloc(sechdrs[pcpuindex].sh_size, | 2144 | err = percpu_modalloc(mod, sechdrs[pcpuindex].sh_size, |
| 2098 | sechdrs[pcpuindex].sh_addralign, | 2145 | sechdrs[pcpuindex].sh_addralign); |
| 2099 | mod->name); | 2146 | if (err) |
| 2100 | if (!percpu) { | ||
| 2101 | err = -ENOMEM; | ||
| 2102 | goto free_mod; | 2147 | goto free_mod; |
| 2103 | } | ||
| 2104 | sechdrs[pcpuindex].sh_flags &= ~(unsigned long)SHF_ALLOC; | 2148 | sechdrs[pcpuindex].sh_flags &= ~(unsigned long)SHF_ALLOC; |
| 2105 | mod->percpu = percpu; | ||
| 2106 | } | 2149 | } |
| 2107 | 2150 | ||
| 2108 | /* Determine total sizes, and put offsets in sh_entsize. For now | 2151 | /* Determine total sizes, and put offsets in sh_entsize. For now |
| @@ -2317,7 +2360,7 @@ static noinline struct module *load_module(void __user *umod, | |||
| 2317 | sort_extable(mod->extable, mod->extable + mod->num_exentries); | 2360 | sort_extable(mod->extable, mod->extable + mod->num_exentries); |
| 2318 | 2361 | ||
| 2319 | /* Finally, copy percpu area over. */ | 2362 | /* Finally, copy percpu area over. */ |
| 2320 | percpu_modcopy(mod->percpu, (void *)sechdrs[pcpuindex].sh_addr, | 2363 | percpu_modcopy(mod, (void *)sechdrs[pcpuindex].sh_addr, |
| 2321 | sechdrs[pcpuindex].sh_size); | 2364 | sechdrs[pcpuindex].sh_size); |
| 2322 | 2365 | ||
| 2323 | add_kallsyms(mod, sechdrs, hdr->e_shnum, symindex, strindex, | 2366 | add_kallsyms(mod, sechdrs, hdr->e_shnum, symindex, strindex, |
| @@ -2409,8 +2452,7 @@ static noinline struct module *load_module(void __user *umod, | |||
| 2409 | module_free(mod, mod->module_core); | 2452 | module_free(mod, mod->module_core); |
| 2410 | /* mod will be freed with core. Don't access it beyond this line! */ | 2453 | /* mod will be freed with core. Don't access it beyond this line! */ |
| 2411 | free_percpu: | 2454 | free_percpu: |
| 2412 | if (percpu) | 2455 | percpu_modfree(mod); |
| 2413 | percpu_modfree(percpu); | ||
| 2414 | free_mod: | 2456 | free_mod: |
| 2415 | kfree(args); | 2457 | kfree(args); |
| 2416 | kfree(strmap); | 2458 | kfree(strmap); |
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 | * |
| @@ -232,6 +232,7 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma) | |||
| 232 | out_error_free_anon_vma: | 232 | out_error_free_anon_vma: |
| 233 | anon_vma_free(anon_vma); | 233 | anon_vma_free(anon_vma); |
| 234 | out_error: | 234 | out_error: |
| 235 | unlink_anon_vmas(vma); | ||
| 235 | return -ENOMEM; | 236 | return -ENOMEM; |
| 236 | } | 237 | } |
| 237 | 238 | ||
