Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 10 Oct 2016 04:57:33 +0000 (UTC)
From:      Julian Elischer <julian@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: r306935 - stable/10/sys/kern
Message-ID:  <201610100457.u9A4vXlI092950@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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"));



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