gpt4 book ai didi

linux - printk 中断禁用和锁定

转载 作者:行者123 更新时间:2023-12-04 02:41:03 30 4
gpt4 key购买 nike

我有一个关于在 3.10 内核中实现 printk() 的问题。我看到了calls local_irq_save一开始。然后我看到了 calls raw_spin_lock(&logbuf_lock) .如果在此之前已经禁用了中断,那么 logbuf_lock 的目的是什么?是不是因为即使在当前 CPU 上禁用了中断,其他 CPU 仍然可以调用 printk,因此需要停止写入日志缓冲区?

基本上我有三个问题:

  • 我看到printk获取logbuf_lock并写入日志缓冲区,然后尝试获取控制台信号量并释放logbuf_lock。然后在里面 console_unlock在循环中,它获取 logbuf_lock 并禁用中断,然后释放 logbuf_lock 并调用控制台驱动程序,然后恢复中断。这个锁定/禁用中断序列的目的是什么?
  • 我在 printk() 中看到关于日志缓冲区可能再次被填满的注释,因此缓冲区可能不得不再次刷新到控制台。考虑到我在上面 #1 中询问的所有锁定,这种情况将如何发生?
  • 如果在任何给定时刻只有 1 个 CPU 上的代码调用 printk(),SMP 系统中的其他内核是否仍然可以处理中断?我还试图了解 printk 对中断延迟的影响。

  • 谢谢。

    一些后续行动:

    你能澄清一下吗:

    local_irq_save()protects against interrupts on the local CPU (and also avoids being rescheduled on another CPU while using a cpu variable to access per-CPU data)



    你的意思是打电话 local_irq_save()仅当它访问每个 CPU 数据时才会阻止当前线程在另一个 CPU 上重新调度,或者它会阻止当前线程在另一个 CPU 周期上重新调度?在这里使用 printk() 的情况下 local_irq_save() 的目的是什么?我记得在 LMKL 上读过一个线程,它说禁用中断是为了确保日志缓冲区中的条目顺序反射(reflect)了 printk() 调用发生的实际顺序。

    最佳答案

    What's the purpose of logbuf_lock if interrupts have been already disabled prior to this? Is it because even though interrupts are disabled on the current CPU other CPUs can still call printk so they need to be stopped from writing to the log buffer?



    是的。 local_irq_save()防止本地 CPU 上的中断(并且还避免在使用 cpu 变量访问每个 CPU 数据时在另一个 CPU 上重新调度),而自旋锁防止其他 CPU。

    If code on only 1 CPU were to be calling printk() at any given moment, could interrupts still be handled on other cores in an SMP system?



    是的。

    I see printk acquires logbuf_lock and writes to the log buffer and then tries to grab the console semaphore and releases logbuf_lock. Then inside console_unlock inside a loop it acquires logbuf_lock and disables interrupts, then releases logbuf_lock and calls the console drivers, and then restores interrupts. What's the purpose of this locking/disabling interrupt sequence?



    有两件事需要保护:日志缓冲区和控制台驱动程序。 logbuf_lock保护日志缓冲区,同时 console_sem保护对控制台驱动程序列表和实际控制台本身的访问。

    打印内核消息是一个两步过程。首先将消息放入日志缓冲区,然后将日志缓冲区发送到 console_unlock() 中的控制台。 .这两个步骤不需要在对 printk() 的同一次调用中发生。 .此外,在注册/启动/恢复/...控制台时可能会发生将日志缓冲区刷新到控制台的情况。

    将消息放入日志缓冲区后, printk()试图获得 console_sem .它甚至可以在中断上下文中执行此操作,因为 down_trylock()不 sleep 。如果它获得了信号量,它可以继续将日志缓冲区内容发送到控制台。如果它没有获取信号量,则控制台信号量的持有者有责任将日志缓冲区内容发送到控制台,在 console_unlock() .
    虽然 console_unlock()正在发送到控制台,可能有其他 CPU 调用 printk() .所以 console_unlock()循环直到没有更多内容可以发送到控制台。在每个循环中,它都会获取指向日志缓冲区部分的指针以发送到控制台,位于 logbuf_lock 下。 ,然后,不再下 logbuf_lock , 将输出发送到控制台。在向控制台发送内容时,由于 logbuf_lock没有被占用,其他 CPU 可以继续向日志缓冲区添加内容。

    I see comments in printk() about the log buffer possibly being filled up again so the buffer may have to get flushed to the console again. How would this situation occur given all the locking I asked about in #1 above?



    释放 logbuf_lock 后缓冲区可能已被填满但之前 up() ing console_sem .和 logbuf_lock之前发布 up() ing console_sem因为 up()可能导致唤醒,这需要获取运行队列锁,这可能会产生针对 printk() 的优先级反转问题。使用运行队列锁调用 ( commit 0b5e1c5255 )。

    有提议的补丁可以更改此锁定方案: Jan Kara [PATCH 0/8 v4] printk: Cleanups and softlockup avoidance ,其中包括尝试避免 CPU 在 console_unlock() 上无限循环。 (它发生在通过慢速串行控制台记录大量启动事件的大型系统上),将这些工作移交给其他 CPU;并尽量减少在 printk() 上禁用中断的时间.

    Do you mean calling local_irq_save() will prevent the current thread from being rescheduled on another CPU only if it accesses per-CPU data or it prevents the current thread from being rescheduled on another CPU period?



    后者。 local_irq_save()阻止在本地 CPU 上处理中断,这可能最终会调用 schedule()从 ISR 返回时。 schedule()也可以从其他地方调用,但由于 printk()应该能够在中断上下文中使用,它调用的任何内容都不应该最终调用 schedule() (例如:出于这个原因,使用 down_trylock() 而不是 down())。

    What is the purpose of local_irq_save() in the case of printk() here?



    扬·卡拉的 [PATCH 3/8] printk: Enable interrupts before calling console_trylock_for_printk()尽量减少中断被禁用的时间。在该补丁之前,至少出于以下原因禁用中断:
  • 保护logbuf_lock - 保护数据免受中断。这种情况通常通过自旋锁的 IRQ 变体来解决(例如: raw_spin_lock_irqsave() ),但这里有更多的东西可以在禁用中断的情况下运行。
  • 防止当前CPU突然变化。 can_use_console() ,来自 console_trylock_for_printk() ,问:这个时候我们真的可以在这个cpu上使用console吗?,注意:console驱动可能会假设per-cpu的资源已经分配好了。转移到另一个 CPU 可能会令人困惑。
  • 虽然 console_semdown_trylock()所以如果中断也尝试 printk() 应该不是问题,在持有 console_sem() 时被抢占会阻止其他人打印到控制台。

  • 因此,上述补丁不再禁用后两种情况的中断,而是将它们包装在 preempt_disable() 中。/ preempt_enable() ,允许中断,但不允许抢占。

    I remember reading a thread on LMKL that said the disabling of interrupts was to ensure the order of entries in the log buffer reflected the actual order in which the printk() calls occurred.



    您可以分享对该线程的引用吗?您可能会注意到在 3.10 中有一个 cont缓冲区,它保存了最后一个换行符的所有字符。当换行符到达或不同的任务时,它会被刷新到“真实的”日志缓冲区 printk() s。

    在该线程上,除了以下摘录外,我没有发现有关日志条目排序的任何有效问题:

    Well, I believe someone got DDetetccctted ed 113223 HHzz CPUCPU



    AFAIK,这完全是假的。日志缓冲区的排序由 logbuf_lock 保证,
    这是在写入 vprintk_emit() 中的日志缓冲区时已禁用中断的情况下进行的。 ,并与锁定变体一起使用,该变体在从 raw_spin_lock_irqsave() 上读取时禁用中断( console_unlock() ) .因此,访问日志缓冲区是安全的,不会受到其他 CPU 或中断的干扰。

    仍然存在将日志行拆分为多个 printk() 的情况。在较新的内核中,这种情况包含在 cont 中。缓冲区,它保存部分行,并在另一个 CPU/中断干扰时刷新它们,因此日志行可能被分成几行并且它们之间有不相关的日志行,但没有日志行应该有混合输出。

    另一个可能的损坏原因是,由于日志缓冲区是一个环形缓冲区,理论上它可能会溢出,这意味着覆盖以前的消息。

    该线程中的一个有效问题是日志缓冲区的及时输出。这是通过尝试拨打 console_unlock() 来实现的。 (调用控制台驱动程序)在每个 vprintk_emit() 中称呼。如果无法获取控制台信号量,则消息已经在日志缓冲区中,当前信号量所有者会将其输出到控制台。

    该线程中提到的一件有趣的事情是在 commit a0f1ccfd8d: "lockdep: do not recurse in printk" 之前, ( printk.c beforeafter ) 在调用 release_console_sem() 之前重新启用中断(这是 console_unlock() 的前一个化身)。显然,当 lockdep(一个锁验证器,能够检测可能的死锁和其他锁定问题,以及 打印 诊断)被启用时,尝试从 printk() 打印时可能会导致锁定。 .因此,调用 spin_{,un}lock_irq{save,restore}()分为禁用/启用中断和获取/释放锁, lockdep_on/off()在两者之间添加了调用,并且扩展了 lockdep 和中断的禁用以涵盖整个功能。

    回到:

    I see printk acquires logbuf_lock and writes to the log buffer and then tries to grab the console semaphore and releases logbuf_lock. Then inside console_unlock inside a loop it acquires logbuf_lock and disables interrupts, then releases logbuf_lock and calls the console drivers, and then restores interrupts. What's the purpose of this locking/disabling interrupt sequence?


    console_unlock()不仅来自 vprintk_emit() ,它也会在注册新控制台、恢复控制台、将 CPU 与待处理的输出热插拔到控制台时调用,......这些地方通常启用中断。所以, console_unlock()必须考虑到这一点。

    您似乎已经注意到,在使用 logbuf_lock 时,while 中断被禁用。在 console_unlock() (它调用 raw_spin_lock_irqsave() ),它们在释放锁( raw_spin_unlock() )时不会(可能)重新启用,它们只有在 local_irq_console() 之后才可能重新启用( call_console_drivers() ) .我看到打电话的唯一原因 call_console_drivers()禁用中断是为了避免 CPU 在我们的控制下发生变化(控制台驱动程序可以访问每个 CPU 的变量)。

    一个有趣的数据点是 -rt(实时)补丁集在调用 console_unlock() 之前重新启用中断。 ,以及之前 call_console_drivers() (在 -rt 中受 migrate_disable()/migrate_enable() 保护,不允许 CPU 迁移)在 console_unlock() 中.这样做是为了最大限度地减少 printk() 期间的中断延迟。 . PREEMPT_RT_FULL支持低中断延迟。你可以在 printk.c in the linux-stable-rt tree at git.kernel.org 看到它,相关补丁为 printk-rt-aware .

    关于linux - printk 中断禁用和锁定,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25873754/

    30 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com