reading it could cause a lock-up if the list grow too large
without limiting the output.
"(truncated)" will be shown if the list is larger than the limit.
+.
+.It Sy spl_taskq_thread_timeout_ms Ns = Ns Sy 10000 Pq uint
+(Linux-only)
+How long a taskq has to have had no work before we tear it down.
+Previously, we would tear down a dynamic taskq worker as soon
+as we noticed it had no work, but it was observed that this led
+to a lot of churn in tearing down things we then immediately
+spawned anew.
+In practice, it seems any nonzero value will remove the vast
+majority of this churn, while the nontrivially larger value
+was chosen to help filter out the little remaining churn on
+a mostly idle system.
+Setting this value to
+.Sy 0
+will revert to the previous behavior.
.El
module_param(spl_taskq_thread_bind, int, 0644);
MODULE_PARM_DESC(spl_taskq_thread_bind, "Bind taskq thread to CPU by default");
+static uint_t spl_taskq_thread_timeout_ms = 10000;
+/* BEGIN CSTYLED */
+module_param(spl_taskq_thread_timeout_ms, uint, 0644);
+/* END CSTYLED */
+MODULE_PARM_DESC(spl_taskq_thread_timeout_ms,
+ "Time to require a dynamic thread be idle before it gets cleaned up");
static int spl_taskq_thread_dynamic = 1;
module_param(spl_taskq_thread_dynamic, int, 0444);
tqt_thread_list) == tqt)
return (0);
- return
+ int no_work =
((tq->tq_nspawn == 0) && /* No threads are being spawned */
(tq->tq_nactive == 0) && /* No threads are handling tasks */
(tq->tq_nthreads > 1) && /* More than 1 thread is running */
(!taskq_next_ent(tq)) && /* There are no pending tasks */
(spl_taskq_thread_dynamic)); /* Dynamic taskqs are allowed */
+
+ /*
+ * If we would have said stop before, let's instead wait a bit, maybe
+ * we'll see more work come our way soon...
+ */
+ if (no_work) {
+ /* if it's 0, we want the old behavior. */
+ /* if the taskq is being torn down, we also want to go away. */
+ if (spl_taskq_thread_timeout_ms == 0 ||
+ !(tq->tq_flags & TASKQ_ACTIVE))
+ return (1);
+ unsigned long lasttime = tq->lastshouldstop;
+ if (lasttime > 0) {
+ if (time_after(jiffies, lasttime +
+ msecs_to_jiffies(spl_taskq_thread_timeout_ms)))
+ return (1);
+ else
+ return (0);
+ } else {
+ tq->lastshouldstop = jiffies;
+ }
+ } else {
+ tq->lastshouldstop = 0;
+ }
+ return (0);
}
static int
tq->tq_flags = (flags | TASKQ_ACTIVE);
tq->tq_next_id = TASKQID_INITIAL;
tq->tq_lowest_id = TASKQID_INITIAL;
+ tq->lastshouldstop = 0;
INIT_LIST_HEAD(&tq->tq_free_list);
INIT_LIST_HEAD(&tq->tq_pend_list);
INIT_LIST_HEAD(&tq->tq_prio_list);