From e2b61231aead9900431b3a097d211fd98a479d65 Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Tue, 26 Jul 2022 14:05:01 +0200 Subject: [PATCH] pc: avoid use usleep_range in m/udelay emulation m/udelay is called with closed interrupts. Linux contrib code expects no other task to be run respectively to be re-scheduled. usleep_range leads to executing other tasks, which causes assertions in "spinlock taken twice" or "unexpected preempt count" errors, seen on runtime with wifi_drv and intel_fb. The assertion triggered with the changes by #4562. --- .../intel/pc/shadow/asm/vdso/processor.h | 6 +-- repos/pc/src/lib/pc/lx_emul/delay.c | 46 ++++++++++++++++--- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/repos/pc/src/drivers/framebuffer/intel/pc/shadow/asm/vdso/processor.h b/repos/pc/src/drivers/framebuffer/intel/pc/shadow/asm/vdso/processor.h index db54ee6637..08414f0c1a 100644 --- a/repos/pc/src/drivers/framebuffer/intel/pc/shadow/asm/vdso/processor.h +++ b/repos/pc/src/drivers/framebuffer/intel/pc/shadow/asm/vdso/processor.h @@ -19,9 +19,6 @@ #ifndef __ASSEMBLY__ #include -#include - -extern u64 jiffies_64; static __always_inline void rep_nop(void) @@ -33,8 +30,7 @@ static __always_inline void rep_nop(void) static __always_inline void cpu_relax(void) { /* break busy loop of slchi() in drivers/i2c/algos/i2c-algo-bit.c */ - u64 const us = jiffies_to_usecs(1); - usleep_range(us, us); + __udelay(100); } #endif /* __ASSEMBLY__ */ diff --git a/repos/pc/src/lib/pc/lx_emul/delay.c b/repos/pc/src/lib/pc/lx_emul/delay.c index 05d9063b6c..febbbd9a1e 100644 --- a/repos/pc/src/lib/pc/lx_emul/delay.c +++ b/repos/pc/src/lib/pc/lx_emul/delay.c @@ -1,6 +1,7 @@ /* * \brief Supplement for emulation for linux/include/asm-generic/delay.h * \author Josef Soentgen + * \author Alexander Boettcher * \date 2022-05-05 */ @@ -14,21 +15,54 @@ #include #include +#include #include void __const_udelay(unsigned long xloops) { - unsigned long usecs = xloops / 0x10C7UL; - if (usecs < 100) - lx_emul_time_udelay(usecs); - else - usleep_range(usecs, usecs * 10); + unsigned long const usecs = xloops / 0x10C7UL; + __udelay(usecs); } void __udelay(unsigned long usecs) { - lx_emul_time_udelay(usecs); + /* + * Account for delays summing up to equivalent of 1 jiffie during + * jiffies_64 stays same. When 1 jiffie is reached in sum, + * increase jiffie_64 to break endless loops, seen in + * * intel_fb - cpu_relax(void) emulation used by + * busy loop of slchi() in drivers/i2c/algos/i2c-algo-bit.c + * * wifi_drv - net/wireless/intel/iwlwif* code during error code handling + */ + static uint64_t last_jiffie = 0; + static unsigned long delayed_sum = 0; + + if (jiffies_64 == last_jiffie) { + delayed_sum += usecs; + } else { + last_jiffie = jiffies_64; + delayed_sum = usecs; + } + + if (usecs < 100) + lx_emul_time_udelay(usecs); + else { + unsigned long slept = 0; + while (slept < usecs) { + lx_emul_time_udelay(100); + slept += 100; + } + } + + /* + * When delays sum up to the equivalent of 1 jiffie, + * increase it to break endless loops. + */ + if (delayed_sum >= jiffies_to_usecs(1)) { + jiffies_64 ++; + delayed_sum = 0; + } }