Re: [PATCH 1/8] PM: Add suspend block api.

Arve Hjønnevåg
2009-04-29T23:45:04Z

2009/4/29 Rafael J. Wysocki <rjw@sisk.pl>:
> On Wednesday 15 April 2009, Arve Hjønnevåg wrote:
>> diff --git a/Documentation/power/suspend-blockers.txt b/Documentation/power/suspend-blockers.txt
>> new file mode 100644
>> index 0000000..743b870
>> --- /dev/null
>> +++ b/Documentation/power/suspend-blockers.txt
>> @@ -0,0 +1,76 @@
>> +Suspend blockers
>> +================
>> +
>> +A suspend_blocker prevents the system from entering suspend.
>
> I'd say "suspend blocker", without the underscore.
>
> Also, I'd try to provide a general explanation of how this is supposed to
> work here, as an introduction.  Something like:
>
> "Suspend blockers provide a mechanism allowing device drivers and user
> space processes to prevent the system from entering a sleep state, such as
> the ACPI S3 state.
>
> To use a suspend blocker, a device driver needs a struct suspend_blocker object
> that has to be initialized with the help of suspend_blocker_init().  When no
> longer needed, the suspend blocker must be garbage collected with the help
> of suspend_blocker_destroy().
>
> A suspend blocker is activated using suspend_block(), which prevents the
> system from entering any sleep state until the suspend blocker is deactivated
> with suspend_unblock().  Many suspend blockers may be used simultaneously
> and the system is prevented from entering sleep states as long as at least one
> of them is active."
>

I added most of this, see new text below.

> BTW, I don't really like the names "suspend_block" and "suspend_unblock".
> See below for possible alternatives.
>
>> +If the suspend operation has already started when calling suspend_block on a
>> +suspend_blocker, it will abort the suspend operation as long it has not already
>> +reached the sysdev suspend stage. This means that calling suspend_block from an
>> +interrupt handler or a freezeable thread always works, but if you call
>> +block_suspend
>
> Surely suspend_block() ?

Yes.

>
>> from a sysdev suspend handler you must also return an error from
>> +that handler to abort suspend.
>
> Well, if a sysdev suspend handler returns an error code, suspend will be
> aborted anyway, so I'm not sure what the point in using suspend blockers from
> sysdev suspend handlers really is.  Perhaps it's better to say "don't use
> suspend blockers from withing sysdev suspend handlers"?

You still need the suspend blocker to prevent the system from trying
to enter suspend again. I changed the text to "to abort a suspend
sequence that was already started".

>
>> +
>
> I'd say "For example" here.
>
>> +Suspend blockers can be used to allow user-space to decide which keys should
>> +wake the full system up and turn the screen on. Use set_irq_wake or a platform
>> +specific api to make sure the keypad interrupt wakes up the cpu. Once the keypad
>> +driver has resumed, the sequence of events can look like this:
>> +- The Keypad driver gets an interrupt. It then calls suspend_block on the
>> +  keypad-scan suspend_blocker and starts scanning the keypad matrix.
>> +- The keypad-scan code detects a key change and reports it to the input-event
>> +  driver.
>> +- The input-event driver sees the key change, enqueues an event, and calls
>> +  suspend_block on the input-event-queue suspend_blocker.
>> +- The keypad-scan code detects that no keys are held and calls suspend_unblock
>> +  on the keypad-scan suspend_blocker.
>> +- The user-space input-event thread returns from select/poll, calls
>> +  suspend_block on the process-input-events suspend_blocker and then calls read
>> +  on the input-event device.
>> +- The input-event driver dequeues the key-event and, since the queue is now
>> +  empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
>> +- The user-space input-event thread returns from read. It determines that the
>> +  key should not wake up the full system, calls suspend_unblock on the
>> +  process-input-events suspend_blocker and calls select or poll.
>> +
>> +                 Key pressed   Key released
>> +                     |             |
>> +keypad-scan          ++++++++++++++++++
>> +input-event-queue        +++          +++
>> +process-input-events       +++          +++
>> +
>> +
>> +Driver API
>> +==========
>> +
>> +A driver can use the suspend block api by adding a suspend_blocker variable to
>> +its state and calling suspend_blocker_init. For instance:
>> +struct state {
>> +     struct suspend_blocker suspend_blocker;
>> +}
>> +
>> +init() {
>> +     suspend_blocker_init(&state->suspend_blocker, "suspend-blocker-name");
>> +}
>> +
>> +Before freeing the memory, suspend_blocker_destroy must be called:
>> +
>> +uninit() {
>> +     suspend_blocker_destroy(&state->suspend_blocker);
>> +}
>> +
>> +When the driver determines that it needs to run (usually in an interrupt
>> +handler) it calls suspend_block:
>> +     suspend_block(&state->suspend_blocker);
>
> suspend_blocker_activate() ?
> suspend_blocker_enable() ?
> suspend_blocker_start() ?
>

I don't like any of those better than suspend_block, but if there is
some agreement on what it should be changed to, I'll change it.

>> +
>> +When it no longer needs to run it calls suspend_unblock:
>> +     suspend_unblock(&state->suspend_blocker);
>
> suspend_blocker_deactivate() ?
> suspend_blocker_disable() ?
> suspend_blocker_finish() ?
>
>> +
>> +Calling suspend_block when the suspend blocker is active or suspend_unblock when
>> +it is not active has no effect. This allows drivers to update their state and
>> +call suspend suspend_block or suspend_unblock based on the result.
>> +For instance:
>> +
>> +if (list_empty(&state->pending_work))
>> +     suspend_unblock(&state->suspend_blocker);
>> +else
>> +     suspend_block(&state->suspend_blocker);
>> +

--
Arve Hjønnevåg

----

Suspend blockers
================

Suspend blockers provide a mechanism for device drivers and user-space processes
to prevent the system from entering suspend. Only suspend states requested
through /sys/power/request_state are prevented. Writing to /sys/power/state
will ignore suspend blockers, and sleep states entered from idle are unaffected.

To use a suspend blocker, a device driver needs a struct suspend_blocker object
that has to be initialized with suspend_blocker_init. When no longer needed,
the suspend blocker must be destroyed with suspend_blocker_destroy.

A suspend blocker is activated using suspend_block, which prevents the system
from entering suspend until the suspend blocker is deactivated with
suspend_unblock. Many suspend blockers may be used simultaneously, and the
system is prevented from entering suspend as long as at least one of them is
active.

If the suspend operation has already started when calling suspend_block on a
suspend_blocker, it will abort the suspend operation as long it has not already
reached the sysdev suspend stage. This means that calling suspend_block from an
interrupt handler or a freezeable thread always works, but if you call
suspend_block from a sysdev suspend handler you must also return an error from
that handler to abort a suspend sequence that was already started.

In cell phones or other embedded systems where powering the screen is a
significant drain on the battery, suspend blockers can be used to allow
user-space to decide whether a keystroke received while the system is suspended
should cause the screen to be turned back on or allow the system to go back into
suspend. Use set_irq_wake or a platform specific api to make sure the keypad
interrupt wakes up the cpu. Once the keypad driver has resumed, the sequence of
events can look like this:

- The Keypad driver gets an interrupt. It then calls suspend_block on the
keypad-scan suspend_blocker and starts scanning the keypad matrix.
- The keypad-scan code detects a key change and reports it to the input-event
driver.
- The input-event driver sees the key change, enqueues an event, and calls
suspend_block on the input-event-queue suspend_blocker.
- The keypad-scan code detects that no keys are held and calls suspend_unblock
on the keypad-scan suspend_blocker.
- The user-space input-event thread returns from select/poll, calls
suspend_block on the process-input-events suspend_blocker and then calls read
on the input-event device.
- The input-event driver dequeues the key-event and, since the queue is now
empty, it calls suspend_unblock on the input-event-queue suspend_blocker.
- The user-space input-event thread returns from read. If it determines that
the key should leave the screen off, it calls suspend_unblock on the
process_input_events suspend_blocker and then calls select or poll. The
system will automatically suspend again, since now no suspend blockers are
active.

Key pressed Key released
| |
keypad-scan ++++++++++++++++++
input-event-queue +++ +++
process-input-events +++ +++

Driver API
==========

A driver can use the suspend block api by adding a suspend_blocker variable to
its state and calling suspend_blocker_init. For instance:
struct state {
struct suspend_blocker suspend_blocker;
}

init() {
suspend_blocker_init(&state->suspend_blocker, "suspend-blocker-name");
}

Before freeing the memory, suspend_blocker_destroy must be called:

uninit() {
suspend_blocker_destroy(&state->suspend_blocker);
}

When the driver determines that it needs to run (usually in an interrupt
handler) it calls suspend_block:
suspend_block(&state->suspend_blocker);

When it no longer needs to run it calls suspend_unblock:
suspend_unblock(&state->suspend_blocker);

Calling suspend_block when the suspend blocker is active or suspend_unblock when
it is not active has no effect. This allows drivers to update their state and
call suspend suspend_block or suspend_unblock based on the result.
For instance:

if (list_empty(&state->pending_work))
suspend_unblock(&state->suspend_blocker);
else
suspend_block(&state->suspend_blocker);