diff options
| -rw-r--r-- | kernel/futex.c | 32 |
1 files changed, 23 insertions, 9 deletions
diff --git a/kernel/futex.c b/kernel/futex.c index 6801b3751a95..5f589279e462 100644 --- a/kernel/futex.c +++ b/kernel/futex.c | |||
| @@ -70,7 +70,10 @@ | |||
| 70 | #include "locking/rtmutex_common.h" | 70 | #include "locking/rtmutex_common.h" |
| 71 | 71 | ||
| 72 | /* | 72 | /* |
| 73 | * Basic futex operation and ordering guarantees: | 73 | * READ this before attempting to hack on futexes! |
| 74 | * | ||
| 75 | * Basic futex operation and ordering guarantees | ||
| 76 | * ============================================= | ||
| 74 | * | 77 | * |
| 75 | * The waiter reads the futex value in user space and calls | 78 | * The waiter reads the futex value in user space and calls |
| 76 | * futex_wait(). This function computes the hash bucket and acquires | 79 | * futex_wait(). This function computes the hash bucket and acquires |
| @@ -119,7 +122,7 @@ | |||
| 119 | * sys_futex(WAIT, futex, val); | 122 | * sys_futex(WAIT, futex, val); |
| 120 | * futex_wait(futex, val); | 123 | * futex_wait(futex, val); |
| 121 | * | 124 | * |
| 122 | * waiters++; | 125 | * waiters++; (a) |
| 123 | * mb(); (A) <-- paired with -. | 126 | * mb(); (A) <-- paired with -. |
| 124 | * | | 127 | * | |
| 125 | * lock(hash_bucket(futex)); | | 128 | * lock(hash_bucket(futex)); | |
| @@ -135,14 +138,14 @@ | |||
| 135 | * unlock(hash_bucket(futex)); | 138 | * unlock(hash_bucket(futex)); |
| 136 | * schedule(); if (waiters) | 139 | * schedule(); if (waiters) |
| 137 | * lock(hash_bucket(futex)); | 140 | * lock(hash_bucket(futex)); |
| 138 | * wake_waiters(futex); | 141 | * else wake_waiters(futex); |
| 139 | * unlock(hash_bucket(futex)); | 142 | * waiters--; (b) unlock(hash_bucket(futex)); |
| 140 | * | 143 | * |
| 141 | * Where (A) orders the waiters increment and the futex value read -- this | 144 | * Where (A) orders the waiters increment and the futex value read through |
| 142 | * is guaranteed by the head counter in the hb spinlock; and where (B) | 145 | * atomic operations (see hb_waiters_inc) and where (B) orders the write |
| 143 | * orders the write to futex and the waiters read -- this is done by the | 146 | * to futex and the waiters read -- this is done by the barriers in |
| 144 | * barriers in get_futex_key_refs(), through either ihold or atomic_inc, | 147 | * get_futex_key_refs(), through either ihold or atomic_inc, depending on the |
| 145 | * depending on the futex type. | 148 | * futex type. |
| 146 | * | 149 | * |
| 147 | * This yields the following case (where X:=waiters, Y:=futex): | 150 | * This yields the following case (where X:=waiters, Y:=futex): |
| 148 | * | 151 | * |
| @@ -155,6 +158,17 @@ | |||
| 155 | * Which guarantees that x==0 && y==0 is impossible; which translates back into | 158 | * Which guarantees that x==0 && y==0 is impossible; which translates back into |
| 156 | * the guarantee that we cannot both miss the futex variable change and the | 159 | * the guarantee that we cannot both miss the futex variable change and the |
| 157 | * enqueue. | 160 | * enqueue. |
| 161 | * | ||
| 162 | * Note that a new waiter is accounted for in (a) even when it is possible that | ||
| 163 | * the wait call can return error, in which case we backtrack from it in (b). | ||
| 164 | * Refer to the comment in queue_lock(). | ||
| 165 | * | ||
| 166 | * Similarly, in order to account for waiters being requeued on another | ||
| 167 | * address we always increment the waiters for the destination bucket before | ||
| 168 | * acquiring the lock. It then decrements them again after releasing it - | ||
| 169 | * the code that actually moves the futex(es) between hash buckets (requeue_futex) | ||
| 170 | * will do the additional required waiter count housekeeping. This is done for | ||
| 171 | * double_lock_hb() and double_unlock_hb(), respectively. | ||
| 158 | */ | 172 | */ |
| 159 | 173 | ||
| 160 | #ifndef CONFIG_HAVE_FUTEX_CMPXCHG | 174 | #ifndef CONFIG_HAVE_FUTEX_CMPXCHG |
