linux-api.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Petr Mladek <pmladek@suse.com>
To: Andrew Morton <akpm@linux-foundation.org>,
	Oleg Nesterov <oleg@redhat.com>, Tejun Heo <tj@kernel.org>,
	Ingo Molnar <mingo@redhat.com>,
	Peter Zijlstra <peterz@infradead.org>
Cc: Steven Rostedt <rostedt@goodmis.org>,
	"Paul E. McKenney" <paulmck@linux.vnet.ibm.com>,
	Josh Triplett <josh@joshtriplett.org>,
	Thomas Gleixner <tglx@linutronix.de>,
	Linus Torvalds <torvalds@linux-foundation.org>,
	Jiri Kosina <jkosina@suse.cz>, Borislav Petkov <bp@suse.de>,
	Michal Hocko <mhocko@suse.cz>,
	linux-mm@kvack.org, Vlastimil Babka <vbabka@suse.cz>,
	linux-api@vger.kernel.org, linux-kernel@vger.kernel.org,
	Petr Mladek <pmladek@suse.com>
Subject: [PATCH v3 09/22] kthread: Allow to cancel kthread work
Date: Wed, 18 Nov 2015 14:25:14 +0100	[thread overview]
Message-ID: <1447853127-3461-10-git-send-email-pmladek@suse.com> (raw)
In-Reply-To: <1447853127-3461-1-git-send-email-pmladek@suse.com>

We are going to use kthread workers more widely and sometimes we will need
to make sure that the work is neither pending nor running.

This patch implements cancel_*_sync() operations as inspired by
workqueues. Well, we are synchronized against the other operations
via the worker lock, we use del_timer_sync() and a counter to count
parallel cancel operations. Therefore the implementation might be easier.

First, we try to lock the work. If it does not work, it means that
no worker is assigned and that we are done.

Second, we try to cancel the timer when it exists. A problem is when
the timer callback is running at the same time. In this case, we need
to release the lock to avoid a deadlock and start from the beginning.

Third, we try to remove the work from the worker list.

Fourth, if the work is running, we call flush_kthread_work(). It might
take an arbitrary time. In the meantime, queuing of the work is blocked
by the new canceling counter.

As already mentioned, the check for a pending kthread work is done under
a lock. In compare with workqueues, we do not need to fight for a single
PENDING bit to block other operations. Therefore do not suffer from
the thundering storm problem and all parallel canceling jobs might use
kthread_work_flush(). Any queuing is blocked until the counter is zero.

Signed-off-by: Petr Mladek <pmladek@suse.com>
---
 include/linux/kthread.h |   4 ++
 kernel/kthread.c        | 142 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 146 insertions(+)

diff --git a/include/linux/kthread.h b/include/linux/kthread.h
index 1a5738dcdf8d..dd2a587a2bd7 100644
--- a/include/linux/kthread.h
+++ b/include/linux/kthread.h
@@ -77,6 +77,7 @@ struct kthread_work {
 	kthread_work_func_t	func;
 	struct kthread_worker	*worker;
 	struct timer_list	*timer;
+	int			canceling;
 };
 
 struct delayed_kthread_work {
@@ -170,6 +171,9 @@ bool queue_delayed_kthread_work(struct kthread_worker *worker,
 void flush_kthread_work(struct kthread_work *work);
 void flush_kthread_worker(struct kthread_worker *worker);
 
+bool cancel_kthread_work_sync(struct kthread_work *work);
+bool cancel_delayed_kthread_work_sync(struct delayed_kthread_work *work);
+
 void destroy_kthread_worker(struct kthread_worker *worker);
 
 #endif /* _LINUX_KTHREAD_H */
diff --git a/kernel/kthread.c b/kernel/kthread.c
index 0f4b348c2c7e..d12aa91cc44d 100644
--- a/kernel/kthread.c
+++ b/kernel/kthread.c
@@ -567,6 +567,7 @@ EXPORT_SYMBOL_GPL(__init_kthread_worker);
  * Returns true when there is a pending operation for this work.
  * In particular, it checks if the work is:
  *	- queued
+ *	- being cancelled
  *	- a timer is running to queue this delayed work
  *
  * This function must be called with locked work.
@@ -574,6 +575,7 @@ EXPORT_SYMBOL_GPL(__init_kthread_worker);
 static inline bool kthread_work_pending(const struct kthread_work *work)
 {
 	return !list_empty(&work->node) ||
+	       work->canceling ||
 	       (work->timer && timer_active(work->timer));
 }
 
@@ -950,6 +952,146 @@ retry:
 EXPORT_SYMBOL_GPL(flush_kthread_work);
 
 /**
+ * try_to_cancel_kthread_work - Try to cancel kthread work.
+ * @work: work item to cancel
+ * @lock: lock used to protect the work
+ * @flags: flags stored when the lock was taken
+ *
+ * This function tries to cancel the given kthread work by deleting
+ * the timer and by removing the work from the queue.
+ *
+ * If the timer callback is in progress, it waits until it finishes
+ * but it has to drop the lock to avoid a deadlock.
+ *
+ * Return:
+ *  1		if @work was pending and successfully canceled
+ *  0		if @work was not pending
+ *  -EAGAIN	if the lock was dropped. The caller is supposed to
+ *		take the lock again and repeat the operation.
+ */
+static int
+try_to_cancel_kthread_work(struct kthread_work *work,
+				   spinlock_t *lock,
+				   unsigned long *flags)
+{
+	int ret = 0;
+
+	if (work->timer) {
+		/* Try to cancel the timer if pending. */
+		if (del_timer(work->timer)) {
+			ret = 1;
+			goto out;
+		}
+
+		/* Are we racing with the timer callback? */
+		if (timer_active(work->timer)) {
+			/* Bad luck, need to avoid a deadlock. */
+			spin_unlock_irqrestore(lock, *flags);
+			del_timer_sync(work->timer);
+			ret = -EAGAIN;
+			goto out;
+		}
+	}
+
+	/* Try to remove queued work before it is being executed. */
+	if (!list_empty(&work->node)) {
+		list_del_init(&work->node);
+		ret = 1;
+	}
+
+out:
+	return ret;
+}
+
+static bool __cancel_kthread_work_sync(struct kthread_work *work)
+{
+	struct kthread_worker *worker;
+	unsigned long flags;
+	int ret;
+
+try_again:
+	local_irq_save(flags);
+	if (!try_lock_kthread_work(work)) {
+		local_irq_restore(flags);
+		ret = 0;
+		goto out;
+	}
+	worker = work->worker;
+
+	ret = try_to_cancel_kthread_work(work, &worker->lock, &flags);
+	if (ret == -EAGAIN)
+		goto try_again;
+
+	if (worker->current_work != work)
+		goto out_fast;
+
+	/*
+	 * Need to wait until the work finished. Block queueing
+	 * in the meantime.
+	 */
+	work->canceling++;
+	spin_unlock_irqrestore(&worker->lock, flags);
+	flush_kthread_work(work);
+	/*
+	 * Nobody is allowed to switch the worker or queue the work
+	 * when .canceling is set
+	 */
+	spin_lock_irqsave(&worker->lock, flags);
+	work->canceling--;
+
+out_fast:
+	/*
+	 * Allow to queue the work into another worker if there is no other
+	 * pending operation.
+	 */
+	if (!work->canceling)
+		work->worker = NULL;
+	spin_unlock_irqrestore(&worker->lock, flags);
+
+out:
+	return ret;
+}
+
+/**
+ * cancel_kthread_work_sync - cancel a kthread work and wait for it to finish
+ * @work: the kthread work to cancel
+ *
+ * Cancel @work and wait for its execution to finish.  This function
+ * can be used even if the work re-queues itself. On return from this
+ * function, @work is guaranteed to be not pending or executing on any CPU.
+ *
+ * The caller must ensure that the worker on which @work was last
+ * queued can't be destroyed before this function returns.
+ *
+ * Return:
+ * %true if @work was pending, %false otherwise.
+ */
+bool cancel_kthread_work_sync(struct kthread_work *work)
+{
+	/* Rather use cancel_delayed_kthread_work() for delayed works. */
+	WARN_ON_ONCE(work->timer);
+
+	return __cancel_kthread_work_sync(work);
+}
+EXPORT_SYMBOL_GPL(cancel_kthread_work_sync);
+
+/**
+ * cancel_delayed_kthread_work_sync - cancel a delayed kthread work and
+ *	wait for it to finish.
+ * @dwork: the delayed kthread work to cancel
+ *
+ * This is cancel_kthread_work_sync() for delayed works.
+ *
+ * Return:
+ * %true if @dwork was pending, %false otherwise.
+ */
+bool cancel_delayed_kthread_work_sync(struct delayed_kthread_work *dwork)
+{
+	return __cancel_kthread_work_sync(&dwork->work);
+}
+EXPORT_SYMBOL_GPL(cancel_delayed_kthread_work_sync);
+
+/**
  * flush_kthread_worker - flush all current works on a kthread_worker
  * @worker: worker to flush
  *
-- 
1.8.5.6

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

  parent reply	other threads:[~2015-11-18 13:25 UTC|newest]

Thread overview: 49+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-11-18 13:25 [PATCH v3 00/22] kthread: Use kthread worker API more widely Petr Mladek
2015-11-18 13:25 ` [PATCH v3 01/22] timer: Allow to check when the timer callback has not finished yet Petr Mladek
2015-11-18 22:32   ` Thomas Gleixner
2015-11-19 12:43     ` Petr Mladek
2015-11-18 13:25 ` [PATCH v3 02/22] kthread/smpboot: Do not park in kthread_create_on_cpu() Petr Mladek
     [not found]   ` <1447853127-3461-3-git-send-email-pmladek-IBi9RG/b67k@public.gmane.org>
2015-11-25 21:16     ` Thomas Gleixner
2015-11-18 13:25 ` [PATCH v3 03/22] kthread: Allow to call __kthread_create_on_node() with va_list args Petr Mladek
2015-11-18 13:25 ` [PATCH v3 04/22] kthread: Add create_kthread_worker*() Petr Mladek
2015-11-18 13:25 ` [PATCH v3 05/22] kthread: Add drain_kthread_worker() Petr Mladek
2015-11-18 13:25 ` [PATCH v3 06/22] kthread: Add destroy_kthread_worker() Petr Mladek
2015-11-18 13:25 ` [PATCH v3 07/22] kthread: Detect when a kthread work is used by more workers Petr Mladek
2015-11-23 22:27   ` Tejun Heo
     [not found]     ` <20151123222703.GH19072-qYNAdHglDFBN0TnZuCh8vA@public.gmane.org>
2015-11-24 10:06       ` Petr Mladek
2015-11-24 14:49         ` Tejun Heo
2015-11-24 16:28           ` Petr Mladek
     [not found]         ` <20151124100650.GF10750-KsEp0d+Q8qECVLCxKZUutA@public.gmane.org>
2015-11-24 14:56           ` Peter Zijlstra
2015-11-18 13:25 ` [PATCH v3 08/22] kthread: Initial support for delayed kthread work Petr Mladek
2015-11-18 13:25 ` Petr Mladek [this message]
     [not found]   ` <1447853127-3461-10-git-send-email-pmladek-IBi9RG/b67k@public.gmane.org>
2015-11-23 22:58     ` [PATCH v3 09/22] kthread: Allow to cancel " Tejun Heo
2015-11-24 10:21       ` Petr Mladek
     [not found]       ` <20151123225823.GI19072-qYNAdHglDFBN0TnZuCh8vA@public.gmane.org>
2015-11-24 20:23         ` Linus Torvalds
     [not found]           ` <CA+55aFyW=hp-myZGcL+5r2x+fUbpBJLmxDY66QB5VQj-nNsCxQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-11-24 20:28             ` Tejun Heo
2015-11-24 20:49               ` Linus Torvalds
2015-11-18 13:25 ` [PATCH v3 10/22] kthread: Allow to modify delayed " Petr Mladek
2015-11-18 13:25 ` [PATCH v3 11/22] kthread: Better support freezable kthread workers Petr Mladek
2015-11-18 13:25 ` [PATCH v3 12/22] kthread: Use try_lock_kthread_work() in flush_kthread_work() Petr Mladek
2015-11-18 13:25 ` [PATCH v3 13/22] mm/huge_page: Convert khugepaged() into kthread worker API Petr Mladek
2015-11-18 13:25 ` [PATCH v3 14/22] ring_buffer: Convert benchmark kthreads " Petr Mladek
2015-11-18 13:25 ` [PATCH v3 15/22] hung_task: Convert hungtaskd " Petr Mladek
2015-11-18 13:25 ` [PATCH v3 16/22] kmemleak: Convert kmemleak kthread " Petr Mladek
2015-11-18 13:25 ` [PATCH v3 17/22] ipmi: Convert kipmi " Petr Mladek
2015-11-23 19:36   ` Corey Minyard
     [not found]     ` <56536AA6.5040102-HInyCGIudOg@public.gmane.org>
2015-11-24 12:12       ` Petr Mladek
     [not found]         ` <20151124121233.GH10750-KsEp0d+Q8qECVLCxKZUutA@public.gmane.org>
2015-11-24 13:30           ` Corey Minyard
2015-11-18 13:25 ` [PATCH v3 18/22] IB/fmr_pool: Convert the cleanup thread " Petr Mladek
2015-11-19 12:46   ` Yuval Shaia
2015-11-18 13:25 ` [PATCH v3 19/22] memstick/r592: Better synchronize debug messages in r592_io kthread Petr Mladek
2015-11-18 13:25 ` [PATCH v3 20/22] memstick/r592: convert r592_io kthread into kthread worker API Petr Mladek
2015-11-18 13:25 ` [PATCH v3 21/22] thermal/intel_powerclamp: Remove duplicated code that starts the kthread Petr Mladek
2015-11-18 13:25 ` [PATCH v3 22/22] thermal/intel_powerclamp: Convert the kthread to kthread worker API Petr Mladek
2016-01-07 19:55   ` Jacob Pan
2016-01-08 16:49     ` Petr Mladek
2016-01-12  2:17       ` Jacob Pan
2016-01-12 10:11         ` Petr Mladek
2016-01-12 16:20           ` Jacob Pan
2016-01-13 10:18             ` Petr Mladek
2016-01-13 17:53               ` Jacob Pan
2016-01-14 15:37                 ` Petr Mladek
2015-11-18 14:25 ` [PATCH v3 00/22] kthread: Use kthread worker API more widely Paul E. McKenney

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1447853127-3461-10-git-send-email-pmladek@suse.com \
    --to=pmladek@suse.com \
    --cc=akpm@linux-foundation.org \
    --cc=bp@suse.de \
    --cc=jkosina@suse.cz \
    --cc=josh@joshtriplett.org \
    --cc=linux-api@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=mhocko@suse.cz \
    --cc=mingo@redhat.com \
    --cc=oleg@redhat.com \
    --cc=paulmck@linux.vnet.ibm.com \
    --cc=peterz@infradead.org \
    --cc=rostedt@goodmis.org \
    --cc=tglx@linutronix.de \
    --cc=tj@kernel.org \
    --cc=torvalds@linux-foundation.org \
    --cc=vbabka@suse.cz \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).