From: Frank Rowand <frank.rowand@xxxxxxxx> The initial implementation of the of_find_node_by_phandle() cache allocates the cache using kcalloc(). Add an early boot allocation of the cache so it will be usable during early boot. Switch over to the kcalloc() based cache once normal memory allocation becomes available. Signed-off-by: Frank Rowand <frank.rowand@xxxxxxxx> --- Changes from v4: - Add checks for memblock_virt_alloc() returning zero. Changes from previous versions: - no changes Version 5 adds checks for memblock_virt_alloc() returning zero. kernel test robot reports 10 out of 10 boot failures on qemu-system-x86_64 with "[PATCH v4 2/2] of: add early boot allocation of of_find_node_by_phandle() cache" applied. The failure appears to me to be due to memblock_virt_alloc() returning zero. kernel BUG at arch/x86/mm/physaddr.c:27 invalid opcode: 0000 [#1] PREEMPT PTI CPU: 0 PID: 1 Comm: swapper Not tainted 4.16.0-rc1-00008-gb013aa4 #1 RIP: 0010:__phys_addr+0x39/0x50 RSP: 0000:ffff880018de3ee0 EFLAGS: 00010087 RAX: 0000780000000000 RBX: 0000000000000001 RCX: 0000000000000002 RDX: 0000000080000000 RSI: ffffffff82e77239 RDI: 0000000000000000 RBP: ffff880018de3ee0 R08: 0000000000000000 R09: 0000000000000001 R10: 00000000000029cb R11: 63561fc2891644ad R12: 0000000000000286 R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 FS: 0000000000000000(0000) GS:ffffffff8309b000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00000000ffffffff CR3: 0000000003074000 CR4: 00000000000006f0 Call Trace: of_core_init+0x30/0x21f driver_init+0x36/0x38 kernel_init_freeable+0x82/0x19f ? rest_init+0x220/0x220 kernel_init+0xe/0x100 ret_from_fork+0x24/0x30 I'm not conversent in the x86 dialect of assembly, but it looks like phandle_cache has the value zero, when calling __phys_addr(): ==== the call from of_core_init() to __pa(phandle_cache) aka __physaddr(): 186 size = (phandle_cache_mask + 1) * sizeof(*phandle_cache); 0xffffffff83334d35 <+27>: mov 0x1771e1d(%rip),%eax # 0xffffffff84aa6b58 <phandle_cache_mask> 0xffffffff83334d42 <+40>: lea 0x1(%rax),%r12d 0xffffffff83334d4b <+49>: shl $0x3,%r12 187 memblock_free(__pa(phandle_cache), size); 0xffffffff83334d3b <+33>: mov 0x1771e1e(%rip),%rdi # 0xffffffff84aa6b60 <phandle_cache> 0xffffffff83334d46 <+44>: callq 0xffffffff81049ad0 <__phys_addr> 0xffffffff83334d4f <+53>: mov %rax,%rdi 0xffffffff83334d52 <+56>: mov %r12,%rsi 0xffffffff83334d55 <+59>: callq 0xffffffff8334e45b <memblock_free> 188 phandle_cache = NULL; 0xffffffff83334d64 <+74>: movq $0x0,0x1771df1(%rip) # 0xffffffff84aa6b60 <phandle_cache> ==== __pa(): (gdb) disass /m __phys_addr Dump of assembler code for function __phys_addr: 15 { 0xffffffff81049ad0 <+0>: callq 0xffffffff822018e0 <__fentry__> 0xffffffff81049ad5 <+5>: push %rbp 0xffffffff81049ade <+14>: mov %rsp,%rbp 16 unsigned long y = x - __START_KERNEL_map; 0xffffffff81049ad6 <+6>: mov $0x80000000,%eax 17 18 /* use the carry flag to determine if x was < __START_KERNEL_map */ 19 if (unlikely(x > y)) { 0xffffffff81049adb <+11>: add %rdi,%rax 0xffffffff81049ae1 <+17>: mov %rax,%rdx 0xffffffff81049ae4 <+20>: jae 0xffffffff81049af8 <__phys_addr+40> 20 x = y + phys_base; 0xffffffff81049ae6 <+22>: add 0x1e30523(%rip),%rax # 0xffffffff82e7a010 21 22 VIRTUAL_BUG_ON(y >= KERNEL_IMAGE_SIZE); 0xffffffff81049aed <+29>: cmp $0x1fffffff,%rdx 0xffffffff81049af4 <+36>: jbe 0xffffffff81049b1e <__phys_addr+78> 0xffffffff81049af6 <+38>: ud2 23 } else { 24 x = y + (__START_KERNEL_map - PAGE_OFFSET); 0xffffffff81049af8 <+40>: movabs $0x780000000000,%rax 0xffffffff81049b02 <+50>: add %rdi,%rax 25 26 /* carry flag will be set if starting x was >= PAGE_OFFSET */ 27 VIRTUAL_BUG_ON((x > y) || !phys_addr_valid(x)); 0xffffffff81049b05 <+53>: cmp %rax,%rdx 0xffffffff81049b08 <+56>: jb 0xffffffff81049b1c <__phys_addr+76> 0xffffffff81049b17 <+71>: test %rdx,%rdx 0xffffffff81049b1a <+74>: je 0xffffffff81049b1e <__phys_addr+78> 0xffffffff81049b1c <+76>: ud2 28 } 29 30 return x; 31 } 0xffffffff81049b1e <+78>: pop %rbp 0xffffffff81049b1f <+79>: retq drivers/of/base.c | 38 ++++++++++++++++++++++++++++++++++++++ drivers/of/fdt.c | 2 ++ drivers/of/of_private.h | 2 ++ 3 files changed, 42 insertions(+) diff --git a/drivers/of/base.c b/drivers/of/base.c index e71d157d7149..48714d8e0bc9 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -16,9 +16,11 @@ #define pr_fmt(fmt) "OF: " fmt +#include <linux/bootmem.h> #include <linux/console.h> #include <linux/ctype.h> #include <linux/cpu.h> +#include <linux/memblock.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> @@ -134,6 +136,32 @@ static void of_populate_phandle_cache(void) raw_spin_unlock_irqrestore(&devtree_lock, flags); } +void __init of_populate_phandle_cache_early(void) +{ + u32 cache_entries; + struct device_node *np; + u32 phandles = 0; + size_t size; + + for_each_of_allnodes(np) + if (np->phandle && np->phandle != OF_PHANDLE_ILLEGAL) + phandles++; + + cache_entries = roundup_pow_of_two(phandles); + phandle_cache_mask = cache_entries - 1; + + size = cache_entries * sizeof(*phandle_cache); + phandle_cache = memblock_virt_alloc(size, 4); + if (!phandle_cache) + return; + + memset(phandle_cache, 0, size); + + for_each_of_allnodes(np) + if (np->phandle && np->phandle != OF_PHANDLE_ILLEGAL) + phandle_cache[np->phandle & phandle_cache_mask] = np; +} + #ifndef CONFIG_MODULES static int __init of_free_phandle_cache(void) { @@ -153,7 +181,17 @@ static int __init of_free_phandle_cache(void) void __init of_core_init(void) { + unsigned long flags; struct device_node *np; + phys_addr_t size; + + if (phandle_cache) { + raw_spin_lock_irqsave(&devtree_lock, flags); + size = (phandle_cache_mask + 1) * sizeof(*phandle_cache); + memblock_free(__pa(phandle_cache), size); + phandle_cache = NULL; + raw_spin_unlock_irqrestore(&devtree_lock, flags); + } of_populate_phandle_cache(); diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 84aa9d676375..cb320df23f26 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -1264,6 +1264,8 @@ void __init unflatten_device_tree(void) of_alias_scan(early_init_dt_alloc_memory_arch); unittest_unflatten_overlay_base(); + + of_populate_phandle_cache_early(); } /** diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index fa70650136b4..6720448c84cc 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -134,6 +134,8 @@ extern void __of_sysfs_remove_bin_file(struct device_node *np, /* illegal phandle value (set when unresolved) */ #define OF_PHANDLE_ILLEGAL 0xdeadbeef +extern void __init of_populate_phandle_cache_early(void); + /* iterators for transactions, used for overlays */ /* forward iterator */ #define for_each_transaction_entry(_oft, _te) \ -- Frank Rowand <frank.rowand@xxxxxxxx> -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html