Since commit e1fad12dcb66 ("sched/fair: Switch to task based throttle
model"), a throttled task is only dequeued when TWA_RESUME task_work
runs on its way back to user mode.
vhost workers never return to user mode, and vhost_task_fn() only runs
task_work via get_signal() when a signal is pending. A busy vhost worker
therefore ignores its cgroup's cpu.max, while its runtime is still
charged to the group, throttling the group's other tasks (typically the
VM's vCPUs) harder.
This also fixes memory.high and block cgroup throttling for vhost
workers, which are handled in resume_user_mode_work() as well.
Run pending notify-resume work at the top of the worker loop, as io-wq
does in io_run_task_work(). Commit 12013e3d4695 ("KVM: powerpc: Use
generic xfer to guest work function") fixed the same problem for KVM.
Fixes: e1fad12dcb66 ("sched/fair: Switch to task based throttle model")
Cc: [email protected] # 6.18+
Reviewed-by: Shakeel Butt <[email protected]>
Tested-by: Shakeel Butt <[email protected]>
Assisted-by: LLM
Signed-off-by: Aamir Ahmed <[email protected]>
---
v2:
- Also describe the memory.high and block cgroup throttling fixes
(Shakeel)
- Add Shakeel's Reviewed-by and Tested-by
- Update the code comment to match
- v1:
https://lore.kernel.org/as8p251mb00012469e6ecbea2b7d87bb0c8...@as8p251mb0001.eurp251.prod.outlook.com
Tested on x86_64 with a vhost_task-based reproducer: in a 0.2-CPU cgroup
a busy worker drops from 100% to 19% of a CPU. Reproducer available on
request.
kernel/vhost_task.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/kernel/vhost_task.c b/kernel/vhost_task.c
index 3717885a599..f63ac005214 100644
--- a/kernel/vhost_task.c
+++ b/kernel/vhost_task.c
@@ -6,6 +6,7 @@
#include <linux/completion.h>
#include <linux/sched/task.h>
#include <linux/sched/vhost_task.h>
+#include <linux/resume_user_mode.h>
#include <linux/sched/signal.h>
enum vhost_task_flags {
@@ -31,6 +32,14 @@ static int vhost_task_fn(void *data)
for (;;) {
bool did_work;
+ /*
+ * We never return to user mode, so TWA_RESUME task_work and
+ * memory.high/blkcg throttling would otherwise never run. Run
+ * them here, where no locks are held, like io-wq workers do.
+ */
+ if (test_thread_flag(TIF_NOTIFY_RESUME))
+ resume_user_mode_work(NULL);
+
if (signal_pending(current)) {
struct ksignal ksig;
base-commit: c03c1a14507dfaf8b856c2608f9ac216021fc222
--
2.55.0