[PATCH v2 2/2] powerpc: introduce CONFIG_CACHE_FLUSH_WATCHDOG_THRESHOLD

Stefan Roese sr at denx.de
Thu Apr 15 07:34:27 CEST 2021


On 13.04.21 21:38, Rasmus Villemoes wrote:
> When flush_cache() is called during boot on our ~7M kernel image, the
> hundreds of thousands of WATCHDOG_RESET calls end up adding
> significantly to boottime. Flushing a single cache line doesn't take
> many microseconds, so doing these calls for every cache line is
> complete overkill.
> 
> The generic watchdog_reset() provided by wdt-uclass.c actually
> contains some rate-limiting logic that should in theory mitigate this,
> but alas, that rate-limiting must be disabled on powerpc because of
> its get_timer() implementation - get_timer() works just fine until
> interrupts are disabled, but it just so happens that the "big"
> flush_cache() call happens in the part of bootm where interrupts are
> indeed disabled. [1] [2] [3]
> 
> I have checked with objdump that the generated code doesn't change
> when this option is left at its default value of 0: gcc is smart
> enough to see that wd_limit is constant 0, the ">=" comparisons are
> tautologically true, hence all assignments to "flushed" are eliminated

Nitpicking:
s/tautologically/autologically. BTW, I've never heard or read this word
before.

> as dead stores.
> 
> On our board, setting the option to something like 65536 ends up
> reducing total boottime by about 0.8 seconds.
> 
> [1] https://patchwork.ozlabs.org/project/uboot/patch/20200605111657.28773-1-rasmus.villemoes@prevas.dk/
> [2] https://lists.denx.de/pipermail/u-boot/2021-April/446906.html
> [3] https://lists.denx.de/pipermail/u-boot/2021-April/447280.html
> 
> Signed-off-by: Rasmus Villemoes <rasmus.villemoes at prevas.dk>
> ---
>   arch/powerpc/Kconfig     |  1 +
>   arch/powerpc/lib/Kconfig |  9 +++++++++
>   arch/powerpc/lib/cache.c | 14 ++++++++++++--
>   3 files changed, 22 insertions(+), 2 deletions(-)
>   create mode 100644 arch/powerpc/lib/Kconfig
> 
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index 6a2e88fed2..133447648c 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -49,5 +49,6 @@ source "arch/powerpc/cpu/mpc83xx/Kconfig"
>   source "arch/powerpc/cpu/mpc85xx/Kconfig"
>   source "arch/powerpc/cpu/mpc86xx/Kconfig"
>   source "arch/powerpc/cpu/mpc8xx/Kconfig"
> +source "arch/powerpc/lib/Kconfig"
>   
>   endmenu
> diff --git a/arch/powerpc/lib/Kconfig b/arch/powerpc/lib/Kconfig
> new file mode 100644
> index 0000000000..b30b5edf7c
> --- /dev/null
> +++ b/arch/powerpc/lib/Kconfig
> @@ -0,0 +1,9 @@
> +config CACHE_FLUSH_WATCHDOG_THRESHOLD
> +	int "Bytes to flush between WATCHDOG_RESET calls"
> +	default 0
> +	help
> +	  The flush_cache() function periodically, and by default for
> +	  every cache line, calls WATCHDOG_RESET(). When flushing a
> +	  large area, that may add a significant amount of
> +	  overhead. This option allows you to set a threshold for how
> +	  many bytes to flush between each WATCHDOG_RESET call.
> diff --git a/arch/powerpc/lib/cache.c b/arch/powerpc/lib/cache.c
> index 3e487f50fe..115ae8e160 100644
> --- a/arch/powerpc/lib/cache.c
> +++ b/arch/powerpc/lib/cache.c
> @@ -12,6 +12,8 @@
>   void flush_cache(ulong start_addr, ulong size)
>   {
>   	ulong addr, start, end;
> +	ulong wd_limit = CONFIG_CACHE_FLUSH_WATCHDOG_THRESHOLD;
> +	ulong flushed = 0;
>   
>   	start = start_addr & ~(CONFIG_SYS_CACHELINE_SIZE - 1);
>   	end = start_addr + size - 1;
> @@ -19,7 +21,11 @@ void flush_cache(ulong start_addr, ulong size)
>   	for (addr = start; (addr <= end) && (addr >= start);
>   			addr += CONFIG_SYS_CACHELINE_SIZE) {
>   		asm volatile("dcbst 0,%0" : : "r" (addr) : "memory");
> -		WATCHDOG_RESET();
> +		flushed += CONFIG_SYS_CACHELINE_SIZE;
> +		if (flushed >= wd_limit) {
> +			WATCHDOG_RESET();
> +			flushed = 0;
> +		}
>   	}
>   	/* wait for all dcbst to complete on bus */
>   	asm volatile("sync" : : : "memory");
> @@ -27,7 +33,11 @@ void flush_cache(ulong start_addr, ulong size)
>   	for (addr = start; (addr <= end) && (addr >= start);
>   			addr += CONFIG_SYS_CACHELINE_SIZE) {
>   		asm volatile("icbi 0,%0" : : "r" (addr) : "memory");
> -		WATCHDOG_RESET();
> +		flushed += CONFIG_SYS_CACHELINE_SIZE;
> +		if (flushed >= wd_limit) {
> +			WATCHDOG_RESET();
> +			flushed = 0;
> +		}

It might make sense to pull these 2 identical code snippets into a
function to not duplicate this code.

Other than this:

Reviewed-by: Stefan Roese <sr at denx.de>

Thanks,
Stefan


More information about the U-Boot mailing list