svn commit: r306935 - stable/10/sys/kern

Julian Elischer julian at FreeBSD.org
Mon Oct 10 04:57:34 UTC 2016


Author: julian
Date: Mon Oct 10 04:57:33 2016
New Revision: 306935
URL: https://svnweb.freebsd.org/changeset/base/306935

Log:
  While the thread is sleeping in taskqueue_drain_all() it is
  posible that the queue entry it is looking at is removed
  from the queue, but we make no effort to account
  for this. when we wake up we need to check it's still there.
  
  PR: 209580
  Sponsored by:	Panzura inc
  Differential Revision:	D8160

Modified:
  stable/10/sys/kern/subr_taskqueue.c

Modified: stable/10/sys/kern/subr_taskqueue.c
==============================================================================
--- stable/10/sys/kern/subr_taskqueue.c	Mon Oct 10 04:53:15 2016	(r306934)
+++ stable/10/sys/kern/subr_taskqueue.c	Mon Oct 10 04:57:33 2016	(r306935)
@@ -454,9 +454,26 @@ taskqueue_drain_all(struct taskqueue *qu
 
 	TQ_LOCK(queue);
 	task = STAILQ_LAST(&queue->tq_queue, task, ta_link);
-	if (task != NULL)
-		while (task->ta_pending != 0)
-			TQ_SLEEP(queue, task, &queue->tq_mutex, PWAIT, "-", 0);
+	while (task != NULL && task->ta_pending != 0) {
+		struct task *oldtask;
+		TQ_SLEEP(queue, task, &queue->tq_mutex, PWAIT, "-", 0);
+		/*
+		 * While we were asleeep the last entry may have been freed.
+		 * We need to check if it's still even in the queue.
+		 * Not perfect, but it's better than referencing bad memory.
+		 * first guess is the current 'end of queue' but if a new
+		 * item has been added we need to take the expensive path
+		 * Better fix in 11.
+		 */
+		oldtask = task;
+		if (oldtask !=
+		    (task = STAILQ_LAST(&queue->tq_queue, task, ta_link))) {
+			STAILQ_FOREACH(task, &queue->tq_queue, ta_link) {
+				if (task == oldtask)
+					break;
+			}
+		}
+	}
 	taskqueue_drain_running(queue);
 	KASSERT(STAILQ_EMPTY(&queue->tq_queue),
 	    ("taskqueue queue is not empty after draining"));


More information about the svn-src-all mailing list