Skip to contentSkip to conversation

__init and friends support for loadable modules

RFC PATCH In review Andrey Panin 3 participants 3 messages May 8, 2002 – May 9, 2002

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

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
diff -urN -X /usr/share/dontdiff linux.vanilla/include/asm-alpha/module.h linux/include/asm-alpha/module.h
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/include/asm-arm/module.h linux/include/asm-arm/module.h
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/include/asm-cris/module.h linux/include/asm-cris/module.h
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/include/asm-i386/module.h linux/include/asm-i386/module.h
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/include/asm-ia64/module.h linux/include/asm-ia64/module.h
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/include/asm-m68k/module.h linux/include/asm-m68k/module.h
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/include/asm-mips/module.h linux/include/asm-mips/module.h
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/include/asm-mips64/module.h linux/include/asm-mips64/module.h
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/include/asm-ppc/module.h linux/include/asm-ppc/module.h
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/include/asm-ppc64/module.h linux/include/asm-ppc64/module.h
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/include/asm-s390/module.h linux/include/asm-s390/module.h
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/include/asm-s390x/module.h linux/include/asm-s390x/module.h
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/include/asm-sh/module.h linux/include/asm-sh/module.h
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/include/asm-sparc/module.h linux/include/asm-sparc/module.h
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/include/asm-sparc64/module.h linux/include/asm-sparc64/module.h
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/include/asm-x86_64/module.h linux/include/asm-x86_64/module.h
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/include/linux/init.h linux/include/linux/init.h
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/include/linux/vmalloc.h linux/include/linux/vmalloc.h
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/kernel/ksyms.c linux/kernel/ksyms.c
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/kernel/module.c linux/kernel/module.c
@@ -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
diff -urN -X /usr/share/dontdiff linux.vanilla/mm/vmalloc.c linux/mm/vmalloc.c
@@ -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;
#1 lore raw
Quoted 4 lines · Andrey Panin

attached patch adds support for freeing .init sections of loadable modules
after init_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".

#2 lore raw
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
after init_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