* [PATCH] Replacement for Arm initrd memblock reserve and free inconsistency. @ 2016-11-09 16:35 william.helsby at stfc.ac.uk 2016-11-10 17:46 ` Russell King - ARM Linux 0 siblings, 1 reply; 7+ messages in thread From: william.helsby at stfc.ac.uk @ 2016-11-09 16:35 UTC (permalink / raw) To: linux-arm-kernel A boot time system crash was noticed with a segmentation fault just after the initrd image had been used to initialise the ramdisk. This occurred when the U-Boot loaded the ramdisk image from a FAT partition, but not when loaded by TFTPBOOT. This is not understood? However the problem was caused by free_initrd_mem freeing and "poisoning" memory that had been allocted to init/main.c to store the saved_command_line This patch reverses "ARM: 8167/1: extend the reserved memory for initrd to be page aligned" because it is safer to leave a partial head or tail page reserved (wasted) than to free a page which is partially still in use. If this is not acceptable (particularly if wanting large contiguous physical areas for DMA) then a better solution is required. This would extend the region reserved to page boundaries, if possible without overlapping other regions. My previous attempt to fix this coded this scheme, to grow the are reserved. However, this? again is not safe if in growing the area it then overlaps a region that is in use. Note this path is against the 4.6 kernel, but as far as I can tell applies equally to 4.8. Signed-off-by: William Helsby <wih73@xilinxsrv1.dl.ac.uk> --- arch/arm/mm/init.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 370581a..ff3e9c3 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -770,12 +770,6 @@ static int keep_initrd; void free_initrd_mem(unsigned long start, unsigned long end) { ??????? if (!keep_initrd) { -?????????????? if (start == initrd_start) -?????????????????????? start = round_down(start, PAGE_SIZE); -?????????????? if (end == initrd_end) -?????????????????????? end = round_up(end, PAGE_SIZE); - -???? ??????????poison_init_mem((void *)start, PAGE_ALIGN(end) - start); ??????????????? free_reserved_area((void *)start, (void *)end, -1, "initrd"); ??????? } } -- 1.8.3.1 Science and Technology Facilities Council SciTech Daresbury Keckwick Lane Daresbury Warrington WA4 4AD Tel +44(0)1925 603250 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH] Replacement for Arm initrd memblock reserve and free inconsistency. 2016-11-09 16:35 [PATCH] Replacement for Arm initrd memblock reserve and free inconsistency william.helsby at stfc.ac.uk @ 2016-11-10 17:46 ` Russell King - ARM Linux 2016-11-11 15:46 ` william.helsby at stfc.ac.uk 2016-11-15 15:45 ` william.helsby at stfc.ac.uk 0 siblings, 2 replies; 7+ messages in thread From: Russell King - ARM Linux @ 2016-11-10 17:46 UTC (permalink / raw) To: linux-arm-kernel On Wed, Nov 09, 2016 at 04:35:37PM +0000, william.helsby at stfc.ac.uk wrote: > A boot time system crash was noticed with a segmentation fault just after the initrd image had been used to initialise the ramdisk. > This occurred when the U-Boot loaded the ramdisk image from a FAT partition, but not when loaded by TFTPBOOT. This is not understood? > However the problem was caused by free_initrd_mem freeing and "poisoning" memory that had been allocted to init/main.c to store the saved_command_line > This patch reverses "ARM: 8167/1: extend the reserved memory for initrd to be page aligned" because it is safer to leave a partial head or tail page reserved (wasted) than to free a page which is partially still in use. > If this is not acceptable (particularly if wanting large contiguous physical areas for DMA) then a better solution is required. > This would extend the region reserved to page boundaries, if possible without overlapping other regions. My previous attempt to fix this coded this scheme, to grow the are reserved. > However, this? again is not safe if in growing the area it then overlaps a region that is in use. > Note this path is against the 4.6 kernel, but as far as I can tell applies equally to 4.8. Please wrap commit messages at or before column 72, the exception is for lines with a URL. > diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c > index 370581a..ff3e9c3 100644 > --- a/arch/arm/mm/init.c > +++ b/arch/arm/mm/init.c > @@ -770,12 +770,6 @@ static int keep_initrd; > void free_initrd_mem(unsigned long start, unsigned long end) > { > ??????? if (!keep_initrd) { > -?????????????? if (start == initrd_start) > -?????????????????????? start = round_down(start, PAGE_SIZE); > -?????????????? if (end == initrd_end) > -?????????????????????? end = round_up(end, PAGE_SIZE); > - > -???? ??????????poison_init_mem((void *)start, PAGE_ALIGN(end) - start); We're definitely not getting rid of the poisoning of the pages - the poisoning there is to detect accesses to this memory which should not be made. The point of rounding up and down is to ensure that the partly-used pages (which would have been previously reserved) are freed. Probably a better fix is to round the start up/end down of the initrd when reserving the memory region: arch/arm/mm/init.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 370581aeb871..ee8509e4329d 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -255,7 +255,11 @@ void __init arm_memblock_init(const struct machine_desc *mdesc) phys_initrd_start = phys_initrd_size = 0; } if (phys_initrd_size) { - memblock_reserve(phys_initrd_start, phys_initrd_size); + phys_addr_t start, size; + + start = round_down(phys_initrd_start, PAGE_SIZE); + end = round_up(phys_initrd_start + phys_initrd_size, PAGE_SIZE); + memblock_reserve(start, end - start); /* Now convert initrd to virtual addresses */ initrd_start = __phys_to_virt(phys_initrd_start); and this should ensure that memblock_alloc() doesn't try to allocate memory overlapping the pages containing the initrd. Intentionally using pages overlapping the initrd is a recipe for problems... -- RMK's Patch system: http://www.armlinux.org.uk/developer/patches/ FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up according to speedtest.net. ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH] Replacement for Arm initrd memblock reserve and free inconsistency. 2016-11-10 17:46 ` Russell King - ARM Linux @ 2016-11-11 15:46 ` william.helsby at stfc.ac.uk 2016-11-15 15:45 ` william.helsby at stfc.ac.uk 1 sibling, 0 replies; 7+ messages in thread From: william.helsby at stfc.ac.uk @ 2016-11-11 15:46 UTC (permalink / raw) To: linux-arm-kernel >From 45272bc4d3f27f2c316f5b607441d1337a5501ec Mon Sep 17 00:00:00 2001 From: William Helsby <william.helsby@stfc.ac.uk> Date: Fri, 11 Nov 2016 15:04:04 +0000 Subject: [PATCH] arm: zynq::Fixing inconsistent reserve and free for initrd image My first attempt at fixing this was very similar to your proposed patch, though due me not understanding the need to switch outlook to plain text it never got posted. In the current case, with the initrd image starting page aligned, with space above it, the patch worked. However, on reflection it struck me that the placement of the ramdisk image is done by the bootloader, so may not always be like this. This patch tries to round down the start and up the end, but checks that this does not cause an overlap. If it does overlap, it tries aligning just the end or start and if neither is possible falls back to not expanding the area reserved. The code then remembers the start and end of reserved area, with any successful expansions to page boundaries. If when ./init/initramfs.c calls free_initrd_mem(), the start or end match, The respective extended values are used so complete pages are released. Note that according to comments in ./init/initramfs.c, the crashkernel region can overlap the initrd region, though I don't know anything about this. The POISON_FREE_INITMEM value is used to cause free_reserved_area to poison only the pages which are released. Despite this code being careful not to overlap the kernel, there still may be an issue with it extending the RAMDISK image reserved are to overlap the device tree or something else I am unaware of. Perhaps the call to early_init_fdt_reserve_self() should be moved to before the initrd code? For the sake of freeing at most 2 partial 4k pages, I am not sure whether complexity and technical risk of this solution is justified - unless these pages are going to be in the middle of a DMA contiguous area. Note that the arm64 code reverted to not extending the released area, but enable clearing of the released areas at https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/commit/arch/arm64/mm/init.c?id=6b00f7efb5303418c231994c91fb8239f5ada260 Now void free_initrd_mem(unsigned long start, unsigned long end) { if (!keep_initrd) free_reserved_area((void *)start, (void *)end, 0, "initrd"); } Signed-off-by: William Helsby <william.helsby@stfc.ac.uk> --- arch/arm/mm/init.c | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 370581a..00a489d 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -49,6 +49,8 @@ unsigned long __init __clear_cr(unsigned long mask) static phys_addr_t phys_initrd_start __initdata = 0; static unsigned long phys_initrd_size __initdata = 0; +static unsigned long initrd_reservation_start __initdata = 0; +static unsigned long initrd_reservation_end __initdata = 0; static int __init early_initrd(char *p) { @@ -255,11 +257,38 @@ void __init arm_memblock_init(const struct machine_desc *mdesc) phys_initrd_start = phys_initrd_size = 0; } if (phys_initrd_size) { - memblock_reserve(phys_initrd_start, phys_initrd_size); + /* try to round the initrd start and end down and up to page boundaries, + so when freed, whole pages can be released. + However the initrd image may be adjacent to something else, so check that this rounding is OK + */ + /* First try rounding the start down and end up */ + phys_addr_t phys_initrd_reservation_start = phys_initrd_start & PAGE_MASK; + unsigned long size_to_reserve = PAGE_ALIGN(phys_initrd_start+phys_initrd_size) - phys_initrd_reservation_start; + if (!memblock_is_region_memory(phys_initrd_reservation_start, size_to_reserve) || + memblock_is_region_reserved(phys_initrd_reservation_start, size_to_reserve)) { + /* This either does fit or overlaps something else, so try just rounding end up */ + phys_initrd_reservation_start = phys_initrd_start; + size_to_reserve = PAGE_ALIGN(phys_initrd_start+phys_initrd_size) - phys_initrd_reservation_start; + if (!memblock_is_region_memory(phys_initrd_reservation_start, size_to_reserve) || + memblock_is_region_reserved(phys_initrd_reservation_start, size_to_reserve)) { + /* This either does not fit or overlaps something else, so try just rounding start down */ + phys_initrd_reservation_start = phys_initrd_start & PAGE_MASK; + size_to_reserve = (phys_initrd_start+phys_initrd_size) - phys_initrd_reservation_start; + if (!memblock_is_region_memory(phys_initrd_reservation_start, size_to_reserve) || + memblock_is_region_reserved(phys_initrd_reservation_start, size_to_reserve)) { + /* This either does not fit or overlaps something else, so do not round at all */ + phys_initrd_reservation_start = phys_initrd_start; + size_to_reserve = (phys_initrd_start+phys_initrd_size) - phys_initrd_reservation_start; + } + } + } + memblock_reserve(phys_initrd_reservation_start, size_to_reserve); /* Now convert initrd to virtual addresses */ initrd_start = __phys_to_virt(phys_initrd_start); initrd_end = initrd_start + phys_initrd_size; + initrd_reservation_start = __phys_to_virt(phys_initrd_reservation_start); + initrd_reservation_end = initrd_reservation_start + size_to_reserve; } #endif @@ -771,12 +800,11 @@ void free_initrd_mem(unsigned long start, unsigned long end) { if (!keep_initrd) { if (start == initrd_start) - start = round_down(start, PAGE_SIZE); + start = initrd_reservation_start; if (end == initrd_end) - end = round_up(end, PAGE_SIZE); + end = initrd_reservation_end; - poison_init_mem((void *)start, PAGE_ALIGN(end) - start); - free_reserved_area((void *)start, (void *)end, -1, "initrd"); + free_reserved_area((void *)start, (void *)end, POISON_FREE_INITMEM, "initrd"); } } -- 1.8.3.1 > -----Original Message----- > From: Russell King - ARM Linux [mailto:linux at armlinux.org.uk] > Sent: 10 November 2016 17:47 >To: Helsby, William (STFC,DL,TECH) > Cc: linux-arm-kernel at lists.infradead.org > Subject: Re: [PATCH] Replacement for Arm initrd memblock reserve and free inconsistency. >On Wed, Nov 09, 2016 at 04:35:37PM +0000, william.helsby at stfc.ac.uk wrote: >> A boot time system crash was noticed with a segmentation fault just after the initrd image had been used to initialise the ramdisk. >> This occurred when the U-Boot loaded the ramdisk image from a FAT partition, but not when loaded by TFTPBOOT. This is not understood? >> However the problem was caused by free_initrd_mem freeing and >> "poisoning" memory that had been allocted to init/main.c to store the saved_command_line. >> This patch reverses "ARM: 8167/1: extend the reserved memory for initrd to be page aligned" >> because it is safer to leave a partial head or tail page reserved (wasted) than to free a page which is partially still in use. >> If this is not acceptable (particularly if wanting large contiguous physical areas for DMA) then a better solution is required. >> This would extend the region reserved to page boundaries, if possible without overlapping other regions. >>My previous attempt to fix this coded this scheme, to grow the are reserved. >> However, this? again is not safe if in growing the area it then overlaps a region that is in use. >> Note this patch is against the 4.6 kernel, but as far as I can tell applies equally to 4.8. >> diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index > 370581a..ff3e9c3 100644 >> --- a/arch/arm/mm/init.c >> +++ b/arch/arm/mm/init.c >> @@ -770,12 +770,6 @@ static int keep_initrd; void >> free_initrd_mem(unsigned long start, unsigned long end) { >> ??????? if (!keep_initrd) { >> -?????????????? if (start == initrd_start) >> -?????????????????????? start = round_down(start, PAGE_SIZE); >> -?????????????? if (end == initrd_end) >> -?????????????????????? end = round_up(end, PAGE_SIZE); >> - >> -???? ??????????poison_init_mem((void *)start, PAGE_ALIGN(end) - >> start); > We're definitely not getting rid of the poisoning of the pages - the poisoning there is to detect accesses to this memory which should not be made. > The point of rounding up and down is to ensure that the partly-used pages (which would have been previously reserved) are freed. > Probably a better fix is to round the start up/end down of the initrd when reserving the memory region: > arch/arm/mm/init.c | 6 +++++- > 1 file changed, 5 insertions(+), 1 deletion(-) > > diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 370581aeb871..ee8509e4329d 100644 > --- a/arch/arm/mm/init.c > +++ b/arch/arm/mm/init.c > @@ -255,7 +255,11 @@ void __init arm_memblock_init(const struct machine_desc *mdesc) > phys_initrd_start = phys_initrd_size = 0; > } > if (phys_initrd_size) { > - memblock_reserve(phys_initrd_start, phys_initrd_size); > + phys_addr_t start, size; > + > + start = round_down(phys_initrd_start, PAGE_SIZE); > + end = round_up(phys_initrd_start + phys_initrd_size, PAGE_SIZE); > + memblock_reserve(start, end - start); > > /* Now convert initrd to virtual addresses */ > initrd_start = __phys_to_virt(phys_initrd_start); > > and this should ensure that memblock_alloc() doesn't try to allocate memory overlapping the pages containing the initrd. > Intentionally using pages overlapping the initrd is a recipe for problems... > -- > RMK's Patch system: http://www.armlinux.org.uk/developer/patches/ > FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up according to speedtest.net. ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH] Replacement for Arm initrd memblock reserve and free inconsistency. 2016-11-10 17:46 ` Russell King - ARM Linux 2016-11-11 15:46 ` william.helsby at stfc.ac.uk @ 2016-11-15 15:45 ` william.helsby at stfc.ac.uk 2016-11-16 23:47 ` Russell King - ARM Linux 1 sibling, 1 reply; 7+ messages in thread From: william.helsby at stfc.ac.uk @ 2016-11-15 15:45 UTC (permalink / raw) To: linux-arm-kernel The option of moving the initrd code later (after early_init_fdt_reserve_self(); early_init_fdt_scan_reserved_mem() ) was tested. This resulted in the initrd being disabled because early_init_fdt_scan_reserved_mem Has already reserved the initrd area. With memblock=debug Machine model: Xspress3 Mini in PicoZed Developement Board bootconsole [earlycon0] enabled memblock_add: [0x00000000000000-0x0000000fffffff] flags 0x0 arm_add_ memory+0x14c/0x174 memblock_reserve: [0x00000000100000-0x00000000b71de7] flags 0x0 arm_ memblock_init+0x1c/0x284 memblock_reserve: [0x00000000004000-0x00000000007fff] flags 0x0 arm_ memblock_init+0x20/0x284 memblock_reserve: [0x00000000000000-0x00000000003fff] flags 0x0 arm_ memblock_init+0x30/0x284 memblock_reserve: [0x0000000fa5f000-0x0000000fa6207f] flags 0x0 arm_ memblock_init+0x38/0x284 memblock_reserve: [0x0000000fa5f000-0x0000000fa61fff] flags 0x0 early_ init_fdt_scan_reserved_mem+0x54/0x78 memblock_reserve: [0x0000000fa65000-0x0000000ffff46a] flags 0x0 early_ init_fdt_scan_reserved_mem+0x54/0x78 memblock_reserve: [0x0000000e800000-0x0000000f7fffff] flags 0x0 membl ock_alloc_range_nid+0x3c/0x50 cma: Reserved 16 MiB at 0x0e800000 INITRD: 0x0fa65000+0x0059a46b overlaps in-use memory region - disabling initrd MEMBLOCK configuration: With the initrd code back to its normal place memblock_add: [0x00000000000000-0x0000000fffffff] flags 0x0 arm_add_ memory+0x14c/0x174 memblock_reserve: [0x00000000100000-0x00000000b71de7] flags 0x0 arm_m emblock_init+0x24/0x284 memblock_reserve: [0x0000000fa65000-0x0000000fffffff] flags 0x0 arm_me mblock_init+0x1e4/0x284 memblock_reserve: [0x00000000004000-0x00000000007fff] flags 0x0 arm_me mblock_init+0x210/0x284 memblock_reserve: [0x00000000000000-0x00000000003fff] flags 0x0 arm_me mblock_init+0x220/0x284 memblock_reserve: [0x0000000fa5f000-0x0000000fa6207f] flags 0x0 arm_me mblock_init+0x224/0x284 memblock_reserve: [0x0000000fa5f000-0x0000000fa61fff] flags 0x0 early_ini t_fdt_scan_reserved_mem+0x54/0x78 memblock_reserve: [0x0000000fa65000-0x0000000ffff46a] flags 0x0 early_in it_fdt_scan_reserved_mem+0x54/0x78 memblock_reserve: [0x0000000e800000-0x0000000f7fffff] flags 0x0 memblo ck_alloc_range_nid+0x3c/0x50 cma: Reserved 16 MiB at 0x0e800000 MEMBLOCK configuration: memory size = 0x10000000 reserved size = 0x2017e68 memory.cnt = 0x1 memory[0x0] [0x00000000000000-0x0000000fffffff], 0x10000000 bytes f lags: 0x0 reserved.cnt = 0x5 reserved[0x0] [0x00000000000000-0x00000000007fff], 0x8000 bytes fl ags: 0x0 reserved[0x1] [0x00000000100000-0x00000000b71de7], 0xa71de8 bytes fl ags: 0x0 reserved[0x2] [0x0000000e800000-0x0000000f7fffff], 0x1000000 bytes flags: 0x0 reserved[0x3] [0x0000000fa5f000-0x0000000fa6207f], 0x3080 bytes flag s: 0x0 reserved[0x4] [0x0000000fa65000-0x0000000fffffff], 0x59b000 bytes fl ags: 0x0 Memory policy: Data cache writealloc So poison the memory all the way from the start to end I would now suggest: --- ../linux-xlnx-4.6.0-orig/arch/arm/mm/init.c 2016-11-15 15:10:44.476001509 +0000 +++ arch/arm/mm/init.c 2016-11-15 14:05:19.640969244 +0000 @@ -49,6 +49,8 @@ static phys_addr_t phys_initrd_start __initdata = 0; static unsigned long phys_initrd_size __initdata = 0; +static unsigned long initrd_reservation_start __initdata = 0; +static unsigned long initrd_reservation_end __initdata = 0; static int __init early_initrd(char *p) { @@ -255,11 +257,38 @@ phys_initrd_start = phys_initrd_size = 0; } if (phys_initrd_size) { - memblock_reserve(phys_initrd_start, phys_initrd_size); + /* try to round the initrd start and end down and up to page boundaries, + so when freed, whole pages can be released. + However the initrd image may be adjacent to something else, so check that this rounding is OK + */ + /* First try rounding the start down and end up */ + phys_addr_t phys_initrd_reservation_start = phys_initrd_start & PAGE_MASK; + unsigned long size_to_reserve = PAGE_ALIGN(phys_initrd_start+phys_initrd_size) - phys_initrd_reservation_start; + if (!memblock_is_region_memory(phys_initrd_reservation_start, size_to_reserve) || + memblock_is_region_reserved(phys_initrd_reservation_start, size_to_reserve)) { + /* This either does fit or overlaps something else, so try just rounding end up */ + phys_initrd_reservation_start = phys_initrd_start; + size_to_reserve = PAGE_ALIGN(phys_initrd_start+phys_initrd_size) - phys_initrd_reservation_start; + if (!memblock_is_region_memory(phys_initrd_reservation_start, size_to_reserve) || + memblock_is_region_reserved(phys_initrd_reservation_start, size_to_reserve)) { + /* This either does not fit or overlaps something else, so try just rounding start down */ + phys_initrd_reservation_start = phys_initrd_start & PAGE_MASK; + size_to_reserve = (phys_initrd_start+phys_initrd_size) - phys_initrd_reservation_start; + if (!memblock_is_region_memory(phys_initrd_reservation_start, size_to_reserve) || + memblock_is_region_reserved(phys_initrd_reservation_start, size_to_reserve)) { + /* This either does not fit or overlaps something else, so do not round at all */ + phys_initrd_reservation_start = phys_initrd_start; + size_to_reserve = (phys_initrd_start+phys_initrd_size) - phys_initrd_reservation_start; + } + } + } + memblock_reserve(phys_initrd_reservation_start, size_to_reserve); /* Now convert initrd to virtual addresses */ initrd_start = __phys_to_virt(phys_initrd_start); initrd_end = initrd_start + phys_initrd_size; + initrd_reservation_start = __phys_to_virt(phys_initrd_reservation_start); + initrd_reservation_end = initrd_reservation_start + size_to_reserve; } #endif @@ -325,7 +354,12 @@ */ static inline void poison_init_mem(void *s, size_t count) { - u32 *p = (u32 *)s; + u32 *p; + unsigned long start = (unsigned long)s, start_align; + start_align = (start+3) & ~3L; // Round up to 32 bit word boundary. + count -= (start_align-start); + p = (u32 *)start_align; + count &= ~3L; for (; count != 0; count -= 4) *p++ = 0xe7fddef0; } @@ -771,11 +805,11 @@ { if (!keep_initrd) { if (start == initrd_start) - start = round_down(start, PAGE_SIZE); + start = initrd_reservation_start; if (end == initrd_end) - end = round_up(end, PAGE_SIZE); + end = initrd_reservation_end; - poison_init_mem((void *)start, PAGE_ALIGN(end) - start); + poison_init_mem((void *)start, end-start); free_reserved_area((void *)start, (void *)end, -1, "initrd"); } } ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH] Replacement for Arm initrd memblock reserve and free inconsistency. 2016-11-15 15:45 ` william.helsby at stfc.ac.uk @ 2016-11-16 23:47 ` Russell King - ARM Linux 2016-11-17 10:41 ` william.helsby at stfc.ac.uk 0 siblings, 1 reply; 7+ messages in thread From: Russell King - ARM Linux @ 2016-11-16 23:47 UTC (permalink / raw) To: linux-arm-kernel On Tue, Nov 15, 2016 at 03:45:55PM +0000, william.helsby at stfc.ac.uk wrote: > The option of moving the initrd code later (after > early_init_fdt_reserve_self(); > early_init_fdt_scan_reserved_mem() ) > was tested. Moving stuff around tends to break... I'd prefer to do it this way, as it's much more readable (and we prize readability... see Documentation/CodingStyle.) arch/arm/mm/init.c | 71 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 370581aeb871..ffae20b25929 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -227,6 +227,48 @@ phys_addr_t __init arm_memblock_steal(phys_addr_t size, phys_addr_t align) return phys; } +static void __init arm_initrd_init(void) +{ +#ifdef CONFIG_BLK_DEV_INITRD + unsigned long size; + phys_addr_t start; + + /* FDT scan will populate initrd_start */ + if (initrd_start && !phys_initrd_size) { + phys_initrd_start = __virt_to_phys(initrd_start); + phys_initrd_size = initrd_end - initrd_start; + } + + initrd_start = initrd_end = 0; + + if (!phys_initrd_size) + return; + + start = phys_initrd_start & PAGE_MASK; + size = PAGE_ALIGN(phys_initrd_size + phys_initrd_start - start); + + if (!memblock_is_region_memory(start, size)) { + pr_err("INITRD: 0x%08llx+0x%08lx is not a memory region - disabling initrd\n", + (u64)start, size); + phys_initrd_start = phys_initrd_size = 0; + return; + } + + if (memblock_is_region_reserved(start, size)) { + pr_err("INITRD: 0x%08llx+0x%08lx overlaps in-use memory region - disabling initrd\n", + (u64)start, size); + phys_initrd_start = phys_initrd_size = 0; + return; + } + + memblock_reserve(start, size); + + /* Now convert initrd to virtual addresses */ + initrd_start = __phys_to_virt(phys_initrd_start); + initrd_end = initrd_start + phys_initrd_size; +#endif +} + void __init arm_memblock_init(const struct machine_desc *mdesc) { /* Register the kernel text, kernel data and initrd with memblock. */ @@ -235,34 +277,7 @@ void __init arm_memblock_init(const struct machine_desc *mdesc) #else memblock_reserve(__pa(_stext), _end - _stext); #endif -#ifdef CONFIG_BLK_DEV_INITRD - /* FDT scan will populate initrd_start */ - if (initrd_start && !phys_initrd_size) { - phys_initrd_start = __virt_to_phys(initrd_start); - phys_initrd_size = initrd_end - initrd_start; - } - initrd_start = initrd_end = 0; - if (phys_initrd_size && - !memblock_is_region_memory(phys_initrd_start, phys_initrd_size)) { - pr_err("INITRD: 0x%08llx+0x%08lx is not a memory region - disabling initrd\n", - (u64)phys_initrd_start, phys_initrd_size); - phys_initrd_start = phys_initrd_size = 0; - } - if (phys_initrd_size && - memblock_is_region_reserved(phys_initrd_start, phys_initrd_size)) { - pr_err("INITRD: 0x%08llx+0x%08lx overlaps in-use memory region - disabling initrd\n", - (u64)phys_initrd_start, phys_initrd_size); - phys_initrd_start = phys_initrd_size = 0; - } - if (phys_initrd_size) { - memblock_reserve(phys_initrd_start, phys_initrd_size); - - /* Now convert initrd to virtual addresses */ - initrd_start = __phys_to_virt(phys_initrd_start); - initrd_end = initrd_start + phys_initrd_size; - } -#endif - + arm_initrd_init(); arm_mm_memblock_reserve(); /* reserve any platform specific memblock areas */ -- RMK's Patch system: http://www.armlinux.org.uk/developer/patches/ FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up according to speedtest.net. ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH] Replacement for Arm initrd memblock reserve and free inconsistency. 2016-11-16 23:47 ` Russell King - ARM Linux @ 2016-11-17 10:41 ` william.helsby at stfc.ac.uk 2017-01-16 16:22 ` Russell King - ARM Linux 0 siblings, 1 reply; 7+ messages in thread From: william.helsby at stfc.ac.uk @ 2016-11-17 10:41 UTC (permalink / raw) To: linux-arm-kernel My understanding of your proposed patch is that it rounds the area to be reserved for the initrd image to page boundaries. It then checks that this rounding does not grow the initrd to overlap other areas. If it does not overlap, all is well. The kernel will not kmalloc anything within the partial pages at the beginning and end of the initrd image. When the initrd image is freed, complete pages are released and there is no wasted memory. However, if it does overlap other regions, it disables the initrd image and the kernel panics with no root file system. My patch rounds the area to be reserved for the initrd image to page boundaries. It then checks that this rounding does not grow the initrd to overlap other areas. If it does not overlap the system initialised the ramdisk from the initrd image and then frees the initrd area with no wasted memory. All is good. If it does overlap, my code tried to determine the cause of the overlap and fell back to, in the worst case, just reserving the space actually occupied by the initrd image. In this case the kernel would start, initialise the ramdisk and try to free the initrd image. At worst 2 partial pages at the beginning and end of the initrd image would not be freed, wasting up to 8kBytes. To me this is a more acceptable fallback than deleting the root file system. Also not that in the current code free_initrd_mem checks: if (end == initrd_end) end = round_up(end, PAGE_SIZE); So only rounds up the end if the whole initrd image is being freed. If free_initrd_mem is called with end != initrd_end (because the crash kernel area overlaps), then the end is not rounded up. However: poison_init_mem((void *)start, PAGE_ALIGN(end) - start); will still page align the end address and could potentially overwrite the crash kernel area. I don't know anything about if and when the crash kernel area is used, but in principle this may be a bad thing. William. -----Original Message----- From: Russell King - ARM Linux [mailto:linux at armlinux.org.uk] Sent: 16 November 2016 23:48 To: Helsby, William (STFC,DL,TECH) Cc: linux-arm-kernel at lists.infradead.org Subject: Re: [PATCH] Replacement for Arm initrd memblock reserve and free inconsistency. On Tue, Nov 15, 2016 at 03:45:55PM +0000, william.helsby at stfc.ac.uk wrote: > The option of moving the initrd code later (after > early_init_fdt_reserve_self(); > early_init_fdt_scan_reserved_mem() ) was tested. Moving stuff around tends to break... I'd prefer to do it this way, as it's much more readable (and we prize readability... see Documentation/CodingStyle.) arch/arm/mm/init.c | 71 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 370581aeb871..ffae20b25929 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -227,6 +227,48 @@ phys_addr_t __init arm_memblock_steal(phys_addr_t size, phys_addr_t align) return phys; } +static void __init arm_initrd_init(void) { #ifdef CONFIG_BLK_DEV_INITRD + unsigned long size; + phys_addr_t start; + + /* FDT scan will populate initrd_start */ + if (initrd_start && !phys_initrd_size) { + phys_initrd_start = __virt_to_phys(initrd_start); + phys_initrd_size = initrd_end - initrd_start; + } + + initrd_start = initrd_end = 0; + + if (!phys_initrd_size) + return; + + start = phys_initrd_start & PAGE_MASK; + size = PAGE_ALIGN(phys_initrd_size + phys_initrd_start - start); + + if (!memblock_is_region_memory(start, size)) { + pr_err("INITRD: 0x%08llx+0x%08lx is not a memory region - disabling initrd\n", + (u64)start, size); + phys_initrd_start = phys_initrd_size = 0; + return; + } + + if (memblock_is_region_reserved(start, size)) { + pr_err("INITRD: 0x%08llx+0x%08lx overlaps in-use memory region - disabling initrd\n", + (u64)start, size); + phys_initrd_start = phys_initrd_size = 0; + return; + } + + memblock_reserve(start, size); + + /* Now convert initrd to virtual addresses */ + initrd_start = __phys_to_virt(phys_initrd_start); + initrd_end = initrd_start + phys_initrd_size; #endif } + void __init arm_memblock_init(const struct machine_desc *mdesc) { /* Register the kernel text, kernel data and initrd with memblock. */ @@ -235,34 +277,7 @@ void __init arm_memblock_init(const struct machine_desc *mdesc) #else memblock_reserve(__pa(_stext), _end - _stext); #endif -#ifdef CONFIG_BLK_DEV_INITRD - /* FDT scan will populate initrd_start */ - if (initrd_start && !phys_initrd_size) { - phys_initrd_start = __virt_to_phys(initrd_start); - phys_initrd_size = initrd_end - initrd_start; - } - initrd_start = initrd_end = 0; - if (phys_initrd_size && - !memblock_is_region_memory(phys_initrd_start, phys_initrd_size)) { - pr_err("INITRD: 0x%08llx+0x%08lx is not a memory region - disabling initrd\n", - (u64)phys_initrd_start, phys_initrd_size); - phys_initrd_start = phys_initrd_size = 0; - } - if (phys_initrd_size && - memblock_is_region_reserved(phys_initrd_start, phys_initrd_size)) { - pr_err("INITRD: 0x%08llx+0x%08lx overlaps in-use memory region - disabling initrd\n", - (u64)phys_initrd_start, phys_initrd_size); - phys_initrd_start = phys_initrd_size = 0; - } - if (phys_initrd_size) { - memblock_reserve(phys_initrd_start, phys_initrd_size); - - /* Now convert initrd to virtual addresses */ - initrd_start = __phys_to_virt(phys_initrd_start); - initrd_end = initrd_start + phys_initrd_size; - } -#endif - + arm_initrd_init(); arm_mm_memblock_reserve(); /* reserve any platform specific memblock areas */ -- RMK's Patch system: http://www.armlinux.org.uk/developer/patches/ FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up according to speedtest.net. ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH] Replacement for Arm initrd memblock reserve and free inconsistency. 2016-11-17 10:41 ` william.helsby at stfc.ac.uk @ 2017-01-16 16:22 ` Russell King - ARM Linux 0 siblings, 0 replies; 7+ messages in thread From: Russell King - ARM Linux @ 2017-01-16 16:22 UTC (permalink / raw) To: linux-arm-kernel On Thu, Nov 17, 2016 at 10:41:19AM +0000, william.helsby at stfc.ac.uk wrote: > My understanding of your proposed patch is that it rounds the area to be reserved for the initrd image to page > boundaries. It then checks that this rounding does not grow the initrd to overlap other areas. > > If it does not overlap, all is well. The kernel will not kmalloc anything within the partial pages > at the beginning and end of the initrd image. When the initrd image is freed, complete pages are > released and there is no wasted memory. > However, if it does overlap other regions, it disables the initrd image and the kernel panics with no root file system. As best I can do when replying to this... (due to the stupidly long lines)... Yes, that's what will happen. The kernel memory allocator deals with things on a per-page basis, and that's not going to change. If someone wants to place objects on a non-page aligned basis into memory, then they're intentionally making life hard for the kernel. You talk about kmalloc() above, but kmalloc() is not really relevant here - kmalloc() should be thought as a sub-page memory allocator (even though it's capable of larger allocations.) It sources its memory from page_alloc(), which not surprisingly deals with page-sized objects, and those are either "in-use" or "free". There's no finer granularity there. So, if you have a page partially in-use, as far as the kernel's normal allocators are concerned, the whole page is in use. Boot time is slightly different until the normal allocators are initialised - we have memblock, which doesn't really know about stuff like pages. It only knows about ranges of addresses. However, during initialisation, when memblock hands over to the normal allocators, any page that is partially reserved is treated as wholely reserved because - as I say above - the normal allocators can't deal with that level of information. Rounding the initrd up/down when reserving it in memblock is the right thing to do to ensure that when memory is requested from memblock, memblock doesn't allocate the overlapping pages - because we want to free that memory later. So, I don't think your idea of trying to cover all the cases of part-used pages is really worth it - and the boot loader has to understand that the kernel operates on a page-basis, and expects objects to be aligned to the size of a page. -- RMK's Patch system: http://www.armlinux.org.uk/developer/patches/ FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up according to speedtest.net. ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2017-01-16 16:22 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2016-11-09 16:35 [PATCH] Replacement for Arm initrd memblock reserve and free inconsistency william.helsby at stfc.ac.uk 2016-11-10 17:46 ` Russell King - ARM Linux 2016-11-11 15:46 ` william.helsby at stfc.ac.uk 2016-11-15 15:45 ` william.helsby at stfc.ac.uk 2016-11-16 23:47 ` Russell King - ARM Linux 2016-11-17 10:41 ` william.helsby at stfc.ac.uk 2017-01-16 16:22 ` Russell King - ARM Linux
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).