Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xtensa: keep sysmem banks ordered in add_sysmem_bank

Rewrite add_sysmem_bank so that it keeps bank order and merges
adjacent/overlapping banks.

Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>

+103 -5
+5
arch/xtensa/include/asm/sysmem.h
··· 18 18 unsigned long end; 19 19 }; 20 20 21 + /* 22 + * Bank array is sorted by .start. 23 + * Banks don't overlap and there's at least one page gap 24 + * between adjacent bank entries. 25 + */ 21 26 struct sysmem_info { 22 27 int nr_banks; 23 28 struct meminfo bank[SYSMEM_BANKS_MAX];
+98 -5
arch/xtensa/mm/init.c
··· 8 8 * for more details. 9 9 * 10 10 * Copyright (C) 2001 - 2005 Tensilica Inc. 11 + * Copyright (C) 2014 Cadence Design Systems Inc. 11 12 * 12 13 * Chris Zankel <chris@zankel.net> 13 14 * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com> ··· 32 31 33 32 struct sysmem_info sysmem __initdata; 34 33 34 + /* 35 + * Find bank with maximal .start such that bank.start <= start 36 + */ 37 + static inline struct meminfo * __init find_bank(unsigned long start) 38 + { 39 + unsigned i; 40 + struct meminfo *it = NULL; 41 + 42 + for (i = 0; i < sysmem.nr_banks; ++i) 43 + if (sysmem.bank[i].start <= start) 44 + it = sysmem.bank + i; 45 + else 46 + break; 47 + return it; 48 + } 49 + 50 + /* 51 + * Move all memory banks starting at 'from' to a new place at 'to', 52 + * adjust nr_banks accordingly. 53 + * Both 'from' and 'to' must be inside the sysmem.bank. 54 + * 55 + * Returns: 0 (success), -ENOMEM (not enough space in the sysmem.bank). 56 + */ 57 + static int __init move_banks(struct meminfo *to, struct meminfo *from) 58 + { 59 + unsigned n = sysmem.nr_banks - (from - sysmem.bank); 60 + 61 + if (to > from && to - from + sysmem.nr_banks > SYSMEM_BANKS_MAX) 62 + return -ENOMEM; 63 + if (to != from) 64 + memmove(to, from, n * sizeof(struct meminfo)); 65 + sysmem.nr_banks += to - from; 66 + return 0; 67 + } 68 + 69 + /* 70 + * Add new bank to sysmem. Resulting sysmem is the union of bytes of the 71 + * original sysmem and the new bank. 72 + * 73 + * Returns: 0 (success), < 0 (error) 74 + */ 35 75 int __init add_sysmem_bank(unsigned long start, unsigned long end) 36 76 { 37 - if (sysmem.nr_banks >= SYSMEM_BANKS_MAX) { 38 - pr_warn("Ignoring memory bank 0x%08lx size %ldKB\n", 77 + unsigned i; 78 + struct meminfo *it = NULL; 79 + unsigned long sz; 80 + unsigned long bank_sz = 0; 81 + 82 + if (start == end || 83 + (start < end) != (PAGE_ALIGN(start) < (end & PAGE_MASK))) { 84 + pr_warn("Ignoring small memory bank 0x%08lx size: %ld bytes\n", 39 85 start, end - start); 40 86 return -EINVAL; 41 87 } 42 - sysmem.bank[sysmem.nr_banks].start = PAGE_ALIGN(start); 43 - sysmem.bank[sysmem.nr_banks].end = end & PAGE_MASK; 44 - sysmem.nr_banks++; 45 88 89 + start = PAGE_ALIGN(start); 90 + end &= PAGE_MASK; 91 + sz = end - start; 92 + 93 + it = find_bank(start); 94 + 95 + if (it) 96 + bank_sz = it->end - it->start; 97 + 98 + if (it && bank_sz >= start - it->start) { 99 + if (end - it->start > bank_sz) 100 + it->end = end; 101 + else 102 + return 0; 103 + } else { 104 + if (!it) 105 + it = sysmem.bank; 106 + else 107 + ++it; 108 + 109 + if (it - sysmem.bank < sysmem.nr_banks && 110 + it->start - start <= sz) { 111 + it->start = start; 112 + if (it->end - it->start < sz) 113 + it->end = end; 114 + else 115 + return 0; 116 + } else { 117 + if (move_banks(it + 1, it) < 0) { 118 + pr_warn("Ignoring memory bank 0x%08lx size %ld bytes\n", 119 + start, end - start); 120 + return -EINVAL; 121 + } 122 + it->start = start; 123 + it->end = end; 124 + return 0; 125 + } 126 + } 127 + sz = it->end - it->start; 128 + for (i = it + 1 - sysmem.bank; i < sysmem.nr_banks; ++i) 129 + if (sysmem.bank[i].start - it->start <= sz) { 130 + if (sz < sysmem.bank[i].end - it->start) 131 + it->end = sysmem.bank[i].end; 132 + } else { 133 + break; 134 + } 135 + 136 + move_banks(it + 1, sysmem.bank + i); 46 137 return 0; 47 138 } 48 139