Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
c51a024e
Commit
c51a024e
authored
Dec 16, 2017
by
Rafael J. Wysocki
Browse files
Options
Browse Files
Download
Plain Diff
Merge back PM core material for v4.16.
parents
3487972d
34fb8f0b
Changes
7
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
252 additions
and
132 deletions
+252
-132
Documentation/driver-api/pm/devices.rst
Documentation/driver-api/pm/devices.rst
+25
-2
Documentation/power/pci.txt
Documentation/power/pci.txt
+11
-0
drivers/acpi/device_pm.c
drivers/acpi/device_pm.c
+24
-3
drivers/base/power/main.c
drivers/base/power/main.c
+84
-18
drivers/base/power/sysfs.c
drivers/base/power/sysfs.c
+79
-103
drivers/pci/pci-driver.c
drivers/pci/pci-driver.c
+17
-2
include/linux/pm.h
include/linux/pm.h
+12
-4
No files found.
Documentation/driver-api/pm/devices.rst
View file @
c51a024e
...
...
@@ -788,6 +788,29 @@ must reflect the "active" status for runtime PM in that case.
During system-wide resume from a sleep state it's easiest to put devices into
the full-power state, as explained in :file:`Documentation/power/runtime_pm.txt`.
Refer to that document for more information regarding this particular issue as
[
Refer to that document for more information regarding this particular issue as
well as for information on the device runtime power management framework in
general.
general.]
However, it often is desirable to leave devices in suspend after system
transitions to the working state, especially if those devices had been in
runtime suspend before the preceding system-wide suspend (or analogous)
transition. Device drivers can use the ``DPM_FLAG_LEAVE_SUSPENDED`` flag to
indicate to the PM core (and middle-layer code) that they prefer the specific
devices handled by them to be left suspended and they have no problems with
skipping their system-wide resume callbacks for this reason. Whether or not the
devices will actually be left in suspend may depend on their state before the
given system suspend-resume cycle and on the type of the system transition under
way. In particular, devices are not left suspended if that transition is a
restore from hibernation, as device states are not guaranteed to be reflected
by the information stored in the hibernation image in that case.
The middle-layer code involved in the handling of the device is expected to
indicate to the PM core if the device may be left in suspend by setting its
:c:member:`power.may_skip_resume` status bit which is checked by the PM core
during the "noirq" phase of the preceding system-wide suspend (or analogous)
transition. The middle layer is then responsible for handling the device as
appropriate in its "noirq" resume callback, which is executed regardless of
whether or not the device is left suspended, but the other resume callbacks
(except for ``->complete``) will be skipped automatically by the PM core if the
device really can be left in suspend.
Documentation/power/pci.txt
View file @
c51a024e
...
...
@@ -994,6 +994,17 @@ into D0 going forward), but if it is in runtime suspend in pci_pm_thaw_noirq(),
the function will set the power.direct_complete flag for it (to make the PM core
skip the subsequent "thaw" callbacks for it) and return.
Setting the DPM_FLAG_LEAVE_SUSPENDED flag means that the driver prefers the
device to be left in suspend after system-wide transitions to the working state.
This flag is checked by the PM core, but the PCI bus type informs the PM core
which devices may be left in suspend from its perspective (that happens during
the "noirq" phase of system-wide suspend and analogous transitions) and next it
uses the dev_pm_may_skip_resume() helper to decide whether or not to return from
pci_pm_resume_noirq() early, as the PM core will skip the remaining resume
callbacks for the device during the transition under way and will set its
runtime PM status to "suspended" if dev_pm_may_skip_resume() returns "true" for
it.
3.2. Device Runtime Power Management
------------------------------------
In addition to providing device power management callbacks PCI device drivers
...
...
drivers/acpi/device_pm.c
View file @
c51a024e
...
...
@@ -990,7 +990,7 @@ void acpi_subsys_complete(struct device *dev)
* the sleep state it is going out of and it has never been resumed till
* now, resume it in case the firmware powered it up.
*/
if
(
dev
->
power
.
direct_complete
&&
pm_resume_via_firmware
())
if
(
pm_runtime_suspended
(
dev
)
&&
pm_resume_via_firmware
())
pm_request_resume
(
dev
);
}
EXPORT_SYMBOL_GPL
(
acpi_subsys_complete
);
...
...
@@ -1039,10 +1039,28 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late);
*/
int
acpi_subsys_suspend_noirq
(
struct
device
*
dev
)
{
if
(
dev_pm_smart_suspend_and_suspended
(
dev
))
int
ret
;
if
(
dev_pm_smart_suspend_and_suspended
(
dev
))
{
dev
->
power
.
may_skip_resume
=
true
;
return
0
;
}
ret
=
pm_generic_suspend_noirq
(
dev
);
if
(
ret
)
return
ret
;
/*
* If the target system sleep state is suspend-to-idle, it is sufficient
* to check whether or not the device's wakeup settings are good for
* runtime PM. Otherwise, the pm_resume_via_firmware() check will cause
* acpi_subsys_complete() to take care of fixing up the device's state
* anyway, if need be.
*/
dev
->
power
.
may_skip_resume
=
device_may_wakeup
(
dev
)
||
!
device_can_wakeup
(
dev
);
return
pm_generic_suspend_noirq
(
dev
)
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
acpi_subsys_suspend_noirq
);
...
...
@@ -1052,6 +1070,9 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend_noirq);
*/
int
acpi_subsys_resume_noirq
(
struct
device
*
dev
)
{
if
(
dev_pm_may_skip_resume
(
dev
))
return
0
;
/*
* Devices with DPM_FLAG_SMART_SUSPEND may be left in runtime suspend
* during system suspend, so update their runtime PM status to "active"
...
...
drivers/base/power/main.c
View file @
c51a024e
...
...
@@ -18,7 +18,6 @@
*/
#include <linux/device.h>
#include <linux/kallsyms.h>
#include <linux/export.h>
#include <linux/mutex.h>
#include <linux/pm.h>
...
...
@@ -540,6 +539,18 @@ void dev_pm_skip_next_resume_phases(struct device *dev)
dev
->
power
.
is_suspended
=
false
;
}
/**
* dev_pm_may_skip_resume - System-wide device resume optimization check.
* @dev: Target device.
*
* Checks whether or not the device may be left in suspend after a system-wide
* transition to the working state.
*/
bool
dev_pm_may_skip_resume
(
struct
device
*
dev
)
{
return
!
dev
->
power
.
must_resume
&&
pm_transition
.
event
!=
PM_EVENT_RESTORE
;
}
/**
* device_resume_noirq - Execute a "noirq resume" callback for given device.
* @dev: Device to handle.
...
...
@@ -588,6 +599,18 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn
error
=
dpm_run_callback
(
callback
,
dev
,
state
,
info
);
dev
->
power
.
is_noirq_suspended
=
false
;
if
(
dev_pm_may_skip_resume
(
dev
))
{
/*
* The device is going to be left in suspend, but it might not
* have been in runtime suspend before the system suspended, so
* its runtime PM status needs to be updated to avoid confusing
* the runtime PM framework when runtime PM is enabled for the
* device again.
*/
pm_runtime_set_suspended
(
dev
);
dev_pm_skip_next_resume_phases
(
dev
);
}
Out:
complete_all
(
&
dev
->
power
.
completion
);
TRACE_RESUME
(
error
);
...
...
@@ -1089,6 +1112,22 @@ static pm_message_t resume_event(pm_message_t sleep_state)
return
PMSG_ON
;
}
static
void
dpm_superior_set_must_resume
(
struct
device
*
dev
)
{
struct
device_link
*
link
;
int
idx
;
if
(
dev
->
parent
)
dev
->
parent
->
power
.
must_resume
=
true
;
idx
=
device_links_read_lock
();
list_for_each_entry_rcu
(
link
,
&
dev
->
links
.
suppliers
,
c_node
)
link
->
supplier
->
power
.
must_resume
=
true
;
device_links_read_unlock
(
idx
);
}
/**
* __device_suspend_noirq - Execute a "noirq suspend" callback for given device.
* @dev: Device to handle.
...
...
@@ -1140,10 +1179,28 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
}
error
=
dpm_run_callback
(
callback
,
dev
,
state
,
info
);
if
(
!
error
)
dev
->
power
.
is_noirq_suspended
=
true
;
else
if
(
error
)
{
async_error
=
error
;
goto
Complete
;
}
dev
->
power
.
is_noirq_suspended
=
true
;
if
(
dev_pm_test_driver_flags
(
dev
,
DPM_FLAG_LEAVE_SUSPENDED
))
{
/*
* The only safe strategy here is to require that if the device
* may not be left in suspend, resume callbacks must be invoked
* for it.
*/
dev
->
power
.
must_resume
=
dev
->
power
.
must_resume
||
!
dev
->
power
.
may_skip_resume
||
atomic_read
(
&
dev
->
power
.
usage_count
)
>
1
;
}
else
{
dev
->
power
.
must_resume
=
true
;
}
if
(
dev
->
power
.
must_resume
)
dpm_superior_set_must_resume
(
dev
);
Complete:
complete_all
(
&
dev
->
power
.
completion
);
...
...
@@ -1435,6 +1492,22 @@ static int legacy_suspend(struct device *dev, pm_message_t state,
return
error
;
}
static
void
dpm_propagate_to_parent
(
struct
device
*
dev
)
{
struct
device
*
parent
=
dev
->
parent
;
if
(
!
parent
)
return
;
spin_lock_irq
(
&
parent
->
power
.
lock
);
parent
->
power
.
direct_complete
=
false
;
if
(
dev
->
power
.
wakeup_path
&&
!
parent
->
power
.
ignore_children
)
parent
->
power
.
wakeup_path
=
true
;
spin_unlock_irq
(
&
parent
->
power
.
lock
);
}
static
void
dpm_clear_suppliers_direct_complete
(
struct
device
*
dev
)
{
struct
device_link
*
link
;
...
...
@@ -1500,6 +1573,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
dev
->
power
.
direct_complete
=
false
;
}
dev
->
power
.
may_skip_resume
=
false
;
dev
->
power
.
must_resume
=
false
;
dpm_watchdog_set
(
&
wd
,
dev
);
device_lock
(
dev
);
...
...
@@ -1543,19 +1619,8 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
End:
if
(
!
error
)
{
struct
device
*
parent
=
dev
->
parent
;
dev
->
power
.
is_suspended
=
true
;
if
(
parent
)
{
spin_lock_irq
(
&
parent
->
power
.
lock
);
dev
->
parent
->
power
.
direct_complete
=
false
;
if
(
dev
->
power
.
wakeup_path
&&
!
dev
->
parent
->
power
.
ignore_children
)
dev
->
parent
->
power
.
wakeup_path
=
true
;
spin_unlock_irq
(
&
parent
->
power
.
lock
);
}
dpm_propagate_to_parent
(
dev
);
dpm_clear_suppliers_direct_complete
(
dev
);
}
...
...
@@ -1665,8 +1730,9 @@ static int device_prepare(struct device *dev, pm_message_t state)
if
(
dev
->
power
.
syscore
)
return
0
;
WARN_ON
(
dev_pm_test_driver_flags
(
dev
,
DPM_FLAG_SMART_SUSPEND
)
&&
!
pm_runtime_enabled
(
dev
));
WARN_ON
(
!
pm_runtime_enabled
(
dev
)
&&
dev_pm_test_driver_flags
(
dev
,
DPM_FLAG_SMART_SUSPEND
|
DPM_FLAG_LEAVE_SUSPENDED
));
/*
* If a device's parent goes into runtime suspend at the wrong time,
...
...
drivers/base/power/sysfs.c
View file @
c51a024e
This diff is collapsed.
Click to expand it.
drivers/pci/pci-driver.c
View file @
c51a024e
...
...
@@ -699,7 +699,7 @@ static void pci_pm_complete(struct device *dev)
pm_generic_complete
(
dev
);
/* Resume device if platform firmware has put it in reset-power-on */
if
(
dev
->
power
.
direct_complete
&&
pm_resume_via_firmware
())
{
if
(
pm_runtime_suspended
(
dev
)
&&
pm_resume_via_firmware
())
{
pci_power_t
pre_sleep_state
=
pci_dev
->
current_state
;
pci_update_current_state
(
pci_dev
,
pci_dev
->
current_state
);
...
...
@@ -783,8 +783,10 @@ static int pci_pm_suspend_noirq(struct device *dev)
struct
pci_dev
*
pci_dev
=
to_pci_dev
(
dev
);
const
struct
dev_pm_ops
*
pm
=
dev
->
driver
?
dev
->
driver
->
pm
:
NULL
;
if
(
dev_pm_smart_suspend_and_suspended
(
dev
))
if
(
dev_pm_smart_suspend_and_suspended
(
dev
))
{
dev
->
power
.
may_skip_resume
=
true
;
return
0
;
}
if
(
pci_has_legacy_pm_support
(
pci_dev
))
return
pci_legacy_suspend_late
(
dev
,
PMSG_SUSPEND
);
...
...
@@ -838,6 +840,16 @@ static int pci_pm_suspend_noirq(struct device *dev)
Fixup:
pci_fixup_device
(
pci_fixup_suspend_late
,
pci_dev
);
/*
* If the target system sleep state is suspend-to-idle, it is sufficient
* to check whether or not the device's wakeup settings are good for
* runtime PM. Otherwise, the pm_resume_via_firmware() check will cause
* pci_pm_complete() to take care of fixing up the device's state
* anyway, if need be.
*/
dev
->
power
.
may_skip_resume
=
device_may_wakeup
(
dev
)
||
!
device_can_wakeup
(
dev
);
return
0
;
}
...
...
@@ -847,6 +859,9 @@ static int pci_pm_resume_noirq(struct device *dev)
struct
device_driver
*
drv
=
dev
->
driver
;
int
error
=
0
;
if
(
dev_pm_may_skip_resume
(
dev
))
return
0
;
/*
* Devices with DPM_FLAG_SMART_SUSPEND may be left in runtime suspend
* during system suspend, so update their runtime PM status to "active"
...
...
include/linux/pm.h
View file @
c51a024e
...
...
@@ -556,9 +556,10 @@ struct pm_subsys_data {
* These flags can be set by device drivers at the probe time. They need not be
* cleared by the drivers as the driver core will take care of that.
*
* NEVER_SKIP: Do not skip system suspend/resume callbacks for the device.
* NEVER_SKIP: Do not skip
all
system suspend/resume callbacks for the device.
* SMART_PREPARE: Check the return value of the driver's ->prepare callback.
* SMART_SUSPEND: No need to resume the device from runtime suspend.
* LEAVE_SUSPENDED: Avoid resuming the device during system resume if possible.
*
* Setting SMART_PREPARE instructs bus types and PM domains which may want
* system suspend/resume callbacks to be skipped for the device to return 0 from
...
...
@@ -572,10 +573,14 @@ struct pm_subsys_data {
* necessary from the driver's perspective. It also may cause them to skip
* invocations of the ->suspend_late and ->suspend_noirq callbacks provided by
* the driver if they decide to leave the device in runtime suspend.
*
* Setting LEAVE_SUSPENDED informs the PM core and middle-layer code that the
* driver prefers the device to be left in suspend after system resume.
*/
#define DPM_FLAG_NEVER_SKIP BIT(0)
#define DPM_FLAG_SMART_PREPARE BIT(1)
#define DPM_FLAG_SMART_SUSPEND BIT(2)
#define DPM_FLAG_NEVER_SKIP BIT(0)
#define DPM_FLAG_SMART_PREPARE BIT(1)
#define DPM_FLAG_SMART_SUSPEND BIT(2)
#define DPM_FLAG_LEAVE_SUSPENDED BIT(3)
struct
dev_pm_info
{
pm_message_t
power_state
;
...
...
@@ -597,6 +602,8 @@ struct dev_pm_info {
bool
wakeup_path
:
1
;
bool
syscore
:
1
;
bool
no_pm_callbacks
:
1
;
/* Owned by the PM core */
unsigned
int
must_resume
:
1
;
/* Owned by the PM core */
unsigned
int
may_skip_resume
:
1
;
/* Set by subsystems */
#else
unsigned
int
should_wakeup
:
1
;
#endif
...
...
@@ -766,6 +773,7 @@ extern int pm_generic_poweroff(struct device *dev);
extern
void
pm_generic_complete
(
struct
device
*
dev
);
extern
void
dev_pm_skip_next_resume_phases
(
struct
device
*
dev
);
extern
bool
dev_pm_may_skip_resume
(
struct
device
*
dev
);
extern
bool
dev_pm_smart_suspend_and_suspended
(
struct
device
*
dev
);
#else
/* !CONFIG_PM_SLEEP */
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment