kthread: initial support for delayed kthread work
[deliverable/linux.git] / kernel / kthread.c
index 48002a46b647a6264e71d45ef487ea441c8d4790..647b60cc5f903bfdb86ac90c48ac69ffa7f0d268 100644 (file)
@@ -559,6 +559,7 @@ void __kthread_init_worker(struct kthread_worker *worker,
        spin_lock_init(&worker->lock);
        lockdep_set_class_and_name(&worker->lock, key, name);
        INIT_LIST_HEAD(&worker->work_list);
+       INIT_LIST_HEAD(&worker->delayed_work_list);
        worker->task = NULL;
 }
 EXPORT_SYMBOL_GPL(__kthread_init_worker);
@@ -763,6 +764,107 @@ bool kthread_queue_work(struct kthread_worker *worker,
 }
 EXPORT_SYMBOL_GPL(kthread_queue_work);
 
+/**
+ * kthread_delayed_work_timer_fn - callback that queues the associated kthread
+ *     delayed work when the timer expires.
+ * @__data: pointer to the data associated with the timer
+ *
+ * The format of the function is defined by struct timer_list.
+ * It should have been called from irqsafe timer with irq already off.
+ */
+void kthread_delayed_work_timer_fn(unsigned long __data)
+{
+       struct kthread_delayed_work *dwork =
+               (struct kthread_delayed_work *)__data;
+       struct kthread_work *work = &dwork->work;
+       struct kthread_worker *worker = work->worker;
+
+       /*
+        * This might happen when a pending work is reinitialized.
+        * It means that it is used a wrong way.
+        */
+       if (WARN_ON_ONCE(!worker))
+               return;
+
+       spin_lock(&worker->lock);
+       /* Work must not be used with >1 worker, see kthread_queue_work(). */
+       WARN_ON_ONCE(work->worker != worker);
+
+       /* Move the work from worker->delayed_work_list. */
+       WARN_ON_ONCE(list_empty(&work->node));
+       list_del_init(&work->node);
+       kthread_insert_work(worker, work, &worker->work_list);
+
+       spin_unlock(&worker->lock);
+}
+EXPORT_SYMBOL(kthread_delayed_work_timer_fn);
+
+void __kthread_queue_delayed_work(struct kthread_worker *worker,
+                                 struct kthread_delayed_work *dwork,
+                                 unsigned long delay)
+{
+       struct timer_list *timer = &dwork->timer;
+       struct kthread_work *work = &dwork->work;
+
+       WARN_ON_ONCE(timer->function != kthread_delayed_work_timer_fn ||
+                    timer->data != (unsigned long)dwork);
+
+       /*
+        * If @delay is 0, queue @dwork->work immediately.  This is for
+        * both optimization and correctness.  The earliest @timer can
+        * expire is on the closest next tick and delayed_work users depend
+        * on that there's no such delay when @delay is 0.
+        */
+       if (!delay) {
+               kthread_insert_work(worker, work, &worker->work_list);
+               return;
+       }
+
+       /* Be paranoid and try to detect possible races already now. */
+       kthread_insert_work_sanity_check(worker, work);
+
+       list_add(&work->node, &worker->delayed_work_list);
+       work->worker = worker;
+       timer_stats_timer_set_start_info(&dwork->timer);
+       timer->expires = jiffies + delay;
+       add_timer(timer);
+}
+
+/**
+ * kthread_queue_delayed_work - queue the associated kthread work
+ *     after a delay.
+ * @worker: target kthread_worker
+ * @dwork: kthread_delayed_work to queue
+ * @delay: number of jiffies to wait before queuing
+ *
+ * If the work has not been pending it starts a timer that will queue
+ * the work after the given @delay. If @delay is zero, it queues the
+ * work immediately.
+ *
+ * Return: %false if the @work has already been pending. It means that
+ * either the timer was running or the work was queued. It returns %true
+ * otherwise.
+ */
+bool kthread_queue_delayed_work(struct kthread_worker *worker,
+                               struct kthread_delayed_work *dwork,
+                               unsigned long delay)
+{
+       struct kthread_work *work = &dwork->work;
+       unsigned long flags;
+       bool ret = false;
+
+       spin_lock_irqsave(&worker->lock, flags);
+
+       if (list_empty(&work->node)) {
+               __kthread_queue_delayed_work(worker, dwork, delay);
+               ret = true;
+       }
+
+       spin_unlock_irqrestore(&worker->lock, flags);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(kthread_queue_delayed_work);
+
 struct kthread_flush_work {
        struct kthread_work     work;
        struct completion       done;
This page took 0.026445 seconds and 5 git commands to generate.