|
@@ -0,0 +1,299 @@
|
|
|
+/*
|
|
|
+ * Generic wait-for-completion handler;
|
|
|
+ *
|
|
|
+ * It differs from semaphores in that their default case is the opposite,
|
|
|
+ * wait_for_completion default blocks whereas semaphore default non-block. The
|
|
|
+ * interface also makes it easy to 'complete' multiple waiting threads,
|
|
|
+ * something which isn't entirely natural for semaphores.
|
|
|
+ *
|
|
|
+ * But more importantly, the primitive documents the usage. Semaphores would
|
|
|
+ * typically be used for exclusion which gives rise to priority inversion.
|
|
|
+ * Waiting for completion is a typically sync point, but not an exclusion point.
|
|
|
+ */
|
|
|
+
|
|
|
+#include <linux/sched.h>
|
|
|
+#include <linux/completion.h>
|
|
|
+
|
|
|
+/**
|
|
|
+ * complete: - signals a single thread waiting on this completion
|
|
|
+ * @x: holds the state of this particular completion
|
|
|
+ *
|
|
|
+ * This will wake up a single thread waiting on this completion. Threads will be
|
|
|
+ * awakened in the same order in which they were queued.
|
|
|
+ *
|
|
|
+ * See also complete_all(), wait_for_completion() and related routines.
|
|
|
+ *
|
|
|
+ * It may be assumed that this function implies a write memory barrier before
|
|
|
+ * changing the task state if and only if any tasks are woken up.
|
|
|
+ */
|
|
|
+void complete(struct completion *x)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&x->wait.lock, flags);
|
|
|
+ x->done++;
|
|
|
+ __wake_up_locked(&x->wait, TASK_NORMAL, 1);
|
|
|
+ spin_unlock_irqrestore(&x->wait.lock, flags);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(complete);
|
|
|
+
|
|
|
+/**
|
|
|
+ * complete_all: - signals all threads waiting on this completion
|
|
|
+ * @x: holds the state of this particular completion
|
|
|
+ *
|
|
|
+ * This will wake up all threads waiting on this particular completion event.
|
|
|
+ *
|
|
|
+ * It may be assumed that this function implies a write memory barrier before
|
|
|
+ * changing the task state if and only if any tasks are woken up.
|
|
|
+ */
|
|
|
+void complete_all(struct completion *x)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&x->wait.lock, flags);
|
|
|
+ x->done += UINT_MAX/2;
|
|
|
+ __wake_up_locked(&x->wait, TASK_NORMAL, 0);
|
|
|
+ spin_unlock_irqrestore(&x->wait.lock, flags);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(complete_all);
|
|
|
+
|
|
|
+static inline long __sched
|
|
|
+do_wait_for_common(struct completion *x,
|
|
|
+ long (*action)(long), long timeout, int state)
|
|
|
+{
|
|
|
+ if (!x->done) {
|
|
|
+ DECLARE_WAITQUEUE(wait, current);
|
|
|
+
|
|
|
+ __add_wait_queue_tail_exclusive(&x->wait, &wait);
|
|
|
+ do {
|
|
|
+ if (signal_pending_state(state, current)) {
|
|
|
+ timeout = -ERESTARTSYS;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ __set_current_state(state);
|
|
|
+ spin_unlock_irq(&x->wait.lock);
|
|
|
+ timeout = action(timeout);
|
|
|
+ spin_lock_irq(&x->wait.lock);
|
|
|
+ } while (!x->done && timeout);
|
|
|
+ __remove_wait_queue(&x->wait, &wait);
|
|
|
+ if (!x->done)
|
|
|
+ return timeout;
|
|
|
+ }
|
|
|
+ x->done--;
|
|
|
+ return timeout ?: 1;
|
|
|
+}
|
|
|
+
|
|
|
+static inline long __sched
|
|
|
+__wait_for_common(struct completion *x,
|
|
|
+ long (*action)(long), long timeout, int state)
|
|
|
+{
|
|
|
+ might_sleep();
|
|
|
+
|
|
|
+ spin_lock_irq(&x->wait.lock);
|
|
|
+ timeout = do_wait_for_common(x, action, timeout, state);
|
|
|
+ spin_unlock_irq(&x->wait.lock);
|
|
|
+ return timeout;
|
|
|
+}
|
|
|
+
|
|
|
+static long __sched
|
|
|
+wait_for_common(struct completion *x, long timeout, int state)
|
|
|
+{
|
|
|
+ return __wait_for_common(x, schedule_timeout, timeout, state);
|
|
|
+}
|
|
|
+
|
|
|
+static long __sched
|
|
|
+wait_for_common_io(struct completion *x, long timeout, int state)
|
|
|
+{
|
|
|
+ return __wait_for_common(x, io_schedule_timeout, timeout, state);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * wait_for_completion: - waits for completion of a task
|
|
|
+ * @x: holds the state of this particular completion
|
|
|
+ *
|
|
|
+ * This waits to be signaled for completion of a specific task. It is NOT
|
|
|
+ * interruptible and there is no timeout.
|
|
|
+ *
|
|
|
+ * See also similar routines (i.e. wait_for_completion_timeout()) with timeout
|
|
|
+ * and interrupt capability. Also see complete().
|
|
|
+ */
|
|
|
+void __sched wait_for_completion(struct completion *x)
|
|
|
+{
|
|
|
+ wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(wait_for_completion);
|
|
|
+
|
|
|
+/**
|
|
|
+ * wait_for_completion_timeout: - waits for completion of a task (w/timeout)
|
|
|
+ * @x: holds the state of this particular completion
|
|
|
+ * @timeout: timeout value in jiffies
|
|
|
+ *
|
|
|
+ * This waits for either a completion of a specific task to be signaled or for a
|
|
|
+ * specified timeout to expire. The timeout is in jiffies. It is not
|
|
|
+ * interruptible.
|
|
|
+ *
|
|
|
+ * Return: 0 if timed out, and positive (at least 1, or number of jiffies left
|
|
|
+ * till timeout) if completed.
|
|
|
+ */
|
|
|
+unsigned long __sched
|
|
|
+wait_for_completion_timeout(struct completion *x, unsigned long timeout)
|
|
|
+{
|
|
|
+ return wait_for_common(x, timeout, TASK_UNINTERRUPTIBLE);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(wait_for_completion_timeout);
|
|
|
+
|
|
|
+/**
|
|
|
+ * wait_for_completion_io: - waits for completion of a task
|
|
|
+ * @x: holds the state of this particular completion
|
|
|
+ *
|
|
|
+ * This waits to be signaled for completion of a specific task. It is NOT
|
|
|
+ * interruptible and there is no timeout. The caller is accounted as waiting
|
|
|
+ * for IO.
|
|
|
+ */
|
|
|
+void __sched wait_for_completion_io(struct completion *x)
|
|
|
+{
|
|
|
+ wait_for_common_io(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(wait_for_completion_io);
|
|
|
+
|
|
|
+/**
|
|
|
+ * wait_for_completion_io_timeout: - waits for completion of a task (w/timeout)
|
|
|
+ * @x: holds the state of this particular completion
|
|
|
+ * @timeout: timeout value in jiffies
|
|
|
+ *
|
|
|
+ * This waits for either a completion of a specific task to be signaled or for a
|
|
|
+ * specified timeout to expire. The timeout is in jiffies. It is not
|
|
|
+ * interruptible. The caller is accounted as waiting for IO.
|
|
|
+ *
|
|
|
+ * Return: 0 if timed out, and positive (at least 1, or number of jiffies left
|
|
|
+ * till timeout) if completed.
|
|
|
+ */
|
|
|
+unsigned long __sched
|
|
|
+wait_for_completion_io_timeout(struct completion *x, unsigned long timeout)
|
|
|
+{
|
|
|
+ return wait_for_common_io(x, timeout, TASK_UNINTERRUPTIBLE);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(wait_for_completion_io_timeout);
|
|
|
+
|
|
|
+/**
|
|
|
+ * wait_for_completion_interruptible: - waits for completion of a task (w/intr)
|
|
|
+ * @x: holds the state of this particular completion
|
|
|
+ *
|
|
|
+ * This waits for completion of a specific task to be signaled. It is
|
|
|
+ * interruptible.
|
|
|
+ *
|
|
|
+ * Return: -ERESTARTSYS if interrupted, 0 if completed.
|
|
|
+ */
|
|
|
+int __sched wait_for_completion_interruptible(struct completion *x)
|
|
|
+{
|
|
|
+ long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_INTERRUPTIBLE);
|
|
|
+ if (t == -ERESTARTSYS)
|
|
|
+ return t;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(wait_for_completion_interruptible);
|
|
|
+
|
|
|
+/**
|
|
|
+ * wait_for_completion_interruptible_timeout: - waits for completion (w/(to,intr))
|
|
|
+ * @x: holds the state of this particular completion
|
|
|
+ * @timeout: timeout value in jiffies
|
|
|
+ *
|
|
|
+ * This waits for either a completion of a specific task to be signaled or for a
|
|
|
+ * specified timeout to expire. It is interruptible. The timeout is in jiffies.
|
|
|
+ *
|
|
|
+ * Return: -ERESTARTSYS if interrupted, 0 if timed out, positive (at least 1,
|
|
|
+ * or number of jiffies left till timeout) if completed.
|
|
|
+ */
|
|
|
+long __sched
|
|
|
+wait_for_completion_interruptible_timeout(struct completion *x,
|
|
|
+ unsigned long timeout)
|
|
|
+{
|
|
|
+ return wait_for_common(x, timeout, TASK_INTERRUPTIBLE);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(wait_for_completion_interruptible_timeout);
|
|
|
+
|
|
|
+/**
|
|
|
+ * wait_for_completion_killable: - waits for completion of a task (killable)
|
|
|
+ * @x: holds the state of this particular completion
|
|
|
+ *
|
|
|
+ * This waits to be signaled for completion of a specific task. It can be
|
|
|
+ * interrupted by a kill signal.
|
|
|
+ *
|
|
|
+ * Return: -ERESTARTSYS if interrupted, 0 if completed.
|
|
|
+ */
|
|
|
+int __sched wait_for_completion_killable(struct completion *x)
|
|
|
+{
|
|
|
+ long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_KILLABLE);
|
|
|
+ if (t == -ERESTARTSYS)
|
|
|
+ return t;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(wait_for_completion_killable);
|
|
|
+
|
|
|
+/**
|
|
|
+ * wait_for_completion_killable_timeout: - waits for completion of a task (w/(to,killable))
|
|
|
+ * @x: holds the state of this particular completion
|
|
|
+ * @timeout: timeout value in jiffies
|
|
|
+ *
|
|
|
+ * This waits for either a completion of a specific task to be
|
|
|
+ * signaled or for a specified timeout to expire. It can be
|
|
|
+ * interrupted by a kill signal. The timeout is in jiffies.
|
|
|
+ *
|
|
|
+ * Return: -ERESTARTSYS if interrupted, 0 if timed out, positive (at least 1,
|
|
|
+ * or number of jiffies left till timeout) if completed.
|
|
|
+ */
|
|
|
+long __sched
|
|
|
+wait_for_completion_killable_timeout(struct completion *x,
|
|
|
+ unsigned long timeout)
|
|
|
+{
|
|
|
+ return wait_for_common(x, timeout, TASK_KILLABLE);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(wait_for_completion_killable_timeout);
|
|
|
+
|
|
|
+/**
|
|
|
+ * try_wait_for_completion - try to decrement a completion without blocking
|
|
|
+ * @x: completion structure
|
|
|
+ *
|
|
|
+ * Return: 0 if a decrement cannot be done without blocking
|
|
|
+ * 1 if a decrement succeeded.
|
|
|
+ *
|
|
|
+ * If a completion is being used as a counting completion,
|
|
|
+ * attempt to decrement the counter without blocking. This
|
|
|
+ * enables us to avoid waiting if the resource the completion
|
|
|
+ * is protecting is not available.
|
|
|
+ */
|
|
|
+bool try_wait_for_completion(struct completion *x)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+ int ret = 1;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&x->wait.lock, flags);
|
|
|
+ if (!x->done)
|
|
|
+ ret = 0;
|
|
|
+ else
|
|
|
+ x->done--;
|
|
|
+ spin_unlock_irqrestore(&x->wait.lock, flags);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(try_wait_for_completion);
|
|
|
+
|
|
|
+/**
|
|
|
+ * completion_done - Test to see if a completion has any waiters
|
|
|
+ * @x: completion structure
|
|
|
+ *
|
|
|
+ * Return: 0 if there are waiters (wait_for_completion() in progress)
|
|
|
+ * 1 if there are no waiters.
|
|
|
+ *
|
|
|
+ */
|
|
|
+bool completion_done(struct completion *x)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+ int ret = 1;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&x->wait.lock, flags);
|
|
|
+ if (!x->done)
|
|
|
+ ret = 0;
|
|
|
+ spin_unlock_irqrestore(&x->wait.lock, flags);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(completion_done);
|