Skip to contentSkip to conversation

ARM: option for loading modules into vmalloc area

RFC PATCH In review Konstantin Khlebnikov 6 participants 22 messages Nov 18, 2014 – Nov 19, 2014 Arm (32-bit)

lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 ARM: option for loading modules into vmalloc area

Usually modules are loaded into small area prior to the kernel text because they are linked with the kernel using short calls. Compile-time instrumentation like GCOV or KASAN bloats code a lot, and as a result huge modules no longer fit into reserved area.

This patch adds option CONFIG_MODULES_USE_VMALLOC which lifts limitation on amount of loaded modules. It links modules using long-calls (option -mlong-calls) and loads them into vmalloc area.

In few places exported symbols are called from inline assembly. This patch adds macro for such call sites: __asmbl and __asmbl_clobber. Call turns into single 'bl' or sequence 'movw; movt; blx' depending on context and state of config option.

Unfortunately this option isn't compatible with CONFIG_FUNCTION_TRACER. Compiler emits short calls to profiling function despite of -mlong-calls. This is a bug in GCC, but ftrace anyway needs an update to handle this.

---
 arch/arm/Kconfig                |   20 ++++++++++++++++++++
 arch/arm/Makefile               |    4 ++++
 arch/arm/include/asm/compiler.h |   13 +++++++++++++
 arch/arm/include/asm/div64.h    |    2 +-
 arch/arm/include/asm/memory.h   |   11 +++++++++++
 arch/arm/include/asm/uaccess.h  |   16 ++++++++--------
 arch/arm/kernel/module.c        |    2 ++
 arch/arm/mm/dump.c              |   10 +++++++++-
 arch/arm/mm/init.c              |    2 ++
 arch/arm/mm/mmu.c               |    7 +++----
 arch/arm/mm/pgd.c               |    5 +++--
 11 files changed, 76 insertions(+), 16 deletions(-)
arch/arm/Kconfig +20 -0
index 89c4b5c..7fc4b22 100644
@@ -1686,6 +1686,26 @@ config HIGHPTE
 	bool "Allocate 2nd-level pagetables from highmem"
 	depends on HIGHMEM
 
+config MODULES_USE_LONG_CALLS
+	bool
+	help
+	  Use long calls for calling exported symbols.
+
+config MODULES_USE_VMALLOC
+	bool "Put modules into vmalloc area"
+	select MODULES_USE_LONG_CALLS
+	depends on MMU && MODULES
+	depends on !XIP_KERNEL
+	depends on !FUNCTION_TRACER
+	help
+	  Usually modules are loaded into small area prior to the kernel text
+	  because they are linked with the kernel using short calls.
+
+	  This option enables long calls and moves modules into vmalloc area.
+	  This allows to load more modules but adds some perfromance penalty.
+
+	  If unsure, say n.
+
 config HW_PERF_EVENTS
 	bool "Enable hardware performance counter support for perf events"
 	depends on PERF_EVENTS
arch/arm/Makefile +4 -0
index 034a949..64541db 100644
@@ -117,6 +117,10 @@ CFLAGS_ISA	:=$(call cc-option,-marm,)
 AFLAGS_ISA	:=$(CFLAGS_ISA)
 endif
 
+ifeq ($(CONFIG_MODULES_USE_LONG_CALLS),y)
+CFLAGS_MODULE	+= -mlong-calls
+endif
+
 # Need -Uarm for gcc < 3.x
 KBUILD_CFLAGS	+=$(CFLAGS_ABI) $(CFLAGS_ISA) $(arch-y) $(tune-y) $(call cc-option,-mshort-load-bytes,$(call cc-option,-malignment-traps,)) -msoft-float -Uarm
 KBUILD_AFLAGS	+=$(CFLAGS_ABI) $(AFLAGS_ISA) $(arch-y) $(tune-y) -include asm/unified.h -msoft-float
arch/arm/include/asm/compiler.h +13 -0
index 8155db2..d953067 100644
@@ -11,5 +11,18 @@
  */
 #define __asmeq(x, y)  ".ifnc " x "," y " ; .err ; .endif\n\t"
 
+/*
+ * This is used for calling exported symbols from inline assembly code.
+ */
+#if defined(MODULE) && defined(CONFIG_MODULES_USE_LONG_CALLS)
+#define __asmbl(cond, reg, target) \
+	"movw	" reg ", #:lower16:" target "\n\t" \
+	"movt	" reg ", #:upper16:" target "\n\t" \
+	"blx" cond "	" reg "\n\t"
+#define __asmbl_clobber(reg)	,reg
+#else
+#define __asmbl(cond, reg, target) "bl" cond "	" target"\n\t"
+#define __asmbl_clobber(reg)
+#endif
 
 #endif /* __ASM_ARM_COMPILER_H */
arch/arm/include/asm/div64.h +1 -1
index 662c7bd..fc7548d 100644
@@ -38,7 +38,7 @@
 		__asmeq("%1", "r2")				\
 		__asmeq("%2", "r0")				\
 		__asmeq("%3", "r4")				\
-		"bl	__do_div64"				\
+		__asmbl("", "ip",  "__do_div64")		\
 		: "=r" (__rem), "=r" (__res)			\
 		: "r" (__n), "r" (__base)			\
 		: "ip", "lr", "cc");				\
arch/arm/include/asm/memory.h +11 -0
index e731018..17745c2 100644
@@ -47,6 +47,15 @@
  */
 #define TASK_SIZE_26		(UL(1) << 26)
 
+#ifdef CONFIG_MODULES_USE_VMALLOC
+/*
+ * Modules might be anywhere in the vmalloc area.
+ */
+#define MODULES_VADDR		VMALLOC_START
+#define MODULES_END		VMALLOC_END
+
+#else /* CONFIG_MODULES_USE_VMALLOC */
+
 /*
  * The module space lives between the addresses given by TASK_SIZE
  * and PAGE_OFFSET - it must be within 32MB of the kernel text.
@@ -71,6 +80,8 @@
 #define MODULES_END		(PAGE_OFFSET)
 #endif
 
+#endif /* CONFIG_MODULES_USE_VMALLOC */
+
 /*
  * The XIP kernel gets mapped@the bottom of the module vm area.
  * Since we use sections to map it, this macro replaces the physical address
arch/arm/include/asm/uaccess.h +8 -8
index 4767eb9..c4c8d26 100644
@@ -113,21 +113,21 @@ extern int __get_user_64t_1(void *);
 extern int __get_user_64t_2(void *);
 extern int __get_user_64t_4(void *);
 
-#define __GUP_CLOBBER_1	"lr", "cc"
+#define __GUP_CLOBBER_1	"lr", "cc" __asmbl_clobber("ip")
 #ifdef CONFIG_CPU_USE_DOMAINS
 #define __GUP_CLOBBER_2	"ip", "lr", "cc"
 #else
-#define __GUP_CLOBBER_2 "lr", "cc"
+#define __GUP_CLOBBER_2 "lr", "cc" __asmbl_clobber("ip")
 #endif
-#define __GUP_CLOBBER_4	"lr", "cc"
-#define __GUP_CLOBBER_32t_8 "lr", "cc"
-#define __GUP_CLOBBER_8	"lr", "cc"
+#define __GUP_CLOBBER_4	"lr", "cc" __asmbl_clobber("ip")
+#define __GUP_CLOBBER_32t_8 "lr", "cc" __asmbl_clobber("ip")
+#define __GUP_CLOBBER_8	"lr", "cc" __asmbl_clobber("ip")
 
 #define __get_user_x(__r2,__p,__e,__l,__s)				\
 	   __asm__ __volatile__ (					\
 		__asmeq("%0", "r0") __asmeq("%1", "r2")			\
 		__asmeq("%3", "r1")					\
-		"bl	__get_user_" #__s				\
+		__asmbl("", "ip", "__get_user_" #__s)			\
 		: "=&r" (__e), "=r" (__r2)				\
 		: "0" (__p), "r" (__l)					\
 		: __GUP_CLOBBER_##__s)
@@ -149,7 +149,7 @@ extern int __get_user_64t_4(void *);
 	   __asm__ __volatile__ (					\
 		__asmeq("%0", "r0") __asmeq("%1", "r2")			\
 		__asmeq("%3", "r1")					\
-		"bl	__get_user_64t_" #__s				\
+		__asmbl("", "ip", "__get_user_64t_" #__s)		\
 		: "=&r" (__e), "=r" (__r2)				\
 		: "0" (__p), "r" (__l)					\
 		: __GUP_CLOBBER_##__s)
@@ -211,7 +211,7 @@ extern int __put_user_8(void *, unsigned long long);
 	   __asm__ __volatile__ (					\
 		__asmeq("%0", "r0") __asmeq("%2", "r2")			\
 		__asmeq("%3", "r1")					\
-		"bl	__put_user_" #__s				\
+		__asmbl("", "ip", "__put_user_" #__s)			\
 		: "=&r" (__e)						\
 		: "0" (__p), "r" (__r2), "r" (__l)			\
 		: "ip", "lr", "cc")
arch/arm/kernel/module.c +2 -0
index 6a4dffe..081da90 100644
@@ -38,12 +38,14 @@
 #endif
 
 #ifdef CONFIG_MMU
+#ifndef CONFIG_MODULES_USE_VMALLOC
 void *module_alloc(unsigned long size)
 {
 	return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END,
 				GFP_KERNEL, PAGE_KERNEL_EXEC, NUMA_NO_NODE,
 				__builtin_return_address(0));
 }
+#endif /* CONFIG_MODULES_USE_VMALLOC */
 #endif
 
 int
arch/arm/mm/dump.c +9 -1
index 5942493..d4d4f75d 100644
@@ -19,6 +19,7 @@
 
 #include <asm/fixmap.h>
 #include <asm/pgtable.h>
+#include <asm/highmem.h>
 
 struct addr_marker {
 	unsigned long start_address;
@@ -26,7 +27,12 @@ struct addr_marker {
 };
 
 static struct addr_marker address_markers[] = {
+#ifndef CONFIG_MODULES_USE_VMALLOC
 	{ MODULES_VADDR,	"Modules" },
+#endif
+#ifdef CONFIG_HIGHMEM
+	{ PKMAP_BASE,		"Page kmap" },
+#endif
 	{ PAGE_OFFSET,		"Kernel Mapping" },
 	{ 0,			"vmalloc() Area" },
 	{ VMALLOC_END,		"vmalloc() End" },
@@ -356,7 +362,9 @@ static int ptdump_init(void)
 			for (j = 0; j < pg_level[i].num; j++)
 				pg_level[i].mask |= pg_level[i].bits[j].mask;
 
-	address_markers[2].start_address = VMALLOC_START;
+	i = 1 + !IS_ENABLED(CONFIG_MODULES_USE_VMALLOC) +
+		!!IS_ENABLED(CONFIG_HIGHMEM);
+	address_markers[i].start_address = VMALLOC_START;
 
 	pe = debugfs_create_file("kernel_page_tables", 0400, NULL, NULL,
 				 &ptdump_fops);
arch/arm/mm/init.c +2 -0
index 9481f85..985aed8 100644
@@ -595,7 +595,9 @@ void __init mem_init(void)
 	 * be detected@build time already.
 	 */
 #ifdef CONFIG_MMU
+#ifndef CONFIG_MODULES_USE_VMALLOC
 	BUILD_BUG_ON(TASK_SIZE				> MODULES_VADDR);
+#endif
 	BUG_ON(TASK_SIZE 				> MODULES_VADDR);
 #endif
 
arch/arm/mm/mmu.c +3 -4
index 9d2cdda..9e0c4f4 100644
@@ -1161,16 +1161,15 @@ void __init sanity_check_meminfo(void)
 
 static inline void prepare_page_table(void)
 {
-	unsigned long addr;
+	unsigned long addr = 0;
 	phys_addr_t end;
 
 	/*
 	 * Clear out all the mappings below the kernel image.
 	 */
-	for (addr = 0; addr < MODULES_VADDR; addr += PMD_SIZE)
-		pmd_clear(pmd_off_k(addr));
-
 #ifdef CONFIG_XIP_KERNEL
+	for ( ; addr < MODULES_VADDR; addr += PMD_SIZE)
+		pmd_clear(pmd_off_k(addr));
 	/* The XIP kernel is mapped in the module area -- skip over it */
 	addr = ((unsigned long)_etext + PMD_SIZE - 1) & PMD_MASK;
 #endif
arch/arm/mm/pgd.c +3 -2
index 3fbcb5a..ce23923 100644
@@ -57,11 +57,11 @@ pgd_t *pgd_alloc(struct mm_struct *mm)
 	clean_dcache_area(new_pgd, TTBR0_PTRS_PER_PGD * sizeof(pgd_t));
 
 #ifdef CONFIG_ARM_LPAE
+#if defined(CONFIG_HIGHMEM) || !defined(CONFIG_MODULES_USE_VMALLOC)
 	/*
 	 * Allocate PMD table for modules and pkmap mappings.
 	 */
-	new_pud = pud_alloc(mm, new_pgd + pgd_index(MODULES_VADDR),
-			    MODULES_VADDR);
+	new_pud = pud_alloc(mm, new_pgd + pgd_index(PKMAP_BASE), PKMAP_BASE);
 	if (!new_pud)
 		goto no_pud;
 
@@ -69,6 +69,7 @@ pgd_t *pgd_alloc(struct mm_struct *mm)
 	if (!new_pmd)
 		goto no_pmd;
 #endif
+#endif
 
 	if (!vectors_high()) {
 		/*
#1 lore raw
Quoted 9 lines · Konstantin Khlebnikov

Usually modules are loaded into small area prior to the kernel text because they are linked with the kernel using short calls. Compile-time instrumentation like GCOV or KASAN bloats code a lot, and as a result huge modules no longer fit into reserved area.

This patch adds option CONFIG_MODULES_USE_VMALLOC which lifts limitation on amount of loaded modules. It links modules using long-calls (option -mlong-calls) and loads them into vmalloc area.

Quoted 8 lines

In few places exported symbols are called from inline assembly. This patch adds macro for such call sites: __asmbl and __asmbl_clobber. Call turns into single 'bl' or sequence 'movw; movt; blx' depending on context and state of config option.

Unfortunately this option isn't compatible with CONFIG_FUNCTION_TRACER. Compiler emits short calls to profiling function despite of -mlong-calls. This is a bug in GCC, but ftrace anyway needs an update to handle this.

It also isn't compatible with the older architectures which don't have "blx".

Quoted 14 lines
arch/arm/mm/mmu.c +1 -1
index 9d2cdda..9e0c4f4 100644
@@ -1161,16 +1161,15 @@ void __init sanity_check_meminfo(void)
 
 static inline void prepare_page_table(void)
 {
-	unsigned long addr;
+	unsigned long addr = 0;
 	phys_addr_t end;
 
 	/*
 	 * Clear out all the mappings below the kernel image.

arch/arm/mm/mmu.c void __init sanity_check_meminfo(void) · line 1175

Quoted 10 lines
arch/arm/mm/mmu.c +2 -3
 	 */
-	for (addr = 0; addr < MODULES_VADDR; addr += PMD_SIZE)
-		pmd_clear(pmd_off_k(addr));
-
 #ifdef CONFIG_XIP_KERNEL
+	for ( ; addr < MODULES_VADDR; addr += PMD_SIZE)
+		pmd_clear(pmd_off_k(addr));
 	/* The XIP kernel is mapped in the module area -- skip over it */
 	addr = ((unsigned long)_etext + PMD_SIZE - 1) & PMD_MASK;
 #endif

This seems to be an unrelated change. In any case, it looks wrong to me. Please describe what you are trying to achieve here.

Signature · 2 lines
FTTC broadband for 0.8mile line: currently at 9.5Mbps down 400kbps up
according to speedtest.net.
#2 lore raw
Quoted 15 lines · Russell King - ARM Linux
Quoted 14 lines · Konstantin Khlebnikov

Usually modules are loaded into small area prior to the kernel text because they are linked with the kernel using short calls. Compile-time instrumentation like GCOV or KASAN bloats code a lot, and as a result huge modules no longer fit into reserved area.

This patch adds option CONFIG_MODULES_USE_VMALLOC which lifts limitation on amount of loaded modules. It links modules using long-calls (option -mlong-calls) and loads them into vmalloc area.

In few places exported symbols are called from inline assembly. This patch adds macro for such call sites: __asmbl and __asmbl_clobber. Call turns into single 'bl' or sequence 'movw; movt; blx' depending on context and state of config option.

Quoted 5 lines
Quoted 3 lines

Unfortunately this option isn't compatible with CONFIG_FUNCTION_TRACER. Compiler emits short calls to profiling function despite of -mlong-calls. This is a bug in GCC, but ftrace anyway needs an update to handle this.

It also isn't compatible with the older architectures which don't have "blx".

Ok, I'll add "depends on CPU_V6 || CPU_V7" I don't think that it is necessary for older cpus.

Quoted 12 lines
Quoted 12 lines
arch/arm/mm/mmu.c +1 -1
index 9d2cdda..9e0c4f4 100644
@@ -1161,16 +1161,15 @@ void __init sanity_check_meminfo(void)
 
  static inline void prepare_page_table(void)
  {
-	unsigned long addr;
+	unsigned long addr = 0;
  	phys_addr_t end;
 
Quoted 15 lines

arch/arm/mm/mmu.c void __init sanity_check_meminfo(void) · line 1175

Quoted 12 lines
arch/arm/mm/mmu.c +2 -3
  	/*
  	 * Clear out all the mappings below the kernel image.
  	 */
-	for (addr = 0; addr < MODULES_VADDR; addr += PMD_SIZE)
-		pmd_clear(pmd_off_k(addr));
-
  #ifdef CONFIG_XIP_KERNEL
+	for ( ; addr < MODULES_VADDR; addr += PMD_SIZE)
+		pmd_clear(pmd_off_k(addr));
  	/* The XIP kernel is mapped in the module area -- skip over it */
  	addr = ((unsigned long)_etext + PMD_SIZE - 1) & PMD_MASK;
  #endif

This seems to be an unrelated change. In any case, it looks wrong to me. Please describe what you are trying to achieve here.

My patch changes MODULES_VADDR, if option enabled it becomes alias for VMALLOC_START

It's used as border between two loops, before patch this code looks like:

     /*
      * Clear out all the mappings below the kernel image.
      */
     for (addr = 0; addr < MODULES_VADDR; addr += PMD_SIZE)
         pmd_clear(pmd_off_k(addr));
#ifdef CONFIG_XIP_KERNEL
     /* The XIP kernel is mapped in the module area -- skip over it */
     addr = ((unsigned long)_etext + PMD_SIZE - 1) & PMD_MASK;
#endif
     for ( ; addr < PAGE_OFFSET; addr += PMD_SIZE)
         pmd_clear(pmd_off_k(addr));

It's written with assumption that MODULES_VADDR < PAGE_OFFSET, but this no longer true if modules are placed inside vmalloc area.

After patch:

     unsigned long addr = 0;

     /*
      * Clear out all the mappings below the kernel image.
      */
#ifdef CONFIG_XIP_KERNEL
     for ( ; addr < MODULES_VADDR; addr += PMD_SIZE)
         pmd_clear(pmd_off_k(addr));
     /* The XIP kernel is mapped in the module area -- skip over it */
     addr = ((unsigned long)_etext + PMD_SIZE - 1) & PMD_MASK;
#endif
     for ( ; addr < PAGE_OFFSET; addr += PMD_SIZE)
         pmd_clear(pmd_off_k(addr));

Without XIP there is no reason for splitting it into two loops.

#3 lore raw
Quoted 16 lines · On Tuesday 18 November 2014 21:13:56 Konstantin Khlebnikov wrote:
Quoted 15 lines · Russell King - ARM Linux
Quoted 14 lines · Konstantin Khlebnikov

Usually modules are loaded into small area prior to the kernel text because they are linked with the kernel using short calls. Compile-time instrumentation like GCOV or KASAN bloats code a lot, and as a result huge modules no longer fit into reserved area.

This patch adds option CONFIG_MODULES_USE_VMALLOC which lifts limitation on amount of loaded modules. It links modules using long-calls (option -mlong-calls) and loads them into vmalloc area.

In few places exported symbols are called from inline assembly. This patch adds macro for such call sites: __asmbl and __asmbl_clobber. Call turns into single 'bl' or sequence 'movw; movt; blx' depending on context and state of config option.

Quoted 8 lines
Quoted 5 lines
Quoted 3 lines

Unfortunately this option isn't compatible with CONFIG_FUNCTION_TRACER. Compiler emits short calls to profiling function despite of -mlong-calls. This is a bug in GCC, but ftrace anyway needs an update to handle this.

It also isn't compatible with the older architectures which don't have "blx".

Ok, I'll add "depends on CPU_V6 || CPU_V7" I don't think that it is necessary for older cpus.

Why not just use a different branch instruction for the older CPUs?

Arnd

#4 lore raw
Quoted 20 lines · Arnd Bergmann
Quoted 19 lines · On Tuesday 18 November 2014 21:13:56 Konstantin Khlebnikov wrote:
Quoted 18 lines · Russell King - ARM Linux
Quoted 17 lines · Konstantin Khlebnikov

Usually modules are loaded into small area prior to the kernel text because they are linked with the kernel using short calls. Compile-time instrumentation like GCOV or KASAN bloats code a lot, and as a result huge modules no longer fit into reserved area.

This patch adds option CONFIG_MODULES_USE_VMALLOC which lifts limitation on amount of loaded modules. It links modules using long-calls (option -mlong-calls) and loads them into vmalloc area.

In few places exported symbols are called from inline assembly. This patch adds macro for such call sites: __asmbl and __asmbl_clobber. Call turns into single 'bl' or sequence 'movw; movt; blx' depending on context and state of config option.

Unfortunately this option isn't compatible with CONFIG_FUNCTION_TRACER. Compiler emits short calls to profiling function despite of -mlong-calls. This is a bug in GCC, but ftrace anyway needs an update to handle this.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

It also isn't compatible with the older architectures which don't have "blx".

Ok, I'll add "depends on CPU_V6 || CPU_V7" I don't think that it is necessary for older cpus.

Why not just use a different branch instruction for the older CPUs?

ARMv6 doesn't support movw/movt so this will only work on v7.

What about doing 'mov lr, pc; ldr pc,=symbol' instead? You clearly don't care about performance in this case, so the performance hit (due to the dcache access and interfering with the return stack predictors) should be tolerable. The only thing to be careful about is thumb2
kernels: you would need to set the thumb bit in lr manually but only if the call is made /from/ thumb. You would probably be better off just depending on !THUMB2_KERNEL.

Signature · 1 line
Ard.
#5 lore raw
Quoted 32 lines · Ard Biesheuvel
Quoted 20 lines · Arnd Bergmann
Quoted 19 lines · On Tuesday 18 November 2014 21:13:56 Konstantin Khlebnikov wrote:
Quoted 18 lines · Russell King - ARM Linux
Quoted 17 lines · Konstantin Khlebnikov

Usually modules are loaded into small area prior to the kernel text because they are linked with the kernel using short calls. Compile-time instrumentation like GCOV or KASAN bloats code a lot, and as a result huge modules no longer fit into reserved area.

This patch adds option CONFIG_MODULES_USE_VMALLOC which lifts limitation on amount of loaded modules. It links modules using long-calls (option -mlong-calls) and loads them into vmalloc area.

In few places exported symbols are called from inline assembly. This patch adds macro for such call sites: __asmbl and __asmbl_clobber. Call turns into single 'bl' or sequence 'movw; movt; blx' depending on context and state of config option.

Unfortunately this option isn't compatible with CONFIG_FUNCTION_TRACER. Compiler emits short calls to profiling function despite of -mlong-calls. This is a bug in GCC, but ftrace anyway needs an update to handle this.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

It also isn't compatible with the older architectures which don't have "blx".

Ok, I'll add "depends on CPU_V6 || CPU_V7" I don't think that it is necessary for older cpus.

Why not just use a different branch instruction for the older CPUs?

ARMv6 doesn't support movw/movt so this will only work on v7.

Quoted 7 lines

What about doing 'mov lr, pc; ldr pc,=symbol' instead? You clearly don't care about performance in this case, so the performance hit (due to the dcache access and interfering with the return stack predictors) should be tolerable. The only thing to be careful about is thumb2
kernels: you would need to set the thumb bit in lr manually but only if the call is made /from/ thumb. You would probably be better off just depending on !THUMB2_KERNEL.

Do you mean ldr pc, =symbol ?

In this case I get this error:

/tmp/ccAHtONU.s: Assembler messages:
/tmp/ccAHtONU.s:220: Error: invalid literal constant: pool needs to be closer

Probably constant pool doesn't work well in inline assembly.

Something like this seems work:

add lr, pc, #4
ldr pc, [pc, #-4]
.long symbol

Quoted 6 lines

Ard.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

#6 lore raw
Quoted 49 lines · Konstantin Khlebnikov
Quoted 32 lines · Ard Biesheuvel
Quoted 20 lines · Arnd Bergmann
Quoted 19 lines · On Tuesday 18 November 2014 21:13:56 Konstantin Khlebnikov wrote:
Quoted 18 lines · Russell King - ARM Linux
Quoted 17 lines · Konstantin Khlebnikov

Usually modules are loaded into small area prior to the kernel text because they are linked with the kernel using short calls. Compile-time instrumentation like GCOV or KASAN bloats code a lot, and as a result huge modules no longer fit into reserved area.

This patch adds option CONFIG_MODULES_USE_VMALLOC which lifts limitation on amount of loaded modules. It links modules using long-calls (option -mlong-calls) and loads them into vmalloc area.

In few places exported symbols are called from inline assembly. This patch adds macro for such call sites: __asmbl and __asmbl_clobber. Call turns into single 'bl' or sequence 'movw; movt; blx' depending on context and state of config option.

Unfortunately this option isn't compatible with CONFIG_FUNCTION_TRACER. Compiler emits short calls to profiling function despite of -mlong-calls. This is a bug in GCC, but ftrace anyway needs an update to handle this.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

It also isn't compatible with the older architectures which don't have "blx".

Ok, I'll add "depends on CPU_V6 || CPU_V7" I don't think that it is necessary for older cpus.

Why not just use a different branch instruction for the older CPUs?

ARMv6 doesn't support movw/movt so this will only work on v7.

Quoted 7 lines

What about doing 'mov lr, pc; ldr pc,=symbol' instead? You clearly don't care about performance in this case, so the performance hit (due to the dcache access and interfering with the return stack predictors) should be tolerable. The only thing to be careful about is thumb2
kernels: you would need to set the thumb bit in lr manually but only if the call is made /from/ thumb. You would probably be better off just depending on !THUMB2_KERNEL.

Do you mean ldr pc, =symbol ?

In this case I get this error:

/tmp/ccAHtONU.s: Assembler messages:
/tmp/ccAHtONU.s:220: Error: invalid literal constant: pool needs to be closer

Quoted 9 lines

Probably constant pool doesn't work well in inline assembly.

Something like this seems work:

add lr, pc, #4
ldr pc, [pc, #-4]
.long symbol

You can add a '.ltorg' instruction which tells the assembler to dump the literal pool, but you still need to jump over it, i.e.,

adr lr, 0f
ldr pc, =symbol
.ltorg
0:

Signature · 1 line
Ard.
#7 lore raw
Quoted 18 lines · Ard Biesheuvel
Quoted 7 lines · Konstantin Khlebnikov

Do you mean ldr pc, =symbol ?

In this case I get this error:

/tmp/ccAHtONU.s: Assembler messages:
/tmp/ccAHtONU.s:220: Error: invalid literal constant: pool needs to be closer

Quoted 9 lines

Probably constant pool doesn't work well in inline assembly.

Something like this seems work:

add lr, pc, #4
ldr pc, [pc, #-4]
.long symbol

Quoted 7 lines

You can add a '.ltorg' instruction which tells the assembler to dump the literal pool, but you still need to jump over it, i.e.,

adr lr, 0f
ldr pc, =symbol
.ltorg
0:

Which is not a good idea either, because the compiler needs to know how far away its own manually generated literal pool is from the instructions which reference it. The .ltorg statement can end up emitting any number of literals at that point, which makes it indeterminant how many words are contained within the asm() statement.

Yes, it isn't desirable to waste an entire data cache line per indirect call like the original quote above, but I don't see a practical alternative.

Signature · 2 lines
FTTC broadband for 0.8mile line: currently at 9.5Mbps down 400kbps up
according to speedtest.net.
#8 lore raw
Quoted 27 lines · Russell King - ARM Linux
Quoted 18 lines · Ard Biesheuvel
Quoted 7 lines · Konstantin Khlebnikov

Do you mean ldr pc, =symbol ?

In this case I get this error:

/tmp/ccAHtONU.s: Assembler messages:
/tmp/ccAHtONU.s:220: Error: invalid literal constant: pool needs to be closer

Quoted 9 lines

Probably constant pool doesn't work well in inline assembly.

Something like this seems work:

add lr, pc, #4
ldr pc, [pc, #-4]
.long symbol

Quoted 7 lines

You can add a '.ltorg' instruction which tells the assembler to dump the literal pool, but you still need to jump over it, i.e.,

adr lr, 0f
ldr pc, =symbol
.ltorg
0:

Quoted 6 lines

Which is not a good idea either, because the compiler needs to know how far away its own manually generated literal pool is from the instructions which reference it. The .ltorg statement can end up emitting any number of literals at that point, which makes it indeterminant how many words are contained within the asm() statement.

That applies to any inline asm statement in general: the compiler assumes that the expanded size will not interfere with its ability to emit literals after the function's return instruction. Sometimes it will put a literal pool in the middle of the function if it is very large, and I am not sure if an inline asm by itself would ever trigger that heuristic to kick in.

But by the same logic, i.e., due to the fact that GCC manages its own literals, the literal pool at the assembly level is unlikely to be so large that you will actually hit this condition.

Quoted 4 lines

Yes, it isn't desirable to waste an entire data cache line per indirect call like the original quote above, but I don't see a practical alternative.

We could at least add some labels instead of doing explicit pc arithmetic, i.e.,

adr lr, 1f
ldr pc, 0f
0: .long symbol
1:

#9 lore raw
Quoted 52 lines · Ard Biesheuvel
Quoted 27 lines · Russell King - ARM Linux
Quoted 18 lines · Ard Biesheuvel
Quoted 7 lines · Konstantin Khlebnikov

Do you mean ldr pc, =symbol ?

In this case I get this error:

/tmp/ccAHtONU.s: Assembler messages:
/tmp/ccAHtONU.s:220: Error: invalid literal constant: pool needs to be closer

Quoted 9 lines

Probably constant pool doesn't work well in inline assembly.

Something like this seems work:

add lr, pc, #4
ldr pc, [pc, #-4]
.long symbol

Quoted 7 lines

You can add a '.ltorg' instruction which tells the assembler to dump the literal pool, but you still need to jump over it, i.e.,

adr lr, 0f
ldr pc, =symbol
.ltorg
0:

Quoted 6 lines

Which is not a good idea either, because the compiler needs to know how far away its own manually generated literal pool is from the instructions which reference it. The .ltorg statement can end up emitting any number of literals at that point, which makes it indeterminant how many words are contained within the asm() statement.

That applies to any inline asm statement in general: the compiler assumes that the expanded size will not interfere with its ability to emit literals after the function's return instruction. Sometimes it will put a literal pool in the middle of the function if it is very large, and I am not sure if an inline asm by itself would ever trigger that heuristic to kick in.

But by the same logic, i.e., due to the fact that GCC manages its own literals, the literal pool at the assembly level is unlikely to be so large that you will actually hit this condition.

Quoted 4 lines

Yes, it isn't desirable to waste an entire data cache line per indirect call like the original quote above, but I don't see a practical alternative.

Quoted 6 lines

We could at least add some labels instead of doing explicit pc arithmetic, i.e.,

adr lr, 1f
ldr pc, 0f
0: .long symbol
1:

I think we need some unique prefix here, this macro is used inside bigger inline assembly constructions and probably another macro.

#10 lore raw
Quoted 56 lines · Konstantin Khlebnikov
Quoted 54 lines · Ard Biesheuvel
Quoted 27 lines · Russell King - ARM Linux
Quoted 18 lines · Ard Biesheuvel
Quoted 7 lines · Konstantin Khlebnikov

Do you mean ldr pc, =symbol ?

In this case I get this error:

/tmp/ccAHtONU.s: Assembler messages:
/tmp/ccAHtONU.s:220: Error: invalid literal constant: pool needs to be closer

Quoted 9 lines

Probably constant pool doesn't work well in inline assembly.

Something like this seems work:

add lr, pc, #4
ldr pc, [pc, #-4]
.long symbol

Quoted 7 lines

You can add a '.ltorg' instruction which tells the assembler to dump the literal pool, but you still need to jump over it, i.e.,

adr lr, 0f
ldr pc, =symbol
.ltorg
0:

Quoted 6 lines

Which is not a good idea either, because the compiler needs to know how far away its own manually generated literal pool is from the instructions which reference it. The .ltorg statement can end up emitting any number of literals at that point, which makes it indeterminant how many words are contained within the asm() statement.

That applies to any inline asm statement in general: the compiler assumes that the expanded size will not interfere with its ability to emit literals after the function's return instruction. Sometimes it will put a literal pool in the middle of the function if it is very large, and I am not sure if an inline asm by itself would ever trigger that heuristic to kick in.

But by the same logic, i.e., due to the fact that GCC manages its own literals, the literal pool at the assembly level is unlikely to be so large that you will actually hit this condition.

Quoted 4 lines

Yes, it isn't desirable to waste an entire data cache line per indirect call like the original quote above, but I don't see a practical alternative.

We could at least add some labels instead of doing explicit pc arithmetic, i.e.,

Quoted 7 lines
Quoted 4 lines

adr lr, 1f
ldr pc, 0f
0: .long symbol
1:

I think we need some unique prefix here, this macro is used inside bigger inline assembly constructions and probably another macro.

Numbers are disambiguated by the f and b suffixes, so they can be reused in the same .s file. So as long as you use a strictly numerical prefix, you can deal correctly with the case where, for instance, do_div() is called twice in the same compilation unit, and still not clash with other inline asm

Signature · 1 line
Ard.
#11 lore raw
Quoted 5 lines · Ard Biesheuvel

Numbers are disambiguated by the f and b suffixes, so they can be reused in the same .s file. So as long as you use a strictly numerical prefix, you can deal correctly with the case where, for instance, do_div() is called twice in the same compilation unit, and still not clash with other inline asm

What's not particularly nice though is to hide these in a macro, which itself may be part of a larger macro or code fragment also using small numbers.

We probably ought to be a bit more inteligent about how we choose these numbers inside macros, rather than just randomly picking some and hoping that they don't clash.

Signature · 2 lines
FTTC broadband for 0.8mile line: currently at 9.5Mbps down 400kbps up
according to speedtest.net.
#12 lore raw
Quoted 7 lines · Russell King - ARM Linux
Quoted 5 lines · Ard Biesheuvel

Numbers are disambiguated by the f and b suffixes, so they can be reused in the same .s file. So as long as you use a strictly numerical prefix, you can deal correctly with the case where, for instance, do_div() is called twice in the same compilation unit, and still not clash with other inline asm

Quoted 7 lines

What's not particularly nice though is to hide these in a macro, which itself may be part of a larger macro or code fragment also using small numbers.

We probably ought to be a bit more inteligent about how we choose these numbers inside macros, rather than just randomly picking some and hoping that they don't clash.

Maybe in this case, the macro content is so simple that labels are not warranted? Getting away without them certainly solves the issue.

Nicolas

#13 lore raw
In reply to Ard Biesheuvel
Quoted 10 lines · Ard Biesheuvel
Quoted 7 lines · Russell King - ARM Linux

On Wed, Nov 19, 2014 at 05:02:40PM +0100, Ard Biesheuvel wrote:
Which is not a good idea either, because the compiler needs to know how far away its own manually generated literal pool is from the instructions which reference it. The .ltorg statement can end up emitting any number of literals at that point, which makes it indeterminant how many words are contained within the asm() statement.

Quoted 6 lines

That applies to any inline asm statement in general: the compiler assumes that the expanded size will not interfere with its ability to emit literals after the function's return instruction. Sometimes it will put a literal pool in the middle of the function if it is very large, and I am not sure if an inline asm by itself would ever trigger that heuristic to kick in.

The compiler works it out by counting the number of assembler delimiters (iow, semicolons or newlines) in the asm() statement, and using that to track how many instructions are present.

Quoted 10 lines
Quoted 3 lines

Yes, it isn't desirable to waste an entire data cache line per indirect call like the original quote above, but I don't see a practical alternative.

We could at least add some labels instead of doing explicit pc arithmetic, i.e.,

adr lr, 1f
ldr pc, 0f
0: .long symbol
1:

Yes, but this doesn't get away from the performance impact of having one word used in a D-cache line scattered throughout the code. This is the reason why I never looked at this as a serious option for kernel modules, and decided to put the kernel modules below the kernel itself instead.

In older kernels, when we had the linking done by userspace insmod, I was able to be much more clever in this regard - I was able to detect which relocations were out of range, and I generated trampolines for each such symbol. What this relied upon was being able to parse the relocations before allocating module space, so we knew what the maximum size of trampolines needed for a particular module would be.

We don't have that luxury with the current approach - the earliest we get to see the module is after the module space has been allocated, and the module has been copied to that module. That leaves no room to extend the allocation for the trampolines.

Signature · 2 lines
FTTC broadband for 0.8mile line: currently at 9.5Mbps down 400kbps up
according to speedtest.net.
#14 lore raw
In reply to Russell King - ARM Linux
Quoted 33 lines · Russell King - ARM Linux
Quoted 18 lines · Ard Biesheuvel
Quoted 7 lines · Konstantin Khlebnikov

Do you mean ldr pc, =symbol ?

In this case I get this error:

/tmp/ccAHtONU.s: Assembler messages:
/tmp/ccAHtONU.s:220: Error: invalid literal constant: pool needs to be closer

Quoted 9 lines

Probably constant pool doesn't work well in inline assembly.

Something like this seems work:

add lr, pc, #4
ldr pc, [pc, #-4]
.long symbol

Quoted 7 lines

You can add a '.ltorg' instruction which tells the assembler to dump the literal pool, but you still need to jump over it, i.e.,

adr lr, 0f
ldr pc, =symbol
.ltorg
0:

Which is not a good idea either, because the compiler needs to know how far away its own manually generated literal pool is from the instructions which reference it. The .ltorg statement can end up emitting any number of literals at that point, which makes it indeterminant how many words are contained within the asm() statement.

Quoted 3 lines

Yes, it isn't desirable to waste an entire data cache line per indirect call like the original quote above, but I don't see a practical alternative.

Modules could be built without far calls by default, and then the module linker would only have to redirect those calls whose destination is too far away to a dynamically created trampoline table.

If I remember correctly you even posted some patches to that effect a couple years ago. Maybe those could be salvaged?

I would largely recommend a solution where the link process could deal with it automatically and as needed rather than sprinkling yet more manually maintained macros into assembly code.

Nicolas

#15 lore raw
Quoted 43 lines · Nicolas Pitre
Quoted 33 lines · Russell King - ARM Linux
Quoted 18 lines · Ard Biesheuvel
Quoted 7 lines · Konstantin Khlebnikov

Do you mean ldr pc, =symbol ?

In this case I get this error:

/tmp/ccAHtONU.s: Assembler messages:
/tmp/ccAHtONU.s:220: Error: invalid literal constant: pool needs to be closer

Quoted 9 lines

Probably constant pool doesn't work well in inline assembly.

Something like this seems work:

add lr, pc, #4
ldr pc, [pc, #-4]
.long symbol

Quoted 7 lines

You can add a '.ltorg' instruction which tells the assembler to dump the literal pool, but you still need to jump over it, i.e.,

adr lr, 0f
ldr pc, =symbol
.ltorg
0:

Which is not a good idea either, because the compiler needs to know how far away its own manually generated literal pool is from the instructions which reference it. The .ltorg statement can end up emitting any number of literals at that point, which makes it indeterminant how many words are contained within the asm() statement.

Quoted 3 lines

Yes, it isn't desirable to waste an entire data cache line per indirect call like the original quote above, but I don't see a practical alternative.

Modules could be built without far calls by default, and then the module linker would only have to redirect those calls whose destination is too far away to a dynamically created trampoline table.

Quoted 7 lines

If I remember correctly you even posted some patches to that effect a couple years ago. Maybe those could be salvaged?

I would largely recommend a solution where the link process could deal with it automatically and as needed rather than sprinkling yet more manually maintained macros into assembly code.

Yes, that would be preferable. I played around with 'bl symbol at PLT' instead of plain 'bl symbol' but unfortunately, our .ko's are not proper ELF shared libraries, so that doesn't work.
But essentially, we just need a (eager) PLT.

Signature · 1 line
Ard.
#16 lore raw
In reply to Nicolas Pitre
Quoted 11 lines · Nicolas Pitre
Quoted 9 lines · Russell King - ARM Linux

Which is not a good idea either, because the compiler needs to know how far away its own manually generated literal pool is from the instructions which reference it. The .ltorg statement can end up emitting any number of literals at that point, which makes it indeterminant how many words are contained within the asm() statement.

Yes, it isn't desirable to waste an entire data cache line per indirect call like the original quote above, but I don't see a practical alternative.

Quoted 6 lines

Modules could be built without far calls by default, and then the module linker would only have to redirect those calls whose destination is too far away to a dynamically created trampoline table.

If I remember correctly you even posted some patches to that effect a couple years ago. Maybe those could be salvaged?

I don't think I ever did, because its pretty much impossible to do as I explained in a follow up to this thread.

We _used_ to do this with the userspace insmod methods, but since we got this kernel-side linker, it's been pretty much impossible to do without rewriting the module code. That's not going to happen on account of one quirky architecture which Linus doesn't particularly like.

Signature · 2 lines
FTTC broadband for 0.8mile line: currently at 9.5Mbps down 400kbps up
according to speedtest.net.
#17 lore raw
Quoted 19 lines · Russell King - ARM Linux
Quoted 11 lines · Nicolas Pitre
Quoted 9 lines · Russell King - ARM Linux

Which is not a good idea either, because the compiler needs to know how far away its own manually generated literal pool is from the instructions which reference it. The .ltorg statement can end up emitting any number of literals at that point, which makes it indeterminant how many words are contained within the asm() statement.

Yes, it isn't desirable to waste an entire data cache line per indirect call like the original quote above, but I don't see a practical alternative.

Quoted 6 lines

Modules could be built without far calls by default, and then the module linker would only have to redirect those calls whose destination is too far away to a dynamically created trampoline table.

If I remember correctly you even posted some patches to that effect a couple years ago. Maybe those could be salvaged?

Quoted 7 lines

I don't think I ever did, because its pretty much impossible to do as I explained in a follow up to this thread.

We _used_ to do this with the userspace insmod methods, but since we got this kernel-side linker, it's been pretty much impossible to do without rewriting the module code. That's not going to happen on account of one quirky architecture which Linus doesn't particularly like.

Still... We could try adding a hook in the generic module linker code for a pre-relocation pass. Maybe only ARM would use it, but if the need to load big modules is real then I imagine Linus could be amenable to a compromise.

Nicolas

#18 lore raw
Quoted 9 lines · Nicolas Pitre
Quoted 7 lines · Russell King - ARM Linux

I don't think I ever did, because its pretty much impossible to do as I explained in a follow up to this thread.

We _used_ to do this with the userspace insmod methods, but since we got this kernel-side linker, it's been pretty much impossible to do without rewriting the module code. That's not going to happen on account of one quirky architecture which Linus doesn't particularly like.

Quoted 4 lines

Still... We could try adding a hook in the generic module linker code for a pre-relocation pass. Maybe only ARM would use it, but if the need to load big modules is real then I imagine Linus could be amenable to a compromise.

So, how big a table would you allocate for the trampolines, based upon not knowing anything about the module being loaded? 4K? 8K? 64K?

Signature · 2 lines
FTTC broadband for 0.8mile line: currently at 9.5Mbps down 400kbps up
according to speedtest.net.
#19 lore raw
Quoted 10 lines · Russell King - ARM Linux
Quoted 9 lines · Nicolas Pitre
Quoted 7 lines · Russell King - ARM Linux

I don't think I ever did, because its pretty much impossible to do as I explained in a follow up to this thread.

We _used_ to do this with the userspace insmod methods, but since we got this kernel-side linker, it's been pretty much impossible to do without rewriting the module code. That's not going to happen on account of one quirky architecture which Linus doesn't particularly like.

Quoted 7 lines
Quoted 4 lines

Still... We could try adding a hook in the generic module linker code for a pre-relocation pass. Maybe only ARM would use it, but if the need to load big modules is real then I imagine Linus could be amenable to a compromise.

So, how big a table would you allocate for the trampolines, based upon not knowing anything about the module being loaded? 4K? 8K? 64K?

The idea of a pre-relocation pass is to determine that. That could be something similar to calling apply_relocate() twice: once to determine the number of trampoline entries, and a second time to perform the actual relocation.

Nicolas

#20 lore raw
Quoted 17 lines · Nicolas Pitre
Quoted 15 lines · Russell King - ARM Linux
Quoted 13 lines · Nicolas Pitre
Quoted 7 lines · Russell King - ARM Linux

I don't think I ever did, because its pretty much impossible to do as I explained in a follow up to this thread.

We _used_ to do this with the userspace insmod methods, but since we got this kernel-side linker, it's been pretty much impossible to do without rewriting the module code. That's not going to happen on account of one quirky architecture which Linus doesn't particularly like.

Still... We could try adding a hook in the generic module linker code for a pre-relocation pass. Maybe only ARM would use it, but if the need to load big modules is real then I imagine Linus could be amenable to a compromise.

Quoted 8 lines
Quoted 2 lines

So, how big a table would you allocate for the trampolines, based upon not knowing anything about the module being loaded? 4K? 8K? 64K?

The idea of a pre-relocation pass is to determine that. That could be something similar to calling apply_relocate() twice: once to determine the number of trampoline entries, and a second time to perform the actual relocation.

Well, the veneers shouldn't take more than 3 words each, right?

ldr ip, [pc]
bx ip
.long symbol

and you would need at most one veneer per unique external symbol referenced by one or more R_ARM_CALL relocations. Is there no way to just add that to the static mem footprint as padding, and let the loader populate it as needed at module relocation time?

Signature · 1 line
Ard.
#21 lore raw
Quoted 27 lines · Ard Biesheuvel
Quoted 17 lines · Nicolas Pitre
Quoted 15 lines · Russell King - ARM Linux
Quoted 13 lines · Nicolas Pitre
Quoted 7 lines · Russell King - ARM Linux

I don't think I ever did, because its pretty much impossible to do as I explained in a follow up to this thread.

We _used_ to do this with the userspace insmod methods, but since we got this kernel-side linker, it's been pretty much impossible to do without rewriting the module code. That's not going to happen on account of one quirky architecture which Linus doesn't particularly like.

Still... We could try adding a hook in the generic module linker code for a pre-relocation pass. Maybe only ARM would use it, but if the need to load big modules is real then I imagine Linus could be amenable to a compromise.

Quoted 8 lines
Quoted 2 lines

So, how big a table would you allocate for the trampolines, based upon not knowing anything about the module being loaded? 4K? 8K? 64K?

The idea of a pre-relocation pass is to determine that. That could be something similar to calling apply_relocate() twice: once to determine the number of trampoline entries, and a second time to perform the actual relocation.

Quoted 5 lines

Well, the veneers shouldn't take more than 3 words each, right?

ldr ip, [pc]
bx ip
.long symbol

You could possibly do:

ldr pc, [pc, #-4]
.long symbol

Or, as RMK suggested a while ago:

.rep 8
ldr pc, [pc, #(32 - 8)]
.endr
.long sym1, sym2, sym3, sym4, sym5, sym6, sym7, sym8

The later is much nicer on the i and d caches.

Quoted 4 lines

and you would need at most one veneer per unique external symbol referenced by one or more R_ARM_CALL relocations. Is there no way to just add that to the static mem footprint as padding, and let the loader populate it as needed at module relocation time?

That's the actual question: how much padding do you need? Everything converge to that very problem. We need to determine it without too much impact on the generic module loader code.

Nicolas