svn commit: r306441 - in head: share/man/man9 sys/kern

Hans Petter Selasky hselasky at FreeBSD.org
Thu Sep 29 10:38:22 UTC 2016


Author: hselasky
Date: Thu Sep 29 10:38:20 2016
New Revision: 306441
URL: https://svnweb.freebsd.org/changeset/base/306441

Log:
  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
  MFC after:	1 week

Modified:
  head/share/man/man9/taskqueue.9
  head/sys/kern/subr_taskqueue.c

Modified: head/share/man/man9/taskqueue.9
==============================================================================
--- head/share/man/man9/taskqueue.9	Thu Sep 29 09:16:02 2016	(r306440)
+++ head/share/man/man9/taskqueue.9	Thu Sep 29 10:38:20 2016	(r306441)
@@ -223,6 +223,8 @@ Otherwise, the task is scheduled for enq
 after the absolute value of
 .Va ticks
 is passed.
+This function will return -1 if the queue is being drained.
+Otherwise the number of pending calls will be returned.
 .Pp
 The
 .Fn taskqueue_cancel

Modified: head/sys/kern/subr_taskqueue.c
==============================================================================
--- head/sys/kern/subr_taskqueue.c	Thu Sep 29 09:16:02 2016	(r306440)
+++ head/sys/kern/subr_taskqueue.c	Thu Sep 29 10:38:20 2016	(r306441)
@@ -81,6 +81,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 {								\
@@ -299,7 +300,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 {
@@ -559,8 +564,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


More information about the svn-src-head mailing list