DF-5200 / fix.diff
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, ¤t->kt_flags) == 0) return; - lwkt_deschedule_self(curthread); + set_bit(KTHREAD_IS_PARKED, ¤t->kt_flags); + wakeup(current->dfly_td); /* release the parker */ + while (test_bit(KTHREAD_SHOULD_PARK, ¤t->kt_flags)) + tsleep(current, 0, "ktpark", hz); + clear_bit(KTHREAD_IS_PARKED, ¤t->kt_flags); } |