kthread: allow to modify delayed kthread work
[deliverable/linux.git] / kernel / kthread.c
index 526646971ec1d9cfd59457de64ff316e8750b4b1..0de621e1c73206db56d376ad540c53a299ebd181 100644 (file)
@@ -972,6 +972,59 @@ static bool __kthread_cancel_work(struct kthread_work *work, bool is_dwork,
        return false;
 }
 
+/**
+ * kthread_mod_delayed_work - modify delay of or queue a kthread delayed work
+ * @worker: kthread worker to use
+ * @dwork: kthread delayed work to queue
+ * @delay: number of jiffies to wait before queuing
+ *
+ * If @dwork is idle, equivalent to kthread_queue_delayed_work(). Otherwise,
+ * modify @dwork's timer so that it expires after @delay. If @delay is zero,
+ * @work is guaranteed to be queued immediately.
+ *
+ * Return: %true if @dwork was pending and its timer was modified,
+ * %false otherwise.
+ *
+ * A special case is when the work is being canceled in parallel.
+ * It might be caused either by the real kthread_cancel_delayed_work_sync()
+ * or yet another kthread_mod_delayed_work() call. We let the other command
+ * win and return %false here. The caller is supposed to synchronize these
+ * operations a reasonable way.
+ *
+ * This function is safe to call from any context including IRQ handler.
+ * See __kthread_cancel_work() and kthread_delayed_work_timer_fn()
+ * for details.
+ */
+bool kthread_mod_delayed_work(struct kthread_worker *worker,
+                             struct kthread_delayed_work *dwork,
+                             unsigned long delay)
+{
+       struct kthread_work *work = &dwork->work;
+       unsigned long flags;
+       int ret = false;
+
+       spin_lock_irqsave(&worker->lock, flags);
+
+       /* Do not bother with canceling when never queued. */
+       if (!work->worker)
+               goto fast_queue;
+
+       /* Work must not be used with >1 worker, see kthread_queue_work() */
+       WARN_ON_ONCE(work->worker != worker);
+
+       /* Do not fight with another command that is canceling this work. */
+       if (work->canceling)
+               goto out;
+
+       ret = __kthread_cancel_work(work, true, &flags);
+fast_queue:
+       __kthread_queue_delayed_work(worker, dwork, delay);
+out:
+       spin_unlock_irqrestore(&worker->lock, flags);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(kthread_mod_delayed_work);
+
 static bool __kthread_cancel_work_sync(struct kthread_work *work, bool is_dwork)
 {
        struct kthread_worker *worker = work->worker;
This page took 0.02838 seconds and 5 git commands to generate.