X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=kernel%2Fkthread.c;fp=kernel%2Fkthread.c;h=0de621e1c73206db56d376ad540c53a299ebd181;hb=3432b8886c14b4a19f958d0566901a3a8ef24d5d;hp=526646971ec1d9cfd59457de64ff316e8750b4b1;hpb=a105b741832399ba61d5be735d42e1f3c6732e99;p=deliverable%2Flinux.git diff --git a/kernel/kthread.c b/kernel/kthread.c index 526646971ec1..0de621e1c732 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -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;