Skip to content

Commit 0e11d25

Browse files
committed
Merge branch 'locking-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull locking fixes from Ingo Molnar: "Misc fixes: pvqspinlocks: - an instrumentation fix futexes: - preempt-count vs pagefault_disable decouple corner case fix - futex requeue plist race window fix - futex UNLOCK_PI transaction fix for a corner case" * 'locking-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: asm-generic/futex: Re-enable preemption in futex_atomic_cmpxchg_inatomic() futex: Acknowledge a new waiter in counter before plist futex: Handle unlock_pi race gracefully locking/pvqspinlock: Fix division by zero in qstat_read()
2 parents 16ecb41 + fba7cd6 commit 0e11d25

File tree

3 files changed

+34
-9
lines changed

3 files changed

+34
-9
lines changed

include/asm-generic/futex.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,15 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
108108
u32 val;
109109

110110
preempt_disable();
111-
if (unlikely(get_user(val, uaddr) != 0))
111+
if (unlikely(get_user(val, uaddr) != 0)) {
112+
preempt_enable();
112113
return -EFAULT;
114+
}
113115

114-
if (val == oldval && unlikely(put_user(newval, uaddr) != 0))
116+
if (val == oldval && unlikely(put_user(newval, uaddr) != 0)) {
117+
preempt_enable();
115118
return -EFAULT;
119+
}
116120

117121
*uval = val;
118122
preempt_enable();

kernel/futex.c

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,10 +1295,20 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this,
12951295
if (unlikely(should_fail_futex(true)))
12961296
ret = -EFAULT;
12971297

1298-
if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval))
1298+
if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) {
12991299
ret = -EFAULT;
1300-
else if (curval != uval)
1301-
ret = -EINVAL;
1300+
} else if (curval != uval) {
1301+
/*
1302+
* If a unconditional UNLOCK_PI operation (user space did not
1303+
* try the TID->0 transition) raced with a waiter setting the
1304+
* FUTEX_WAITERS flag between get_user() and locking the hash
1305+
* bucket lock, retry the operation.
1306+
*/
1307+
if ((FUTEX_TID_MASK & curval) == uval)
1308+
ret = -EAGAIN;
1309+
else
1310+
ret = -EINVAL;
1311+
}
13021312
if (ret) {
13031313
raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
13041314
return ret;
@@ -1525,8 +1535,8 @@ void requeue_futex(struct futex_q *q, struct futex_hash_bucket *hb1,
15251535
if (likely(&hb1->chain != &hb2->chain)) {
15261536
plist_del(&q->list, &hb1->chain);
15271537
hb_waiters_dec(hb1);
1528-
plist_add(&q->list, &hb2->chain);
15291538
hb_waiters_inc(hb2);
1539+
plist_add(&q->list, &hb2->chain);
15301540
q->lock_ptr = &hb2->lock;
15311541
}
15321542
get_futex_key_refs(key2);
@@ -2622,6 +2632,15 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags)
26222632
*/
26232633
if (ret == -EFAULT)
26242634
goto pi_faulted;
2635+
/*
2636+
* A unconditional UNLOCK_PI op raced against a waiter
2637+
* setting the FUTEX_WAITERS bit. Try again.
2638+
*/
2639+
if (ret == -EAGAIN) {
2640+
spin_unlock(&hb->lock);
2641+
put_futex_key(&key);
2642+
goto retry;
2643+
}
26252644
/*
26262645
* wake_futex_pi has detected invalid state. Tell user
26272646
* space.

kernel/locking/qspinlock_stat.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,12 @@ static ssize_t qstat_read(struct file *file, char __user *user_buf,
136136
}
137137

138138
if (counter == qstat_pv_hash_hops) {
139-
u64 frac;
139+
u64 frac = 0;
140140

141-
frac = 100ULL * do_div(stat, kicks);
142-
frac = DIV_ROUND_CLOSEST_ULL(frac, kicks);
141+
if (kicks) {
142+
frac = 100ULL * do_div(stat, kicks);
143+
frac = DIV_ROUND_CLOSEST_ULL(frac, kicks);
144+
}
143145

144146
/*
145147
* Return a X.XX decimal number

0 commit comments

Comments
 (0)