diff mbox

[RFC] PM / Hibernate: no kernel_power_off when pm_power_off NULL

Message ID 1395348795-8554-2-git-send-email-sebastian.capella@linaro.org
State New
Headers show

Commit Message

Sebastian Capella March 20, 2014, 8:53 p.m. UTC
Reboot logic in kernel/reboot will avoid calling kernel_power_off
when pm_power_off is null, and instead uses kernel_halt.  Change
hibernate's power_down to follow the behavior in the reboot call.

Calling the notifier twice (once for SYS_POWER_OFF and again for
SYS_HALT) causes a panic during hibernation on Kirkwood
Openblocks A6 board.

Signed-off-by: Sebastian Capella <sebastian.capella@linaro.org>
Reported-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
Cc: Len Brown <len.brown@intel.com>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
---
 kernel/power/hibernate.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

Comments

Pavel Machek March 20, 2014, 9:23 p.m. UTC | #1
Hi!

> Reboot logic in kernel/reboot will avoid calling kernel_power_off
> when pm_power_off is null, and instead uses kernel_halt.  Change
> hibernate's power_down to follow the behavior in the reboot call.
> 
> Calling the notifier twice (once for SYS_POWER_OFF and again for
> SYS_HALT) causes a panic during hibernation on Kirkwood
> Openblocks A6 board.

I can't say I like this patch.

kernel_power_off should work with pm_power_off == NULL, see for
example x86.

static void native_machine_power_off(void)
{
        if (pm_power_off) {
                if (!reboot_force)
			machine_shutdown();
                pm_power_off();
        }
        /* A fallback in case there is no PM info available */
        tboot_shutdown(TB_SHUTDOWN_HALT);
}

. arch/arm/process.c implementation is strange:

void machine_halt(void)
{
        local_irq_disable();
	smp_send_stop();

        local_irq_disable();
        while (1);
}

## Why second disable?

/*                                                                                                 
 * Power-off simply requires that the secondary CPUs stop performing
 any                           
 * activity (executing tasks, handling interrupts). smp_send_stop()                                
 * achieves this. When the system power is turned off, it will take
 all CPUs                       
 * with it.                                                                                        
 */
void machine_power_off(void)
{
	local_irq_disable();
        smp_send_stop();

	if (pm_power_off)
                pm_power_off();
}

## It really should do while (1) here.
   
									Pavel
Alan Cox March 20, 2014, 9:35 p.m. UTC | #2
> 	if (pm_power_off)
>                 pm_power_off();
> }
> 
> ## It really should do while (1) here.

	while(1)
		cpu_relax();

or similar at minimum.

Alan
--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sebastian Capella March 20, 2014, 9:36 p.m. UTC | #3
Thanks Pavel and Alan for your comments!

I'll rework and try again.

Sebastian

On 20 March 2014 14:35, One Thousand Gnomes <gnomes@lxorguk.ukuu.org.uk> wrote:
>>       if (pm_power_off)
>>                 pm_power_off();
>> }
>>
>> ## It really should do while (1) here.
>
>         while(1)
>                 cpu_relax();
>
> or similar at minimum.
>
> Alan
--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sebastian Capella March 26, 2014, 5:22 p.m. UTC | #4
> On 20 March 2014 14:35, One Thousand Gnomes <gnomes@lxorguk.ukuu.org.uk> wrote:
>>>       if (pm_power_off)
>>>                 pm_power_off();
>>> ## It really should do while (1) here.
>>         while(1)
>>                 cpu_relax();
>> or similar at minimum.

Hi Alan, Pavel,

I prepared the changes suggested for ARM, but Russell disagrees that
this code must block, and points to the kernel/reboot.c function from
which I'd taken the original change.

> On Mar 26, Russell King - ARM Linux wrote:
>> I don't see why we should make this change.  kernel/reboot.c handles
>> this function returning, so other places should do too.
>> Even on x86, this function can return:
>>
>> Therefore, I'd say... it's a bug in the hibernation code - or we probably
>> have many buggy architectures.  I'd suggest fixing the hibernation code
>> rather than stuffing some workaround like an endless loop into every
>> architecture.

In this case, the hibernation code would need to changed prevent
calling both sets of notifiers.

The discussions are very short, but the details are here:

https://lkml.org/lkml/2014/3/25/554  -- linux-arm discussion
https://lkml.org/lkml/2014/3/20/649  -- linux-pm discussion

It doesn't appear definitive that the machine_power_off should block,
since the reboot call is working around the possibility.

If we go the route Russell proposes, the change would basically be
this patch.  Are you ok with me trying to adjust the hibernation
handling here?  Do you have any further thoughts on this?

Thanks!

Sebastian
--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sebastian Capella April 15, 2014, 6:34 p.m. UTC | #5
Ping..

There appears to be disagreement on the correct path to take on this.

Pavel and Alan recommend that arm's machine_power_off shall never return

Russell suggests hibernation be modified to handle machine_power_off
returning; that x86 architecture (and others as well) can have
machine_power_off returning.

Discussions available at the links below:
https://lkml.org/lkml/2014/3/25/554  -- linux-arm discussion
https://lkml.org/lkml/2014/3/20/649  -- linux-pm discussion

Should I continue with the original hibernation patch from the
linux-pm discussion?

Does anyone have any response to Russel's commentsl?

Thanks!

Sebastian
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Russell King - ARM Linux April 15, 2014, 8:54 p.m. UTC | #6
On Tue, Apr 15, 2014 at 11:34:52AM -0700, Sebastian Capella wrote:
> Ping..
> 
> There appears to be disagreement on the correct path to take on this.
> 
> Pavel and Alan recommend that arm's machine_power_off shall never return
> 
> Russell suggests hibernation be modified to handle machine_power_off
> returning; that x86 architecture (and others as well) can have
> machine_power_off returning.
> 
> Discussions available at the links below:
> https://lkml.org/lkml/2014/3/25/554  -- linux-arm discussion
> https://lkml.org/lkml/2014/3/20/649  -- linux-pm discussion
> 
> Should I continue with the original hibernation patch from the
> linux-pm discussion?
> 
> Does anyone have any response to Russel's commentsl?

What I'm basically saying is that I see no reason for ARM to do something
different to what x86 does.

What is pretty clear to me is that ARM is compatible with x86, which is
compatible with kernel/reboot.c, and it's the hibernate code which is
the odd one out.
Pavel Machek April 15, 2014, 9:18 p.m. UTC | #7
On Tue 2014-04-15 21:54:53, Russell King - ARM Linux wrote:
> On Tue, Apr 15, 2014 at 11:34:52AM -0700, Sebastian Capella wrote:
> > Ping..
> > 
> > There appears to be disagreement on the correct path to take on this.
> > 
> > Pavel and Alan recommend that arm's machine_power_off shall never return
> > 
> > Russell suggests hibernation be modified to handle machine_power_off
> > returning; that x86 architecture (and others as well) can have
> > machine_power_off returning.
> > 
> > Discussions available at the links below:
> > https://lkml.org/lkml/2014/3/25/554  -- linux-arm discussion
> > https://lkml.org/lkml/2014/3/20/649  -- linux-pm discussion
> > 
> > Should I continue with the original hibernation patch from the
> > linux-pm discussion?
> > 
> > Does anyone have any response to Russel's commentsl?
> 
> What I'm basically saying is that I see no reason for ARM to do something
> different to what x86 does.
> 
> What is pretty clear to me is that ARM is compatible with x86, which is
> compatible with kernel/reboot.c, and it's the hibernate code which is
> the odd one out.

I'm pretty sure the original code did not return. Anyway, the best
solution, given how many platforms are out there, would be to

a) document that it should not return

b) fix hibernation to handle the returning case, anyway.

									Pavel
Sebastian Capella April 16, 2014, 4:28 p.m. UTC | #8
On 15 April 2014 14:18, Pavel Machek <pavel@ucw.cz> wrote:
> On Tue 2014-04-15 21:54:53, Russell King - ARM Linux wrote:
>> What I'm basically saying is that I see no reason for ARM to do something
>> different to what x86 does.
>>
>> What is pretty clear to me is that ARM is compatible with x86, which is
>> compatible with kernel/reboot.c, and it's the hibernate code which is
>> the odd one out.
>
> I'm pretty sure the original code did not return. Anyway, the best
> solution, given how many platforms are out there, would be to
>
> a) document that it should not return
>
> b) fix hibernation to handle the returning case, anyway.

Thanks Russell and Pavel!

This sounds fine to me.  Any objections?

Thanks!

Sebastian
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Russell King - ARM Linux April 16, 2014, 8:41 p.m. UTC | #9
On Wed, Apr 16, 2014 at 09:28:28AM -0700, Sebastian Capella wrote:
> On 15 April 2014 14:18, Pavel Machek <pavel@ucw.cz> wrote:
> > On Tue 2014-04-15 21:54:53, Russell King - ARM Linux wrote:
> >> What I'm basically saying is that I see no reason for ARM to do something
> >> different to what x86 does.
> >>
> >> What is pretty clear to me is that ARM is compatible with x86, which is
> >> compatible with kernel/reboot.c, and it's the hibernate code which is
> >> the odd one out.
> >
> > I'm pretty sure the original code did not return. Anyway, the best
> > solution, given how many platforms are out there, would be to
> >
> > a) document that it should not return
> >
> > b) fix hibernation to handle the returning case, anyway.
> 
> Thanks Russell and Pavel!
> 
> This sounds fine to me.  Any objections?

Here is the i386 code from 2.2.20:

void machine_halt(void)
{
}

void machine_power_off(void)
{
        if (acpi_power_off)
                acpi_power_off();
}

Both can return.  On the other hand, machine_restart() can never return as
the final attempt to perform that action in machine_real_restart is a jump
to 16-bit code.

2.4 kernels then modified it to this:

void machine_halt(void)
{
}

void machine_power_off(void)
{
        if (pm_power_off)
                pm_power_off();
}

with machine_restart() doing similar to v2.2.

2.6.0 also did the same as 2.4 kernels.  2.6.16 then changed to this:

void machine_restart(char * __unused)
{
        machine_shutdown();
        machine_emergency_restart();
}

void machine_halt(void)
{
}

void machine_power_off(void)
{
        if (pm_power_off) {
                machine_shutdown();
                pm_power_off();
        }
}

which starts adding some extra stuff into the power off hook - but
again, it's a no-op unless the pm_power_off function pointer is
initialised.

Today, it's:

void machine_power_off(void)
{
        machine_ops.power_off();
}

which calls native_power_off():

static void native_machine_power_off(void)
{
        if (pm_power_off) {
                if (!reboot_force)
                        machine_shutdown();
                pm_power_off();
        }
        /* A fallback in case there is no PM info available */
        tboot_shutdown(TB_SHUTDOWN_HALT);
}

and tboot_shutdown():

void tboot_shutdown(u32 shutdown_type)
{
        void (*shutdown)(void);

        if (!tboot_enabled())
                return;

so it's entirely possible today for machine_power_off() on x86 to return.

So... the i386 code has had this "machine_power_off() can return"
behaviour for a significant number of years and persists to this day.

I'd say scrap (a) _unless_ we're going to add while (1) loops to all
the architectures.  Alternatively, we could just accept that
machine_power_off() may return and deal with that case (iow, not
crash) in generic code.
Alan Cox April 16, 2014, 8:57 p.m. UTC | #10
> I'd say scrap (a) _unless_ we're going to add while (1) loops to all
> the architectures.  Alternatively, we could just accept that
> machine_power_off() may return and deal with that case (iow, not
> crash) in generic code.

What would the right behaviour be

while(1);

isn't really nice behaviour on a modern device 

 
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Russell King - ARM Linux April 16, 2014, 9:09 p.m. UTC | #11
On Wed, Apr 16, 2014 at 09:57:18PM +0100, One Thousand Gnomes wrote:
> > I'd say scrap (a) _unless_ we're going to add while (1) loops to all
> > the architectures.  Alternatively, we could just accept that
> > machine_power_off() may return and deal with that case (iow, not
> > crash) in generic code.
> 
> What would the right behaviour be
> 
> while(1);
> 
> isn't really nice behaviour on a modern device 

That's an entirely different question... one which also needs fixing
in the hibernate code.  We already know that cpu_relax() in there is
a good thing to do, so that would be a good start.
Sebastian Capella April 18, 2014, 9:38 p.m. UTC | #12
Thanks Russell, Alan!

So we're OK with the current patch + replacing while(1) after
kernel_halt at the end of power_down in hibernate.c with a while (1)
cpu_relax()?

Any other changes needed?

If not, I'll send a follow up patch with just these.

Thanks!

Sebastian
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Pavel Machek April 20, 2014, 2:06 p.m. UTC | #13
On Fri 2014-04-18 14:38:34, Sebastian Capella wrote:
> Thanks Russell, Alan!
> 
> So we're OK with the current patch + replacing while(1) after
> kernel_halt at the end of power_down in hibernate.c with a while (1)
> cpu_relax()?
> 
> Any other changes needed?
> 
> If not, I'll send a follow up patch with just these.

That sounds like a plan. Thanks.
									Pavel
diff mbox

Patch

diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index 37170d4..6a278dc 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -595,7 +595,8 @@  static void power_down(void)
 	case HIBERNATION_PLATFORM:
 		hibernation_platform_enter();
 	case HIBERNATION_SHUTDOWN:
-		kernel_power_off();
+		if (pm_power_off)
+			kernel_power_off();
 		break;
 #ifdef CONFIG_SUSPEND
 	case HIBERNATION_SUSPEND: