Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 10 Oct 2016 11:25:12 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org
Subject:   svn commit: r306947 - in stable/10: share/man/man9 sys/kern
Message-ID:  <201610101125.u9ABPCAT040647@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Mon Oct 10 11:25:11 2016
New Revision: 306947
URL: https://svnweb.freebsd.org/changeset/base/306947

Log:
  MFC r306441 and r306634:
  While draining a timeout task prevent the taskqueue_enqueue_timeout()
  function from restarting the timer.
  
  Commonly taskqueue_enqueue_timeout() is called from within the task
  function itself without any checks for teardown. Then it can happen
  the timer stays active after the return of taskqueue_drain_timeout(),
  because the timeout and task is drained separately.
  
  This patch factors out the teardown flag into the timeout task itself,
  allowing existing code to stay as-is instead of applying a teardown
  flag to each and every of the timeout task consumers.
  
  Add assert to taskqueue_drain_timeout() which prevents parallel
  execution on the same timeout task.
  
  Update manual page documenting the return value of
  taskqueue_enqueue_timeout().
  
  Differential Revision:	https://reviews.freebsd.org/D8012
  Reviewed by:	kib, trasz

Modified:
  stable/10/share/man/man9/taskqueue.9
  stable/10/sys/kern/subr_taskqueue.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/share/man/man9/taskqueue.9
==============================================================================
--- stable/10/share/man/man9/taskqueue.9	Mon Oct 10 11:22:11 2016	(r306946)
+++ stable/10/share/man/man9/taskqueue.9	Mon Oct 10 11:25:11 2016	(r306947)
@@ -221,6 +221,8 @@ Otherwise, the task is scheduled for enq
 after the absolute value of
 .Va ticks
 is passed.
+This function returns -1 if the task is being drained.
+Otherwise, the number of pending calls is returned.
 .Pp
 The
 .Fn taskqueue_cancel

Modified: stable/10/sys/kern/subr_taskqueue.c
==============================================================================
--- stable/10/sys/kern/subr_taskqueue.c	Mon Oct 10 11:22:11 2016	(r306946)
+++ stable/10/sys/kern/subr_taskqueue.c	Mon Oct 10 11:25:11 2016	(r306947)
@@ -75,6 +75,7 @@ struct taskqueue {
 #define	TQ_FLAGS_UNLOCKED_ENQUEUE	(1 << 2)
 
 #define	DT_CALLOUT_ARMED	(1 << 0)
+#define	DT_DRAIN_IN_PROGRESS	(1 << 1)
 
 #define	TQ_LOCK(tq)							\
 	do {								\
@@ -279,7 +280,11 @@ taskqueue_enqueue_timeout(struct taskque
 	KASSERT(!queue->tq_spin, ("Timeout for spin-queue"));
 	timeout_task->q = queue;
 	res = timeout_task->t.ta_pending;
-	if (ticks == 0) {
+	if (timeout_task->f & DT_DRAIN_IN_PROGRESS) {
+		/* Do nothing */
+		TQ_UNLOCK(queue);
+		res = -1;
+	} else if (ticks == 0) {
 		taskqueue_enqueue_locked(queue, &timeout_task->t);
 		/* The lock is released inside. */
 	} else {
@@ -485,8 +490,24 @@ taskqueue_drain_timeout(struct taskqueue
     struct timeout_task *timeout_task)
 {
 
+	/*
+	 * Set flag to prevent timer from re-starting during drain:
+	 */
+	TQ_LOCK(queue);
+	KASSERT((timeout_task->f & DT_DRAIN_IN_PROGRESS) == 0,
+	    ("Drain already in progress"));
+	timeout_task->f |= DT_DRAIN_IN_PROGRESS;
+	TQ_UNLOCK(queue);
+
 	callout_drain(&timeout_task->c);
 	taskqueue_drain(queue, &timeout_task->t);
+
+	/*
+	 * Clear flag to allow timer to re-start:
+	 */
+	TQ_LOCK(queue);
+	timeout_task->f &= ~DT_DRAIN_IN_PROGRESS;
+	TQ_UNLOCK(queue);
 }
 
 static void



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201610101125.u9ABPCAT040647>