|
@@ -91,6 +91,15 @@ prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state)
|
|
|
}
|
|
|
EXPORT_SYMBOL(prepare_to_wait_exclusive);
|
|
|
|
|
|
+/*
|
|
|
+ * finish_wait - clean up after waiting in a queue
|
|
|
+ * @q: waitqueue waited on
|
|
|
+ * @wait: wait descriptor
|
|
|
+ *
|
|
|
+ * Sets current thread back to running state and removes
|
|
|
+ * the wait descriptor from the given waitqueue if still
|
|
|
+ * queued.
|
|
|
+ */
|
|
|
void finish_wait(wait_queue_head_t *q, wait_queue_t *wait)
|
|
|
{
|
|
|
unsigned long flags;
|
|
@@ -117,6 +126,39 @@ void finish_wait(wait_queue_head_t *q, wait_queue_t *wait)
|
|
|
}
|
|
|
EXPORT_SYMBOL(finish_wait);
|
|
|
|
|
|
+/*
|
|
|
+ * abort_exclusive_wait - abort exclusive waiting in a queue
|
|
|
+ * @q: waitqueue waited on
|
|
|
+ * @wait: wait descriptor
|
|
|
+ * @state: runstate of the waiter to be woken
|
|
|
+ * @key: key to identify a wait bit queue or %NULL
|
|
|
+ *
|
|
|
+ * Sets current thread back to running state and removes
|
|
|
+ * the wait descriptor from the given waitqueue if still
|
|
|
+ * queued.
|
|
|
+ *
|
|
|
+ * Wakes up the next waiter if the caller is concurrently
|
|
|
+ * woken up through the queue.
|
|
|
+ *
|
|
|
+ * This prevents waiter starvation where an exclusive waiter
|
|
|
+ * aborts and is woken up concurrently and noone wakes up
|
|
|
+ * the next waiter.
|
|
|
+ */
|
|
|
+void abort_exclusive_wait(wait_queue_head_t *q, wait_queue_t *wait,
|
|
|
+ unsigned int mode, void *key)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ __set_current_state(TASK_RUNNING);
|
|
|
+ spin_lock_irqsave(&q->lock, flags);
|
|
|
+ if (!list_empty(&wait->task_list))
|
|
|
+ list_del_init(&wait->task_list);
|
|
|
+ else if (waitqueue_active(q))
|
|
|
+ __wake_up_common(q, mode, 1, 0, key);
|
|
|
+ spin_unlock_irqrestore(&q->lock, flags);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(abort_exclusive_wait);
|
|
|
+
|
|
|
int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key)
|
|
|
{
|
|
|
int ret = default_wake_function(wait, mode, sync, key);
|
|
@@ -177,17 +219,20 @@ int __sched
|
|
|
__wait_on_bit_lock(wait_queue_head_t *wq, struct wait_bit_queue *q,
|
|
|
int (*action)(void *), unsigned mode)
|
|
|
{
|
|
|
- int ret = 0;
|
|
|
-
|
|
|
do {
|
|
|
+ int ret;
|
|
|
+
|
|
|
prepare_to_wait_exclusive(wq, &q->wait, mode);
|
|
|
- if (test_bit(q->key.bit_nr, q->key.flags)) {
|
|
|
- if ((ret = (*action)(q->key.flags)))
|
|
|
- break;
|
|
|
- }
|
|
|
+ if (!test_bit(q->key.bit_nr, q->key.flags))
|
|
|
+ continue;
|
|
|
+ ret = action(q->key.flags);
|
|
|
+ if (!ret)
|
|
|
+ continue;
|
|
|
+ abort_exclusive_wait(wq, &q->wait, mode, &q->key);
|
|
|
+ return ret;
|
|
|
} while (test_and_set_bit(q->key.bit_nr, q->key.flags));
|
|
|
finish_wait(wq, &q->wait);
|
|
|
- return ret;
|
|
|
+ return 0;
|
|
|
}
|
|
|
EXPORT_SYMBOL(__wait_on_bit_lock);
|
|
|
|