SoftIrq 流程分析

------------------------ kernel/softirq.c -----------------------

static __init int spawn_ksoftirqd(void);
static __init int spawn_ksoftirqd(void)
{
void *cpu = (void *)(long)smp_processor_id();
int err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu);

BUG_ON(err == NOTIFY_BAD);
cpu_callback(&cpu_nfb, CPU_ONLINE, cpu);
register_cpu_notifier(&cpu_nfb);
return 0;
}

early_initcall(spawn_ksoftirqd);

spawn_ksoftirqd 在系统启动的时候会被执行,这个函数调用了两次cpu_callback,根据两次传入的CPU_UP_PREPARECPU_ONLINE 这两个状态来判断,一般稍微有点觉悟的好同志都会立刻想到这两个状态是彼此相互联系的。下面具体看cpu_allback的实现

static int __cpuinit cpu_callback(struct notifier_block *nfb,
unsigned long action,
void *hcpu)
{
int hotcpu = (unsigned long)hcpu;
struct task_struct *p;

switch (action) {
case CPU_UP_PREPARE:
case CPU_UP_PREPARE_FROZEN:
p = kthread_create(ksoftirqd, hcpu, "ksoftirqd/%d", hotcpu);
if (IS_ERR(p)) {
printk("ksoftirqd for %i failed\n", hotcpu);
return NOTIFY_BAD;
}
kthread_bind(p, hotcpu);
per_cpu(ksoftirqd, hotcpu) = p;
break;
case CPU_ONLINE:
case CPU_ONLINE_FROZEN:
wake_up_process(per_cpu(ksoftirqd, hotcpu));
break;

}
return NOTIFY_OK;
}

当前CPU的状态为CPU_UP_PREPARE, CPU还没有完全开始调度线程之前,我们利用kthread_create来创建一个内核线程 ksoftirqd ;接下来当CPU的状态为CPU_ONLINE后, wake_up_process 唤醒之前创建的ksoftirqd线程。那现在问题就是ksoftirqd 这个线程被唤醒之后都干了哪些勾当?! 

static int ksoftirqd(void * __bind_cpu)
{
set_current_state(TASK_INTERRUPTIBLE);

while (!kthread_should_stop()) {
preempt_disable();
if (!local_softirq_pending()) {
preempt_enable_no_resched();
schedule();
preempt_disable();
}

__set_current_state(TASK_RUNNING);

while (local_softirq_pending()) {  等待CPU状态从CPU_UP_PREPARE进入CPU_ONLINE后,被唤醒!
/* Preempt disable stops cpu going offline.
If already offline, we'll be on wrong CPU:
don't process */
if (cpu_is_offline((long)__bind_cpu))
goto wait_to_die;
do_softirq();  处理各种 soft irq这个函数可以是kernel/softirq.c中的do_softirq,也可以在不同体系结构中自己实现;
preempt_enable_no_resched();
cond_resched();
preempt_disable();
rcu_qsctr_inc((long)__bind_cpu);
}
preempt_enable();
set_current_state(TASK_INTERRUPTIBLE);
}
__set_current_state(TASK_RUNNING);
return 0;

wait_to_die:
preempt_enable();
/* Wait for kthread_stop */
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
schedule();
set_current_state(TASK_INTERRUPTIBLE);
}
__set_current_state(TASK_RUNNING);
return 0;
}

------------------------- kernel/softirq.c -----------------------
asmlinkage void __do_softirq(void)
{
struct softirq_action *h;
__u32 pending;
int max_restart = MAX_SOFTIRQ_RESTART;
int cpu;

pending = local_softirq_pending();Q1返回pending值的含义 ?
account_system_vtime(current);

__local_bh_disable((unsigned long)__builtin_return_address(0));
lockdep_softirq_enter();

cpu = smp_processor_id();
restart:
/* Reset the pending bitmask before enabling irqs */
set_softirq_pending(0);

local_irq_enable();

h = softirq_vec;   Q2:这个数组的含义?

do {
if (pending & 1) {
int prev_count = preempt_count();

trace_softirq_entry(h, softirq_vec);
h->action(h);     Q3:这里调用的具体是哪个函数?
trace_softirq_exit(h, softirq_vec);
if (unlikely(prev_count != preempt_count())) {
printk(KERN_ERR "huh, entered softirq %td %s %p"
"with preempt_count %08x,"
" exited with %08x?\n", h - softirq_vec,
softirq_to_name[h - softirq_vec],
h->action, prev_count, preempt_count());
preempt_count() = prev_count;
}

rcu_bh_qsctr_inc(cpu);
}
h++;
pending >>= 1;
} while (pending);

local_irq_disable();

pending = local_softirq_pending();
if (pending && --max_restart)
goto restart;

if (pending)
wakeup_softirqd();

lockdep_softirq_exit();

account_system_vtime(current);
_local_bh_enable();
}


#ifndef __ARCH_HAS_DO_SOFTIRQ

asmlinkage void do_softirq(void)
{
__u32 pending;
unsigned long flags;

if (in_interrupt())
return;

local_irq_save(flags);

pending = local_softirq_pending();

if (pending)
__do_softirq();

local_irq_restore(flags);
}

#endif

Q1:
-----------------arch/arm/include/asm/hardirq.h-----------------------
typedef struct {
unsigned int __softirq_pending;
unsigned int local_timer_irqs;
} ____cacheline_aligned irq_cpustat_t;

-----------------incude/linux/irq_cpustat.h-----------------------
#ifndef __ARCH_IRQ_STAT
extern irq_cpustat_t irq_stat[];        /* defined in asm/hardirq.h */
#define __IRQ_STAT(cpu, member)    (irq_stat[cpu].member)  __IRQ_STAT用于存取第cpuCPU对应的irq_stat结构体中的member成员;确实有点绕口!
#endif

/* arch independent irq_stat fields */
#define local_softirq_pending() \
__IRQ_STAT(smp_processor_id(), __softirq_pending)

local_softirq_pending宏用去存取当前CPU对应的irq_cpustat_t结构体中的__softirq_pending成员;

#ifndef __ARCH_SET_SOFTIRQ_PENDING
#define set_softirq_pending(x) (local_softirq_pending() = (x))
#define or_softirq_pending(x)  (local_softirq_pending() |= (x))
#endif

#define __raise_softirq_irqoff(nr) do { or_softirq_pending(1UL << (nr)); } while (0)

/*
* This function must run with irqs disabled!
*/
inline void raise_softirq_irqoff(unsigned int nr)
{
__raise_softirq_irqoff(nr);

/*
* If we're in an interrupt or softirq, we're done
* (this also catches softirq-disabled code). We will
* actually run the softirq once we return from
* the irq or softirq.
*
* Otherwise we wake up ksoftirqd to make sure we
* schedule the softirq soon.
*/
if (!in_interrupt())
wakeup_softirqd();
}

void raise_softirq(unsigned int nr)
{
unsigned long flags;

local_irq_save(flags);
raise_softirq_irqoff(nr);
local_irq_restore(flags);
}

也就是说raise_softirq--->raise_softirq_irqoff--->__raise_softirq_irqoff--->or_softirq_pending 会将当前CPU对应的irq_cpustat_t结构体中的__softirq_pending成员的第nrBIT置位。nr的具体的含义在Q2中解释。
提前先科普下,对于TIMER_SOFTIRQ来说,每次system clock产生中断时,即一个tick 到来时,在system clock的中断处理函数中会调用run_local_timers来设置TIMER_SOFTIRQ触发条件;也就是当前CPU对应的irq_cpustat_t结构体中的__softirq_pending成员的第TIMER_SOFTIRQBIT被置为1。 而当这个条件满足时,
ksoftirqd线程会被唤醒,然后按照下面的流程调用TIMER_SOFTIRQ数组softirq_vec中注册的actionrun_timer_softirq

ksoftirqd--->do_softirq--->__do_softirq--->softirq_vec[TIMER_SOFTIRQ].action

启示:
1TIMER_SOFTIRQ的最小粒度为一个tick的时间间隔
2之所以成为softirq,是因为这些中断是由硬件中断来间接触发的
3__do_softirq会优先处理__softirq_pending中的BIT_0,HI_SOFTIRQ,然后依次检查__softirq_pendingBIT_1 BIT_7是否被置位,如果置位,执行对应的action



/*
* Called by the local, per-CPU timer interrupt on SMP.
*/
void run_local_timers(void)
{
hrtimer_run_queues();
raise_softirq(TIMER_SOFTIRQ);
softlockup_tick();
}

Q2:
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */

NR_SOFTIRQS
};

struct softirq_action
{
void    (*action)(struct softirq_action *);
};

static struct softirq_action softirq_vec[NR_SOFTIRQS] ;
内核中定义了用于不同场合中的各种软中断,例如网络收发、tasklet、高精度定时器和软中断等等应用场合;没一种应用场合都对应一个总的处理函数,这个函数就是struct softirq_action中的action成员。这写action都是在提前需要提前注册好的。我们以TIMER_SOFTIRQ为例:

Q3
start_kernel--> init_timers
void __init init_timers(void)
{
int err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,
(void *)(long)smp_processor_id());

init_timer_stats();

BUG_ON(err == NOTIFY_BAD);
register_cpu_notifier(&timers_nb);
open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
}

void open_softirq(int nr, void (*action)(struct softirq_action *))
{
softirq_vec[nr].action = action;
}
也就是说,当TIMER_SOFTIRQ这个softirq发生时,run_timer_softirq函数会被调用!
问题是谁回去触发这个TIMER_SOFTIRQ信号呢?


总结:
ksoftirqd 是内核启动时创建的一个内核线程;
该线程会一次检查。。。。。。。。。。。
。。。。。。。。。。。。。。。。。。。
。。。。。。当时钟中断发生时。。。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值