DF-1977 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | diff --git a/sys/dev/drm/include/linux/workqueue.h b/sys/dev/drm/include/linux/workqueue.h --- a/sys/dev/drm/include/linux/workqueue.h +++ b/sys/dev/drm/include/linux/workqueue.h @@ -55,6 +55,8 @@ STAILQ_HEAD(ws_list, work_struct) ws_list_head; struct thread *worker_thread; struct lock worker_lock; + volatile int in_flight; /* # of works currently executing */ + volatile int exiting; /* set by destroy_workqueue */ }; struct workqueue_struct { --- a/sys/dev/drm/linux_workqueue.c +++ b/sys/dev/drm/linux_workqueue.c @@ -85,10 +85,12 @@ } work->running = true; + worker->in_flight++; lockmgr(&worker->worker_lock, LK_RELEASE); work->func(work); lwkt_yield(); lockmgr(&worker->worker_lock, LK_EXCLUSIVE); + worker->in_flight--; if (work->on_queue == false) work->worker = NULL; didcan = work->canceled; @@ -105,11 +107,13 @@ struct workqueue_worker *worker = arg; lockmgr(&worker->worker_lock, LK_EXCLUSIVE); - while (1) { + while (!worker->exiting) { process_all_work(worker); lksleep(worker, &worker->worker_lock, 0, "wqidle", 0); } lockmgr(&worker->worker_lock, LK_RELEASE); + worker->worker_thread = NULL; + lwkt_exit(); } /* @@ -243,13 +247,25 @@ void destroy_workqueue(struct workqueue_struct *wq) { + int i; + drain_workqueue(wq); -// wq->is_draining = true; -#if 0 /* XXX TODO */ - kill_all_threads; - kfree(wq->wq_threads); - kfree(wq); -#endif + wq->is_draining = true; + + for (i = 0; i < wq->num_workers; i++) { + struct workqueue_worker *worker = &(*wq->workers)[i]; + lockmgr(&worker->worker_lock, LK_EXCLUSIVE); + worker->exiting = true; + wakeup(worker); + lockmgr(&worker->worker_lock, LK_RELEASE); + } + for (i = 0; i < wq->num_workers; i++) { + struct workqueue_worker *worker = &(*wq->workers)[i]; + while (worker->worker_thread != NULL) + tsleep(worker, 0, "wqdie", 1); + } +\tkfree(wq->workers); +\tkfree(wq); } SYSINIT(linux_workqueue_init, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, init_workqueues, NULL); @@ -267,22 +283,23 @@ drain_workqueue(struct workqueue_struct *wq) { struct workqueue_worker *worker; + int i; wq->is_draining = true; - for (int i=0;i < wq->num_workers; i++) { + for (i = 0; i < wq->num_workers; i++) { worker = &(*wq->workers)[i]; - - lockmgr(&worker->worker_lock, LK_EXCLUSIVE); - while (!STAILQ_EMPTY(&worker->ws_list_head)) { - /* XXX: introduces latency */ + for (;;) { + bool done; + lockmgr(&worker->worker_lock, LK_EXCLUSIVE); + done = STAILQ_EMPTY(&worker->ws_list_head) && + (worker->in_flight == 0); + lockmgr(&worker->worker_lock, LK_RELEASE); + if (done) + break; tsleep(&drain_workqueue, 0, "wkdrain", 1); } - lockmgr(&worker->worker_lock, LK_RELEASE); } - - /* XXX: No more work will be queued. is that right ? */ -// wq->is_draining = false; } bool |