del_timer_sync 的kernel code 有个反例如注释
为了解决这种死锁问题,采用 try_to_del_timer_sync
/**
* del_timer_sync - deactivate a timer and wait for the handler to finish.
* @timer: the timer to be deactivated
*
* This function only differs from del_timer() on SMP: besides deactivating
* the timer it also makes sure the handler has finished executing on other
* CPUs.
*
* Synchronization rules: Callers must prevent restarting of the timer,
* otherwise this function is meaningless. It must not be called from
* interrupt contexts unless the timer is an irqsafe one. The caller must
* not hold locks which would prevent completion of the timer's
* handler. The timer's handler must not call add_timer_on(). Upon exit the
* timer is not queued and the handler is not running on any CPU.
*
* Note: For !irqsafe timers, you must not hold locks that are held in
* interrupt context while calling this function. Even if the lock has
* nothing to do with the timer in question. Here's why::
*
* CPU0 CPU1
* ---- ----
*
* call_timer_fn();
* base->running_timer = mytimer;
* spin_lock_irq(somelock);
*
* spin_lock(somelock);
* del_timer_sync(mytimer);
* while (base->running_timer == mytimer);
*
* Now del_timer_sync() will never return and never release somelock.
* The interrupt on the other CPU is waiting to grab somelock but
* it has interrupted the softirq that CPU0 is waiting to finish.
*
* The function returns whether it has deactivated a pending timer or not.
*/
int del_timer_sync(struct timer_list *timer)
{
int ret;
#ifdef CONFIG_LOCKDEP
unsigned long flags;
/*
* If lockdep gives a backtrace here, please reference
* the synchronization rules above.
*/
local_irq_save(flags);
lock_map_acquire(&timer->lockdep_map);
lock_map_release(&timer->lockdep_map);
local_irq_restore(flags);
#endif
/*
* don't use it in hardirq context, because it
* could lead to deadlock.
*/
WARN_ON(in_irq() && !(timer->flags & TIMER_IRQSAFE));
/*
* Must be able to sleep on PREEMPT_RT because of the slowpath in
* del_timer_wait_running().
*/
if (IS_ENABLED(CONFIG_PREEMPT_RT) && !(timer->flags & TIMER_IRQSAFE))
lockdep_assert_preemption_enabled();
do {
ret = try_to_del_timer_sync(timer);
if (unlikely(ret < 0)) {
del_timer_wait_running(timer);
cpu_relax();
}
} while (ret < 0);
return ret;
}
EXPORT_SYMBOL(del_timer_sync);
例子不太完美,因为这把自旋锁保护的东西比较宽泛
但是解决如上问题的思路可值得借鉴
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static struct timer_list demo_timer;
static spinlock_t protect_timer_lock;
static struct task_struct *demo_task;
//每4秒执行一次while动作
static void demo_timer_handler(struct timer_list *unused)
{
int cur_cpu_id;
int i = 8000;
pr_info("%s ===>\n", __func__);
cur_cpu_id = get_cpu();
pr_info("%s, cur_cpu_id is %d\n", __func__, cur_cpu_id);
put_cpu();
spin_lock(&protect_timer_lock);
pr_info("%s, before execute something\n", __func__);
// 模拟timer中执行任务
while (i--) {
;
}
pr_info("%s, after execute something\n", __func__);
mod_timer(&demo_timer, jiffies + msecs_to_jiffies(1000*4));
spin_unlock(&protect_timer_lock);
pr_info("%s <===\n", __func__);
}
// 每5秒询问一次
int demo_timer_stop_thread(void *data) {
int cur_cpu_id;
unsigned long flags;
int ret;
pr_info("%s ===>\n", __func__);
cur_cpu_id = get_cpu();
pr_info("%s, cur_cpu_id is %d\n", __func__, cur_cpu_id);
put_cpu();
while(!kthread_should_stop()) {
msleep(1000*5);
spin_lock_irqsave(&protect_timer_lock, flags);
ret = try_to_del_timer_sync(&demo_timer);
if (ret >= 0) {
pr_info("%s, timer deactivates success\n", __func__);
} else {
pr_info("%s, timer is active or has been deactivatesd\n", __func__);
}
spin_unlock_irqrestore(&protect_timer_lock, flags);
}
pr_info("%s <===\n", __func__);
return 23;
}
static int demo_init(void) {
int result = 0;
int cur_cpu_id;
pr_info("%s ===>\n", __func__);
spin_lock_init(&protect_timer_lock);
cur_cpu_id = get_cpu(); // 关抢占
pr_info("%s, cur_cpu_id is %d\n", __func__, cur_cpu_id);
put_cpu(); // 开抢占
timer_setup(&demo_timer, demo_timer_handler, 0);
mod_timer(&demo_timer, jiffies + msecs_to_jiffies(1000*4));
demo_task = kthread_run(demo_timer_stop_thread, NULL, "demo_timer_stop");
if (IS_ERR(demo_task)) {
demo_task = NULL;
pr_err("%s, thread create failed\n", __func__);
result = -EINVAL;
} else {
pr_info("%s, thread create success\n", __func__);
}
pr_info("%s <===\n", __func__);
return result;
}
static void demo_exit(void) {
int ret;
pr_info("%s ===>\n", __func__);
if (!IS_ERR(demo_task)) {
ret = kthread_stop(demo_task);
pr_info("%s, kthread stop, exit code %d\n", __func__, ret);
} else {
pr_err("%s, demo task invalid\n", __func__);
}
demo_task = NULL;
pr_info("%s <===\n", __func__);
return;
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
本地用的是4.15的源码,不能用 kthread_create_on_cpu
但是可以证明,软中断的优先级就是比进程上下文高
同时发现,如果软中断下文和进程同时跑,就会到两个CPU上
注意本人对spinlock的用法,低优先级的环境要加强保护,防止被高优先级的软中断侵入
[ 4897.495004] demo_init ===>
[ 4897.495010] demo_init, cur_cpu_id is 1
[ 4897.497205] demo_init, thread create success
[ 4897.497209] demo_init <===
[ 4897.499245] demo_timer_stop_thread ===>
[ 4897.499250] demo_timer_stop_thread, cur_cpu_id is 1
[ 4901.580970] demo_timer_handler ===>
[ 4901.581100] demo_timer_handler, cur_cpu_id is 1
[ 4901.581116] demo_timer_handler, before execute something
[ 4901.581131] demo_timer_handler, after execute something
[ 4901.581148] demo_timer_handler <===
[ 4902.603886] demo_timer_stop_thread, timer deactivates success
[ 4907.724239] demo_timer_stop_thread, timer deactivates success
[ 4912.843899] demo_timer_stop_thread, timer deactivates success
[ 4917.964659] demo_timer_stop_thread, timer deactivates success
[ 4923.083797] demo_timer_stop_thread, timer deactivates success
[ 4928.203954] demo_timer_stop_thread, timer deactivates success
[ 4933.324146] demo_timer_stop_thread, timer deactivates success
[ 4936.415462] demo_exit ===>
[ 4938.348353] demo_timer_stop_thread, timer deactivates success
[ 4938.348362] demo_timer_stop_thread <===
[ 4938.348611] demo_exit, kthread stop, exit code 23
[ 4938.348618] demo_exit <===
该实例不死机,爽