msleep 不能用在atomic context,但是mdelay 可以

时间:2022-07-10 19:55:22
在atomic context环境中不能调用msleep等函数,atomic context基本就是中断或者被spin_lock/spin_unlock等包起来的部分,我们先看看msleep 为啥在atomic context中使用
void msleep(unsigned int msecs)
{
    unsigned long timeout = msecs_to_jiffies(msecs) + 1;

    while (timeout)
        timeout = schedule_timeout_uninterruptible(timeout);
}
原来是调用schedule_timeout_uninterruptible
signed long __sched schedule_timeout_uninterruptible(signed long timeout)
{
    __set_current_state(TASK_UNINTERRUPTIBLE);
    return schedule_timeout(timeout);
}
在schedule_timeout_uninterruptible->__schedule->schedule_debug 最终会出现 BUG: scheduling while atomic经典bug 这样经典的bug
但是如果真的要在atomic context delay该怎么办呢?答案是可以调用mdelay函数
#ifndef mdelay
#define mdelay(n) (\
    (__builtin_constant_p(n) && (n)<=MAX_UDELAY_MS) ? udelay((n)*1000) : \
    ({unsigned long __ms=(n); while (__ms--) udelay(1000);}))
这里都是调用udelay
#define udelay(n)                            \
    ({                                \
        if (__builtin_constant_p(n)) {                \
            if ((n) / 20000 >= 1)                \
                 __bad_udelay();            \
            else                        \
                __const_udelay((n) * 0x10c7ul);        \
        } else {                        \
            __udelay(n);                    \
        }                            \
    })
加入走else的case
void __udelay(unsigned long usecs)
{
    __const_udelay(usecs * 0x10C7UL); /* 2**32 / 1000000 (rounded up) */
}
可见最终也是调用__const_udelay
static inline void cpu_relax(void)
{
    asm volatile("yield" ::: "memory");
}

void __delay(unsigned long cycles)
{
    cycles_t start = get_cycles();

    while ((get_cycles() - start) < cycles)
        cpu_relax();
}
EXPORT_SYMBOL(__delay);

inline void __const_udelay(unsigned long xloops)
{
    unsigned long loops;

    loops = xloops * loops_per_jiffy * HZ;
    __delay(loops >> 32);
}

原来最终是调用cpu_relax 啊,所以说mdelay可以用在atomic context环境中