1. 定义
cpu_irqtime为per-cpu变量,定义在kernel/sched/cputime.c文件中
DEFINE_PER_CPU(struct irqtime, cpu_irqtime);
本功能的开启由config CONFIG_IRQ_TIME_ACCOUNTING控制,默认是开启状态
config IRQ_TIME_ACCOUNTING
bool "Fine granularity task level IRQ time accounting"
depends on HAVE_IRQ_TIME_ACCOUNTING && !VIRT_CPU_ACCOUNTING_NATIVE
help
Select this option to enable fine granularity task irq time
accounting. This is done by reading a timestamp on each
transitions between softirq and hardirq state, so there can be a
small performance impact.
If in doubt, say N here.
2. 调用
在系统中有三处调用位置:
2.1 irqtime_account_irq
首先看其实现:
/*
* Called after incrementing preempt_count on {soft,}irq_enter
* and before decrementing preempt_count on {soft,}irq_exit.
*/
void irqtime_account_irq(struct task_struct *curr, unsigned int offset)
{
struct irqtime *irqtime = this_cpu_ptr(&cpu_irqtime);
unsigned int pc;
s64 delta;
int cpu;
if (!sched_clock_irqtime) //若未使能,直接退出
return;
cpu = smp_processor_id();
delta = sched_clock_cpu(cpu) - irqtime->irq_start_time;
irqtime->irq_start_time += delta;
pc = irq_count() - offset; //减去本次正常处理的中断,将剩余中断赋值给临时变量pc
/*
* We do not account for softirq time from ksoftirqd here.
* We want to continue accounting softirq time to ksoftirqd thread
* in that case, so as not to confuse scheduler with a special task
* that do not consume any time, but still wants to run.
*/
if (pc & HARDIRQ_MASK) //若还有硬中断未处理完
irqtime_account_delta(irqtime, delta, CPUTIME_IRQ);
else if ((pc & SOFTIRQ_OFFSET) && curr != this_cpu_ksoftirqd()) //仍有软中断,并且当前task不是ksoftirqd
irqtime_account_delta(irqtime, delta, CPUTIME_SOFTIRQ);
}
判断sched_clock_irqtime变量值,该变量的定义含义是:是否使能了clock_irqtime功能。
该变量值定义如下,并通过以下两个接口调用。
static int sched_clock_irqtime;
void enable_sched_clock_irqtime(void)
{
sched_clock_irqtime = 1;
}
void disable_sched_clock_irqtime(void)
{
sched_clock_irqtime = 0;
}
再继续向下,记录本cpu的本次的中断处理时长,赋值给delta,并更新irq_start_time。
根据中断标志位判断是否还有剩余的HARDIRQ or SOFTIRQ未处理(不在ksoftirqd调用),若有就更新如下变量
a. per-cpu kernel_cpustat->cpustat 区分中断类型
b. irqtime->total
c. irqtime->tick_delta
struct kernel_cpustat {
u64 cpustat[NR_STATS];
};
DECLARE_PER_CPU(struct kernel_cpustat, kernel_cpustat);
#define kcpustat_this_cpu this_cpu_ptr(&kernel_cpustat)
static void irqtime_account_delta(struct irqtime *irqtime, u64 delta,
enum cpu_usage_stat idx)
{
u64 *cpustat = kcpustat_this_cpu->cpustat;
u64_stats_update_begin(&irqtime->sync);
cpustat[idx] += delta; //加入对应类型中断的cpustat中
irqtime->total += delta; //加入total
irqtime->tick_delta += delta; //加入tick_delta
u64_stats_update_end(&irqtime->sync);
}
2.1.1 enable_sched_clock_irqtime
enable_sched_clock_irqtime的调用,对于arm/arm64架构来说只有一处:
kernel/time/sched_clock.c
static int irqtime = -1;
core_param(irqtime, irqtime, int, 0400);
void __init
sched_clock_register(u64 (*read)(void), int bits, unsigned long rate)
{
...
/* Enable IRQ time accounting if we have a fast enough sched_clock() */
if (irqtime > 0 || (irqtime == -1 && rate >= 1000000))
enable_sched_clock_irqtime();
...
}
调用需要两个判断条件:
a. irqtime>0
irqtime支持两种配置方式:
cmdline
echo -n ${value} > /sys/module/${modulename}/parameters/${parm}
b. (irqtime == -1 && rate >= 1000000)
sched_clock_register在timer注册时调用,系统中最大rate为arch_timer为26M,>1000000。
2.1.2 disable_sched_clock_irqtime
在arm/arm64系统中无调用位置,忽略。
2.1.3 irq_count
统计此时处理的中断的数量。包括nmi中断,硬中断,软中断。
#define irq_count() (nmi_count() | hardirq_count() | softirq_count())
本文详细介绍了Linux内核中的CPU中断时间会计机制,包括`cpu_irqtime`变量的定义、`irqtime_account_irq`函数的调用流程以及`enable_sched_clock_irqtime`和`disable_sched_clock_irqtime`的启用条件。该机制用于精细粒度的中断时间统计,通过`sched_clock_irqtime`变量控制,并在中断进入和退出时调整。文章还提及了`irq_count`函数用于统计当前处理的中断数量。

1210

被折叠的 条评论
为什么被折叠?



