为什么在中断中使用 mutex_trylock 不安全?

Why isn't mutex_trylock safe for use in interrupts?

Linux Robert Love 的内核开发声明:

A mutex cannot be acquired by an interrupt handler or bottom half, even with mutex_trylock()

http://landley.net/kdocs/htmldocs/kernel-locking.html,它提到

mutex_trylock() does not suspend your task but returns non-zero if it could lock the mutex on the first try or 0 if not. This function cannot be safely used in hardware or software interrupt contexts despite not sleeping.

不明白为什么在不休眠的情况下不能用?

想象一下,如果您有一个平台,其原生低级原始互斥锁没有 "try lock" 操作。在这种情况下,要实现这样的高级互斥锁,您必须使用条件变量和受低级互斥锁保护的布尔值 "is locked" 来指示高级互斥锁已锁定。

因此可以使用低级原始互斥锁(不支持 "trylock" 操作)实现可等待互斥锁来实现高级互斥锁(支持)。 "high-level mutex" 可以只是受低级互斥锁保护的布尔值。

根据该设计,mutex_lock 将实现如下:

  1. 获取低级互斥锁(这是对原语、实现互斥锁的真正锁操作)。
  2. 如果持有高级互斥锁,则执行条件等待高级互斥锁。
  3. 获取高级互斥锁(仅locked = true;)。
  4. 释放低级互斥量。

mutex_unlock的实现方式如下:

  1. 获取低级互斥量。
  2. 释放高级互斥量(仅locked = false;
  3. 向条件变量发出信号。
  4. 释放低级互斥。

在这种情况下,mutex_trylock 将按如下方式实施:

  1. 获取低级互斥量。
  2. 检查是否持有高级互斥量。
  3. 如果是,则释放低级互斥锁并return失败。
  4. 获取高级互斥。
  5. 释放低级互斥量。
  6. Return成功。

想象一下,如果我们在第 2 步之后但在第 3 步之前被打断。

可能是因为在 mutex_trylock 中没有禁用中断。如果我们有这样一种情况,在中断上下文中使用 mutex_trylock 锁定了一个互斥锁,并且另一个中断试图获取相同的互斥锁。它可能会导致死锁的情况。

原因是我们要求互斥语义允许完整 Priority-Inheritance,即使默认实现不这样做(PREEMPT_RT 将互斥切换为 mutex_rt 然后它确实有 PI)。

假设您的中断 mutex_trylock() 成功,那么中断,或者更确切地说,被中断的任务,成为锁的所有者。任何竞争者 mutex_lock() 都会尝试 PI-boost 该所有者。有人可能会争辩说,由于中断是 non-preemptible,上下文实际上是 prio-ceiling,一切都很好。 但是,如果中断命中空闲任务,我们最终会尝试提升空闲任务,这是一个很大的no-no。

另请阅读此处的主题:

https://lkml.kernel.org/r/20191218135047.GS2844@hirez.programming.kicks-ass.net