__init and friends support for loadable modules
Subsystems (2)
RFC PATCH 1/1 __init and friends support for loadable modules
Hi all,
attached patch adds support for freeing .init sections of loadable modules
after init_module() function exits. Modutils have support for this since 1998, but kernel support didn't exist.
All architectures that use vmalloc/vfree for module_map/unmap should work.
Patch contains three major part:
- shrink_vm_area() function for reducing size of vmalloc'ed area;
- init.h changes to allow .init sections in modules;
- change in kernel/module.c.
Comments, suggestions ?
Best regards.
Signature · 2 lines
Andrey Panin | Embedded systems software engineer [email protected] | PGP key: wwwkeys.eu.pgp.net
include/asm-alpha/module.h +1 -0
@@ -6,6 +6,7 @@#define module_map (x) vmalloc(x)#define module_unmap (x) vfree(x) +#define module_remap (ptr, size) shrink_vm_area(ptr, size)#define module_arch_init (x) alpha_module_init(x)#define arch_init_modules (x) alpha_init_modules(x)
include/asm-arm/module.h +1 -0
@@ -6,6 +6,7 @@#define module_map (x) vmalloc(x)#define module_unmap (x) vfree(x) +#define module_remap (ptr, size) shrink_vm_area(ptr, size)#define module_arch_init (x) (0)#define arch_init_modules (x) do { } while (0)
include/asm-cris/module.h +1 -0
@@ -6,6 +6,7 @@#define module_map (x) vmalloc(x)#define module_unmap (x) vfree(x) +#define module_remap (ptr, size) shrink_vm_area(ptr, size)#define module_arch_init (x) (0)#define arch_init_modules (x) do { } while (0)
include/asm-i386/module.h +1 -0
@@ -6,6 +6,7 @@#define module_map (x) vmalloc(x)#define module_unmap (x) vfree(x) +#define module_remap (ptr, size) shrink_vm_area(ptr, size)#define module_arch_init (x) (0)#define arch_init_modules (x) do { } while (0)
include/asm-ia64/module.h +1 -0
@@ -13,6 +13,7 @@#define module_map (x) vmalloc(x)#define module_unmap (x) ia64_module_unmap(x) +#define module_remap (ptr, size) (-ENOSYS)#define module_arch_init (x) ia64_module_init(x)/*
include/asm-m68k/module.h +1 -0
@@ -6,6 +6,7 @@#define module_map (x) vmalloc(x)#define module_unmap (x) vfree(x) +#define module_remap (ptr, size) shrink_vm_area(ptr, size)#define module_arch_init (x) (0)#define arch_init_modules (x) do { } while (0)
include/asm-mips/module.h +1 -0
@@ -9,6 +9,7 @@#define module_map (x) vmalloc(x)#define module_unmap (x) vfree(x) +#define module_remap (ptr, size) shrink_vm_area(ptr, size)#define module_arch_init (x) mips_module_init(x)#define arch_init_modules (x) mips_init_modules(x)
include/asm-mips64/module.h +1 -0
@@ -9,6 +9,7 @@#define module_map (x) vmalloc(x)#define module_unmap (x) vfree(x) +#define module_remap (ptr, size) shrink_vm_area(ptr, size)#define module_arch_init (x) mips64_module_init(x)#define arch_init_modules (x) mips64_init_modules(x)
include/asm-ppc/module.h +1 -0
@@ -9,6 +9,7 @@#define module_map (x) vmalloc(x)#define module_unmap (x) vfree(x) +#define module_remap (ptr, size) shrink_vm_area(ptr, size)#define module_arch_init (x) (0)#define arch_init_modules (x) do { } while (0)
include/asm-ppc64/module.h +1 -0
@@ -13,6 +13,7 @@#define module_map (x) vmalloc(x)#define module_unmap (x) vfree(x) +#define module_remap (ptr, size) shrink_vm_area(ptr, size)#define arch_init_modules (x) do { } while (0)#define module_arch_init (x) (0)#endif /* _ASM_PPC64_MODULE_H */
include/asm-s390/module.h +1 -0
@@ -6,6 +6,7 @@#define module_map (x) vmalloc(x)#define module_unmap (x) vfree(x) +#define module_remap (ptr, size) shrink_vm_area(ptr, size)#define module_arch_init (x) (0)#define arch_init_modules (x) do { } while (0)
include/asm-s390x/module.h +1 -0
@@ -6,6 +6,7 @@#define module_map (x) vmalloc(x)#define module_unmap (x) vfree(x) +#define module_remap (ptr, size) shrink_vm_area(ptr, size)#define module_arch_init (x) (0)#define arch_init_modules (x) do { } while (0)
include/asm-sh/module.h +1 -0
@@ -6,6 +6,7 @@#define module_map (x) vmalloc(x)#define module_unmap (x) vfree(x) +#define module_remap (ptr, size) shrink_vm_area(ptr, size)#define module_arch_init (x) (0)#define arch_init_modules (x) do { } while (0)
include/asm-sparc/module.h +1 -0
@@ -6,6 +6,7 @@#define module_map (x) vmalloc(x)#define module_unmap (x) vfree(x) +#define module_remap (ptr, size) shrink_vm_area(ptr, size)#define module_arch_init (x) (0)#define arch_init_modules (x) do { } while (0)
include/asm-sparc64/module.h +1 -0
@@ -6,6 +6,7 @@extern void *module_map (unsigned long size);extern void module_unmap (void *addr); +#define module_remap (ptr, size) (-ENOSYS)#define module_arch_init (x) (0)#define arch_init_modules (x) do { } while (0)
include/asm-x86_64/module.h +2 -0
@@ -9,6 +9,8 @@extern void *module_map (unsigned long );extern void module_unmap (void *); +#define module_remap (ptr, size) (-ENOSYS) +#define module_arch_init (x) (0)#define arch_init_modules (x) do { } while (0)
include/linux/init.h +14 -19
@@ -38,6 +38,18 @@* Also note, that this data cannot be "const". */ +/* +* Mark functions and data as being only used at initialization +* or exit time. +*/ +#define __init __attribute__ ((__section__ (".text.init"))) +#define __initdata __attribute__ ((__section__ (".data.init"))) + +/* For assembly routines */ +#define __INIT .section ".text.init","ax" +#define __FINIT .previous +#define __INITDATA .section ".data.init","aw" +#ifndef MODULE #ifndef __ASSEMBLY__
@@ -89,23 +101,12 @@#endif /* __ASSEMBLY__ */ -/* -* Mark functions and data as being only used at initialization -* or exit time. -*/ -#define __init __attribute__ ((__section__ (".text.init"))) -#define __exit __attribute__ ((unused, __section__(".text.exit"))) -#define __initdata __attribute__ ((__section__ (".data.init"))) -#define __exitdata __attribute__ ((unused, __section__ (".data.exit")))#define __initsetup __attribute__ ((unused,__section__ (".setup.init")))#define __init_call (level) __attribute__ ((unused,__section__ (".initcall" level ".init"))) +#define __exit __attribute__ ((unused, __section__(".text.exit"))) +#define __exitdata __attribute__ ((unused, __section__ (".data.exit")))#define __exit_call __attribute__ ((unused,__section__ (".exitcall.exit"))) -/* For assembly routines */ -#define __INIT .section ".text.init","ax" -#define __FINIT .previous -#define __INITDATA .section ".data.init","aw" -/** * module_init() - driver initialization entry point * @x: function to be run at kernel boot time or module insertion
@@ -131,15 +132,9 @@#else -#define __init#define __exit -#define __initdata#define __exitdata#define __initcall (fn) -/* For assembly routines */ -#define __INIT -#define __FINIT -#define __INITDATA/* These macros create a dummy inline: gcc 2.9x does not count alias as usage, hence the `unused function' warning when __init functions
include/linux/vmalloc.h +1 -0
@@ -24,6 +24,7 @@extern void vmfree_area_pages (unsigned long address,unsigned long size);extern int vmalloc_area_pages (unsigned long address,unsigned long size,int gfp_mask,pgprot_t prot); +extern int shrink_vm_area (void * addr,unsigned long size);/* * Allocate any pages
kernel/ksyms.c +1 -0
@@ -108,6 +108,7 @@EXPORT_SYMBOL (vfree);EXPORT_SYMBOL (__vmalloc);EXPORT_SYMBOL (vmalloc_to_page); +EXPORT_SYMBOL (shrink_vm_area);EXPORT_SYMBOL (mem_map);EXPORT_SYMBOL (remap_page_range);EXPORT_SYMBOL (max_mapnr);
kernel/module.c +7 -0
@@ -558,6 +558,13 @@ error = -EBUSY ; goto err0; } + +/* It's time to throw away .init sections */ +if (mod->runsize) { +if (!module_remap (mod, mod->runsize)) + mod->size = mod->runsize; + } +atomic_dec (&mod->uc.usecount);/* And set it running. */
mm/vmalloc.c +38 -0
@@ -174,6 +174,44 @@return ret; } +int shrink_vm_area (void * addr,unsigned long size) +{ +int ret; +struct vm_struct **p, *tmp; + +if (!addr || !(unsigned long )addr & (PAGE_SIZE -1 ) || !size) +return -EINVAL ; + + size =PAGE_ALIGN (size) +PAGE_SIZE ; + + ret =0 ; +write_lock (&vmlist_lock); +for (p = &vmlist; (tmp = *p) ; p = &tmp->next) { +if (tmp->addr == addr) { +int delta; +unsigned long end; + +if (size > tmp->size) { + ret = -EINVAL ; +break ; + } +if (size == tmp->size) +break ; + + delta = tmp->size - size; + end = (unsigned long ) tmp->addr + size -PAGE_SIZE ; + + tmp->size = size +PAGE_SIZE ; +vmfree_area_pages (VMALLOC_VMADDR (end), delta); + +break ; + } + } + +write_unlock (&vmlist_lock); +return ret; +} +struct vm_struct *get_vm_area (unsigned long size,unsigned long flags) {unsigned long addr;
Quoted 4 lines · Andrey Panin
attached patch adds support for freeing .init sections of loadable modules
afterinit_module()function exits. Modutils have support for this since 19= 98,
but kernel support didn't exist.
The main reason I have not done this myself is the interaction between freeing code areas and the exception and unwind tables. When you free code, you should remove or nullify the related unwind and exception entries. Another module could be loaded into the area that used to contain init code and it would then be mapped by the first module's tables, oops.
Fixing exception tables is relatively easy, search_exception_table() can check if the address fits within the module (taking runsize into account) before running the exception table. Unwind data is harder, it is all architecture specific. Your patch will not be complete without fixing all tables that point at module code. Don't forget the MIPS DBE table.
Rusty and I plan to completely redo module loading and unloading and the corresponding modutils support in 2.5. That will (hopefully) include additional features such as per-node replication of pure module areas for NUMA boxes. For me, the ability to free .init text and data from modules falls into the category of "would be nice but the gain is not worth the bother just for 2.4".
Quoted 7 lines · On Thu, 09 May 2002 00:02:07 +1000 Keith Owens <[email protected]> wrote:
Quoted 4 lines · Andrey Panin
attached patch adds support for freeing .init sections of loadable modules
afterinit_module()function exits. Modutils have support for this since 19= 98,
but kernel support didn't exist.
Quoted 6 lines
The main reason I have not done this myself is the interaction between freeing code areas and the exception and unwind tables. When you free code, you should remove or nullify the related unwind and exception entries. Another module could be loaded into the area that used to contain init code and it would then be mapped by the first module's tables, oops.
Excellent catch, Keith. I missed this in my current implementation, and will have to patch the exception tables in the init release callback. I also get around this because I allow archs to refuse to have separate init and core sections if they want.
BTW, I just got ppc64 in-kernel relocations working. I am starting on ia64, in the theory that if you can do ia64, you can do anything.
Cheers,
Rusty.
Signature · 2 lines
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy