linux时钟中断

尝试了一下使用local_irq_disable好像不能将时钟中断屏蔽,不知道搞错没有

时钟中断上半部(rm vexpress,定时器SP804)

void __init __sp804_clockevents_init(void __iomem *base, unsigned int irq, struct clk *clk, const char *name)
{
	struct clock_event_device *evt = &sp804_clockevent;
	long rate;

	if (!clk)
		clk = clk_get_sys("sp804", name);
	if (IS_ERR(clk)) {
		pr_err("sp804: %s clock not found: %d\n", name,
			(int)PTR_ERR(clk));
		return;
	}

	rate = sp804_get_clock_rate(clk);
	if (rate < 0)
		return;

	clkevt_base = base;
	clkevt_reload = DIV_ROUND_CLOSEST(rate, HZ);
	evt->name = name;
	evt->irq = irq;
	evt->cpumask = cpu_possible_mask;

	writel(0, base + TIMER_CTRL);

	setup_irq(irq, &sp804_timer_irq);
	clockevents_config_and_register(evt, rate, 0xf, 0xffffffff);
}

可以看到时钟中断的注册的处理函数是sp804_timer_interrupt

setup_irq(irq, &sp804_timer_irq);
static struct irqaction sp804_timer_irq = {
	.name		= "timer",
	.flags		= IRQF_TIMER | IRQF_IRQPOLL,
	.handler	= sp804_timer_interrupt,
	.dev_id		= &sp804_clockevent,
};

int x = 1;
static irqreturn_t sp804_timer_interrupt(int irq, void *dev_id)
{
	struct clock_event_device *evt = dev_id;

	/* clear the interrupt */
	writel(1, clkevt_base + TIMER_INTCLR);

	evt->event_handler(evt);
	if (x == 1)
	{
		x = 0;
		printk(KERN_EMERG "evt->event_handler %p\n",evt->event_handler);
	}
	return IRQ_HANDLED;
}

 evt->event_handler=tick_handle_periodic

void tick_handle_periodic(struct clock_event_device *dev)
{
	int cpu = smp_processor_id();
	/* 获取时钟中断下次执行的时间 */
	ktime_t next = dev->next_event;

	tick_periodic(cpu);

	if (dev->mode != CLOCK_EVT_MODE_ONESHOT)
		return;
	for (;;) {
		/*
		 * Setup the next period for devices, which do not have
		 * periodic mode:
		 */
		/* 计算下一次触发时间 */
		next = ktime_add(next, tick_period);
		/* 设置下一次触发时间, 0表示成功 */
		if (!clockevents_program_event(dev, next, false))
			return;
		/*
		 * Have to be careful here. If we're in oneshot mode,
		 * before we call tick_periodic() in a loop, we need
		 * to be sure we're using a real hardware clocksource.
		 * Otherwise we could get trapped in an infinite
		 * loop, as the tick_periodic() increments jiffies,
		 * which then will increment time, possibly causing
		 * the loop to trigger again and again.
		 */
		if (timekeeping_valid_for_hres())
			tick_periodic(cpu);
	}
}

tick_handle_periodic-->tick_periodic

static void tick_periodic(int cpu)
{
	/* 当前CPU负责更新时间 */
	if (tick_do_timer_cpu == cpu) {
		write_seqlock(&jiffies_lock);

		/* Keep track of the next tick event */
		tick_next_period = ktime_add(tick_next_period, tick_period);

		do_timer(1);/* 更新jiffies_64+=1 */
		write_sequnlock(&jiffies_lock);
		/* 内核维护了另外一个wall time时间:xtime,
		取决于用于对xtime计时的clocksource,它的精度甚至可以达到纳秒级别
		因为xtime实际上是一个内存中的变量,它的访问速度非常快,
		内核大部分时间都是使用xtime来获得当前时间信息。
		xtime记录的是自1970年1月1日24时到当前时刻所经历的纳秒数。    */
		update_wall_time();/* 更新墙上时间 */
	}
	/* 更新进程信息 */
	update_process_times(user_mode(get_irq_regs()));
	profile_tick(CPU_PROFILING);
}

void update_process_times(int user_tick)
{
	struct task_struct *p = current;
	int cpu = smp_processor_id();

	/* Note: this timer irq context must be accounted for as well. */
	/*  */
	/* 更新当前进程内核态和用户态的占用率 */
	account_process_tick(p, user_tick);
	/* 检查有没有定时器到期,有就运行到期定时器的处理 */
	run_local_timers();
	rcu_check_callbacks(cpu, user_tick);
#ifdef CONFIG_IRQ_WORK
	if (in_irq())
		irq_work_run();
#endif
	/* 调度器的tick */
	scheduler_tick();
	run_posix_cpu_timers(p);
}

 run_local_timer会主动触发软中断,将本cpu的软中断状态寄存器对应的bit为置上,然后在中断上半部退出irq_exit时,就可以去执行软中断,自然也能执行到定时器的软中断

void run_local_timers(void)
{
	hrtimer_run_queues();
	raise_softirq(TIMER_SOFTIRQ);
}

时钟中断下半部

void __init init_timers(void)
{
	int err;

	/* ensure there are enough low bits for flags in timer->base pointer */
	BUILD_BUG_ON(__alignof__(struct tvec_base) & TIMER_FLAG_MASK);

	err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,
			       (void *)(long)smp_processor_id());
	BUG_ON(err != NOTIFY_OK);

	init_timer_stats();
	register_cpu_notifier(&timers_nb);
	open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
}

 可以看到针对时钟中断的中断下半部是一个软中断,对应的处理函数是run_timer_softirq

open_softirq(TIMER_SOFTIRQ, run_timer_softirq);

static void run_timer_softirq(struct softirq_action *h)
{
	struct tvec_base *base = __this_cpu_read(tvec_bases);

	hrtimer_run_pending();/* 高精度定时器的处理 */
	/* jiffies大于等于本cpu的timer_jiffies说明有软件时钟到期了 */
	if (time_after_eq(jiffies, base->timer_jiffies))
		__run_timers(base);
}
static inline void __run_timers(struct tvec_base *base)
{
	struct timer_list *timer;

	spin_lock_irq(&base->lock);
	if (catchup_timer_jiffies(base)) {
		spin_unlock_irq(&base->lock);
		return;
	}
	while (time_after_eq(jiffies, base->timer_jiffies)) {
		struct list_head work_list;
		struct list_head *head = &work_list;
		int index = base->timer_jiffies & TVR_MASK;

		/*
		 * Cascade timers:
		 */
		if (!index &&
			(!cascade(base, &base->tv2, INDEX(0))) &&
				(!cascade(base, &base->tv3, INDEX(1))) &&
					!cascade(base, &base->tv4, INDEX(2)))
			cascade(base, &base->tv5, INDEX(3));
		++base->timer_jiffies;
		list_replace_init(base->tv1.vec + index, head);
		while (!list_empty(head)) {
			void (*fn)(unsigned long);
			unsigned long data;
			bool irqsafe;

			timer = list_first_entry(head, struct timer_list,entry);
			fn = timer->function;
			data = timer->data;
			irqsafe = tbase_get_irqsafe(timer->base);

			timer_stats_account_timer(timer);

			base->running_timer = timer;
			detach_expired_timer(timer, base);

			if (irqsafe) {
				spin_unlock(&base->lock);
				call_timer_fn(timer, fn, data);
				spin_lock(&base->lock);
			} else {
				spin_unlock_irq(&base->lock);
				call_timer_fn(timer, fn, data);
				spin_lock_irq(&base->lock);
			}
		}
	}
	base->running_timer = NULL;
	spin_unlock_irq(&base->lock);
}

1、看这样,定时器设置的回调函数是运行在中断上下文的 

2、在调用定时器回调函数前使用了detach_expired_timer将定时器从tvec_base中删除。所以如果需要定时器被再次调用,则需要使用add_timer/mod_timer将定时器重新加进去才行

3、此外可以看到定时器是基于软中断实现的,同样也存在延时。即定时器回调函数被调用的时候,存在jiffies超过设置的超时时间的情况

detach_expired_timer(timer, base);
if (irqsafe) {
	spin_unlock(&base->lock);
	call_timer_fn(timer, fn, data);
	spin_lock(&base->lock);
} else {
	spin_unlock_irq(&base->lock);
	call_timer_fn(timer, fn, data);
	spin_lock_irq(&base->lock);
}

static void call_timer_fn(struct timer_list *timer, void (*fn)(unsigned long),
			  unsigned long data)
{
............
	fn(data);/* 调用定时器设置的回调函数 */
	trace_timer_expire_exit(timer);

	lock_map_release(&lockdep_map);

	if (count != preempt_count()) {
		WARN_ONCE(1, "timer: %pF preempt leak: %08x -> %08x\n",
			  fn, count, preempt_count());
		/*
		 * Restore the preempt count. That gives us a decent
		 * chance to survive and extract information. If the
		 * callback kept a lock held, bad luck, but not worse
		 * than the BUG() we had.
		 */
		preempt_count_set(count);
	}
}

定时器的回调函数运行在中断上下文验证:

struct timer_list mytimer;
void myCallback(void)
{
	printk(KERN_EMERG "\r\nfunc %s, line %d in_interrupt() %lu\n", __FUNCTION__, __LINE__, in_interrupt());
	msleep(10);
}
static int smsc911x_init(struct net_device *dev)
{
    .................
    init_timer(&mytimer);
	mytimer.function = myCallback; /* 定时器处理函数*/
	mytimer.expires = jiffies + msecs_to_jiffies(10000); /* 超时时间为2s */
	mytimer.data = NULL; /* 自己的数据 */
	add_timer(&mytimer); /* 启动定时器 */
}

 在回调函数中调用了msleep进行休眠。会出现原子上下文不允许调度的错误

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值