Author: hselasky
Date: Mon Oct 10 11:27:59 2016
New Revision: 306948
URL: https://svnweb.freebsd.org/changeset/base/306948

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/9/share/man/man9/taskqueue.9
  stable/9/sys/kern/subr_taskqueue.c
Directory Properties:
  stable/9/share/   (props changed)
  stable/9/share/man/   (props changed)
  stable/9/share/man/man9/   (props changed)
  stable/9/sys/   (props changed)

Modified: stable/9/share/man/man9/taskqueue.9
==============================================================================
--- stable/9/share/man/man9/taskqueue.9 Mon Oct 10 11:25:11 2016        
(r306947)
+++ stable/9/share/man/man9/taskqueue.9 Mon Oct 10 11:27:59 2016        
(r306948)
@@ -193,6 +193,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/9/sys/kern/subr_taskqueue.c
==============================================================================
--- stable/9/sys/kern/subr_taskqueue.c  Mon Oct 10 11:25:11 2016        
(r306947)
+++ stable/9/sys/kern/subr_taskqueue.c  Mon Oct 10 11:27:59 2016        
(r306948)
@@ -70,6 +70,7 @@ struct taskqueue {
 #define        TQ_FLAGS_PENDING        (1 << 2)
 
 #define        DT_CALLOUT_ARMED        (1 << 0)
+#define        DT_DRAIN_IN_PROGRESS    (1 << 1)
 
 #define        TQ_LOCK(tq)                                                     
\
        do {                                                            \
@@ -244,7 +245,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);
        } else {
                if ((timeout_task->f & DT_CALLOUT_ARMED) != 0) {
@@ -434,8 +439,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
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to