workqueue: Control intensive warning threshold through cmdline
authorXuewen Yan <xuewen.yan@unisoc.com>
Thu, 22 Feb 2024 07:28:08 +0000 (15:28 +0800)
committerTejun Heo <tj@kernel.org>
Thu, 22 Feb 2024 17:50:45 +0000 (07:50 -1000)
When CONFIG_WQ_CPU_INTENSIVE_REPORT is set, the kernel will report
the work functions which violate the intensive_threshold_us repeatedly.
And now, only when the violate times exceed 4 and is a power of 2,
the kernel warning could be triggered.

However, sometimes, even if a long work execution time occurs only once,
it may cause other work to be delayed for a long time. This may also
cause some problems sometimes.

In order to freely control the threshold of warninging, a boot argument
is added so that the user can control the warning threshold to be printed.
At the same time, keep the exponential backoff to prevent reporting too much.

By default, the warning threshold is 4.

tj: Updated kernel-parameters.txt description.

Signed-off-by: Xuewen Yan <xuewen.yan@unisoc.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
Documentation/admin-guide/kernel-parameters.txt
kernel/workqueue.c

index 6ee0f9a..c0e9abb 100644 (file)
                        threshold repeatedly. They are likely good
                        candidates for using WQ_UNBOUND workqueues instead.
 
+       workqueue.cpu_intensive_warning_thresh=<uint>
+                       If CONFIG_WQ_CPU_INTENSIVE_REPORT is set, the kernel
+                       will report the work functions which violate the
+                       intensive_threshold_us repeatedly. In order to prevent
+                       spurious warnings, start printing only after a work
+                       function has violated this threshold number of times.
+
+                       The default is 4 times. 0 disables the warning.
+
        workqueue.power_efficient
                        Per-cpu workqueues are generally preferred because
                        they show better performance thanks to cache
index 65a27be..38783e3 100644 (file)
@@ -409,6 +409,10 @@ static const char *wq_affn_names[WQ_AFFN_NR_TYPES] = {
  */
 static unsigned long wq_cpu_intensive_thresh_us = ULONG_MAX;
 module_param_named(cpu_intensive_thresh_us, wq_cpu_intensive_thresh_us, ulong, 0644);
+#ifdef CONFIG_WQ_CPU_INTENSIVE_REPORT
+static unsigned int wq_cpu_intensive_warning_thresh = 4;
+module_param_named(cpu_intensive_warning_thresh, wq_cpu_intensive_warning_thresh, uint, 0644);
+#endif
 
 /* see the comment above the definition of WQ_POWER_EFFICIENT */
 static bool wq_power_efficient = IS_ENABLED(CONFIG_WQ_POWER_EFFICIENT_DEFAULT);
@@ -1327,11 +1331,13 @@ restart:
                u64 cnt;
 
                /*
-                * Start reporting from the fourth time and back off
+                * Start reporting from the warning_thresh and back off
                 * exponentially.
                 */
                cnt = atomic64_inc_return_relaxed(&ent->cnt);
-               if (cnt >= 4 && is_power_of_2(cnt))
+               if (wq_cpu_intensive_warning_thresh &&
+                   cnt >= wq_cpu_intensive_warning_thresh &&
+                   is_power_of_2(cnt + 1 - wq_cpu_intensive_warning_thresh))
                        printk_deferred(KERN_WARNING "workqueue: %ps hogged CPU for >%luus %llu times, consider switching to WQ_UNBOUND\n",
                                        ent->func, wq_cpu_intensive_thresh_us,
                                        atomic64_read(&ent->cnt));
@@ -1360,10 +1366,12 @@ restart:
 
        ent = &wci_ents[wci_nr_ents++];
        ent->func = func;
-       atomic64_set(&ent->cnt, 1);
+       atomic64_set(&ent->cnt, 0);
        hash_add_rcu(wci_hash, &ent->hash_node, (unsigned long)func);
 
        raw_spin_unlock(&wci_lock);
+
+       goto restart;
 }
 
 #else  /* CONFIG_WQ_CPU_INTENSIVE_REPORT */