| From c3e917377562343369ac0d5db184a23b3797f8e7 Mon Sep 17 00:00:00 2001 |
| From: Barret Rhoden <brho@google.com> |
| Date: Tue, 14 Apr 2020 18:29:20 -0400 |
| Subject: [PATCH] perf: Add cond_resched() to task_function_call() |
| |
| commit 2ed6edd33a214bca02bd2b45e3fc3038a059436b upstream. |
| |
| Under rare circumstances, task_function_call() can repeatedly fail and |
| cause a soft lockup. |
| |
| There is a slight race where the process is no longer running on the cpu |
| we targeted by the time remote_function() runs. The code will simply |
| try again. If we are very unlucky, this will continue to fail, until a |
| watchdog fires. This can happen in a heavily loaded, multi-core virtual |
| machine. |
| |
| Reported-by: syzbot+bb4935a5c09b5ff79940@syzkaller.appspotmail.com |
| Signed-off-by: Barret Rhoden <brho@google.com> |
| Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> |
| Link: https://lkml.kernel.org/r/20200414222920.121401-1-brho@google.com |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/kernel/events/core.c b/kernel/events/core.c |
| index ab650d239094..0e6168c4cb7a 100644 |
| --- a/kernel/events/core.c |
| +++ b/kernel/events/core.c |
| @@ -93,11 +93,11 @@ static void remote_function(void *data) |
| * @info: the function call argument |
| * |
| * Calls the function @func when the task is currently running. This might |
| - * be on the current CPU, which just calls the function directly |
| + * be on the current CPU, which just calls the function directly. This will |
| + * retry due to any failures in smp_call_function_single(), such as if the |
| + * task_cpu() goes offline concurrently. |
| * |
| - * returns: @func return value, or |
| - * -ESRCH - when the process isn't running |
| - * -EAGAIN - when the process moved away |
| + * returns @func return value or -ESRCH when the process isn't running |
| */ |
| static int |
| task_function_call(struct task_struct *p, remote_function_f func, void *info) |
| @@ -110,11 +110,16 @@ task_function_call(struct task_struct *p, remote_function_f func, void *info) |
| }; |
| int ret; |
| |
| - do { |
| - ret = smp_call_function_single(task_cpu(p), remote_function, &data, 1); |
| - if (!ret) |
| - ret = data.ret; |
| - } while (ret == -EAGAIN); |
| + for (;;) { |
| + ret = smp_call_function_single(task_cpu(p), remote_function, |
| + &data, 1); |
| + ret = !ret ? data.ret : -EAGAIN; |
| + |
| + if (ret != -EAGAIN) |
| + break; |
| + |
| + cond_resched(); |
| + } |
| |
| return ret; |
| } |
| -- |
| 2.27.0 |
| |