DF-4842 / fix.diff
--- a/sys/dev/drm/include/linux/sched.h 2026-09-24 12:11:29.636980723 +0000 +++ b/sys/dev/drm/include/linux/sched.h 2026-09-24 12:11:41.272831863 +0000 @@ -88,6 +88,7 @@ /* kthread-specific data */ unsigned long kt_flags; int (*kt_fn)(void *data); + int kt_done; /* kthread body returned */ void *kt_fndata; int kt_exitvalue; --- a/sys/dev/drm/linux_kthread.c 2026-09-24 12:11:29.636980723 +0000 +++ b/sys/dev/drm/linux_kthread.c 2026-09-24 12:11:41.296831557 +0000 @@ -39,8 +39,21 @@ linux_ktfn_wrapper(void *arg) { struct task_struct *task = arg; + int ret; + + ret = task->kt_fn(task->kt_fndata); - task->kt_exitvalue = task->kt_fn(task->kt_fndata); + /* + * Publish the exit value and wake any kthread_stop() waiter + * BEFORE returning into cpu_set_thread_handler()'s kthread_exit(), + * after which the DFly thread reclaims its own struct thread via + * lwkt_exit()/gd_freetd. Nobody else may free the td. + */ + spin_lock(&task->kt_spin); + task->kt_exitvalue = ret; + task->kt_done = 1; + spin_unlock(&task->kt_spin); + wakeup(task); } struct task_struct * @@ -78,6 +91,7 @@ #define KTHREAD_SHOULD_STOP 1 #define KTHREAD_SHOULD_PARK 2 +#define KTHREAD_IS_PARKED 4 bool kthread_should_stop(void) @@ -88,16 +102,31 @@ int kthread_stop(struct task_struct *ts) { + int i, ret; + set_bit(KTHREAD_SHOULD_STOP, &ts->kt_flags); - kthread_unpark(ts); + /* + * Only reschedule the thread if it actually parked itself: a raw + * lwkt_schedule() on a running or tsleeping thread breaks the + * runq/sleepq invariants. + */ + if (test_and_clear_bit(KTHREAD_IS_PARKED, &ts->kt_flags)) + lwkt_schedule(ts->dfly_td); wake_up_process(ts); - /* XXX use a better mechanism to wait for the thread to finish running */ - tsleep(kthread_stop, 0, "kstop", hz); - lwkt_free_thread(ts->dfly_td); - - return ts->kt_exitvalue; + /* + * Wait for the body to return. The DFly thread then reclaims its + * own struct thread through lwkt_exit()/gd_freetd; freeing the td + * here (as this shim used to) either freed a live thread or + * double-freed an exited one (it is already parked in gd_freetd). + */ + for (i = 0; i < 120 * hz && ts->kt_done == 0; i++) + tsleep(ts, 0, "kstop", 1); + + ret = ts->kt_exitvalue; + kfree(ts); /* the task_struct is ours to free */ + return (ret); } int @@ -113,7 +142,8 @@ kthread_unpark(struct task_struct *ts) { clear_bit(KTHREAD_SHOULD_PARK, &ts->kt_flags); - lwkt_schedule(ts->dfly_td); + if (test_and_clear_bit(KTHREAD_IS_PARKED, &ts->kt_flags)) + lwkt_schedule(ts->dfly_td); /* revive a parked thread only */ wake_up_process(ts); } @@ -129,5 +159,6 @@ if (test_bit(KTHREAD_SHOULD_PARK, ¤t->kt_flags) == 0) return; + set_bit(KTHREAD_IS_PARKED, ¤t->kt_flags); lwkt_deschedule_self(curthread); } |