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.
This commit is contained in:
Alexander Boettcher 2022-07-26 14:05:01 +02:00 committed by Christian Helmuth
parent 6c5d2253c9
commit e2b61231ae
2 changed files with 41 additions and 11 deletions

View File

@ -19,9 +19,6 @@
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/jiffies.h>
extern u64 jiffies_64;
static __always_inline void rep_nop(void) 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) static __always_inline void cpu_relax(void)
{ {
/* break busy loop of slchi() in drivers/i2c/algos/i2c-algo-bit.c */ /* break busy loop of slchi() in drivers/i2c/algos/i2c-algo-bit.c */
u64 const us = jiffies_to_usecs(1); __udelay(100);
usleep_range(us, us);
} }
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */

View File

@ -1,6 +1,7 @@
/* /*
* \brief Supplement for emulation for linux/include/asm-generic/delay.h * \brief Supplement for emulation for linux/include/asm-generic/delay.h
* \author Josef Soentgen * \author Josef Soentgen
* \author Alexander Boettcher
* \date 2022-05-05 * \date 2022-05-05
*/ */
@ -14,21 +15,54 @@
#include <asm-generic/delay.h> #include <asm-generic/delay.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/jiffies.h>
#include <lx_emul.h> #include <lx_emul.h>
void __const_udelay(unsigned long xloops) void __const_udelay(unsigned long xloops)
{ {
unsigned long usecs = xloops / 0x10C7UL; unsigned long const usecs = xloops / 0x10C7UL;
if (usecs < 100) __udelay(usecs);
lx_emul_time_udelay(usecs);
else
usleep_range(usecs, usecs * 10);
} }
void __udelay(unsigned long 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;
}
} }