Skip to contentSkip to conversation

sched,livepatch: call klp_try_switch_task in __cond_resched

RFC In review Song Liu 7 participants 45 messages May 7, 2022 – May 13, 2022

Subsystems (2)
lore
Share & tools
lore

Song Liu

author maintainer
#0 lore raw

RFC 1/1 sched,livepatch: call klp_try_switch_task in __cond_resched

Busy kernel threads may block the transition of livepatch. Call klp_try_switch_task from __cond_resched to make the transition easier.

---
 include/linux/livepatch.h     | 2 ++
 kernel/livepatch/transition.c | 2 +-
 kernel/sched/core.c           | 3 +++
 3 files changed, 6 insertions(+), 1 deletion(-)
include/linux/livepatch.h +2 -0
index 2614247a9781..a9209f62550a 100644
@@ -236,6 +236,7 @@ int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs,
 			     unsigned int symindex, unsigned int secindex,
 			     const char *objname);
 
+bool klp_try_switch_task(struct task_struct *task);
 #else /* !CONFIG_LIVEPATCH */
 
 static inline int klp_module_coming(struct module *mod) { return 0; }
@@ -253,6 +254,7 @@ int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs,
 	return 0;
 }
 
+bool klp_try_switch_task(struct task_struct *task) { return true; }
 #endif /* CONFIG_LIVEPATCH */
 
 #endif /* _LINUX_LIVEPATCH_H_ */
kernel/livepatch/transition.c +1 -1
index f6310f848f34..4257a8eec64b 100644
@@ -278,7 +278,7 @@ static int klp_check_stack(struct task_struct *task, char *err_buf)
  * running, or it's sleeping on a to-be-patched or to-be-unpatched function, or
  * if the stack is unreliable, return false.
  */
-static bool klp_try_switch_task(struct task_struct *task)
+bool klp_try_switch_task(struct task_struct *task)
 {
 	static char err_buf[STACK_ERR_BUF_SIZE];
 	struct rq *rq;
kernel/sched/core.c +3 -0
index a7302b7b65f2..41d1c7a86912 100644
@@ -6990,6 +6990,9 @@ SYSCALL_DEFINE0(sched_yield)
 #if !defined(CONFIG_PREEMPTION) || defined(CONFIG_PREEMPT_DYNAMIC)
 int __sched __cond_resched(void)
 {
+	if (unlikely(klp_patch_pending(current)))
+		klp_try_switch_task(current);
+
 	if (should_resched(0)) {
 		preempt_schedule_common();
 		return 1;
Signature · 1 line
2.30.2
#1 lore raw
Quoted 4 lines · Song Liu

Busy kernel threads may block the transition of livepatch. Call klp_try_switch_task from __cond_resched to make the transition easier.

That seems like a useful idea given what we're seeing on some systems, but I do have a nitpick with your patch :)

kernel/sched/core.c SYSCALL_DEFINE0(sched_yield) · line 6998

Quoted 11 lines

+++ b/kernel/sched/core.c

kernel/sched/core.c +3 -0
@@ -6990,6 +6990,9 @@ SYSCALL_DEFINE0(sched_yield)
  #if !defined(CONFIG_PREEMPTION) || defined(CONFIG_PREEMPT_DYNAMIC)
  int __sched __cond_resched(void)
  {
+       if (unlikely(klp_patch_pending(current)))
+               klp_try_switch_task(current);
+
         if (should_resched(0)) {
                 preempt_schedule_common();
                 return 1;

While should_resched and klp_patch_pending check the same cache line (task->flags), now there are two separate conditionals on this.

Would it make sense to fold the tests for TIF_NEED_RESCHED and TIF_PATCH_PENDING int should_resched(), and then re-do the test for TIF_PATCH_PENDING only if should_resched() returns true?

#2 lore raw
Quoted 28 lines · Rik van Riel
Quoted 4 lines · Song Liu

Busy kernel threads may block the transition of livepatch. Call klp_try_switch_task from __cond_resched to make the transition easier.

That seems like a useful idea given what we're seeing on some systems, but I do have a nitpick with your patch :)

kernel/sched/core.c SYSCALL_DEFINE0(sched_yield) · line 6998

Quoted 11 lines

+++ b/kernel/sched/core.c

kernel/sched/core.c +3 -0
@@ -6990,6 +6990,9 @@ SYSCALL_DEFINE0(sched_yield)
 #if !defined(CONFIG_PREEMPTION) || defined(CONFIG_PREEMPT_DYNAMIC)
 int __sched __cond_resched(void)
 {
+       if (unlikely(klp_patch_pending(current)))
+               klp_try_switch_task(current);
+
        if (should_resched(0)) {
                preempt_schedule_common();
                return 1;

While should_resched and klp_patch_pending check the same cache line (task->flags), now there are two separate conditionals on this.

Quoted 4 lines

Would it make sense to fold the tests for TIF_NEED_RESCHED and TIF_PATCH_PENDING int should_resched(), and then re-do the test for TIF_PATCH_PENDING only if should_resched() returns true?

x86 has a different version of should_resched(), so I am not quite sure what’s the right way o modify shhould_resched(). OTOH, we can probably see should_resched() as-is and just move klp_patch_pending, like

int __sched __cond_resched(void)

{
        if (should_resched(0)) {
                if (unlikely(klp_patch_pending(current)))
                        klp_try_switch_task(current);

                preempt_schedule_common();
                return 1;
        }
#ifndef CONFIG_PREEMPT_RCU
        rcu_all_qs();
#endif
        return 0;
}

Given live patch user space usually waits for many seconds, I guess this should work?

Thanks,
Song

#3 lore raw
Quoted 27 lines · Song Liu
Quoted 27 lines · Rik van Riel
Quoted 4 lines · Song Liu

Busy kernel threads may block the transition of livepatch. Call klp_try_switch_task from __cond_resched to make the transition easier.

That seems like a useful idea given what we're seeing on some systems, but I do have a nitpick with your patch :)

Quoted 11 lines

+++ b/kernel/sched/core.c

kernel/sched/core.c +3 -0
@@ -6990,6 +6990,9 @@ SYSCALL_DEFINE0(sched_yield)
  #if !defined(CONFIG_PREEMPTION) ||
 defined(CONFIG_PREEMPT_DYNAMIC)
  int __sched __cond_resched(void)
  {
+       if (unlikely(klp_patch_pending(current)))
+               klp_try_switch_task(current);
+
         if (should_resched(0)) {
                 preempt_schedule_common();
Quoted 1 line

                return 1;

While should_resched and klp_patch_pending check the same cache line (task->flags), now there are two separate conditionals on this.

Quoted 6 lines
Quoted 4 lines

Would it make sense to fold the tests for TIF_NEED_RESCHED and TIF_PATCH_PENDING int should_resched(), and then re-do the test for TIF_PATCH_PENDING only if should_resched() returns true?

x86 has a different version of should_resched(),

Huh, I just looked at that, and the x86 should_resched() only seems to check that we _can_ resched, not whether we should...

/*
* Returns true when we need to resched and can (barring IRQ state). */
static __always_inline bool should_resched(int preempt_offset)

{
        return unlikely(raw_cpu_read_4(__preempt_count) ==

preempt_offset);
}

I wonder if that was intended, and why, or whether the x86 should_resched should also be checking for TIF_NEED_RESCHED?

If the latter, the check for TIF_PATCH_PENDING could just be merged there, too. Probably in the same macro called from both places.

Quoted 20 lines

so I am not
quite sure what’s the right way o modify shhould_resched(). OTOH, we can probably see should_resched() as-is and just move klp_patch_pending, like

int __sched __cond_resched(void)
{
        if (should_resched(0)) {
                if (unlikely(klp_patch_pending(current))) klp_try_switch_task(current);

                preempt_schedule_common();
                return 1;
        }
#ifndef CONFIG_PREEMPT_RCU
        rcu_all_qs();
#endif
        return 0;
}

Quoted 2 lines

Given live patch user space usually waits for many seconds, I guess this should work?

That should certainly work on x86, where should_resched seems to always return true when we can reschedule?

maintainer
#4 lore raw
Quoted 9 lines · Rik van Riel

Huh, I just looked at that, and the x86 should_resched() only seems to check that we _can_ resched, not whether we should...

/*
* Returns true when we need to resched and can (barring IRQ state). */
static __always_inline bool should_resched(int preempt_offset)

Quoted 8 lines
{
        return unlikely(raw_cpu_read_4(__preempt_count) ==

preempt_offset);
}

I wonder if that was intended, and why, or whether the x86 should_resched should also be checking for TIF_NEED_RESCHED?

No, it does what you think it should do, you're just getting confused by the inverted PREEMPT_NEED_RESCHED bit :-)

#5 lore raw
Quoted 4 lines · Peter Zijlstra

No, it does what you think it should do, you're just getting confused by
the inverted PREEMPT_NEED_RESCHED bit :-)

Fair enough, that makes sense! Thanks for pointing that out. That is a very clever optimization.

I suppose that should_resched() check is also something that KLP could use by periodically poking tasks that get stuck in the KLP transition (and are running) by simply calling set_preempt_need_resched(), after which that klp_patch_pending() check can happen only in the __cond_resched() path where we already call preempt_schedule_common() anyway?

Is that something that would work, and be low enough overhead in the general case to be acceptable?

maintainer
#6 lore raw
In reply to Song Liu
Quoted 2 lines · Song Liu

Busy kernel threads may block the transition of livepatch. Call klp_try_switch_task from __cond_resched to make the transition easier.

What will a PREEMPT=y kernel do? How is it not a problem there, and if it is, this will not help that.

That is; I don't think this can be right.

#7 lore raw
Quoted 12 lines · Peter Zijlstra
Quoted 2 lines · Song Liu

Busy kernel threads may block the transition of livepatch. Call klp_try_switch_task from __cond_resched to make the transition easier.

What will a PREEMPT=y kernel do? How is it not a problem there, and if it is, this will not help that.

That is; I don't think this can be right.

I guess on PREEMPT=y kernel, we can simply preempt the long running kernel thread and check the transition?

In this case (PREEMPT=n), we see a long running kernel thread could not finish the transition. It calls cond_resched() and gets rescheduled (moves among different cores). However, it never finishes the transition, because live patch doesn’t get a chance to check the stack.

Does this answer the question?

Thanks,
Song

maintainer
#8 lore raw
Quoted 8 lines · Song Liu
Quoted 8 lines · Peter Zijlstra
Quoted 2 lines · Song Liu

Busy kernel threads may block the transition of livepatch. Call klp_try_switch_task from __cond_resched to make the transition easier.

Quoted 7 lines
Quoted 4 lines

What will a PREEMPT=y kernel do? How is it not a problem there, and if it is, this will not help that.

That is; I don't think this can be right.

I guess on PREEMPT=y kernel, we can simply preempt the long running kernel thread and check the transition?

This is not a guessing game.

Quoted 6 lines

In this case (PREEMPT=n), we see a long running kernel thread could not finish the transition. It calls cond_resched() and gets rescheduled (moves among different cores). However, it never finishes the transition, because live patch doesn’t get a chance to check the stack.

Does this answer the question?

Not really. There is no difference between an explicit preemption point (cond_resched) or an involuntary preemption point (PREEMPT=y).

So unless you can *exactly* say why it isn't a problem on PREEMPT=y, none of this makes any sense.

#9 lore raw
Quoted 15 lines · Peter Zijlstra
Quoted 5 lines · Song Liu
Quoted 5 lines

On May 9, 2022, at 12:04 AM, Peter Zijlstra
<[email protected]> wrote:

Quoted 8 lines
Quoted 8 lines · Song Liu
Quoted 3 lines

Busy kernel threads may block the transition of livepatch. Call klp_try_switch_task from __cond_resched to make the transition easier.

What will a PREEMPT=y kernel do? How is it not a problem there, and if
it is, this will not help that.

Quoted 6 lines

Not really. There is no difference between an explicit preemption point
(cond_resched) or an involuntary preemption point (PREEMPT=y).

So unless you can *exactly* say why it isn't a problem on PREEMPT=y, none of this makes any sense.

I suspect it is a problem on PREEMPT=y too, but is there some sort of fairly light weight (in terms of stuff we need to add to the kernel) solution that could solve both?

Do we have some real time per-CPU kernel threads we could just issue a NOOP call to, which would preempt long-running kernel threads (like a kworker with oodles of work to do)?

Could the stopper workqueue be a suitable tool for this job?

maintainer
#10 lore raw
Quoted 27 lines · Rik van Riel
Quoted 15 lines · Peter Zijlstra
Quoted 5 lines · Song Liu
Quoted 5 lines

On May 9, 2022, at 12:04 AM, Peter Zijlstra
<[email protected]> wrote:

Quoted 8 lines
Quoted 8 lines · Song Liu
Quoted 3 lines

Busy kernel threads may block the transition of livepatch. Call klp_try_switch_task from __cond_resched to make the transition easier.

What will a PREEMPT=y kernel do? How is it not a problem there, and if
it is, this will not help that.

Quoted 6 lines

Not really. There is no difference between an explicit preemption point
(cond_resched) or an involuntary preemption point (PREEMPT=y).

So unless you can *exactly* say why it isn't a problem on PREEMPT=y, none of this makes any sense.

I suspect it is a problem on PREEMPT=y too, but is there some sort of fairly light weight (in terms of stuff we need to add to the kernel) solution that could solve both?

Quoted 5 lines

Do we have some real time per-CPU kernel threads we could just issue a NOOP call to, which would preempt long-running kernel threads (like a kworker with oodles of work to do)?

Could the stopper workqueue be a suitable tool for this job?

An interesting solution would be to queue irq_work in CPU that is occupied by the long-running kernel task.

It might be queued from klp_try_complete_transition() that is called from the regular klp_transition_work_fn().

Then the task might try to migrate itself from the irq_work. But the problem is that stack_trace_save_tsk_reliable() probably will not be able to store a reliable backtrace for the interrupted task.

So, we might really need to stop the task (CPU). But there still might be problem if stack_trace_save_tsk_reliable() will consider the stack as reliable.

Best Regards,
Petr

maintainer
#11 lore raw
In reply to Song Liu
Quoted 2 lines · Song Liu

Busy kernel threads may block the transition of livepatch. Call klp_try_switch_task from __cond_resched to make the transition easier.

Do you have some numbers how this speeds up the transition and how it slows down the scheduler, please?

cond_resched() is typically called in cycles with many interactions where the task might spend a lot of time. There are two possibilities. cond_resched() is called in:

+ livepatched function

In this case, klp_try_switch_task(current) will always fail. And it will non-necessarily slow down every iteration by checking the very same stack.

+ non-livepatched function

In this case, the transition will succeed on the first attempt. OK, but it would succeed also without that patch. The task would most likely sleep in this cond_resched() so that it might be successfully transitioned on the next occasion.

From my POV this patch this patch brings more harm than good.

Note that scheduling is a fast path. It is repeated zillion-times on any system. But livepatch transition is a slow path. It does not matter if it takes 1 second or 1 hour.

Best Regards,
Petr

#12 lore raw
Quoted 10 lines · Petr Mladek
Quoted 2 lines · Song Liu

Busy kernel threads may block the transition of livepatch. Call klp_try_switch_task from __cond_resched to make the transition easier.

Do you have some numbers how this speeds up the transition and how it slows down the scheduler, please?

We don’t have number on how much this would slow down the scheduler. For the transition, we see cases where the transition cannot finish with in 60 seconds (how much "kpatch load" waits by default).

Quoted 12 lines

cond_resched() is typically called in cycles with many interactions where the task might spend a lot of time. There are two possibilities. cond_resched() is called in:

+ livepatched function

In this case, klp_try_switch_task(current) will always fail. And it will non-necessarily slow down every iteration by checking the very same stack.

Quoted 6 lines

+ non-livepatched function

In this case, the transition will succeed on the first attempt. OK, but it would succeed also without that patch. The task would most likely sleep in this cond_resched() so that it might be successfully transitioned on the next occasion.

We are in the non-live patched case. But the transition didn’t happen in time, because the kernel thread doesn’t go to sleep. While there is clearly something weird with this thread, we think live patch should work because the thread does call cond_resched from time to time.

Thanks,
Song

Quoted 10 lines

From my POV this patch this patch brings more harm than good.

Note that scheduling is a fast path. It is repeated zillion-times on any system. But livepatch transition is a slow path. It does not matter if it takes 1 second or 1 hour.

Best Regards,
Petr

maintainer
#13 lore raw
Quoted 8 lines · Song Liu
Quoted 8 lines · Petr Mladek
Quoted 2 lines · Song Liu

Busy kernel threads may block the transition of livepatch. Call klp_try_switch_task from __cond_resched to make the transition easier.

Quoted 6 lines
Quoted 2 lines

Do you have some numbers how this speeds up the transition and how it slows down the scheduler, please?

We don’t have number on how much this would slow down the scheduler. For the transition, we see cases where the transition cannot finish with in 60 seconds (how much "kpatch load" waits by default).

60s might be too low limit, see below.

Quoted 18 lines
Quoted 11 lines

cond_resched() is typically called in cycles with many interactions where the task might spend a lot of time. There are two possibilities. cond_resched() is called in:

+ livepatched function

In this case, klp_try_switch_task(current) will always fail. And it will non-necessarily slow down every iteration by checking the very same stack.

Quoted 6 lines

+ non-livepatched function

In this case, the transition will succeed on the first attempt. OK, but it would succeed also without that patch. The task would most likely sleep in this cond_resched() so that it might be successfully transitioned on the next occasion.

Quoted 4 lines

We are in the non-live patched case. But the transition didn’t happen in time, because the kernel thread doesn’t go to sleep. While there is clearly something weird with this thread, we think live patch should work because the thread does call cond_resched from time to time.

I guess that it goes to sleep. Otherwise it would trigger soft lockup report if you have the watchdog enabled.

IMHO, the problem is that klp_transition_work_fn() tries the transition "only" once per second, see

void klp_try_complete_transition(void)
{
[...]

		schedule_delayed_work(&klp_transition_work,
				      round_jiffies_relative(HZ));

[...]
}

It means that there are "only" 60 attempts to migrate the busy process. It fails when the process is in the running state or sleeping in a livepatched function. There is a _non-zero_ chance of a bad luck.

It would be great to measure how long it will take to complete the transition if you remove the limit 60s.

Anyway, the limit 60s looks like a bad idea to me. It is too low. For example, we do not use any limit at all in SUSE products. And the only report was that some thread from a 3rd party module could not be migrated. It was stuck with a livepatched function on the stack. The kthread had really problematic design. I am afraid that even this patch would not help in that case.

Now, back to the limit. There are basically two problems when the transition takes too long:

+ It blocks another transition. But the frequency of new livepatches it typically counted in days or even weeks.

+ It means that a process is running the buggy/vulnerable code longer time. But few hours should be acceptable given how long it takes to prepare the livepatch.

Also it looks better to have 99.9% of processes running the fixed code that revert the fix just because a single process needs longer time to get transitioned.

I could imagine that there really is a process that is almost impossible to livepatch. It might get worse on NO_HZ system. The question is it happens in the real life.

I would personally start with prolonging or removing the limit.

Best Regards,
Petr

#14 lore raw
Quoted 12 lines · Petr Mladek

IMHO, the problem is that klp_transition_work_fn() tries the transition "only" once per second, see

void klp_try_complete_transition(void)
{
[...]
                schedule_delayed_work(&klp_transition_work, round_jiffies_relative(HZ)); [...]
}

Quoted 5 lines

It means that there are "only" 60 attempts to migrate the busy process.
It fails when the process is in the running state or sleeping in a livepatched function. There is a _non-zero_ chance of a bad luck.

We are definitely hitting that non-zero chance :)

Quoted 1 line

Anyway, the limit 60s looks like a bad idea to me. It is too low.

That has its own issues, though. System management software tracks whether kpatch succeeds, and a run of the system management software will not complete until all of the commands it has run have completed.

One reason for this is that allowing system management software to just fork more and more things that might potentially get stuck is that you never want your system management software to come even close to resembling a fork bomb :)

Rollout of the next config change to a system should not be blocked on KLP completion.

I think the best approach for us might be to just track what is causing the transition failures, and send in trivial patches to make the outer loop in such kernel threads do the same KLP transition the idle task already does.

maintainer
#15 lore raw
Quoted 28 lines · Rik van Riel
Quoted 12 lines · Petr Mladek

IMHO, the problem is that klp_transition_work_fn() tries the transition "only" once per second, see

void klp_try_complete_transition(void)
{
[...]
                schedule_delayed_work(&klp_transition_work, round_jiffies_relative(HZ)); [...]
}

Quoted 5 lines

It means that there are "only" 60 attempts to migrate the busy process.
It fails when the process is in the running state or sleeping in a livepatched function. There is a _non-zero_ chance of a bad luck.

We are definitely hitting that non-zero chance :)

Quoted 1 line

Anyway, the limit 60s looks like a bad idea to me. It is too low.

That has its own issues, though. System management software tracks whether kpatch succeeds, and a run of the system management software will not complete until all of the commands it has run have completed.

Quoted 7 lines

One reason for this is that allowing system management software to just fork more and more things that might potentially get stuck is that you never want your system management software to come even close to resembling a fork bomb :)

Rollout of the next config change to a system should not be blocked on KLP completion.

Makes sense.

Quoted 4 lines

I think the best approach for us might be to just track what is causing the transition failures, and send in trivial patches to make the outer loop in such kernel threads do the same KLP transition the idle task already does.

I am afraid that is a way to hell. We might end up in doing really crazy things if we want to complete the transition in one minute.

The great thing about the current approach is that it tries to livepatch the system without too much disruption. The more we try to speed up the transition the more we might disrupt the system. Not to say about the code complexity and potential bugs.

IMHO a better approach is to fix your management system. The task is done when the livepatch module is loaded.

If you want to know that there is some problem. Then the livepatch code might write some warning when the transition has not finished within some reasonable time frame (1 hour or so). It might be monitored the same way as the messages from various watchdogs, ...

Best Regards,
Petr

#16 lore raw
Quoted 4 lines · Petr Mladek
Quoted 2 lines · Rik van Riel

On Tue, 2022-05-10 at 09:56 +0200, Petr Mladek wrote:

Quoted 9 lines
Quoted 4 lines

I think the best approach for us might be to just track what is causing the transition failures, and send in trivial patches to make the outer loop in such kernel threads do the same KLP transition the idle task already does.

I am afraid that is a way to hell. We might end up in doing really crazy things if we want to complete the transition in one minute.

Now I wonder if we could just hook up a preempt notifier for kernel live patches. All the distro kernels already need the preempt notifier for KVM, anyway...

Is it crazy? Maybe a little.

Is it self contained, and something that could be done without inserting any extra code into a hot path while not in the middle of a KLP transition? Yes.

I'd be happy to come up with a patch that does that, unless anybody has good reasons I should not :)

maintainer
#17 lore raw
Quoted 18 lines · Rik van Riel
Quoted 4 lines · Petr Mladek
Quoted 2 lines · Rik van Riel

On Tue, 2022-05-10 at 09:56 +0200, Petr Mladek wrote:

Quoted 9 lines
Quoted 4 lines

I think the best approach for us might be to just track what is causing the transition failures, and send in trivial patches to make the outer loop in such kernel threads do the same KLP transition the idle task already does.

I am afraid that is a way to hell. We might end up in doing really crazy things if we want to complete the transition in one minute.

Now I wonder if we could just hook up a preempt notifier for kernel live patches. All the distro kernels already need the preempt notifier for KVM, anyway...

Quoted 8 lines

Is it crazy? Maybe a little.

Is it self contained, and something that could be done without inserting any extra code into a hot path while not in the middle of a KLP transition? Yes.

I'd be happy to come up with a patch that does that, unless anybody has good reasons I should not :)

I wouldn't be opposed to that, but how does it solve this problem? If as Peter said cond_resched() can be a NOP, then preemption would have to be from an interrupt, in which case frame pointers aren't reliable.

Signature · 1 line
Josh
#18 lore raw
Quoted 7 lines · Josh Poimboeuf
Quoted 5 lines · Rik van Riel
Quoted 1 line

Now I wonder if we could just hook up a preempt notifier for kernel live patches. All the distro kernels already need the preempt notifier for KVM, anyway...

Quoted 6 lines

I wouldn't be opposed to that, but how does it solve this problem? If
as Peter said cond_resched() can be a NOP, then preemption would have to
be from an interrupt, in which case frame pointers aren't reliable.

The systems where we are seeing problems do not, as far as I know, throw softlockup errors, so the kworker threads that fail to transition to the new KLP version are sleeping and getting scheduled out at times.

A KLP transition preempt notifier would help those kernel threads transition to the new KLP version at any time they reschedule.

How much it will help is hard to predict, but I should be able to get results from a fairly large sample size of systems within a few weeks :)

maintainer
#19 lore raw
Quoted 14 lines · Rik van Riel
Quoted 7 lines · Josh Poimboeuf
Quoted 5 lines · Rik van Riel
Quoted 1 line

Now I wonder if we could just hook up a preempt notifier for kernel live patches. All the distro kernels already need the preempt notifier for KVM, anyway...

Quoted 6 lines

I wouldn't be opposed to that, but how does it solve this problem? If
as Peter said cond_resched() can be a NOP, then preemption would have to
be from an interrupt, in which case frame pointers aren't reliable.

Quoted 4 lines

The systems where we are seeing problems do not, as far as I know, throw softlockup errors, so the kworker threads that fail to transition to the new KLP version are sleeping and getting scheduled out at times.

Are they sleeping due to an explicit call to cond_resched()?

Quoted 3 lines

A KLP transition preempt notifier would help those kernel threads transition to the new KLP version at any time they reschedule.

... unless cond_resched() is a no-op due to CONFIG_PREEMPT?

Quoted 3 lines

How much it will help is hard to predict, but I should be able to get results from a fairly large sample size of systems within a few weeks :)

As Peter said, keep in mind that we will need to fix other cases beyond Facebook, i.e., CONFIG_PREEMPT combined with non-x86 arches which don't have ORC so they can't reliably unwind from an IRQ.

Signature · 1 line
Josh
#20 lore raw
Quoted 19 lines · Josh Poimboeuf
Quoted 14 lines · Rik van Riel
Quoted 13 lines · Josh Poimboeuf
Quoted 5 lines · Rik van Riel
Quoted 1 line

Now I wonder if we could just hook up a preempt notifier for kernel live patches. All the distro kernels already need the preempt notifier for KVM, anyway...

I wouldn't be opposed to that, but how does it solve this problem? If
as Peter said cond_resched() can be a NOP, then preemption would have to
be from an interrupt, in which case frame pointers aren't reliable.

Quoted 6 lines
Quoted 4 lines

The systems where we are seeing problems do not, as far as I know, throw softlockup errors, so the kworker threads that fail to transition to the new KLP version are sleeping and getting scheduled out at times.

Are they sleeping due to an explicit call to cond_resched()?

In this case, yes. The thread calls cond_resched().

Quoted 6 lines
Quoted 4 lines

A KLP transition preempt notifier would help those kernel threads transition to the new KLP version at any time they reschedule.

... unless cond_resched() is a no-op due to CONFIG_PREEMPT?

Based on my understanding (and a few other folks we chatted with), a kernel thread can legally run for extended time, as long as it calls cond_resched() at a reasonable frequency. Therefore, I think we should be able to patch such thread easily, unless it calls cond_resched() with being-patched function in the stack, of course.

OTOH, Petr's mindset of allowing many minutes for the patch transition is new to me. I need to think more about it. Josh, what’s you opinion on this? IIUC, kpatch is designed to only wait up to 60 seconds (no option to overwrite the time).

Quoted 8 lines
Quoted 4 lines

How much it will help is hard to predict, but I should be able to get results from a fairly large sample size of systems within a few weeks :)

As Peter said, keep in mind that we will need to fix other cases beyond Facebook, i.e., CONFIG_PREEMPT combined with non-x86 arches which don't have ORC so they can't reliably unwind from an IRQ.

I think livepatch transition may fail in different cases, and we don't need to address all of them in one shoot. Fixing some cases is an improvement as long as we don't slow down other cases. I understand that adding tiny overhead to __cond_resched() may end up as a visible regression. But maybe adding it to preempt_schedule_common() is light enough?

Did I miss/misunderstand something?

Thanks,
Song

maintainer
#21 lore raw
Quoted 4 lines · Song Liu
Quoted 4 lines
Quoted 3 lines

A KLP transition preempt notifier would help those kernel threads transition to the new KLP version at any time they reschedule.

Quoted 8 lines
Quoted 1 line

... unless cond_resched() is a no-op due to CONFIG_PREEMPT?

Based on my understanding (and a few other folks we chatted with), a kernel thread can legally run for extended time, as long as it calls cond_resched() at a reasonable frequency. Therefore, I think we should be able to patch such thread easily, unless it calls cond_resched() with being-patched function in the stack, of course.

But again, with CONFIG_PREEMPT, that doesn't work.

Quoted 4 lines

OTOH, Petr's mindset of allowing many minutes for the patch transition is new to me. I need to think more about it. Josh, what’s you opinion on this? IIUC, kpatch is designed to only wait up to 60 seconds (no option to overwrite the time).

I wouldn't be necessarily opposed to changing the kpatch timeout to something bigger, or eliminating it altogether in favor of a WARN() after x minutes.

Quoted 8 lines
Quoted 7 lines
Quoted 3 lines

How much it will help is hard to predict, but I should be able to get results from a fairly large sample size of systems within a few weeks :)

As Peter said, keep in mind that we will need to fix other cases beyond Facebook, i.e., CONFIG_PREEMPT combined with non-x86 arches which don't have ORC so they can't reliably unwind from an IRQ.

Quoted 8 lines

I think livepatch transition may fail in different cases, and we don't need to address all of them in one shoot. Fixing some cases is an improvement as long as we don't slow down other cases. I understand that adding tiny overhead to __cond_resched() may end up as a visible regression. But maybe adding it to preempt_schedule_common() is light enough?

Did I miss/misunderstand something?

If it's a real bug, we should fix it everywhere, not just for Facebook. Otherwise CONFIG_PREEMPT and/or non-x86 arches become second-class citizens.

Signature · 1 line
Josh
#22 lore raw
Quoted 44 lines · Josh Poimboeuf
Quoted 4 lines · Song Liu
Quoted 4 lines
Quoted 3 lines

A KLP transition preempt notifier would help those kernel threads transition to the new KLP version at any time they reschedule.

Quoted 8 lines
Quoted 1 line

... unless cond_resched() is a no-op due to CONFIG_PREEMPT?

Based on my understanding (and a few other folks we chatted with), a kernel thread can legally run for extended time, as long as it calls cond_resched() at a reasonable frequency. Therefore, I think we should be able to patch such thread easily, unless it calls cond_resched() with being-patched function in the stack, of course.

But again, with CONFIG_PREEMPT, that doesn't work.

Quoted 4 lines

OTOH, Petr's mindset of allowing many minutes for the patch transition is new to me. I need to think more about it. Josh, what’s you opinion on this? IIUC, kpatch is designed to only wait up to 60 seconds (no option to overwrite the time).

I wouldn't be necessarily opposed to changing the kpatch timeout to something bigger, or eliminating it altogether in favor of a WARN() after x minutes.

Quoted 15 lines
Quoted 7 lines
Quoted 3 lines

How much it will help is hard to predict, but I should be able to get results from a fairly large sample size of systems within a few weeks :)

As Peter said, keep in mind that we will need to fix other cases beyond Facebook, i.e., CONFIG_PREEMPT combined with non-x86 arches which don't have ORC so they can't reliably unwind from an IRQ.

I think livepatch transition may fail in different cases, and we don't need to address all of them in one shoot. Fixing some cases is an improvement as long as we don't slow down other cases. I understand that adding tiny overhead to __cond_resched() may end up as a visible regression. But maybe adding it to preempt_schedule_common() is light enough?

Quoted 5 lines
Quoted 1 line

Did I miss/misunderstand something?

If it's a real bug, we should fix it everywhere, not just for Facebook. Otherwise CONFIG_PREEMPT and/or non-x86 arches become second-class citizens.

I think "is it a real bug?" is the top question for me. So maybe we should take a step back.

The behavior we see is: A busy kernel thread blocks klp transition for more than a minute. But the transition eventually succeeded after < 10 retries on most systems. The kernel thread is well-behaved, as it calls cond_resched() at a reasonable frequency, so this is not a deadlock.

If I understand Petr correctly, this behavior is expected, and thus is not a bug or issue for the livepatch subsystem. This is different to our original expectation, but if this is what we agree on, we will look into ways to incorporate long wait time for patch transition in our automations.

OTOH, if we think this is a suboptimal behavior (not necessarily a bug, IIUC), we should consider improve it. I personally don’t think shipping an improvement to one configuration makes other configurations second-class citizens. And, it is possible PREEMPT kernel does NOT even have such suboptimal behavior, right? (I really don't know.)

So, if we come back to the same question: is this a bug (or a suboptimal behavior that worth fixing)? If so, we are open to any solution that would also help PREEMPT and/or non-x86 arches.

Lastly, maybe a really naive question: does the following also helps PREEMPT=y configurations?

Thanks,
Song

kernel/sched/core.c +3 -0
index a7302b7b65f2..cc9b1c9c02ba 100644
@@ -5226,6 +5226,9 @@ void __sched schedule_preempt_disabled(void)
 
 static void __sched notrace preempt_schedule_common(void)
 {
+       if (unlikely(klp_patch_pending(current)))
+               klp_try_switch_task(current);
+
        do {
                /*
                 * Because the function tracer can trace preempt_count_sub()
maintainer
#23 lore raw
Quoted 13 lines · Song Liu
Quoted 3 lines

If it's a real bug, we should fix it everywhere, not just for Facebook. Otherwise CONFIG_PREEMPT and/or non-x86 arches become second-class citizens.

I think "is it a real bug?" is the top question for me. So maybe we should take a step back.

The behavior we see is: A busy kernel thread blocks klp transition for more than a minute. But the transition eventually succeeded after < 10 retries on most systems. The kernel thread is well-behaved, as it calls cond_resched() at a reasonable frequency, so this is not a deadlock.

Quoted 5 lines

If I understand Petr correctly, this behavior is expected, and thus is not a bug or issue for the livepatch subsystem. This is different to our original expectation, but if this is what we agree on, we will look into ways to incorporate long wait time for patch transition in our automations.

That's how we've traditionally looked at it, though apparently Red Hat and SUSE have implemented different ideas of what a long wait time is.

In practice, one minute has always been enough for all of kpatch's users -- AFAIK, everybody except SUSE -- up until now.

That timeout is not set in stone, and can be extended if needed, either with "kpatch load" waiting longer, or with it returning immediately and instead reporting livepatch stalls via WARN().

The latter option is a bigger change in behavior, since any errors will be delayed and reported in a different way, but if other users are ok with that, it's fine with me.

Though, these options might be considered workarounds, as it's theoretically possible for a kthread to be CPU-bound indefinitely, beyond any arbitrarily chosen timeout. But maybe that's not realistic beyond a certain timeout value of X and we don't care? I dunno.

Quoted 5 lines

OTOH, if we think this is a suboptimal behavior (not necessarily a bug, IIUC), we should consider improve it. I personally don’t think shipping an improvement to one configuration makes other configurations second-class citizens. And, it is possible PREEMPT kernel does NOT even have such suboptimal behavior, right? (I really don't know.)

No, PREEMPT will have the same problem. Preempting a kthread doesn't patch it unless the task happens to still be blocked during the next invocation of klp_transition_work_fn().

Quoted 3 lines

So, if we come back to the same question: is this a bug (or a suboptimal behavior that worth fixing)? If so, we are open to any solution that would also help PREEMPT and/or non-x86 arches.

You're obviously trying to fix a real world problem. Whether you call that issue a bug, or "suboptimal behavior", it's still a problem.

As you said, the kthread is acting within the allowed parameters of how a kthread should behave. But "kpatch load" will fail. That sounds broken to me. We need to figure out a way to fix that for all configs/arches.

Quoted 2 lines

Lastly, maybe a really naive question: does the following also helps PREEMPT=y configurations?

As I have been trying to say, that won't work for PREEMPT+!ORC, because, when the kthread gets preempted, the stack trace will be attempted from an IRQ and will be reported as unreliable.

Ideally we'd have the ORC unwinder for all arches, that would make this much easier. But we're not there yet.

Signature · 1 line
Josh
maintainer
#24 lore raw
Quoted 20 lines · Josh Poimboeuf
Quoted 13 lines · Song Liu
Quoted 3 lines

If it's a real bug, we should fix it everywhere, not just for Facebook. Otherwise CONFIG_PREEMPT and/or non-x86 arches become second-class citizens.

I think "is it a real bug?" is the top question for me. So maybe we should take a step back.

The behavior we see is: A busy kernel thread blocks klp transition for more than a minute. But the transition eventually succeeded after < 10 retries on most systems. The kernel thread is well-behaved, as it calls cond_resched() at a reasonable frequency, so this is not a deadlock.

Quoted 5 lines

If I understand Petr correctly, this behavior is expected, and thus is not a bug or issue for the livepatch subsystem. This is different to our original expectation, but if this is what we agree on, we will look into ways to incorporate long wait time for patch transition in our automations.

Quoted 5 lines

That's how we've traditionally looked at it, though apparently Red Hat and SUSE have implemented different ideas of what a long wait time is.

In practice, one minute has always been enough for all of kpatch's users -- AFAIK, everybody except SUSE -- up until now.

I am actually surprised that nobody met the problem yet. There are "only" 60 attempts to transition the pending tasks.

Well, the problem is mainly with kthreads. User space processes are migrated also on the kernel boundary. And the fake signal is likely pretty effective here. And it probably is not that common that a kthread would occupy a single CPU all the time.

Quoted 4 lines

Though, these options might be considered workarounds, as it's theoretically possible for a kthread to be CPU-bound indefinitely, beyond any arbitrarily chosen timeout. But maybe that's not realistic beyond a certain timeout value of X and we don't care? I dunno.

I agree that it might happen theoretically. And it would be great to be prepared for this. My only concern is the complexity and risk. We should know that it is worth it.

Quoted 3 lines

As I have been trying to say, that won't work for PREEMPT+!ORC, because, when the kthread gets preempted, the stack trace will be attempted from an IRQ and will be reported as unreliable.

This limits the range of possible solutions quite a lot. But it is how it is.

Quoted 2 lines

Ideally we'd have the ORC unwinder for all arches, that would make this much easier. But we're not there yet.

The alternative solution is that the process has to migrate itself on some safe location.

One crazy idea. It still might be possible to find the called functions on the stack even when it is not reliable. Then it might be possible to add another ftrace handler on these found functions. This other ftrace handler might migrate the task when it calls this function again.

It assumes that the task will call the same functions again and again. Also it might require that the tasks checks its own stack from the ftrace handler. I am not sure if this is possible.

There might be other variants of this approach.

Best Regards,
Petr

#25 lore raw
Quoted 25 lines · Petr Mladek
Quoted 20 lines · Josh Poimboeuf
Quoted 18 lines · Song Liu
Quoted 3 lines

If it's a real bug, we should fix it everywhere, not just for Facebook. Otherwise CONFIG_PREEMPT and/or non-x86 arches become second-class citizens.

I think "is it a real bug?" is the top question for me. So maybe we should take a step back.

The behavior we see is: A busy kernel thread blocks klp transition for more than a minute. But the transition eventually succeeded after < 10 retries on most systems. The kernel thread is well-behaved, as it calls cond_resched() at a reasonable frequency, so this is not a deadlock.

If I understand Petr correctly, this behavior is expected, and thus is not a bug or issue for the livepatch subsystem. This is different to our original expectation, but if this is what we agree on, we will look into ways to incorporate long wait time for patch transition in our automations.

Quoted 8 lines
Quoted 5 lines

That's how we've traditionally looked at it, though apparently Red Hat and SUSE have implemented different ideas of what a long wait time is.

In practice, one minute has always been enough for all of kpatch's users -- AFAIK, everybody except SUSE -- up until now.

I am actually surprised that nobody met the problem yet. There are "only" 60 attempts to transition the pending tasks.

Maybe we should consider increase the frequency we try? Say to 10 times per second? I guess this will solve most of the failures we are seeing in current case.

Quoted 36 lines

Well, the problem is mainly with kthreads. User space processes are migrated also on the kernel boundary. And the fake signal is likely pretty effective here. And it probably is not that common that a kthread would occupy a single CPU all the time.

Quoted 4 lines

Though, these options might be considered workarounds, as it's theoretically possible for a kthread to be CPU-bound indefinitely, beyond any arbitrarily chosen timeout. But maybe that's not realistic beyond a certain timeout value of X and we don't care? I dunno.

I agree that it might happen theoretically. And it would be great to be prepared for this. My only concern is the complexity and risk. We should know that it is worth it.

Quoted 3 lines

As I have been trying to say, that won't work for PREEMPT+!ORC, because, when the kthread gets preempted, the stack trace will be attempted from an IRQ and will be reported as unreliable.

This limits the range of possible solutions quite a lot. But it is how it is.

Quoted 2 lines

Ideally we'd have the ORC unwinder for all arches, that would make this much easier. But we're not there yet.

The alternative solution is that the process has to migrate itself on some safe location.

One crazy idea. It still might be possible to find the called functions on the stack even when it is not reliable. Then it might be possible to add another ftrace handler on these found functions. This other ftrace handler might migrate the task when it calls this function again.

Quoted 6 lines

It assumes that the task will call the same functions again and again. Also it might require that the tasks checks its own stack from the ftrace handler. I am not sure if this is possible.

There might be other variants of this approach.

This might be the ultimate solution! As ftrace allows filtering based on pid (/sys/kernel/tracing/set_ftrace_pid), we can technically trigger klp_try_switch_task() on every function of the pending tasks. If this works, we should finish most of the transition in seconds. And the only failure there would be threads with being patched function at the very sottom of its stack. Am I too optimistic here?

Thanks,
Song

maintainer
#26 lore raw
Quoted 17 lines · Song Liu
Quoted 17 lines
Quoted 2 lines

Ideally we'd have the ORC unwinder for all arches, that would make this much easier. But we're not there yet.

The alternative solution is that the process has to migrate itself on some safe location.

One crazy idea. It still might be possible to find the called functions on the stack even when it is not reliable. Then it might be possible to add another ftrace handler on these found functions. This other ftrace handler might migrate the task when it calls this function again.

It assumes that the task will call the same functions again and again. Also it might require that the tasks checks its own stack from the ftrace handler. I am not sure if this is possible.

Quoted 8 lines
Quoted 1 line

There might be other variants of this approach.

This might be the ultimate solution! As ftrace allows filtering based on pid (/sys/kernel/tracing/set_ftrace_pid), we can technically trigger klp_try_switch_task() on every function of the pending tasks. If this works, we should finish most of the transition in seconds. And the only failure there would be threads with being patched function at the very sottom of its stack. Am I too optimistic here?

It's a crazy idea, but I kind of like it ;-) Especially this variant of tracing all functions for the task. We'd have to make sure unwinding from an ftrace handler works for all arches/unwinders.

Signature · 1 line
Josh
maintainer
#27 lore raw
In reply to Song Liu
Quoted 31 lines · Song Liu
Quoted 31 lines · Petr Mladek
Quoted 25 lines · Josh Poimboeuf
Quoted 13 lines · Song Liu
Quoted 3 lines

If it's a real bug, we should fix it everywhere, not just for Facebook. Otherwise CONFIG_PREEMPT and/or non-x86 arches become second-class citizens.

I think "is it a real bug?" is the top question for me. So maybe we should take a step back.

The behavior we see is: A busy kernel thread blocks klp transition for more than a minute. But the transition eventually succeeded after < 10 retries on most systems. The kernel thread is well-behaved, as it calls cond_resched() at a reasonable frequency, so this is not a deadlock.

Quoted 5 lines

If I understand Petr correctly, this behavior is expected, and thus is not a bug or issue for the livepatch subsystem. This is different to our original expectation, but if this is what we agree on, we will look into ways to incorporate long wait time for patch transition in our automations.

That's how we've traditionally looked at it, though apparently Red Hat and SUSE have implemented different ideas of what a long wait time is.

In practice, one minute has always been enough for all of kpatch's users -- AFAIK, everybody except SUSE -- up until now.

Quoted 6 lines
Quoted 2 lines

I am actually surprised that nobody met the problem yet. There are "only" 60 attempts to transition the pending tasks.

Maybe we should consider increase the frequency we try? Say to 10 times per second? I guess this will solve most of the failures we are seeing in current case.

My concern is that klp_try_complete_transition() checks all processes under read_lock(&tasklist_lock). It might create some contention on this lock. I am not sure if this lock is fair. It might slow down block writers (creating/deleting tasks).

Best Regards,
Petr

maintainer
#28 lore raw
Quoted 4 lines · Petr Mladek

My concern is that klp_try_complete_transition() checks all processes under read_lock(&tasklist_lock). It might create some contention on this lock. I am not sure if this lock is fair. It might slow down block writers (creating/deleting tasks).

rwlock_t is sorta fair. Is it fair for process contect usage, but in-interrupt use will have a reader bias.

That said; contention on tasklist_lock is a real scalability concern.

Can't you use RCU here? With RCU traversal of the tasklist you can miss new entry (which should already run the new code) or miss exits (which will stop running code).

#29 lore raw
In reply to Song Liu
Quoted 6 lines · Song Liu

So, if we come back to the same question: is this a bug (or a suboptimal
behavior that worth fixing)? If so, we are open to any solution that would also help PREEMPT and/or non-x86 arches.

Using the preempt notifiers during KLP transition should work equally well for PREEMPT and !PREEMPT. It also does not insert any additional code into the scheduler while there is no KLP transition going on.

Quoted 11 lines

Lastly, maybe a really naive question: does the following also helps PREEMPT=y configurations?

 static void __sched notrace preempt_schedule_common(void) {
+       if (unlikely(klp_patch_pending(current))) +               klp_try_switch_task(current);
+
        do {

While this would almost certainly speed up KLP transitions, it would also slow down schedule all the time, even while there is no KLP transition going on.

That does not seem like a worthwhile tradeoff.

maintainer
#30 lore raw
Quoted 11 lines · Rik van Riel
Quoted 6 lines · Song Liu

So, if we come back to the same question: is this a bug (or a suboptimal
behavior that worth fixing)? If so, we are open to any solution that would also help PREEMPT and/or non-x86 arches.

Using the preempt notifiers during KLP transition should work equally well for PREEMPT and !PREEMPT. It also does not insert any additional code into the scheduler while there is no KLP transition going on.

As I've been saying, this is not going to work for PREEMPT because, without ORC, we can't reliably unwind from an IRQ handler, so the kthread won't get patched.

Signature · 1 line
Josh
#31 lore raw
Quoted 9 lines · Josh Poimboeuf
Quoted 8 lines · Rik van Riel
Quoted 7 lines · Song Liu

So, if we come back to the same question: is this a bug (or a suboptimal
behavior that worth fixing)? If so, we are open to any solution that
would also help PREEMPT and/or non-x86 arches.

Quoted 9 lines
Quoted 4 lines

Using the preempt notifiers during KLP transition should work equally well for PREEMPT and !PREEMPT. It also does not insert any additional code into the scheduler while there is no KLP transition going on.

As I've been saying, this is not going to work for PREEMPT because, without ORC, we can't reliably unwind from an IRQ handler, so the kthread won't get patched.

Isn't the sched_out preempt notifier always run in process context?

What am I missing?

maintainer
#32 lore raw
Quoted 15 lines · Rik van Riel
Quoted 14 lines · Josh Poimboeuf
Quoted 12 lines · Rik van Riel
Quoted 7 lines · Song Liu

So, if we come back to the same question: is this a bug (or a suboptimal
behavior that worth fixing)? If so, we are open to any solution that
would also help PREEMPT and/or non-x86 arches.

Using the preempt notifiers during KLP transition should work equally well for PREEMPT and !PREEMPT. It also does not insert any additional code into the scheduler while there is no KLP transition going on.

Quoted 8 lines
Quoted 4 lines

As I've been saying, this is not going to work for PREEMPT because, without ORC, we can't reliably unwind from an IRQ handler, so the kthread won't get patched.

Isn't the sched_out preempt notifier always run in process context?

What am I missing?

Maybe it's technically process context at that point. But the important point is that the call to the scheduler via preempt_schedule_irq() originates from the "return from interrupt" path.

So the state of the interrupted task's stack is unknown. For example it could have been interrupted before the frame pointer prologue. Or in a leaf function which doesn't use frame pointers.

Signature · 1 line
Josh
#33 lore raw
Quoted 25 lines · Josh Poimboeuf
Quoted 24 lines · Rik van Riel
Quoted 15 lines · Josh Poimboeuf
Quoted 9 lines · Rik van Riel
Quoted 8 lines · Song Liu

So, if we come back to the same question: is this a bug (or a suboptimal
behavior that worth fixing)? If so, we are open to any solution
that
would also help PREEMPT and/or non-x86 arches.

Quoted 4 lines

Using the preempt notifiers during KLP transition should work equally well for PREEMPT and !PREEMPT. It also does not insert any additional code into the scheduler while there is no KLP transition going on.

Quoted 5 lines

As I've been saying, this is not going to work for PREEMPT because,
without ORC, we can't reliably unwind from an IRQ handler, so the kthread won't get patched.

Isn't the sched_out preempt notifier always run in process context?

Quoted 6 lines
Quoted 1 line

What am I missing?

Maybe it's technically process context at that point.  But the important
point is that the call to the scheduler via preempt_schedule_irq() originates from the "return from interrupt" path.

Ahhhh, I think I understand.

Does that mean if the scheduling of the kernel thread originated from an IRQ, the KLP transition will fail probably?

However, if the call to schedule came from a voluntary preemption, for example through a cond_resched() or due to the thread going to sleep a little bit, the stack walk will be reliable, and the KLP transition may succeed?

maintainer
#34 lore raw
Quoted 33 lines · Rik van Riel
Quoted 25 lines · Josh Poimboeuf
Quoted 24 lines · Rik van Riel
Quoted 15 lines · Josh Poimboeuf
Quoted 9 lines · Rik van Riel
Quoted 8 lines · Song Liu

So, if we come back to the same question: is this a bug (or a suboptimal
behavior that worth fixing)? If so, we are open to any solution
that
would also help PREEMPT and/or non-x86 arches.

Quoted 4 lines

Using the preempt notifiers during KLP transition should work equally well for PREEMPT and !PREEMPT. It also does not insert any additional code into the scheduler while there is no KLP transition going on.

Quoted 5 lines

As I've been saying, this is not going to work for PREEMPT because,
without ORC, we can't reliably unwind from an IRQ handler, so the kthread won't get patched.

Isn't the sched_out preempt notifier always run in process context?

Quoted 6 lines
Quoted 1 line

What am I missing?

Maybe it's technically process context at that point.  But the important
point is that the call to the scheduler via preempt_schedule_irq() originates from the "return from interrupt" path.

Quoted 4 lines

Ahhhh, I think I understand.

Does that mean if the scheduling of the kernel thread originated from an IRQ, the KLP transition will fail probably?

It will fail definitely, unless you have the ORC unwinder.

Quoted 4 lines

However, if the call to schedule came from a voluntary preemption, for example through a cond_resched() or due to the thread going to sleep a little bit, the stack walk will be reliable, and the KLP transition may succeed?

Right.

Signature · 1 line
Josh
#35 lore raw

RFC 1/1 sched,livepatch: call stop_one_cpu in klp_check_and_switch_task

In reply to Song Liu

After talking with Peter, this seems like it might be a potential approach to fix the issue for kernels both with PREEMPT enabled and disabled.

If this looks like a reasonable approach to people, we can run experiments with this patch on a few thousand systems, and compare it with the kernel live patch transition latencies (and number of failures) on a kernel without that patch.

Does this look like an approach that could work?

---8<---
sched,livepatch: call stop_one_cpu in klp_check_and_switch_task

If a running task fails to transition to the new kernel live patch after the first attempt, use the stopper thread to preempt it during subsequent attempts at switching to the new kernel live patch.

<INSERT EXPERIMENTAL RESULTS HERE>

kernel/livepatch/transition.c +9 -0
index 5d03a2ad1066..26e9e5f09822 100644
@@ -9,6 +9,7 @@
 
 #include <linux/cpu.h>
 #include <linux/stacktrace.h>
+#include <linux/stop_machine.h>
 #include "core.h"
 #include "patch.h"
 #include "transition.h"
@@ -281,6 +282,11 @@ static int klp_check_and_switch_task(struct task_struct *task, void *arg)
 	return 0;
 }
 
+static int kpatch_dummy_fn(void *dummy)
+{
+	return 0;
+}
+
 /*
  * Try to safely switch a task to the target patch state.  If it's currently
  * running, or it's sleeping on a to-be-patched or to-be-unpatched function, or
@@ -315,6 +321,9 @@ static bool klp_try_switch_task(struct task_struct *task)
 	case -EBUSY:	/* klp_check_and_switch_task() */
 		pr_debug("%s: %s:%d is running\n",
 			 __func__, task->comm, task->pid);
+		/* Preempt the task from the second KLP switch attempt. */
+		if (klp_signals_cnt)
+			stop_one_cpu(task_cpu(task), kpatch_dummy_fn, NULL);
 		break;
 	case -EINVAL:	/* klp_check_and_switch_task() */
 		pr_debug("%s: %s:%d has an unreliable stack\n",
#36 lore raw

Re: [RFC] sched,livepatch: call stop_one_cpu in klp_check_and_switch_task

Quoted 7 lines · Rik van Riel

After talking with Peter, this seems like it might be a potential approach to fix the issue for kernels both with PREEMPT enabled and disabled.

Quoted 6 lines

If this looks like a reasonable approach to people, we can run experiments with this patch on a few thousand systems, and compare it with the kernel live patch transition latencies (and number of failures) on a kernel without that patch.

Does this look like an approach that could work?

Hi Petr,

IIUC, this is similar to you proposal. Could you please share your thoughts on this?

Thanks,
Song

Quoted 47 lines

---8<---
sched,livepatch: call stop_one_cpu in klp_check_and_switch_task

If a running task fails to transition to the new kernel live patch after the first attempt, use the stopper thread to preempt it during subsequent attempts at switching to the new kernel live patch.

<INSERT EXPERIMENTAL RESULTS HERE>

kernel/livepatch/transition.c +9 -0
index 5d03a2ad1066..26e9e5f09822 100644
@@ -9,6 +9,7 @@
 
 #include <linux/cpu.h>
 #include <linux/stacktrace.h>
+#include <linux/stop_machine.h>
 #include "core.h"
 #include "patch.h"
 #include "transition.h"
@@ -281,6 +282,11 @@ static int klp_check_and_switch_task(struct task_struct *task, void *arg)
 	return 0;
 }
 
+static int kpatch_dummy_fn(void *dummy)
+{
+	return 0;
+}
+
 /*
 * Try to safely switch a task to the target patch state.  If it's currently
 * running, or it's sleeping on a to-be-patched or to-be-unpatched function, or
@@ -315,6 +321,9 @@ static bool klp_try_switch_task(struct task_struct *task)
 	case -EBUSY:	/* klp_check_and_switch_task() */
 		pr_debug("%s: %s:%d is running\n",
 			 __func__, task->comm, task->pid);
+		/* Preempt the task from the second KLP switch attempt. */
+		if (klp_signals_cnt)
+			stop_one_cpu(task_cpu(task), kpatch_dummy_fn, NULL);
 		break;
 	case -EINVAL:	/* klp_check_and_switch_task() */
 		pr_debug("%s: %s:%d has an unreliable stack\n",
maintainer
#37 lore raw

Re: [RFC] sched,livepatch: call stop_one_cpu in klp_check_and_switch_task

In reply to Rik van Riel
Quoted 10 lines · Rik van Riel

Does this look like an approach that could work?

---8<---
sched,livepatch: call stop_one_cpu in klp_check_and_switch_task

If a running task fails to transition to the new kernel live patch after the first attempt, use the stopper thread to preempt it during subsequent attempts at switching to the new kernel live patch.

<INSERT EXPERIMENTAL RESULTS HERE>

It would be helpful to add more info about the original problem being solved, and how this is supposed to fix it.

Quoted 1 line

+static int kpatch_dummy_fn(void *dummy)

s/kpatch/klp/

Quoted 8 lines
kernel/livepatch/transition.c +4 -0
+{
+	return 0;
+}
+
 /*
  * Try to safely switch a task to the target patch state.  If it's currently
  * running, or it's sleeping on a to-be-patched or to-be-unpatched function, or

kernel/livepatch/transition.c static bool klp_try_switch_task(struct task_struct *task) · line 326

Quoted 6 lines
kernel/livepatch/transition.c +3 -0
@@ -315,6 +321,9 @@ static bool klp_try_switch_task(struct task_struct *task)
 	case -EBUSY:	/* klp_check_and_switch_task() */
 		pr_debug("%s: %s:%d is running\n",
 			 __func__, task->comm, task->pid);
+		/* Preempt the task from the second KLP switch attempt. */
+		if (klp_signals_cnt)
+			stop_one_cpu(task_cpu(task), kpatch_dummy_fn, NULL);

I must be missing something, how is briefly preempting a kthread supposed to actually transition it? Won't it likely go back to running on the CPU before the next periodic klp_transition_work_fn() check?

Signature · 1 line
Josh
#38 lore raw

Re: [RFC] sched,livepatch: call stop_one_cpu in klp_check_and_switch_task

Quoted 19 lines · Josh Poimboeuf
Quoted 4 lines · Rik van Riel

Does this look like an approach that could work?

(quoted diff) +0 -0
@@ -315,6 +321,9 @@ static bool klp_try_switch_task(struct
 task_struct *task)
Quoted 8 lines

        case -EBUSY:    /* klp_check_and_switch_task() */ pr_debug("%s: %s:%d is running\n", __func__, task->comm, task->pid); +               /* Preempt the task from the second KLP switch attempt. */
+               if (klp_signals_cnt)
+                       stop_one_cpu(task_cpu(task), kpatch_dummy_fn, NULL);

I must be missing something, how is briefly preempting a kthread supposed to actually transition it?  Won't it likely go back to running
on the CPU before the next periodic klp_transition_work_fn() check?

That's the kind of feedback I was hoping for ;)

I looked around the code a little bit, and it seems that only the idle tasks can transition to another KLP while they are running?

That makes me wonder how the kworker thread that runs the klp switching code transitions itself...

Should kernel threads that can use a lot of CPU have something in their outer loop to transition KLPs, just like the idle task does?

Signature · 1 line
All Rights Reversed.
maintainer
#39 lore raw

Re: [RFC] sched,livepatch: call stop_one_cpu in klp_check_and_switch_task

Quoted 20 lines · Rik van Riel
Quoted 19 lines · Josh Poimboeuf
Quoted 4 lines · Rik van Riel

Does this look like an approach that could work?

(quoted diff) +0 -0
@@ -315,6 +321,9 @@ static bool klp_try_switch_task(struct
 task_struct *task)
Quoted 8 lines

        case -EBUSY:    /* klp_check_and_switch_task() */ pr_debug("%s: %s:%d is running\n", __func__, task->comm, task->pid); +               /* Preempt the task from the second KLP switch attempt. */
+               if (klp_signals_cnt)
+                       stop_one_cpu(task_cpu(task), kpatch_dummy_fn, NULL);

I must be missing something, how is briefly preempting a kthread supposed to actually transition it?  Won't it likely go back to running
on the CPU before the next periodic klp_transition_work_fn() check?

Quoted 5 lines

That's the kind of feedback I was hoping for ;)

I looked around the code a little bit, and it seems that only the idle tasks can transition to another KLP while they are running?

Yes.

Quoted 2 lines

That makes me wonder how the kworker thread that runs the klp switching code transitions itself...

See klp_check_and_switch_task(), in addition to checking blocked tasks, it also checks the current task.

Quoted 3 lines

Should kernel threads that can use a lot of CPU have something in their outer loop to transition KLPs, just like the idle task does?

Maybe - I suppose this is the first time we've had an issue with CPU-bound kthreads. I didn't know that was a thing ;-)

Signature · 1 line
Josh
#40 lore raw

Re: [RFC] sched,livepatch: call stop_one_cpu in klp_check_and_switch_task

Quoted 10 lines · Josh Poimboeuf
Quoted 3 lines · Rik van Riel

Should kernel threads that can use a lot of CPU have something in their outer loop to transition KLPs, just like the idle task does?

Maybe - I suppose this is the first time we've had an issue with CPU-bound kthreads.  I didn't know that was a thing ;-)

Kworkers have as much work as you want them to do, and with things like btrfs compression that can be quite a bit.

Signature · 1 line
All Rights Reversed.
maintainer
#41 lore raw

Re: [RFC] sched,livepatch: call stop_one_cpu in klp_check_and_switch_task

Quoted 8 lines · Rik van Riel
Quoted 7 lines · Josh Poimboeuf
Quoted 3 lines · Rik van Riel

Should kernel threads that can use a lot of CPU have something in their outer loop to transition KLPs, just like the idle task does?

Quoted 5 lines
Quoted 3 lines

Maybe - I suppose this is the first time we've had an issue with CPU-bound kthreads.  I didn't know that was a thing ;-)

Kworkers have as much work as you want them to do, and with things like btrfs compression that can be quite a bit.

To prevent patching, it would need to be some kind of sustained CPU activity, rather than a burst. I guess we haven't seen that show up as a real-world problem until now.

If you're able to identify which kthreads would be problematic, then yeah, defining a "transition point" in their outer loops could be an option.

We could look also at a more general approach, like stack checking from an irq handler. But as Petr alluded to, that would be problematic for CONFIG_FRAME_POINTER.

We could maybe deprecate frame pointers on x86 for live patching, but I think other arches would have a similar problem unless they were to do something like the ORC unwinder.

Signature · 1 line
Josh
#42 lore raw

Re: [RFC] sched,livepatch: call stop_one_cpu in klp_check_and_switch_task

Quoted 16 lines · Josh Poimboeuf
Quoted 11 lines · Rik van Riel
Quoted 10 lines · Josh Poimboeuf
Quoted 3 lines · Rik van Riel

Should kernel threads that can use a lot of CPU have something in their outer loop to transition KLPs, just like the idle task does?

Maybe - I suppose this is the first time we've had an issue with CPU-bound kthreads. I didn't know that was a thing ;-)

Quoted 6 lines
Quoted 2 lines

Kworkers have as much work as you want them to do, and with things like btrfs compression that can be quite a bit.

To prevent patching, it would need to be some kind of sustained CPU activity, rather than a burst. I guess we haven't seen that show up as a real-world problem until now.

Yes, we see this issue with sustained CPU activity from a kernel thread, which might be a bug itself. OTOH, the kernel thread does call cond_sched(), so it is not a deadlock.

Quoted 4 lines

If you're able to identify which kthreads would be problematic, then yeah, defining a "transition point" in their outer loops could be an option.

cond_sched() feels like a natural “transition point” to me, and it should solve many different variations of such problem. I agree that adding something to cond_sched() might be too much overhead for the system. So we are open for other suggestions.

Thanks,
Song

Quoted 8 lines

We could look also at a more general approach, like stack checking from an irq handler. But as Petr alluded to, that would be problematic for CONFIG_FRAME_POINTER.

We could maybe deprecate frame pointers on x86 for live patching, but I think other arches would have a similar problem unless they were to do something like the ORC unwinder.

maintainer
#43 lore raw

Re: [RFC] sched,livepatch: call stop_one_cpu in klp_check_and_switch_task

Quoted 1 line · Song Liu

cond_sched() feels like a natural “transition point” to me, and it

You have to think of cond_resched() as a NOP.

#44 lore raw

Re: [RFC] sched,livepatch: call stop_one_cpu in klp_check_and_switch_task

In reply to Josh Poimboeuf
Quoted 5 lines · Josh Poimboeuf

To prevent patching, it would need to be some kind of sustained CPU activity, rather than a burst.  I guess we haven't seen that show up as
a real-world problem until now.

It's amazing what you see when you have a few million very busy servers. The problems you think of as "one in a million" happen all the time :)

Quoted 4 lines

If you're able to identify which kthreads would be problematic, then yeah, defining a "transition point" in their outer loops could be an option.

I'm in the middle of creating some scripts to gather kpatch output from all the systems, and then sort and count the results so we can easily see what the main pain points are.

We'll be back with patches once we have the data in hand :)

Signature · 1 line
All Rights Reversed.