⬢ DragonFlyBSD Kernel Audit
DF-5200 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/drm/linux_kthread.c b/sys/dev/drm/linux_kthread.c
index 1990268..ab89fc3 100644
--- a/sys/dev/drm/linux_kthread.c
+++ b/sys/dev/drm/linux_kthread.c
@@ -78,6 +78,7 @@ kthread_run(int (*lfn)(void *), void *data, const char *namefmt, ...)
 
 #define KTHREAD_SHOULD_STOP 1
 #define KTHREAD_SHOULD_PARK 2
+#define KTHREAD_IS_PARKED   4
 
 bool
 kthread_should_stop(void)
@@ -103,17 +104,38 @@ kthread_stop(struct task_struct *ts)
 int
 kthread_park(struct task_struct *ts)
 {
+	int i;
+
 	set_bit(KTHREAD_SHOULD_PARK, &ts->kt_flags);
 	wake_up_process(ts);
 
-	return ts->kt_exitvalue;
+	/*
+	 * Linux's kthread_park() blocks until the target has actually
+	 * parked.  Callers rely on that quiescence contract (e.g.
+	 * drm_sched_entity_fini() parks the scheduler thread to make sure
+	 * it is not processing the entity it is about to free; the amdgpu
+	 * ring stop paths park all sched threads across a GPU reset).
+	 * Wait (bounded as a safety valve) for kthread_parkme() to
+	 * publish KTHREAD_IS_PARKED.
+	 */
+	for (i = 0; i < 20; i++) {		/* up to ~2 seconds */
+		if (test_bit(KTHREAD_IS_PARKED, &ts->kt_flags))
+			break;
+		tsleep(ts->dfly_td, 0, "ktparkw", hz / 10);
+	}
+
+	return 0;				/* success */
 }
 
 void
 kthread_unpark(struct task_struct *ts)
 {
 	clear_bit(KTHREAD_SHOULD_PARK, &ts->kt_flags);
-	lwkt_schedule(ts->dfly_td);
+	/*
+	 * Wake the parked thread (kthread_parkme() sleeps on its own
+	 * task_struct) without the raw lwkt_schedule() on a possibly
+	 * running/tsleeping thread.
+	 */
 	wake_up_process(ts);
 }
 
@@ -126,8 +148,19 @@ kthread_should_park(void)
 void
 kthread_parkme(void)
 {
+	/*
+	 * Actually park: publish the parked state for kthread_park(),
+	 * then sleep on our own task_struct until unparked.  The old
+	 * code only called lwkt_deschedule_self(), which is a no-op for
+	 * a running thread (TDF_RUNQ is clear) and provided no
+	 * quiescence whatsoever.
+	 */
 	if (test_bit(KTHREAD_SHOULD_PARK, &current->kt_flags) == 0)
 		return;
 
-	lwkt_deschedule_self(curthread);
+	set_bit(KTHREAD_IS_PARKED, &current->kt_flags);
+	wakeup(current->dfly_td);		/* release the parker */
+	while (test_bit(KTHREAD_SHOULD_PARK, &current->kt_flags))
+		tsleep(current, 0, "ktpark", hz);
+	clear_bit(KTHREAD_IS_PARKED, &current->kt_flags);
 }