1
votes

Linux Kernel Development by Robert Love states:

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

At http://landley.net/kdocs/htmldocs/kernel-locking.html, its mentioned that

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.

I don't understand why it can't be used in such cases when it doesn't go to sleep?

3
At lwn.net/Articles/167034, I found the following statement "This restriction appears to have more to do with keeping mutexes from ever being used as completions than a fundamental restriction" but I can't really understand what it means(what's completions)?user3740387
As I understand, "completions" means using mutex as a completion signal. For instance, A is trying to hold a mutex which is already locked by B. It makes A to wait. Then, by releasing the lock by B, A can proceed. In the article you mentioned, it says "A mutex can only be unlocked by the owner - which must be a task." That being said, mutex can be locked/unlocked by a single task, where the usecase should be a critical section.jaeyong

3 Answers

4
votes

Imagine if you had a platform whose native, low-level primitive mutexes do not have a "try lock" operation. In that case, to implement a high-level mutex that does, you'd have to use a condition variable and a boolean "is locked" protected by the low-level mutex to indicate the high-level mutex was locked.

So a waitable mutex could be implemented using a low-level primitive mutex (that does not support a "trylock" operation) to implement a high-level mutex (that does). The "high-level mutex" can just be a boolean that's protected by the low-level mutex.

With that design, mutex_lock would be implemented as follows:

  1. Acquire low-level mutex (this is a real lock operation on the primitive, implementation mutex).
  2. If high-level mutex is held, do a condition wait for the high-level mutex.
  3. Acquire high-level mutex (just locked = true;).
  4. Release low-level mutex.

And mutex_unlock would be implemented as follows:

  1. Acquire low-level mutex.
  2. Release high-level mutex (just locked = false;)
  3. Signal the condition variable.
  4. Release the low-level mutex.

In that case, mutex_trylock would be implemented as follows:

  1. Acquire low-level mutex.
  2. Check if high-level mutex is held.
  3. If so, release low-level mutex and return failure.
  4. Take high-level mutex.
  5. Release low-level mutex.
  6. Return success.

Imagine if we're interrupted after step 2 but before step 3.

0
votes

may be it is because in mutex_trylock interrupts are not disabled. if we have a scenario where in interrupt context a mutex is locked using mutex_trylock and another interrupt comes that tries to acquire the same mutex. It can result in deadlock kind of situation.

0
votes

The reason is that we require the mutex semantics to allow for full Priority-Inheritance, even if the default implementation does not do that (PREEMPT_RT switches mutex to mutex_rt and then it does get to have PI).

Suppose your interrupt does mutex_trylock() successfully, then the interrupt, or rather the task that was interrupted, becomes the lock owner. Any contending mutex_lock() would try and PI-boost that owner. One might argue that due to interrupts being non-preemptible, the context is effectively a prio-ceiling and all is well. However, if the interrupt hit the idle task, we'll end up trying to boost the idle task, and that is a big no-no.

Also read the thread here:

https://lkml.kernel.org/r/[email protected]