[PATCH] radix-tree: Avoid false wakeups when waiting for exceptional entry lock

From: Jan Kara
Date: Thu Mar 03 2016 - 08:06:42 EST


Signed-off-by: Jan Kara <jack@xxxxxxx>
---
lib/radix-tree.c | 42 +++++++++++++++++++++++++++++++++++++-----
1 file changed, 37 insertions(+), 5 deletions(-)

diff --git a/lib/radix-tree.c b/lib/radix-tree.c
index a97231ab66f0..be9763dd9de5 100644
--- a/lib/radix-tree.c
+++ b/lib/radix-tree.c
@@ -1522,6 +1522,28 @@ static inline void * unlock_slot(void **v)
return (void*)(*l &= ~1UL);
}

+struct exceptional_entry_key {
+ struct radix_tree_root *root;
+ unsigned long index;
+};
+
+struct wait_exceptional_entry_queue {
+ wait_queue_t wait;
+ struct exceptional_entry_key key;
+};
+
+static int wake_exceptional_entry_func(wait_queue_t *wait, unsigned mode,
+ int sync, void *keyp)
+{
+ struct exceptional_entry_key *key = keyp;
+ struct wait_exceptional_entry_queue *ewait =
+ container_of(wait, struct wait_exceptional_entry_queue, wait);
+
+ if (key->root != ewait->key.root || key->index != ewait->key.index)
+ return 0;
+ return autoremove_wake_function(wait, mode, sync, NULL);
+}
+
/**
* radix_tree_lookup_lock - lookup and lock exceptional entry if found
* @root: radix tree root
@@ -1540,7 +1562,12 @@ void *radix_tree_lookup_lock(struct radix_tree_root *root, unsigned long index,
wait_queue_head_t *wq, spinlock_t *lock)
{
void *ret, **slot;
- DEFINE_WAIT(wait);
+ struct wait_exceptional_entry_queue wait;
+
+ init_wait(&wait.wait);
+ wait.wait.func = wake_exceptional_entry_func;
+ wait.key.root = root;
+ wait.key.index = index;

for (;;) {
ret = __radix_tree_lookup(root, index, NULL, &slot);
@@ -1548,10 +1575,10 @@ void *radix_tree_lookup_lock(struct radix_tree_root *root, unsigned long index,
return ret;
if (!slot_locked(slot))
return lock_slot(slot);
- prepare_to_wait(wq, &wait, TASK_UNINTERRUPTIBLE);
+ prepare_to_wait_exclusive(wq, &wait.wait, TASK_UNINTERRUPTIBLE);
spin_unlock(lock);
schedule();
- finish_wait(wq, &wait);
+ finish_wait(wq, &wait.wait);
spin_lock(lock);
}
}
@@ -1579,7 +1606,12 @@ void radix_tree_unlock(struct radix_tree_root *root, unsigned long index,
return;
unlock_slot(slot);

- if (waitqueue_active(wq))
- wake_up(wq);
+ if (waitqueue_active(wq)) {
+ struct exceptional_entry_key key;
+
+ key.root = root;
+ key.index = index;
+ __wake_up(wq, TASK_NORMAL, 1, &key);
+ }
}
EXPORT_SYMBOL(radix_tree_unlock);
--
2.6.2


--0OAP2g/MAC+5xKAE--