Browse Source

Merge branch 'core-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip

* 'core-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  futex: Fix handling of bad requeue syscall pairing
  futex: Fix compat_futex to be same as futex for REQUEUE_PI
  locking, sched: Give waitqueue spinlocks their own lockdep classes
  futex: Update futex_q lock_ptr on requeue proxy lock
Linus Torvalds 15 years ago
parent
commit
919aa96a9c
4 changed files with 37 additions and 11 deletions
  1. 8 1
      include/linux/wait.h
  2. 22 6
      kernel/futex.c
  3. 4 2
      kernel/futex_compat.c
  4. 3 2
      kernel/wait.c

+ 8 - 1
include/linux/wait.h

@@ -77,7 +77,14 @@ struct task_struct;
 #define __WAIT_BIT_KEY_INITIALIZER(word, bit)				\
 #define __WAIT_BIT_KEY_INITIALIZER(word, bit)				\
 	{ .flags = word, .bit_nr = bit, }
 	{ .flags = word, .bit_nr = bit, }
 
 
-extern void init_waitqueue_head(wait_queue_head_t *q);
+extern void __init_waitqueue_head(wait_queue_head_t *q, struct lock_class_key *);
+
+#define init_waitqueue_head(q)				\
+	do {						\
+		static struct lock_class_key __key;	\
+							\
+		__init_waitqueue_head((q), &__key);	\
+	} while (0)
 
 
 #ifdef CONFIG_LOCKDEP
 #ifdef CONFIG_LOCKDEP
 # define __WAIT_QUEUE_HEAD_INIT_ONSTACK(name) \
 # define __WAIT_QUEUE_HEAD_INIT_ONSTACK(name) \

+ 22 - 6
kernel/futex.c

@@ -1010,15 +1010,19 @@ void requeue_futex(struct futex_q *q, struct futex_hash_bucket *hb1,
  * requeue_pi_wake_futex() - Wake a task that acquired the lock during requeue
  * requeue_pi_wake_futex() - Wake a task that acquired the lock during requeue
  * q:	the futex_q
  * q:	the futex_q
  * key:	the key of the requeue target futex
  * key:	the key of the requeue target futex
+ * hb:  the hash_bucket of the requeue target futex
  *
  *
  * During futex_requeue, with requeue_pi=1, it is possible to acquire the
  * During futex_requeue, with requeue_pi=1, it is possible to acquire the
  * target futex if it is uncontended or via a lock steal.  Set the futex_q key
  * target futex if it is uncontended or via a lock steal.  Set the futex_q key
  * to the requeue target futex so the waiter can detect the wakeup on the right
  * to the requeue target futex so the waiter can detect the wakeup on the right
  * futex, but remove it from the hb and NULL the rt_waiter so it can detect
  * futex, but remove it from the hb and NULL the rt_waiter so it can detect
- * atomic lock acquisition.  Must be called with the q->lock_ptr held.
+ * atomic lock acquisition.  Set the q->lock_ptr to the requeue target hb->lock
+ * to protect access to the pi_state to fixup the owner later.  Must be called
+ * with both q->lock_ptr and hb->lock held.
  */
  */
 static inline
 static inline
-void requeue_pi_wake_futex(struct futex_q *q, union futex_key *key)
+void requeue_pi_wake_futex(struct futex_q *q, union futex_key *key,
+			   struct futex_hash_bucket *hb)
 {
 {
 	drop_futex_key_refs(&q->key);
 	drop_futex_key_refs(&q->key);
 	get_futex_key_refs(key);
 	get_futex_key_refs(key);
@@ -1030,6 +1034,11 @@ void requeue_pi_wake_futex(struct futex_q *q, union futex_key *key)
 	WARN_ON(!q->rt_waiter);
 	WARN_ON(!q->rt_waiter);
 	q->rt_waiter = NULL;
 	q->rt_waiter = NULL;
 
 
+	q->lock_ptr = &hb->lock;
+#ifdef CONFIG_DEBUG_PI_LIST
+	q->list.plist.lock = &hb->lock;
+#endif
+
 	wake_up_state(q->task, TASK_NORMAL);
 	wake_up_state(q->task, TASK_NORMAL);
 }
 }
 
 
@@ -1088,7 +1097,7 @@ static int futex_proxy_trylock_atomic(u32 __user *pifutex,
 	ret = futex_lock_pi_atomic(pifutex, hb2, key2, ps, top_waiter->task,
 	ret = futex_lock_pi_atomic(pifutex, hb2, key2, ps, top_waiter->task,
 				   set_waiters);
 				   set_waiters);
 	if (ret == 1)
 	if (ret == 1)
-		requeue_pi_wake_futex(top_waiter, key2);
+		requeue_pi_wake_futex(top_waiter, key2, hb2);
 
 
 	return ret;
 	return ret;
 }
 }
@@ -1247,8 +1256,15 @@ retry_private:
 		if (!match_futex(&this->key, &key1))
 		if (!match_futex(&this->key, &key1))
 			continue;
 			continue;
 
 
-		WARN_ON(!requeue_pi && this->rt_waiter);
-		WARN_ON(requeue_pi && !this->rt_waiter);
+		/*
+		 * FUTEX_WAIT_REQEUE_PI and FUTEX_CMP_REQUEUE_PI should always
+		 * be paired with each other and no other futex ops.
+		 */
+		if ((requeue_pi && !this->rt_waiter) ||
+		    (!requeue_pi && this->rt_waiter)) {
+			ret = -EINVAL;
+			break;
+		}
 
 
 		/*
 		/*
 		 * Wake nr_wake waiters.  For requeue_pi, if we acquired the
 		 * Wake nr_wake waiters.  For requeue_pi, if we acquired the
@@ -1273,7 +1289,7 @@ retry_private:
 							this->task, 1);
 							this->task, 1);
 			if (ret == 1) {
 			if (ret == 1) {
 				/* We got the lock. */
 				/* We got the lock. */
-				requeue_pi_wake_futex(this, &key2);
+				requeue_pi_wake_futex(this, &key2, hb2);
 				continue;
 				continue;
 			} else if (ret) {
 			} else if (ret) {
 				/* -EDEADLK */
 				/* -EDEADLK */

+ 4 - 2
kernel/futex_compat.c

@@ -180,7 +180,8 @@ asmlinkage long compat_sys_futex(u32 __user *uaddr, int op, u32 val,
 	int cmd = op & FUTEX_CMD_MASK;
 	int cmd = op & FUTEX_CMD_MASK;
 
 
 	if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||
 	if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||
-		      cmd == FUTEX_WAIT_BITSET)) {
+		      cmd == FUTEX_WAIT_BITSET ||
+		      cmd == FUTEX_WAIT_REQUEUE_PI)) {
 		if (get_compat_timespec(&ts, utime))
 		if (get_compat_timespec(&ts, utime))
 			return -EFAULT;
 			return -EFAULT;
 		if (!timespec_valid(&ts))
 		if (!timespec_valid(&ts))
@@ -191,7 +192,8 @@ asmlinkage long compat_sys_futex(u32 __user *uaddr, int op, u32 val,
 			t = ktime_add_safe(ktime_get(), t);
 			t = ktime_add_safe(ktime_get(), t);
 		tp = &t;
 		tp = &t;
 	}
 	}
-	if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE)
+	if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||
+	    cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP)
 		val2 = (int) (unsigned long) utime;
 		val2 = (int) (unsigned long) utime;
 
 
 	return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);
 	return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);

+ 3 - 2
kernel/wait.c

@@ -10,13 +10,14 @@
 #include <linux/wait.h>
 #include <linux/wait.h>
 #include <linux/hash.h>
 #include <linux/hash.h>
 
 
-void init_waitqueue_head(wait_queue_head_t *q)
+void __init_waitqueue_head(wait_queue_head_t *q, struct lock_class_key *key)
 {
 {
 	spin_lock_init(&q->lock);
 	spin_lock_init(&q->lock);
+	lockdep_set_class(&q->lock, key);
 	INIT_LIST_HEAD(&q->task_list);
 	INIT_LIST_HEAD(&q->task_list);
 }
 }
 
 
-EXPORT_SYMBOL(init_waitqueue_head);
+EXPORT_SYMBOL(__init_waitqueue_head);
 
 
 void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
 void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
 {
 {