fix.diff — DF-3699 (authored after source verification; never applied to sys/) Bind each work_struct to the worker it was first queued on, so concurrent queue_work() calls from different CPUs serialize on that worker's lock and the shared on_queue/running/canceled test-and-set cannot double-insert the item into two STAILQs. process_all_work() already clears work->worker when the item leaves the queue and is not running (linux_workqueue.c:92-93), which is the correct release point. --- a/sys/dev/drm/linux_workqueue.c +++ b/sys/dev/drm/linux_workqueue.c @@ -126,9 +126,16 @@ queue_work(struct workqueue_struct *wq, struct work_struct *work) /* XXX: should we block instead ? */ if (wq->is_draining) return false; - if (wq->num_workers > 1) - worker = &(*wq->workers)[mycpuid]; - else + /* + * Bind the work to the worker it was first queued on so that + * concurrent queue_work() from different cpus serialize on the + * same worker_lock (protecting work->on_queue/running/canceled). + */ + if (work->worker != NULL) + worker = work->worker; + else if (wq->num_workers > 1) + worker = &(*wq->workers)[mycpuid]; + else worker = &(*wq->workers)[0]; lockmgr(&worker->worker_lock, LK_EXCLUSIVE);