大家知道,中断处理目前一般会为上半部和下半部,上半部为硬中断处理,下半部一般为软中断、tasklet或workqueue来处理。同时内核中为了提高实时性,推出中断线程化,即下半部用中断线程处理(每个中断一个中断线程)。synchronize_irq()用于等待PENDING状态的中断处理函数结束(中断处理包括硬中断的处理以及中断线程的处理)。
synchronize_irq()函数如下所示:
/**
* synchronize_irq - wait for pending IRQ handlers (on other CPUs)
* @irq: interrupt number to wait for
*
* This function waits for any pending IRQ handlers for this interrupt
* to complete before returning. If you use this function while
* holding a resource the IRQ handler may need you will deadlock.
*
* This function may be called - with care - from IRQ context.
*/
void synchronize_irq(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq); //获取中断描述符
if (desc) {
__synchronize_hardirq(desc); //等待硬中断处理结束
/*
* We made sure that no hardirq handler is
* running. Now verify that no threaded handlers are
* active.
*/
wait_event(desc->wait_for_threads,
!atomic_read(&desc->threads_active)); //等待中断线程处理结束
}
}
EXPORT_SYMBOL(synchronize_irq);
等待硬中断处理
其中 __synchronize_hardirq()函数用于等待硬中断的处理完成,在函数handle_irq_event()中处理硬中断之前设置IRQD_IRQ_INPROGRESS,在处理完成后清IRQD_IRQ_INPROGRESS。 __synchronize_hardirq()检查IRQD_IRQ_INPROGRESS是否清除。
static void __synchronize_hardirq(struct irq_desc *desc)
{
bool inprogress;
do {
unsigned long flags;
/*
* Wait until we're out of the critical section. This might
* give the wrong answer due to the lack of memory barriers.
*/
while (irqd_irq_inprogress(&desc->irq_data))
cpu_relax();
/* Ok, that indicated we're done: double-check carefully. */
raw_spin_lock_irqsave(&desc->lock, flags);
inprogress = irqd_irq_inprogress(&desc->irq_data);
raw_spin_unlock_irqrestore(&desc->lock, flags);
/* Oops, that failed? */
} while (inprogress);
}
其中
static inline bool irqd_irq_inprogress(struct irq_data *d)
{
return __irqd_to_state(d) & IRQD_IRQ_INPROGRESS;
}
而该flag的设置在 函数handle_irq_event()中
irqreturn_t handle_irq_event(struct irq_desc *desc)
{
irqreturn_t ret;
desc->istate &= ~IRQS_PENDING;
irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS); //设置IRQD_IRQ_INPROGRESS
raw_spin_unlock(&desc->lock);
ret = handle_irq_event_percpu(desc);
raw_spin_lock(&desc->lock);
irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS); //结束hardirq处理,清IRQD_IRQ_INPROGRESS
return ret;
}
等待中断线程处理
等待中断线程的处理完成通过等待队列完成,在申请中断时对每个中断中中断描述符的desc->wait_for_threads进行初始化(在函数__set_irq()中):
在中断线程irq_thread()完成后唤醒等待队列:
/*
* Interrupt handler thread
*/
static int irq_thread(void *data)
{
struct callback_head on_exit_work;
struct irqaction *action = data;
struct irq_desc *desc = irq_to_desc(action->irq);
irqreturn_t (*handler_fn)(struct irq_desc *desc,
struct irqaction *action);
if (force_irqthreads && test_bit(IRQTF_FORCED_THREAD,
&action->thread_flags))
handler_fn = irq_forced_thread_fn;
else
handler_fn = irq_thread_fn; //设置中断线程处理handler
init_task_work(&on_exit_work, irq_thread_dtor);
task_work_add(current, &on_exit_work, false);
irq_thread_check_affinity(desc, action); //设置中断线程CPU亲和性即中断线程跑在哪些CPU上,继承于中断的亲和性
while (!irq_wait_for_interrupt(action)) {
irqreturn_t action_ret;
irq_thread_check_affinity(desc, action);
action_ret = handler_fn(desc, action);
if (action_ret == IRQ_WAKE_THREAD)
irq_wake_secondary(desc, action);
wake_threads_waitq(desc); //唤醒等待队列
}
/*
* This is the regular exit path. __free_irq() is stopping the
* thread via kthread_stop() after calling
* synchronize_hardirq(). So neither IRQTF_RUNTHREAD nor the
* oneshot mask bit can be set.
*/
task_work_cancel(current, irq_thread_dtor);
return 0;
}
static void wake_threads_waitq(struct irq_desc *desc)
{
if (atomic_dec_and_test(&desc->threads_active))
wake_up(&desc->wait_for_threads);
}