我是靠谱客的博主 奋斗季节,最近开发中收集的这篇文章主要介绍arch_timer驱动分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

arch_timer的初始化

static void __init arch_timer_common_init(void)
{

	arch_timer_banner(arch_timers_present);//打印计时器的信息
	arch_counter_register(arch_timers_present);//注册计数器
	arch_timer_arch_init();
}

static int __init arch_timer_register(void)
{
	arch_timer_evt = alloc_percpu(struct clock_event_device);
	ppi = arch_timer_ppi[PHYS_SECURE_PPI];
	err = request_percpu_irq(ppi, arch_timer_handler_phys, "arch_timer", arch_timer_evt);
	ppi = arch_timer_ppi[PHYS_NONSECURE_PPI];
	err = request_percpu_irq(ppi, arch_timer_handler_phys, "arch_timer", arch_timer_evt);
}//虽然申请了两个中断,但只看到一个arch_timer发生了中断

static void __init arch_timer_init(void)
{
	arch_timer_register();
	arch_timer_common_init();
}

static void __init arch_timer_of_init(struct device_node *np)
{
	arch_timer_init();
}
CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init);
static struct clocksource clocksource_counter = {
	.name	= "arch_sys_counter",
	.rating	= 400,
	.read	= arch_counter_read,
	.mask	= CLOCKSOURCE_MASK(56),
	.flags	= CLOCK_SOURCE_IS_CONTINUOUS,
};

static struct cyclecounter cyclecounter = {
	.read	= arch_counter_read_cc,
	.mask	= CLOCKSOURCE_MASK(56),
};

static void __init arch_counter_register(unsigned type)
{
	u64 start_count;

	/* Register the CP15 based counter if we have one */
	if (type & ARCH_CP15_TIMER) {
		if (IS_ENABLED(CONFIG_ARM64) || arch_timer_use_virtual)
			arch_timer_read_counter = arch_counter_get_cntvct;
		else
			arch_timer_read_counter = arch_counter_get_cntpct;
	} 

	start_count = arch_timer_read_counter();
	clocksource_register_hz(&clocksource_counter, arch_timer_rate);
	cyclecounter.mult = clocksource_counter.mult;
	cyclecounter.shift = clocksource_counter.shift;
	timecounter_init(&timecounter, &cyclecounter, start_count);

	/* 56 bits minimum, so we assume worst case rollover */
	sched_clock_register(arch_timer_read_counter, 56, arch_timer_rate);
}

看下中断处理函数

static __always_inline irqreturn_t timer_handler(const int access,
					struct clock_event_device *evt)
{
		evt->event_handler(evt);
}

static irqreturn_t arch_timer_handler_phys(int irq, void *dev_id)
{
	struct clock_event_device *evt = dev_id;
	return timer_handler(ARCH_TIMER_PHYS_ACCESS, evt);
}

event_handler可以通过cat /proc/timer_list查看,如

Tick Device: mode:     1
Per CPU device: 7
Clock Event Device: arch_sys_timer
 max_delta_ns:   82595524660
 min_delta_ns:   1000
 mult:           111669150
 shift:          32
 mode:           1
 next_event:     6436132000000 nsecs
 set_next_event: arch_timer_set_next_event_phys
 shutdown: arch_timer_shutdown_phys
 event_handler:  hrtimer_interrupt
 retries:        1

然后可以通过在hrtimer_interrupt函数中加入WARN(1, "xxxxxx.n");打印相应的调用关系。

重点关注下hrtimer_interrupt函数

void hrtimer_interrupt(struct clock_event_device *dev)
{
	struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases);
	entry_time = now = hrtimer_update_base(cpu_base);
	__hrtimer_run_queues(cpu_base, now);//找出到期的定时器执行其功能
	expires_next = __hrtimer_get_next_event(cpu_base);//找出下次的超时时间
	tick_program_event(expires_next, 0);//将定时时间设置到机器的计数模块
}
static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now)
{
	struct hrtimer_clock_base *base = cpu_base->clock_base;
	unsigned int active = cpu_base->active_bases;

	for (; active; base++, active >>= 1) {
		struct timerqueue_node *node;
		ktime_t basenow;

		if (!(active & 0x01))
			continue;

		basenow = ktime_add(now, base->offset);

		while ((node = timerqueue_getnext(&base->active))) {
			struct hrtimer *timer;
			timer = container_of(node, struct hrtimer, node);
			if (basenow.tv64 < hrtimer_get_softexpires_tv64(timer))
				break;

			__run_hrtimer(cpu_base, base, timer, &basenow);
		}
	}
}

static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base,
			  struct hrtimer_clock_base *base,
			  struct hrtimer *timer, ktime_t *now)
{
	enum hrtimer_restart (*fn)(struct hrtimer *);
	fn = timer->function;
	restart = fn(timer);
}

同样,查看/proc/timer_list可以查看在运行的每个cpu上不同类型的高精度定时器,如

/*
 * The timer bases:
 *
 * There are more clockids than hrtimer bases. Thus, we index
 * into the timer bases by the hrtimer_base_type enum. When trying
 * to reach a base using a clockid, hrtimer_clockid_to_base()
 * is used to convert from clockid to the proper hrtimer_base_type.
 */
DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) =
{
	.lock = __RAW_SPIN_LOCK_UNLOCKED(hrtimer_bases.lock),
	.seq = SEQCNT_ZERO(hrtimer_bases.seq),
	.clock_base =
	{
		{
			.index = HRTIMER_BASE_MONOTONIC,
			.clockid = CLOCK_MONOTONIC,
			.get_time = &ktime_get,
		},
		{
			.index = HRTIMER_BASE_REALTIME,
			.clockid = CLOCK_REALTIME,
			.get_time = &ktime_get_real,
		},
		{
			.index = HRTIMER_BASE_BOOTTIME,
			.clockid = CLOCK_BOOTTIME,
			.get_time = &ktime_get_boottime,
		},
		{
			.index = HRTIMER_BASE_TAI,
			.clockid = CLOCK_TAI,
			.get_time = &ktime_get_clocktai,
		},
	}
};

高精度的类型

enum  hrtimer_base_type {
    HRTIMER_BASE_MONOTONIC,
    HRTIMER_BASE_REALTIME,
    HRTIMER_BASE_BOOTTIME,
    HRTIMER_BASE_TAI,
    HRTIMER_MAX_CLOCK_BASES,
};

clock 0指HRTIMER_BASE_MONOTONIC

cpu 3指cpu3上的


//重点关注调度定时器

调度的周期 tick_period

union ktime {
	s64	tv64;
};
typedef union ktime ktime_t;		/* Kill this */
#define NSEC_PER_SEC	1000000000L
CONFIG_HZ=100
tick_period = ktime_set(0, NSEC_PER_SEC / HZ); 10ms
static inline ktime_t ktime_set(const s64 secs, const unsigned long nsecs)
{
	if (unlikely(secs >= KTIME_SEC_MAX))
		return (ktime_t){ .tv64 = KTIME_MAX };

	return (ktime_t) { .tv64 = secs * NSEC_PER_SEC + (s64)nsecs };
}
 
void tick_setup_sched_timer(void)
{
	struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
	ktime_t now = ktime_get();

	
	hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
	ts->sched_timer.function = tick_sched_timer;

	hrtimer_set_expires(&ts->sched_timer, tick_init_jiffy_update());
	hrtimer_forward(&ts->sched_timer, now, tick_period);
	hrtimer_start_expires(&ts->sched_timer, HRTIMER_MODE_ABS_PINNED);
	tick_nohz_activate(ts, NOHZ_MODE_HIGHRES);
}

static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer)
{
	struct tick_sched *ts =
		container_of(timer, struct tick_sched, sched_timer);
	struct pt_regs *regs = get_irq_regs();
	ktime_t now = ktime_get();

	tick_sched_do_timer(now); //更新jiffies变量和墙上时间

	/*
	 * Do not call, when we are not in irq context and have
	 * no valid regs pointer
	 */
	if (regs)
		tick_sched_handle(ts, regs);//周期性调度进程

	/* No need to reprogram if we are in idle or full dynticks mode */
	if (unlikely(ts->tick_stopped))
		return HRTIMER_NORESTART;

	hrtimer_forward(timer, now, tick_period);

	return HRTIMER_RESTART;
}
static void tick_sched_do_timer(ktime_t now)
{
	tick_do_update_jiffies64(now);
}

static void tick_do_update_jiffies64(ktime_t now)
{
	do_timer(++ticks);
	update_wall_time();
}

void do_timer(unsigned long ticks)
{
	jiffies_64 += ticks;
}

static inline u64 tk_clock_read(struct tk_read_base *tkr)
{
	struct clocksource *clock = READ_ONCE(tkr->clock);

	return clock->read(clock);
}
void update_wall_time(void)
{
	offset = clocksource_delta(tk_clock_read(&tk->tkr_mono),tk->tkr_mono.cycle_last, tk->tkr_mono.mask);
}
static void tick_sched_handle(struct tick_sched *ts, struct pt_regs *regs)
{
	update_process_times(user_mode(regs));
}

void update_process_times(int user_tick)
{
	run_local_timers();//找出超时的定时器运行
	scheduler_tick();
}

void run_local_timers(void)
{
	hrtimer_run_queues();//高精度定时器
	raise_softirq(TIMER_SOFTIRQ);//低精度定时器
}

void scheduler_tick(void)
{
	int cpu = smp_processor_id();
	curr->sched_class->task_tick(rq, curr, 0);//调度
	calc_global_load_tick(rq);

#ifdef CONFIG_SMP
	rq->idle_balance = idle_cpu(cpu);
	trigger_load_balance(rq);//触发负载平衡
#endif
}

 

最后

以上就是奋斗季节为你收集整理的arch_timer驱动分析的全部内容,希望文章能够帮你解决arch_timer驱动分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(70)

评论列表共有 0 条评论

立即
投稿
返回
顶部