linux-kernel – 延迟工作的CPU处理

嘿所以我在
kernel source code中看这个函数.我试图弄清楚Linux如何处理无法在本地CPU上安排任务的情况.

/**
* queue_delayed_work - queue work on a workqueue after delay
* @wq: workqueue to use
* @dwork: delayable work to queue
* @delay: number of jiffies to wait before queueing
*
* Equivalent to queue_delayed_work_on() but tries to use the local CPU.
*/
static inline bool queue_delayed_work(struct workqueue_struct *wq,
                                      struct delayed_work *dwork,
                                      unsigned long delay)
{
        return queue_delayed_work_on(WORK_CPU_UNBOUND, wq, dwork, delay);
}

这是queue_delayed_work_on

bool queue_delayed_work_on(int cpu, struct workqueue_struct *wq,
                           struct delayed_work *dwork, unsigned long delay)
{
        struct work_struct *work = &dwork->work;
        bool ret = false;
        unsigned long flags;

        /* read the comment in __queue_work() */
        local_irq_save(flags);

        if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) {
                __queue_delayed_work(cpu, wq, dwork, delay);
                ret = true;
        }

        local_irq_restore(flags);
        return ret;
}

假如你有4个CPU并且它无法在CPU 1上安排任务,选择哪一个以及源代码在哪里处理?我一直在找一段时间而找不到它.即使你不理解它是如何工作的,我也非常感谢魔法发生的链接.

最佳答案 如您所见,queue_delayed_work将cpu参数设置为WORK_CPU_UNBOUND.该值定义为大于内核支持的实际CPU数.该值传递给__queue_delayed_work,如果延迟非零,则使用定时器(使用add_timer函数在指定时间后触发回调函数delayed_work_timer_fn(此回调函数在工作队列初始化时定义).所有这些回调函数都是调用__queue_work,仍然将WORK_CPU_UNBOUND作为cpu参数传递.所以整个“魔法”都在那里发生.

此函数将检查cpu参数是否设置为WORK_CPU_UNBOUND并选择cpu作为当前处理器:

if (req_cpu == WORK_CPU_UNBOUND)
    cpu = raw_smp_processor_id()

因此,工作将在处理之前设置的定时器中断的处理器上执行.现在我没有研究定时器代码,而是从LDD3书中研究IIRC,定时器中断将由他们注册的CPU处理(除非此时CPU将被禁用,当然,在这种情况下,定时器IRQ将被移动到其他CPU),但那本书已经老了一些,这可能不再是真的了.

内核代码中还有另一个提示应该证明我写的内容 – 请参阅queue_work函数的注释:“我们将工作排队到提交它的CPU,但如果CPU死了,它可以由另一个CPU处理”.此函数还使用WORK_CPU_UNBOUND作为cpu参数.

计时器迁移细节

如前所述,如果某个处理器出现故障,它将无法再处理IRQ,因此无法处理已注册的定时器.因此,当CPU脱机时,内核会将所有挂起的计时器迁移到其他CPU.此任务由migrate_timers()函数完成,该函数由timer_cpu_notify运行,而timer_cpu_notify又是一个注册为cpu_notifier的回调.

当cpu状态更改为CPU_DEAD或CPU_DEAD_FROZEN时,将运行migrate_timers.通过调用以下方法在_cpu_down函数内设置此状态:

cpu_notify_nofail(CPU_DEAD | mod, hcpu);

它在__cpu_die(cpu)之后调用,它确保我们禁用的CPU不再工作,因此我们可以确保此代码在其他CPU上运行. migrate_timers会将所有计时器重新分配给正在运行的CPU.

那么决定哪个CPU应该接管定时器的决定在哪里?可以说它是由调度程序完成的:

>如果在与要禁用的CPU不同的CPU上调用cpu_down,则这将是将要接管的CPU.
>如果在要禁用的CPU上调用cpu_down,它将在__cpu_die中自行调度,然后其他代码将在其他CPU上重新调度.

点赞