DO_ONCE 和 printk_once

DO_ONCE 这个宏是是kernel 为避免一个函数被调用两次而定义的宏
#define DO_ONCE(func, ...)                             \
    ({                                     \
        bool ___ret = false;                         \
        static bool ___done = false;                     \
        static struct static_key ___once_key = STATIC_KEY_INIT_TRUE; \
        if (static_key_true(&___once_key)) {                 \
            unsigned long ___flags;                     \
            ___ret = __do_once_start(&___done, &___flags);         \
            if (unlikely(___ret)) {                     \
                func(__VA_ARGS__);                 \
                __do_once_done(&___done, &___once_key,         \
                           &___flags);             \
            }                             \
        }                                 \
        ___ret;                                 \
    })

如果有一个func 只能被调用一次,例如初始化函数的话,则可以用这个宏,如下例所示foo这个函数即使被调用两次,也只会运行一次
 *   void foo(void)
 *   {
 *     DO_ONCE(func, arg);
 *   }
 *
 *   foo();
 *   foo();


例如netdev_rss_key_fill 函数可能没调用多次,但是应该除了第一次外,后面的几次直接返回就行了
void netdev_rss_key_fill(void *buffer, size_t len)
{
    BUG_ON(len > sizeof(netdev_rss_key));
    net_get_random_once(netdev_rss_key, sizeof(netdev_rss_key));
    memcpy(buffer, netdev_rss_key, len);
}

#define net_get_random_once(buf, nbytes)            \
    get_random_once((buf), (nbytes))
#define get_random_once(buf, nbytes)                         \
    DO_ONCE(get_random_bytes, (buf), (nbytes))


对于printk 也有一个once的函数,只会被调用一次,但是必须定义CONFIG_PRINTK
#ifdef CONFIG_PRINTK
#define printk_once(fmt, ...)                    \
({                                \
    static bool __print_once __read_mostly;            \
    bool __ret_print_once = !__print_once;            \
                                \
    if (!__print_once) {                    \
        __print_once = true;                \
        printk(fmt, ##__VA_ARGS__);            \
    }                            \
    unlikely(__ret_print_once);                \
})
}
而且kernel 已经帮我们定义好了不同log level的printk_once,一般推荐直接使用下面的函数.
#define pr_emerg_once(fmt, ...)                    \
    printk_once(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
#define pr_alert_once(fmt, ...)                    \
    printk_once(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_crit_once(fmt, ...)                    \
    printk_once(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_err_once(fmt, ...)                    \
    printk_once(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warn_once(fmt, ...)                    \
    printk_once(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
#define pr_notice_once(fmt, ...)                \
    printk_once(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
#define pr_info_once(fmt, ...)                    \
    printk_once(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
#define pr_cont_once(fmt, ...)                    \
    printk_once(KERN_CONT pr_fmt(fmt), ##__VA_ARGS__)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值