del_timer_sync的替代品try_to_del_timer_sync

背景

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 <===

该实例不死机,爽

你可能感兴趣的:(kernel,驱动程序,kernel)