diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-09-05 12:19:08 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-09-05 12:19:08 -0700 |
commit | 439644096c1a6afb9bd9953130f4444a856f76c5 (patch) | |
tree | cdf21533aa0ec95d084988f234186f8a5071d5df | |
parent | b42a362e6d10c342004b183defcb9940331b6737 (diff) | |
parent | d97561f461e4cb5b2e170bc33b466cffbddc8719 (diff) | |
download | linux-439644096c1a6afb9bd9953130f4444a856f76c5.tar.gz linux-439644096c1a6afb9bd9953130f4444a856f76c5.tar.bz2 linux-439644096c1a6afb9bd9953130f4444a856f76c5.zip |
Merge tag 'pm-4.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull power management updates from Rafael Wysocki:
"This time (again) cpufreq gets the majority of changes which mostly
are driver updates (including a major consolidation of intel_pstate),
some schedutil governor modifications and core cleanups.
There also are some changes in the system suspend area, mostly related
to diagnostics and debug messages plus some renames of things related
to suspend-to-idle. One major change here is that suspend-to-idle is
now going to be preferred over S3 on systems where the ACPI tables
indicate to do so and provide requsite support (the Low Power Idle S0
_DSM in particular). The system sleep documentation and the tools
related to it are updated too.
The rest is a few cpuidle changes (nothing major), devfreq updates,
generic power domains (genpd) framework updates and a few assorted
modifications elsewhere.
Specifics:
- Drop the P-state selection algorithm based on a PID controller from
intel_pstate and make it use the same P-state selection method
(based on the CPU load) for all types of systems in the active mode
(Rafael Wysocki, Srinivas Pandruvada).
- Rework the cpufreq core and governors to make it possible to take
cross-CPU utilization updates into account and modify the schedutil
governor to actually do so (Viresh Kumar).
- Clean up the handling of transition latency information in the
cpufreq core and untangle it from the information on which drivers
cannot do dynamic frequency switching (Viresh Kumar).
- Add support for new SoCs (MT2701/MT7623 and MT7622) to the mediatek
cpufreq driver and update its DT bindings (Sean Wang).
- Modify the cpufreq dt-platdev driver to autimatically create
cpufreq devices for the new (v2) Operating Performance Points (OPP)
DT bindings and update its whitelist of supported systems (Viresh
Kumar, Shubhrajyoti Datta, Marc Gonzalez, Khiem Nguyen, Finley
Xiao).
- Add support for Ux500 to the cpufreq-dt driver and drop the
obsolete dbx500 cpufreq driver (Linus Walleij, Arnd Bergmann).
- Add new SoC (R8A7795) support to the cpufreq rcar driver (Khiem
Nguyen).
- Fix and clean up assorted issues in the cpufreq drivers and core
(Arvind Yadav, Christophe Jaillet, Colin Ian King, Gustavo Silva,
Julia Lawall, Leonard Crestez, Rob Herring, Sudeep Holla).
- Update the IO-wait boost handling in the schedutil governor to make
it less aggressive (Joel Fernandes).
- Rework system suspend diagnostics to make it print fewer messages
to the kernel log by default, add a sysfs knob to allow more
suspend-related messages to be printed and add Low Power S0 Idle
constraints checks to the ACPI suspend-to-idle code (Rafael
Wysocki, Srinivas Pandruvada).
- Prefer suspend-to-idle over S3 on ACPI-based systems with the
ACPI_FADT_LOW_POWER_S0 flag set and the Low Power Idle S0 _DSM
interface present in the ACPI tables (Rafael Wysocki).
- Update documentation related to system sleep and rename a number of
items in the code to make it cleare that they are related to
suspend-to-idle (Rafael Wysocki).
- Export a variable allowing device drivers to check the target
system sleep state from the core system suspend code (Florian
Fainelli).
- Clean up the cpuidle subsystem to handle the polling state on x86
in a more straightforward way and to use %pOF instead of full_name
(Rafael Wysocki, Rob Herring).
- Update the devfreq framework to fix and clean up a few minor issues
(Chanwoo Choi, Rob Herring).
- Extend diagnostics in the generic power domains (genpd) framework
and clean it up slightly (Thara Gopinath, Rob Herring).
- Fix and clean up a couple of issues in the operating performance
points (OPP) framework (Viresh Kumar, Waldemar Rymarkiewicz).
- Add support for RV1108 to the rockchip-io Adaptive Voltage Scaling
(AVS) driver (David Wu).
- Fix the usage of notifiers in CPU power management on some
platforms (Alex Shi).
- Update the pm-graph system suspend/hibernation and boot profiling
utility (Todd Brandt).
- Make it possible to run the cpupower utility without CPU0 (Prarit
Bhargava)"
* tag 'pm-4.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (87 commits)
cpuidle: Make drivers initialize polling state
cpuidle: Move polling state initialization code to separate file
cpuidle: Eliminate the CPUIDLE_DRIVER_STATE_START symbol
cpufreq: imx6q: Fix imx6sx low frequency support
cpufreq: speedstep-lib: make several arrays static, makes code smaller
PM: docs: Delete the obsolete states.txt document
PM: docs: Describe high-level PM strategies and sleep states
PM / devfreq: Fix memory leak when fail to register device
PM / devfreq: Add dependency on PM_OPP
PM / devfreq: Move private devfreq_update_stats() into devfreq
PM / devfreq: Convert to using %pOF instead of full_name
PM / AVS: rockchip-io: add io selectors and supplies for RV1108
cpufreq: ti: Fix 'of_node_put' being called twice in error handling path
cpufreq: dt-platdev: Drop few entries from whitelist
cpufreq: dt-platdev: Automatically create cpufreq device with OPP v2
ARM: ux500: don't select CPUFREQ_DT
cpuidle: Convert to using %pOF instead of full_name
cpufreq: Convert to using %pOF instead of full_name
PM / Domains: Convert to using %pOF instead of full_name
cpufreq: Cap the default transition delay value to 10 ms
...
101 files changed, 2835 insertions, 1746 deletions
diff --git a/Documentation/ABI/testing/sysfs-power b/Documentation/ABI/testing/sysfs-power index f523e5a3ac33..713cab1d5f12 100644 --- a/Documentation/ABI/testing/sysfs-power +++ b/Documentation/ABI/testing/sysfs-power @@ -273,3 +273,15 @@ Description: This output is useful for system wakeup diagnostics of spurious wakeup interrupts. + +What: /sys/power/pm_debug_messages +Date: July 2017 +Contact: Rafael J. Wysocki <rjw@rjwysocki.net> +Description: + The /sys/power/pm_debug_messages file controls the printing + of debug messages from the system suspend/hiberbation + infrastructure to the kernel log. + + Writing a "1" to this file enables the debug messages and + writing a "0" (default) to it disables them. Reads from + this file return the current value. diff --git a/Documentation/admin-guide/pm/cpufreq.rst b/Documentation/admin-guide/pm/cpufreq.rst index 7af83a92d2d6..47153e64dfb5 100644 --- a/Documentation/admin-guide/pm/cpufreq.rst +++ b/Documentation/admin-guide/pm/cpufreq.rst @@ -479,14 +479,6 @@ This governor exposes the following tunables: # echo `$(($(cat cpuinfo_transition_latency) * 750 / 1000)) > ondemand/sampling_rate - -``min_sampling_rate`` - The minimum value of ``sampling_rate``. - - Equal to 10000 (10 ms) if :c:macro:`CONFIG_NO_HZ_COMMON` and - :c:data:`tick_nohz_active` are both set or to 20 times the value of - :c:data:`jiffies` in microseconds otherwise. - ``up_threshold`` If the estimated CPU load is above this value (in percent), the governor will set the frequency to the maximum value allowed for the policy. diff --git a/Documentation/admin-guide/pm/index.rst b/Documentation/admin-guide/pm/index.rst index 7f148f76f432..49237ac73442 100644 --- a/Documentation/admin-guide/pm/index.rst +++ b/Documentation/admin-guide/pm/index.rst @@ -5,12 +5,6 @@ Power Management .. toctree:: :maxdepth: 2 - cpufreq - intel_pstate - -.. only:: subproject and html - - Indices - ======= - - * :ref:`genindex` + strategies + system-wide + working-state diff --git a/Documentation/admin-guide/pm/intel_pstate.rst b/Documentation/admin-guide/pm/intel_pstate.rst index 1d6249825efc..d2b6fda3d67b 100644 --- a/Documentation/admin-guide/pm/intel_pstate.rst +++ b/Documentation/admin-guide/pm/intel_pstate.rst @@ -167,35 +167,17 @@ is set. ``powersave`` ............. -Without HWP, this P-state selection algorithm generally depends on the -processor model and/or the system profile setting in the ACPI tables and there -are two variants of it. - -One of them is used with processors from the Atom line and (regardless of the -processor model) on platforms with the system profile in the ACPI tables set to -"mobile" (laptops mostly), "tablet", "appliance PC", "desktop", or -"workstation". It is also used with processors supporting the HWP feature if -that feature has not been enabled (that is, with the ``intel_pstate=no_hwp`` -argument in the kernel command line). It is similar to the algorithm +Without HWP, this P-state selection algorithm is similar to the algorithm implemented by the generic ``schedutil`` scaling governor except that the utilization metric used by it is based on numbers coming from feedback registers of the CPU. It generally selects P-states proportional to the -current CPU utilization, so it is referred to as the "proportional" algorithm. - -The second variant of the ``powersave`` P-state selection algorithm, used in all -of the other cases (generally, on processors from the Core line, so it is -referred to as the "Core" algorithm), is based on the values read from the APERF -and MPERF feedback registers and the previously requested target P-state. -It does not really take CPU utilization into account explicitly, but as a rule -it causes the CPU P-state to ramp up very quickly in response to increased -utilization which is generally desirable in server environments. - -Regardless of the variant, this algorithm is run by the driver's utilization -update callback for the given CPU when it is invoked by the CPU scheduler, but -not more often than every 10 ms (that can be tweaked via ``debugfs`` in `this -particular case <Tuning Interface in debugfs_>`_). Like in the ``performance`` -case, the hardware configuration is not touched if the new P-state turns out to -be the same as the current one. +current CPU utilization. + +This algorithm is run by the driver's utilization update callback for the +given CPU when it is invoked by the CPU scheduler, but not more often than +every 10 ms. Like in the ``performance`` case, the hardware configuration +is not touched if the new P-state turns out to be the same as the current +one. This is the default P-state selection algorithm if the :c:macro:`CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE` kernel configuration option @@ -720,34 +702,7 @@ P-state is called, the ``ftrace`` filter can be set to to gnome-shell-3409 [001] ..s. 2537.650850: intel_pstate_set_pstate <-intel_pstate_timer_func <idle>-0 [000] ..s. 2537.654843: intel_pstate_set_pstate <-intel_pstate_timer_func -Tuning Interface in ``debugfs`` -------------------------------- - -The ``powersave`` algorithm provided by ``intel_pstate`` for `the Core line of -processors in the active mode <powersave_>`_ is based on a `PID controller`_ -whose parameters were chosen to address a number of different use cases at the -same time. However, it still is possible to fine-tune it to a specific workload -and the ``debugfs`` interface under ``/sys/kernel/debug/pstate_snb/`` is -provided for this purpose. [Note that the ``pstate_snb`` directory will be -present only if the specific P-state selection algorithm matching the interface -in it actually is in use.] - -The following files present in that directory can be used to modify the PID -controller parameters at run time: - -| ``deadband`` -| ``d_gain_pct`` -| ``i_gain_pct`` -| ``p_gain_pct`` -| ``sample_rate_ms`` -| ``setpoint`` - -Note, however, that achieving desirable results this way generally requires -expert-level understanding of the power vs performance tradeoff, so extra care -is recommended when attempting to do that. - .. _LCEU2015: http://events.linuxfoundation.org/sites/events/files/slides/LinuxConEurope_2015.pdf .. _SDM: http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-system-programming-manual-325384.html .. _ACPI specification: http://www.uefi.org/sites/default/files/resources/ACPI_6_1.pdf -.. _PID controller: https://en.wikipedia.org/wiki/PID_controller diff --git a/Documentation/admin-guide/pm/sleep-states.rst b/Documentation/admin-guide/pm/sleep-states.rst new file mode 100644 index 000000000000..1e5c0f00cb2f --- /dev/null +++ b/Documentation/admin-guide/pm/sleep-states.rst @@ -0,0 +1,245 @@ +=================== +System Sleep States +=================== + +:: + + Copyright (c) 2017 Intel Corp., Rafael J. Wysocki <rafael.j.wysocki@intel.com> + +Sleep states are global low-power states of the entire system in which user +space code cannot be executed and the overall system activity is significantly +reduced. + + +Sleep States That Can Be Supported +================================== + +Depending on its configuration and the capabilities of the platform it runs on, +the Linux kernel can support up to four system sleep states, includig +hibernation and up to three variants of system suspend. The sleep states that +can be supported by the kernel are listed below. + +.. _s2idle: + +Suspend-to-Idle +--------------- + +This is a generic, pure software, light-weight variant of system suspend (also +referred to as S2I or S2Idle). It allows more energy to be saved relative to +runtime idle by freezing user space, suspending the timekeeping and putting all +I/O devices into low-power states (possibly lower-power than available in the +working state), such that the processors can spend time in their deepest idle +states while the system is suspended. + +The system is woken up from this state by in-band interrupts, so theoretically +any devices that can cause interrupts to be generated in the working state can +also be set up as wakeup devices for S2Idle. + +This state can be used on platforms without support for :ref:`standby <standby>` +or :ref:`suspend-to-RAM <s2ram>`, or it can be used in addition to any of the +deeper system suspend variants to provide reduced resume latency. It is always +supported if the :c:macro:`CONFIG_SUSPEND` kernel configuration option is set. + +.. _standby: + +Standby +------- + +This state, if supported, offers moderate, but real, energy savings, while +providing a relatively straightforward transition back to the working state. No +operating state is lost (the system core logic retains power), so the system can +go back to where it left off easily enough. + +In addition to freezing user space, suspending the timekeeping and putting all +I/O devices into low-power states, which is done for :ref:`suspend-to-idle +<s2idle>` too, nonboot CPUs are taken offline and all low-level system functions +are suspended during transitions into this state. For this reason, it should +allow more energy to be saved relative to :ref:`suspend-to-idle <s2idle>`, but +the resume latency will generally be greater than for that state. + +The set of devices that can wake up the system from this state usually is +reduced relative to :ref:`suspend-to-idle <s2idle>` and it may be necessary to +rely on the platform for setting up the wakeup functionality as appropriate. + +This state is supported if the :c:macro:`CONFIG_SUSPEND` kernel configuration +option is set and the support for it is registered by the platform with the +core system suspend subsystem. On ACPI-based systems this state is mapped to +the S1 system state defined by ACPI. + +.. _s2ram: + +Suspend-to-RAM +-------------- + +This state (also referred to as STR or S2RAM), if supported, offers significant +energy savings as everything in the system is put into a low-power state, except +for memory, which should be placed into the self-refresh mode to retain its +contents. All of the steps carried out when entering :ref:`standby <standby>` +are also carried out during transitions to S2RAM. Additional operations may +take place depending on the platform capabilities. In particular, on ACPI-based +systems the kernel passes control to the platform firmware (BIOS) as the last +step during S2RAM transitions and that usually results in powering down some +more low-level components that are not directly controlled by the kernel. + +The state of devices and CPUs is saved and held in memory. All devices are +suspended and put into low-power states. In many cases, all peripheral buses +lose power when entering S2RAM, so devices must be able to handle the transition +back to the "on" state. + +On ACPI-based systems S2RAM requires some minimal boot-strapping code in the +platform firmware to resume the system from it. This may be the case on other +platforms too. + +The set of devices that can wake up the system from S2RAM usually is reduced +relative to :ref:`suspend-to-idle <s2idle>` and :ref:`standby <standby>` and it +may be necessary to rely on the platform for setting up the wakeup functionality +as appropriate. + +S2RAM is supported if the :c:macro:`CONFIG_SUSPEND` kernel configuration option +is set and the support for it is registered by the platform with the core system +suspend subsystem. On ACPI-based systems it is mapped to the S3 system state +defined by ACPI. + +.. _hibernation: + +Hibernation +----------- + +This state (also referred to as Suspend-to-Disk or STD) offers the greatest +energy savings and can be used even in the absence of low-level platform support +for system suspend. However, it requires some low-level code for resuming the +system to be present for the underlying CPU architecture. + +Hibernation is significantly different from any of the system suspend variants. +It takes three system state changes to put it into hibernation and two system +state changes to resume it. + +First, when hibernation is triggered, the kernel stops all system activity and +creates a snapshot image of memory to be written into persistent storage. Next, +the system goes into a state in which the snapshot image can be saved, the image +is written out and finally the system goes into the target low-power state in +which power is cut from almost all of its hardware components, including memory, +except for a limited set of wakeup devices. + +Once the snapshot image has been written out, the system may either enter a +special low-power state (like ACPI S4), or it may simply power down itself. +Powering down means minimum power draw and it allows this mechanism to work on +any system. However, entering a special low-power state may allow additional +means of system wakeup to be used (e.g. pressing a key on the keyboard or +opening a laptop lid). + +After wakeup, control goes to the platform firmware that runs a boot loader +which boots a fresh instance of the kernel (control may also go directly to +the boot loader, depending on the system configuration, but anyway it causes +a fresh instance of the kernel to be booted). That new instance of the kernel +(referred to as the ``restore kernel``) looks for a hibernation image in +persistent storage and if one is found, it is loaded into memory. Next, all +activity in the system is stopped and the restore kernel overwrites itself with +the image contents and jumps into a special trampoline area in the original +kernel stored in the image (referred to as the ``image kernel``), which is where +the special architecture-specific low-level code is needed. Finally, the +image kernel restores the system to the pre-hibernation state and allows user +space to run again. + +Hibernation is supported if the :c:macro:`CONFIG_HIBERNATION` kernel +configuration option is set. However, this option can only be set if support +for the given CPU architecture includes the low-level code for system resume. + + +Basic ``sysfs`` Interfaces for System Suspend and Hibernation +============================================================= + +The following files located in the :file:`/sys/power/` directory can be used by +user space for sleep states control. + +``state`` + This file contains a list of strings representing sleep states supported + by the kernel. Writing one of these strings into it causes the kernel + to start a transition of the system into the sleep state represented by + that string. + + In particular, the strings "disk", "freeze" and "standby" represent the + :ref:`hibernation <hibernation>`, :ref:`suspend-to-idle <s2idle>` and + :ref:`standby <standby>` sleep states, respectively. The string "mem" + is interpreted in accordance with the contents of the ``mem_sleep`` file + described below. + + If the kernel does not support any system sleep states, this file is + not present. + +``mem_sleep`` + This file contains a list of strings representing supported system + suspend variants and allows user space to select the variant to be + associated with the "mem" string in the ``state`` file described above. + + The strings that may be present in this file are "s2idle", "shallow" + and "deep". The string "s2idle" always represents :ref:`suspend-to-idle + <s2idle>` and, by convention, "shallow" and "deep" represent + :ref:`standby <standby>` and :ref:`suspend-to-RAM <s2ram>`, + respectively. + + Writing one of the listed strings into this file causes the system + suspend variant represented by it to be associated with the "mem" string + in the ``state`` file. The string representing the suspend variant + currently associated with the "mem" string in the ``state`` file + is listed in square brackets. + + If the kernel does not support system suspend, this file is not present. + +``disk`` + This file contains a list of strings representing different operations + that can be carried out after the hibernation image has been saved. The + possible options are as follows: + + ``platform`` + Put the system into a special low-power state (e.g. ACPI S4) to + make additional wakeup options available and possibly allow the + platform firmware to take a simplified initialization path after + wakeup. + + ``shutdown`` + Power off the system. + + ``reboot`` + Reboot the system (useful for diagnostics mostly). + + ``suspend`` + Hybrid system suspend. Put the system into the suspend sleep + state selected through the ``mem_sleep`` file described above. + If the system is successfully woken up from that state, discard + the hibernation image and continue. Otherwise, use the image + to restore the previous state of the system. + + ``test_resume`` + Diagnostic operation. Load the image as though the system had + just woken up from hibernation and the currently running kernel + instance was a restore kernel and follow up with full system + resume. + + Writing one of the listed strings into this file causes the option + represented by it to be selected. + + The currently selected option is shown in square brackets which means + that the operation represented by it will be carried out after creating + and saving the image next time hibernation is triggered by writing + ``disk`` to :file:`/sys/power/state`. + + If the kernel does not support hibernation, this file is not present. + +According to the above, there are two ways to make the system go into the +:ref:`suspend-to-idle <s2idle>` state. The first one is to write "freeze" +directly to :file:`/sys/power/state`. The second one is to write "s2idle" to +:file:`/sys/power/mem_sleep` and then to write "mem" to +:file:`/sys/power/state`. Likewise, there are two ways to make the system go +into the :ref:`standby <standby>` state (the strings to write to the control +files in that case are "standby" or "shallow" and "mem", respectively) if that +state is supported by the platform. However, there is only one way to make the +system go into the :ref:`suspend-to-RAM <s2ram>` state (write "deep" into +:file:`/sys/power/mem_sleep` and "mem" into :file:`/sys/power/state`). + +The default suspend variant (ie. the one to be used without writing anything +into :file:`/sys/power/mem_sleep`) is either "deep" (on the majority of systems +supporting :ref:`suspend-to-RAM <s2ram>`) or "s2idle", but it can be overridden +by the value of the "mem_sleep_default" parameter in the kernel command line. +On some ACPI-based systems, depending on the information in the ACPI tables, the +default may be "s2idle" even if :ref:`suspend-to-RAM <s2ram>` is supported. diff --git a/Documentation/admin-guide/pm/strategies.rst b/Documentation/admin-guide/pm/strategies.rst new file mode 100644 index 000000000000..afe4d3f831fe --- /dev/null +++ b/Documentation/admin-guide/pm/strategies.rst @@ -0,0 +1,52 @@ +=========================== +Power Management Strategies +=========================== + +:: + + Copyright (c) 2017 Intel Corp., Rafael J. Wysocki <rafael.j.wysocki@intel.com> + +The Linux kernel supports two major high-level power management strategies. + +One of them is based on using global low-power states of the whole system in +which user space code cannot be executed and the overall system activity is +significantly reduced, referred to as :doc:`sleep states <sleep-states>`. The +kernel puts the system into one of these states when requested by user space +and the system stays in it until a special signal is received from one of +designated devices, triggering a transition to the ``working state`` in which +user space code can run. Because sleep states are global and the whole system +is affected by the state changes, this strategy is referred to as the +:doc:`system-wide power management <system-wide>`. + +The other strategy, referred to as the :doc:`working-state power management +<working-state>`, is based on adjusting the power states of individual hardware +components of the system, as needed, in the working state. In consequence, if +this strategy is in use, the working state of the system usually does not +correspond to any particular physical configuration of it, but can be treated as +a metastate covering a range of different power states of the system in which +the individual components of it can be either ``active`` (in use) or +``inactive`` (idle). If they are active, they have to be in power states +allowing them to process data and to be accessed by software. In turn, if they +are inactive, ideally, they should be in low-power states in which they may not +be accessible. + +If all of the system components are active, the system as a whole is regarded as +"runtime active" and that situation typically corresponds to the maximum power +draw (or maximum energy usage) of it. If all of them are inactive, the system +as a whole is regarded as "runtime idle" which may be very close to a sleep +state from the physical system configuration and power draw perspective, but +then it takes much less time and effort to start executing user space code than +for the same system in a sleep state. However, transitions from sleep states +back to the working state can only be started by a limited set of devices, so +typically the system can spend much more time in a sleep state than it can be +runtime idle in one go. For this reason, systems usually use less energy in +sleep states than when they are runtime idle most of the time. + +Moreover, the two power management strategies address different usage scenarios. +Namely, if the user indicates that the system will not be in use going forward, +for example by closing its lid (if the system is a laptop), it probably should +go into a sleep state at that point. On the other hand, if the user simply goes +away from the laptop keyboard, it probably should stay in the working state and +use the working-state power management in case it becomes idle, because the user +may come back to it at any time and then may want the system to be immediately +accessible. diff --git a/Documentation/admin-guide/pm/system-wide.rst b/Documentation/admin-guide/pm/system-wide.rst new file mode 100644 index 000000000000..0c81e4c5de39 --- /dev/null +++ b/Documentation/admin-guide/pm/system-wide.rst @@ -0,0 +1,8 @@ +============================ +System-Wide Power Management +============================ + +.. toctree:: + :maxdepth: 2 + + sleep-states diff --git a/Documentation/admin-guide/pm/working-state.rst b/Documentation/admin-guide/pm/working-state.rst new file mode 100644 index 000000000000..fa01bf083dfe --- /dev/null +++ b/Documentation/admin-guide/pm/working-state.rst @@ -0,0 +1,9 @@ +============================== +Working-State Power Management +============================== + +.. toctree:: + :maxdepth: 2 + + cpufreq + intel_pstate diff --git a/Documentation/devicetree/bindings/clock/mt8173-cpu-dvfs.txt b/Documentation/devicetree/bindings/clock/mt8173-cpu-dvfs.txt deleted file mode 100644 index 52b457c23eed..000000000000 --- a/Documentation/devicetree/bindings/clock/mt8173-cpu-dvfs.txt +++ /dev/null @@ -1,83 +0,0 @@ -Device Tree Clock bindins for CPU DVFS of Mediatek MT8173 SoC - -Required properties: -- clocks: A list of phandle + clock-specifier pairs for the clocks listed in clock names. -- clock-names: Should contain the following: - "cpu" - The multiplexer for clock input of CPU cluster. - "intermediate" - A parent of "cpu" clock which is used as "intermediate" clock - source (usually MAINPLL) when the original CPU PLL is under - transition and not stable yet. - Please refer to Documentation/devicetree/bindings/clk/clock-bindings.txt for - generic clock consumer properties. -- proc-supply: Regulator for Vproc of CPU cluster. - -Optional properties: -- sram-supply: Regulator for Vsram of CPU cluster. When present, the cpufreq driver - needs to do "voltage tracking" to step by step scale up/down Vproc and - Vsram to fit SoC specific needs. When absent, the voltage scaling - flow is handled by hardware, hence no software "voltage tracking" is - needed. - -Example: --------- - cpu0: cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x000>; - enable-method = "psci"; - cpu-idle-states = <&CPU_SLEEP_0>; - clocks = <&infracfg CLK_INFRA_CA53SEL>, - <&apmixedsys CLK_APMIXED_MAINPLL>; - clock-names = "cpu", "intermediate"; - }; - - cpu1: cpu@1 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x001>; - enable-method = "psci"; - cpu-idle-states = <&CPU_SLEEP_0>; - clocks = <&infracfg CLK_INFRA_CA53SEL>, - <&apmixedsys CLK_APMIXED_MAINPLL>; - clock-names = "cpu", "intermediate"; - }; - - cpu2: cpu@100 { - device_type = "cpu"; - compatible = "arm,cortex-a57"; - reg = <0x100>; - enable-method = "psci"; - cpu-idle-states = <&CPU_SLEEP_0>; - clocks = <&infracfg CLK_INFRA_CA57SEL>, - <&apmixedsys CLK_APMIXED_MAINPLL>; - clock-names = "cpu", "intermediate"; - }; - - cpu3: cpu@101 { - device_type = "cpu"; - compatible = "arm,cortex-a57"; - reg = <0x101>; - enable-method = "psci"; - cpu-idle-states = <&CPU_SLEEP_0>; - clocks = <&infracfg CLK_INFRA_CA57SEL>, - <&apmixedsys CLK_APMIXED_MAINPLL>; - clock-names = "cpu", "intermediate"; - }; - - &cpu0 { - proc-supply = <&mt6397_vpca15_reg>; - }; - - &cpu1 { - proc-supply = <&mt6397_vpca15_reg>; - }; - - &cpu2 { - proc-supply = <&da9211_vcpu_reg>; - sram-supply = <&mt6397_vsramca7_reg>; - }; - - &cpu3 { - proc-supply = <&da9211_vcpu_reg>; - sram-supply = <&mt6397_vsramca7_reg>; - }; diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-mediatek.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-mediatek.txt new file mode 100644 index 000000000000..f6403089edcf --- /dev/null +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-mediatek.txt @@ -0,0 +1,247 @@ +Binding for MediaTek's CPUFreq driver +===================================== + +Required properties: +- clocks: A list of phandle + clock-specifier pairs for the clocks listed in clock names. +- clock-names: Should contain the following: + "cpu" - The multiplexer for clock input of CPU cluster. + "intermediate" - A parent of "cpu" clock which is used as "intermediate" clock + source (usually MAINPLL) when the original CPU PLL is under + transition and not stable yet. + Please refer to Documentation/devicetree/bindings/clk/clock-bindings.txt for + generic clock consumer properties. +- operating-points-v2: Please refer to Documentation/devicetree/bindings/opp/opp.txt + for detail. +- proc-supply: Regulator for Vproc of CPU cluster. + +Optional properties: +- sram-supply: Regulator for Vsram of CPU cluster. When present, the cpufreq driver + needs to do "voltage tracking" to step by step scale up/down Vproc and + Vsram to fit SoC specific needs. When absent, the voltage scaling + flow is handled by hardware, hence no software "voltage tracking" is + needed. +- #cooling-cells: +- cooling-min-level: +- cooling-max-level: + Please refer to Documentation/devicetree/bindings/thermal/thermal.txt + for detail. + +Example 1 (MT7623 SoC): + + cpu_opp_table: opp_table { + compatible = "operating-points-v2"; + opp-shared; + + opp-598000000 { + opp-hz = /bits/ 64 <598000000>; + opp-microvolt = <1050000>; + }; + + opp-747500000 { + opp-hz = /bits/ 64 <747500000>; + opp-microvolt = <1050000>; + }; + + opp-1040000000 { + opp-hz = /bits/ 64 <1040000000>; + opp-microvolt = <1150000>; + }; + + opp-1196000000 { + opp-hz = /bits/ 64 <1196000000>; + opp-microvolt = <1200000>; + }; + + opp-1300000000 { + opp-hz = /bits/ 64 <1300000000>; + opp-microvolt = <1300000>; + }; + }; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x0>; + clocks = <&infracfg CLK_INFRA_CPUSEL>, + <&apmixedsys CLK_APMIXED_MAINPLL>; + clock-names = "cpu", "intermediate"; + operating-points-v2 = <&cpu_opp_table>; + #cooling-cells = <2>; + cooling-min-level = <0>; + cooling-max-level = <7>; + }; + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x1>; + operating-points-v2 = <&cpu_opp_table>; + }; + cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x2>; + operating-points-v2 = <&cpu_opp_table>; + }; + cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x3>; + operating-points-v2 = <&cpu_opp_table>; + }; + +Example 2 (MT8173 SoC): + cpu_opp_table_a: opp_table_a { + compatible = "operating-points-v2"; + opp-shared; + + opp-507000000 { + opp-hz = /bits/ 64 <507000000>; + opp-microvolt = <859000>; + }; + + opp-702000000 { + opp-hz = /bits/ 64 <702000000>; + opp-microvolt = <908000>; + }; + + opp-1001000000 { + opp-hz = /bits/ 64 <1001000000>; + opp-microvolt = <983000>; + }; + + opp-1105000000 { + opp-hz = /bits/ 64 <1105000000>; + opp-microvolt = <1009000>; + }; + + opp-1183000000 { + opp-hz = /bits/ 64 <1183000000>; + opp-microvolt = <1028000>; + }; + + opp-1404000000 { + opp-hz = /bits/ 64 <1404000000>; + opp-microvolt = <1083000>; + }; + + opp-1508000000 { + opp-hz = /bits/ 64 <1508000000>; + opp-microvolt = <1109000>; + }; + + opp-1573000000 { + opp-hz = /bits/ 64 <1573000000>; + opp-microvolt = <1125000>; + }; + }; + + cpu_opp_table_b: opp_table_b { + compatible = "operating-points-v2"; + opp-shared; + + opp-507000000 { + opp-hz = /bits/ 64 <507000000>; + opp-microvolt = <828000>; + }; + + opp-702000000 { + opp-hz = /bits/ 64 <702000000>; + opp-microvolt = <867000>; + }; + + opp-1001000000 { + opp-hz = /bits/ 64 <1001000000>; + opp-microvolt = <927000>; + }; + + opp-1209000000 { + opp-hz = /bits/ 64 <1209000000>; + opp-microvolt = <968000>; + }; + + opp-1404000000 { + opp-hz = /bits/ 64 <1007000000>; + opp-microvolt = <1028000>; + }; + + opp-1612000000 { + opp-hz = /bits/ 64 <1612000000>; + opp-microvolt = <1049000>; + }; + + opp-1807000000 { + opp-hz = /bits/ 64 <1807000000>; + opp-microvolt = <1089000>; + }; + + opp-1989000000 { + opp-hz = /bits/ 64 <1989000000>; + opp-microvolt = <1125000>; + }; + }; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x000>; + enable-method = "psci"; + cpu-idle-states = <&CPU_SLEEP_0>; + clocks = <&infracfg CLK_INFRA_CA53SEL>, + <&apmixedsys CLK_APMIXED_MAINPLL>; + clock-names = "cpu", "intermediate"; + operating-points-v2 = <&cpu_opp_table_a>; + }; + + cpu1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x001>; + enable-method = "psci"; + cpu-idle-states = <&CPU_SLEEP_0>; + clocks = <&infracfg CLK_INFRA_CA53SEL>, + <&apmixedsys CLK_APMIXED_MAINPLL>; + clock-names = "cpu", "intermediate"; + operating-points-v2 = <&cpu_opp_table_a>; + }; + + cpu2: cpu@100 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x100>; + enable-method = "psci"; + cpu-idle-states = <&CPU_SLEEP_0>; + clocks = <&infracfg CLK_INFRA_CA57SEL>, + <&apmixedsys CLK_APMIXED_MAINPLL>; + clock-names = "cpu", "intermediate"; + operating-points-v2 = <&cpu_opp_table_b>; + }; + + cpu3: cpu@101 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x101>; + enable-method = "psci"; + cpu-idle-states = <&CPU_SLEEP_0>; + clocks = <&infracfg CLK_INFRA_CA57SEL>, + <&apmixedsys CLK_APMIXED_MAINPLL>; + clock-names = "cpu", "intermediate"; + operating-points-v2 = <&cpu_opp_table_b>; + }; + + &cpu0 { + proc-supply = <&mt6397_vpca15_reg>; + }; + + &cpu1 { + proc-supply = <&mt6397_vpca15_reg>; + }; + + &cpu2 { + proc-supply = <&da9211_vcpu_reg>; + sram-supply = <&mt6397_vsramca7_reg>; + }; + + &cpu3 { + proc-supply = <&da9211_vcpu_reg>; + sram-supply = <&mt6397_vsramca7_reg>; + }; diff --git a/Documentation/devicetree/bindings/power/rockchip-io-domain.txt b/Documentation/devicetree/bindings/power/rockchip-io-domain.txt index 43c21fb04564..4a4766e9c254 100644 --- a/Documentation/devicetree/bindings/power/rockchip-io-domain.txt +++ b/Documentation/devicetree/bindings/power/rockchip-io-domain.txt @@ -39,6 +39,8 @@ Required properties: - "rockchip,rk3368-pmu-io-voltage-domain" for rk3368 pmu-domains - "rockchip,rk3399-io-voltage-domain" for rk3399 - "rockchip,rk3399-pmu-io-voltage-domain" for rk3399 pmu-domains + - "rockchip,rv1108-io-voltage-domain" for rv1108 + - "rockchip,rv1108-pmu-io-voltage-domain" for rv1108 pmu-domains Deprecated properties: - rockchip,grf: phandle to the syscon managing the "general register files" diff --git a/Documentation/power/states.txt b/Documentation/power/states.txt deleted file mode 100644 index bc4548245a24..000000000000 --- a/Documentation/power/states.txt +++ /dev/null @@ -1,125 +0,0 @@ -System Power Management Sleep States - -(C) 2014 Intel Corp., Rafael J. Wysocki <rafael.j.wysocki@intel.com> - -The kernel supports up to four system sleep states generically, although three -of them depend on the platform support code to implement the low-level details -for each state. - -The states are represented by strings that can be read or written to the -/sys/power/state file. Those strings may be "mem", "standby", "freeze" and -"disk", where the last three always represent Power-On Suspend (if supported), -Suspend-To-Idle and hibernation (Suspend-To-Disk), respectively. - -The meaning of the "mem" string is controlled by the /sys/power/mem_sleep file. -It contains strings representing the available modes of system suspend that may -be triggered by writing "mem" to /sys/power/state. These modes are "s2idle" -(Suspend-To-Idle), "shallow" (Power-On Suspend) and "deep" (Suspend-To-RAM). -The "s2idle" mode is always available, while the other ones are only available -if supported by the platform (if not supported, the strings representing them -are not present in /sys/power/mem_sleep). The string representing the suspend -mode to be used subsequently is enclosed in square brackets. Writing one of -the other strings present in /sys/power/mem_sleep to it causes the suspend mode -to be used subsequently to change to the one represented by that string. - -Consequently, there are two ways to cause the system to go into the -Suspend-To-Idle sleep state. The first one is to write "freeze" directly to -/sys/power/state. The second one is to write "s2idle" to /sys/power/mem_sleep -and then to write "mem" to /sys/power/state. Similarly, there are two ways -to cause the system to go into the Power-On Suspend sleep state (the strings to -write to the control files in that case are "standby" or "shallow" and "mem", -respectively) if that state is supported by the platform. In turn, there is -only one way to cause the system to go into the Suspend-To-RAM state (write -"deep" into /sys/power/mem_sleep and "mem" into /sys/power/state). - -The default suspend mode (ie. the one to be used without writing anything into -/sys/power/mem_sleep) is either "deep" (if Suspend-To-RAM is supported) or -"s2idle", but it can be overridden by the value of the "mem_sleep_default" -parameter in the kernel command line. - -The properties of all of the sleep states are described below. - - -State: Suspend-To-Idle -ACPI state: S0 -Label: "s2idle" ("freeze") - -This state is a generic, pure software, light-weight, system sleep state. -It allows more energy to be saved relative to runtime idle by freezing user -space and putting all I/O devices into low-power states (possibly -lower-power than available at run time), such that the processors can -spend more time in their idle states. - -This state can be used for platforms without Power-On Suspend/Suspend-to-RAM -support, or it can be used in addition to Suspend-to-RAM to provide reduced -resume latency. It is always supported. - - -State: Standby / Power-On Suspend -ACPI State: S1 -Label: "shallow" ("standby") - -This state, if supported, offers moderate, though real, power savings, while -providing a relatively low-latency transition back to a working system. No -operating state is lost (the CPU retains power), so the system easily starts up -again where it left off. - -In addition to freezing user space and putting all I/O devices into low-power -states, which is done for Suspend-To-Idle too, nonboot CPUs are taken offline -and all low-level system functions are suspended during transitions into this -state. For this reason, it should allow more energy to be saved relative to -Suspend-To-Idle, but the resume latency will generally be greater than for that -state. - - -State: Suspend-to-RAM -ACPI State: S3 -Label: "deep" - -This state, if supported, offers significant power savings as everything in the -system is put into a low-power state, except for memory, which should be placed -into the self-refresh mode to retain its contents. All of the steps carried out -when entering Power-On Suspend are also carried out during transitions to STR. -Additional operations may take place depending on the platform capabilities. In -particular, on ACPI systems the kernel passes control to the BIOS (platform -firmware) as the last step during STR transitions and that usually results in -powering down some more low-level components that aren't directly controlled by -the kernel. - -System and device state is saved and kept in memory. All devices are suspended -and put into low-power states. In many cases, all peripheral buses lose power -when entering STR, so devices must be able to handle the transition back to the -"on" state. - -For at least ACPI, STR requires some minimal boot-strapping code to resume the -system from it. This may be the case on other platforms too. - - -State: Suspend-to-disk -ACPI State: S4 -Label: "disk" - -This state offers the greatest power savings, and can be used even in -the absence of low-level platform support for power management. This -state operates similarly to Suspend-to-RAM, but includes a final step -of writing memory contents to disk. On resume, this is read and memory -is restored to its pre-suspend state. - -STD can be handled by the firmware or the kernel. If it is handled by -the firmware, it usually requires a dedicated partition that must be -setup via another operating system for it to use. Despite the -inconvenience, this method requires minimal work by the kernel, since -the firmware will also handle restoring memory contents on resume. - -For suspend-to-disk, a mechanism called 'swsusp' (Swap Suspend) is used -to write memory contents to free swap space. swsusp has some restrictive -requirements, but should work in most cases. Some, albeit outdated, -documentation can be found in Documentation/power/swsusp.txt. -Alternatively, userspace can do most of the actual suspend to disk work, -see userland-swsusp.txt. - -Once memory state is written to disk, the system may either enter a -low-power state (like ACPI S4), or it may simply power down. Powering -down offers greater savings, and allows this mechanism to work on any -system. However, entering a real low-power state allows the user to -trigger wake up events (e.g. pressing a key or opening a laptop lid). diff --git a/arch/arm/boot/dts/tango4-smp8758.dtsi b/arch/arm/boot/dts/tango4-smp8758.dtsi index d2e65c46bcc7..eca33d568690 100644 --- a/arch/arm/boot/dts/tango4-smp8758.dtsi +++ b/arch/arm/boot/dts/tango4-smp8758.dtsi @@ -13,7 +13,6 @@ reg = <0>; clocks = <&clkgen CPU_CLK>; clock-latency = <1>; - operating-points = <1215000 0 607500 0 405000 0 243000 0 135000 0>; }; cpu1: cpu@1 { diff --git a/arch/arm/mach-tegra/cpuidle-tegra114.c b/arch/arm/mach-tegra/cpuidle-tegra114.c index d3aa9be16621..e3fbcfedf845 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra114.c +++ b/arch/arm/mach-tegra/cpuidle-tegra114.c @@ -60,7 +60,7 @@ static int tegra114_idle_power_down(struct cpuidle_device *dev, return index; } -static void tegra114_idle_enter_freeze(struct cpuidle_device *dev, +static void tegra114_idle_enter_s2idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { @@ -77,7 +77,7 @@ static struct cpuidle_driver tegra_idle_driver = { #ifdef CONFIG_PM_SLEEP [1] = { .enter = tegra114_idle_power_down, - .enter_freeze = tegra114_idle_enter_freeze, + .enter_s2idle = tegra114_idle_enter_s2idle, .exit_latency = 500, .target_residency = 1000, .flags = CPUIDLE_FLAG_TIMER_STOP, diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index fe3d2a40f311..2736e25e9dc6 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -48,6 +48,8 @@ #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_idle"); +#define ACPI_IDLE_STATE_START (IS_ENABLED(CONFIG_ARCH_HAS_CPU_RELAX) ? 1 : 0) + static unsigned int max_cstate __read_mostly = ACPI_PROCESSOR_MAX_POWER; module_param(max_cstate, uint, 0000); static unsigned int nocst __read_mostly; @@ -759,7 +761,7 @@ static int acpi_idle_enter(struct cpuidle_device *dev, if (cx->type != ACPI_STATE_C1) { if (acpi_idle_fallback_to_c1(pr) && num_online_cpus() > 1) { - index = CPUIDLE_DRIVER_STATE_START; + index = ACPI_IDLE_STATE_START; cx = per_cpu(acpi_cstate[index], dev->cpu); } else if (cx->type == ACPI_STATE_C3 && pr->flags.bm_check) { if (cx->bm_sts_skip || !acpi_idle_bm_check()) { @@ -787,7 +789,7 @@ static int acpi_idle_enter(struct cpuidle_device *dev, return index; } -static void acpi_idle_enter_freeze(struct cpuidle_device *dev, +static void acpi_idle_enter_s2idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); @@ -811,7 +813,7 @@ static void acpi_idle_enter_freeze(struct cpuidle_device *dev, static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr, struct cpuidle_device *dev) { - int i, count = CPUIDLE_DRIVER_STATE_START; + int i, count = ACPI_IDLE_STATE_START; struct acpi_processor_cx *cx; if (max_cstate == 0) @@ -838,7 +840,7 @@ static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr, static int acpi_processor_setup_cstates(struct acpi_processor *pr) { - int i, count = CPUIDLE_DRIVER_STATE_START; + int i, count; struct acpi_processor_cx *cx; struct cpuidle_state *state; struct cpuidle_driver *drv = &acpi_idle_driver; @@ -846,6 +848,13 @@ static int acpi_processor_setup_cstates(struct acpi_processor *pr) if (max_cstate == 0) max_cstate = 1; + if (IS_ENABLED(CONFIG_ARCH_HAS_CPU_RELAX)) { + cpuidle_poll_state_init(drv); + count = 1; + } else { + count = 0; + } + for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { cx = &pr->power.states[i]; @@ -865,14 +874,14 @@ static int acpi_processor_setup_cstates(struct acpi_processor *pr) drv->safe_state_index = count; } /* - * Halt-induced C1 is not good for ->enter_freeze, because it + * Halt-induced C1 is not good for ->enter_s2idle, because it * re-enables interrupts on exit. Moreover, C1 is generally not * particularly interesting from the suspend-to-idle angle, so * avoid C1 and the situations in which we may need to fall back * to it altogether. */ if (cx->type != ACPI_STATE_C1 && !acpi_idle_fallback_to_c1(pr)) - state->enter_freeze = acpi_idle_enter_freeze; + state->enter_s2idle = acpi_idle_enter_s2idle; count++; if (count == CPUIDLE_STATE_MAX) @@ -1289,7 +1298,7 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) return -EINVAL; drv->safe_state_index = -1; - for (i = CPUIDLE_DRIVER_STATE_START; i < CPUIDLE_STATE_MAX; i++) { + for (i = ACPI_IDLE_STATE_START; i < CPUIDLE_STATE_MAX; i++) { drv->states[i].name[0] = '\0'; drv->states[i].desc[0] = '\0'; } diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index fa8243c5c062..09460d9f9208 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -669,6 +669,7 @@ static const struct acpi_device_id lps0_device_ids[] = { #define ACPI_LPS0_DSM_UUID "c4eb40a0-6cd2-11e2-bcfd-0800200c9a66" +#define ACPI_LPS0_GET_DEVICE_CONSTRAINTS 1 #define ACPI_LPS0_SCREEN_OFF 3 #define ACPI_LPS0_SCREEN_ON 4 #define ACPI_LPS0_ENTRY 5 @@ -680,6 +681,166 @@ static acpi_handle lps0_device_handle; static guid_t lps0_dsm_guid; static char lps0_dsm_func_mask; +/* Device constraint entry structure */ +struct lpi_device_info { + char *name; + int enabled; + union acpi_object *package; +}; + +/* Constraint package structure */ +struct lpi_device_constraint { + int uid; + int min_dstate; + int function_states; +}; + +struct lpi_constraints { + acpi_handle handle; + int min_dstate; +}; + +static struct lpi_constraints *lpi_constraints_table; +static int lpi_constraints_table_size; + +static void lpi_device_get_constraints(void) +{ + union acpi_object *out_obj; + int i; + + out_obj = acpi_evaluate_dsm_typed(lps0_device_handle, &lps0_dsm_guid, + 1, ACPI_LPS0_GET_DEVICE_CONSTRAINTS, + NULL, ACPI_TYPE_PACKAGE); + + acpi_handle_debug(lps0_device_handle, "_DSM function 1 eval %s\n", + out_obj ? "successful" : "failed"); + + if (!out_obj) + return; + + lpi_constraints_table = kcalloc(out_obj->package.count, + sizeof(*lpi_constraints_table), + GFP_KERNEL); + if (!lpi_constraints_table) + goto free_acpi_buffer; + + acpi_handle_debug(lps0_device_handle, "LPI: constraints list begin:\n"); + + for (i = 0; i < out_obj->package.count; i++) { + struct lpi_constraints *constraint; + acpi_status status; + union acpi_object *package = &out_obj->package.elements[i]; + struct lpi_device_info info = { }; + int package_count = 0, j; + + if (!package) + continue; + + for (j = 0; j < package->package.count; ++j) { + union acpi_object *element = + &(package->package.elements[j]); + + switch (element->type) { + case ACPI_TYPE_INTEGER: + info.enabled = element->integer.value; + break; + case ACPI_TYPE_STRING: + info.name = element->string.pointer; + break; + case ACPI_TYPE_PACKAGE: + package_count = element->package.count; + info.package = element->package.elements; + break; + } + } + + if (!info.enabled || !info.package || !info.name) + continue; + + constraint = &lpi_constraints_table[lpi_constraints_table_size]; + + status = acpi_get_handle(NULL, info.name, &constraint->handle); + if (ACPI_FAILURE(status)) + continue; + + acpi_handle_debug(lps0_device_handle, + "index:%d Name:%s\n", i, info.name); + + constraint->min_dstate = -1; + + for (j = 0; j < package_count; ++j) { + union acpi_object *info_obj = &info.package[j]; + union acpi_object *cnstr_pkg; + union acpi_object *obj; + struct lpi_device_constraint dev_info; + + switch (info_obj->type) { + case ACPI_TYPE_INTEGER: + /* version */ + break; + case ACPI_TYPE_PACKAGE: + if (info_obj->package.count < 2) + break; + + cnstr_pkg = info_obj->package.elements; + obj = &cnstr_pkg[0]; + dev_info.uid = obj->integer.value; + obj = &cnstr_pkg[1]; + dev_info.min_dstate = obj->integer.value; + + acpi_handle_debug(lps0_device_handle, + "uid:%d min_dstate:%s\n", + dev_info.uid, + acpi_power_state_string(dev_info.min_dstate)); + + constraint->min_dstate = dev_info.min_dstate; + break; + } + } + + if (constraint->min_dstate < 0) { + acpi_handle_debug(lps0_device_handle, + "Incomplete constraint defined\n"); + continue; + } + + lpi_constraints_table_size++; + } + + acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n"); + +free_acpi_buffer: + ACPI_FREE(out_obj); +} + +static void lpi_check_constraints(void) +{ + int i; + + for (i = 0; i < lpi_constraints_table_size; ++i) { + struct acpi_device *adev; + + if (acpi_bus_get_device(lpi_constraints_table[i].handle, &adev)) + continue; + + acpi_handle_debug(adev->handle, + "LPI: required min power state:%s current power state:%s\n", + acpi_power_state_string(lpi_constraints_table[i].min_dstate), + acpi_power_state_string(adev->power.state)); + + if (!adev->flags.power_manageable) { + acpi_handle_info(adev->handle, "LPI: Device not power manageble\n"); + continue; + } + + if (adev->power.state < lpi_constraints_table[i].min_dstate) + acpi_handle_info(adev->handle, + "LPI: Constraint not met; min power state:%s current power state:%s\n", + acpi_power_state_string(lpi_constraints_table[i].min_dstate), + acpi_power_state_string(adev->power.state)); + } +} + static void acpi_sleep_run_lps0_dsm(unsigned int func) { union acpi_object *out_obj; @@ -714,6 +875,12 @@ static int lps0_device_attach(struct acpi_device *adev, if ((bitmask & ACPI_S2IDLE_FUNC_MASK) == ACPI_S2IDLE_FUNC_MASK) { lps0_dsm_func_mask = bitmask; lps0_device_handle = adev->handle; + /* + * Use suspend-to-idle by default if the default + * suspend mode was not set from the command line. + */ + if (mem_sleep_default > PM_SUSPEND_MEM) + mem_sleep_current = PM_SUSPEND_TO_IDLE; } acpi_handle_debug(adev->handle, "_DSM function mask: 0x%x\n", @@ -723,6 +890,9 @@ static int lps0_device_attach(struct acpi_device *adev, "_DSM function 0 evaluation failed\n"); } ACPI_FREE(out_obj); + + lpi_device_get_constraints(); + return 0; } @@ -731,14 +901,14 @@ static struct acpi_scan_handler lps0_handler = { .attach = lps0_device_attach, }; -static int acpi_freeze_begin(void) +static int acpi_s2idle_begin(void) { acpi_scan_lock_acquire(); s2idle_in_progress = true; return 0; } -static int acpi_freeze_prepare(void) +static int acpi_s2idle_prepare(void) { if (lps0_device_handle) { acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF); @@ -758,8 +928,12 @@ static int acpi_freeze_prepare(void) return 0; } -static void acpi_freeze_wake(void) +static void acpi_s2idle_wake(void) { + + if (pm_debug_messages_on) + lpi_check_constraints(); + /* * If IRQD_WAKEUP_ARMED is not set for the SCI at this point, it means * that the SCI has triggered while suspended, so cancel the wakeup in @@ -772,7 +946,7 @@ static void acpi_freeze_wake(void) } } -static void acpi_freeze_sync(void) +static void acpi_s2idle_sync(void) { /* * Process all pending events in case there are any wakeup ones. @@ -785,7 +959,7 @@ static void acpi_freeze_sync(void) s2idle_wakeup = false; } -static void acpi_freeze_restore(void) +static void acpi_s2idle_restore(void) { if (acpi_sci_irq_valid()) disable_irq_wake(acpi_sci_irq); @@ -798,19 +972,19 @@ static void acpi_freeze_restore(void) } } -static void acpi_freeze_end(void) +static void acpi_s2idle_end(void) { s2idle_in_progress = false; acpi_scan_lock_release(); } -static const struct platform_freeze_ops acpi_freeze_ops = { - .begin = acpi_freeze_begin, - .prepare = acpi_freeze_prepare, - .wake = acpi_freeze_wake, - .sync = acpi_freeze_sync, - .restore = acpi_freeze_restore, - .end = acpi_freeze_end, +static const struct platform_s2idle_ops acpi_s2idle_ops = { + .begin = acpi_s2idle_begin, + .prepare = acpi_s2idle_prepare, + .wake = acpi_s2idle_wake, + .sync = acpi_s2idle_sync, + .restore = acpi_s2idle_restore, + .end = acpi_s2idle_end, }; static void acpi_sleep_suspend_setup(void) @@ -825,7 +999,7 @@ static void acpi_sleep_suspend_setup(void) &acpi_suspend_ops_old : &acpi_suspend_ops); acpi_scan_add_handler(&lps0_handler); - freeze_set_ops(&acpi_freeze_ops); + s2idle_set_ops(&acpi_s2idle_ops); } #else /* !CONFIG_SUSPEND */ diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 60303aa28587..e8ca5e2cf1e5 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -209,6 +209,34 @@ static void genpd_sd_counter_inc(struct generic_pm_domain *genpd) smp_mb__after_atomic(); } +#ifdef CONFIG_DEBUG_FS +static void genpd_update_accounting(struct generic_pm_domain *genpd) +{ + ktime_t delta, now; + + now = ktime_get(); + delta = ktime_sub(now, genpd->accounting_time); + + /* + * If genpd->status is active, it means we are just + * out of off and so update the idle time and vice + * versa. + */ + if (genpd->status == GPD_STATE_ACTIVE) { + int state_idx = genpd->state_idx; + + genpd->states[state_idx].idle_time = + ktime_add(genpd->states[state_idx].idle_time, delta); + } else { + genpd->on_time = ktime_add(genpd->on_time, delta); + } + + genpd->accounting_time = now; +} +#else +static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {} +#endif + static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) { unsigned int state_idx = genpd->state_idx; @@ -361,6 +389,7 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, } genpd->status = GPD_STATE_POWER_OFF; + genpd_update_accounting(genpd); list_for_each_entry(link, &genpd->slave_links, slave_node) { genpd_sd_counter_dec(link->master); @@ -413,6 +442,8 @@ static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth) goto err; genpd->status = GPD_STATE_ACTIVE; + genpd_update_accounting(genpd); + return 0; err: @@ -1540,6 +1571,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd, genpd->max_off_time_changed = true; genpd->provider = NULL; genpd->has_provider = false; + genpd->accounting_time = ktime_get(); genpd->domain.ops.runtime_suspend = genpd_runtime_suspend; genpd->domain.ops.runtime_resume = genpd_runtime_resume; genpd->domain.ops.prepare = pm_genpd_prepare; @@ -1743,7 +1775,7 @@ static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, mutex_lock(&of_genpd_mutex); list_add(&cp->link, &of_genpd_providers); mutex_unlock(&of_genpd_mutex); - pr_debug("Added domain provider from %s\n", np->full_name); + pr_debug("Added domain provider from %pOF\n", np); return 0; } @@ -2149,16 +2181,16 @@ static int genpd_parse_state(struct genpd_power_state *genpd_state, err = of_property_read_u32(state_node, "entry-latency-us", &entry_latency); if (err) { - pr_debug(" * %s missing entry-latency-us property\n", - state_node->full_name); + pr_debug(" * %pOF missing entry-latency-us property\n", + state_node); return -EINVAL; } err = of_property_read_u32(state_node, "exit-latency-us", &exit_latency); if (err) { - pr_debug(" * %s missing exit-latency-us property\n", - state_node->full_name); + pr_debug(" * %pOF missing exit-latency-us property\n", + state_node); return -EINVAL; } @@ -2212,8 +2244,8 @@ int of_genpd_parse_idle_states(struct device_node *dn, ret = genpd_parse_state(&st[i++], np); if (ret) { pr_err - ("Parsing idle state node %s failed with err %d\n", - np->full_name, ret); + ("Parsing idle state node %pOF failed with err %d\n", + np, ret); of_node_put(np); kfree(st); return ret; @@ -2327,7 +2359,7 @@ exit: return 0; } -static int pm_genpd_summary_show(struct seq_file *s, void *data) +static int genpd_summary_show(struct seq_file *s, void *data) { struct generic_pm_domain *genpd; int ret = 0; @@ -2350,21 +2382,187 @@ static int pm_genpd_summary_show(struct seq_file *s, void *data) return ret; } -static int pm_genpd_summary_open(struct inode *inode, struct file *file) +static int genpd_status_show(struct seq_file *s, void *data) { - return single_open(file, pm_genpd_summary_show, NULL); + static const char * const status_lookup[] = { + [GPD_STATE_ACTIVE] = "on", + [GPD_STATE_POWER_OFF] = "off" + }; + + struct generic_pm_domain *genpd = s->private; + int ret = 0; + + ret = genpd_lock_interruptible(genpd); + if (ret) + return -ERESTARTSYS; + + if (WARN_ON_ONCE(genpd->status >= ARRAY_SIZE(status_lookup))) + goto exit; + + if (genpd->status == GPD_STATE_POWER_OFF) + seq_printf(s, "%s-%u\n", status_lookup[genpd->status], + genpd->state_idx); + else + seq_printf(s, "%s\n", status_lookup[genpd->status]); +exit: + genpd_unlock(genpd); + return ret; } -static const struct file_operations pm_genpd_summary_fops = { - .open = pm_genpd_summary_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +static int genpd_sub_domains_show(struct seq_file *s, void *data) +{ + struct generic_pm_domain *genpd = s->private; + struct gpd_link *link; + int ret = 0; + + ret = genpd_lock_interruptible(genpd); + if (ret) + return -ERESTARTSYS; + + list_for_each_entry(link, &genpd->master_links, master_node) + seq_printf(s, "%s\n", link->slave->name); + + genpd_unlock(genpd); + return ret; +} + +static int genpd_idle_states_show(struct seq_file *s, void *data) +{ + struct generic_pm_domain *genpd = s->private; + unsigned int i; + int ret = 0; + + ret = genpd_lock_interruptible(genpd); + if (ret) + return -ERESTARTSYS; + + seq_puts(s, "State Time Spent(ms)\n"); + + for (i = 0; i < genpd->state_count; i++) { + ktime_t delta = 0; + s64 msecs; + + if ((genpd->status == GPD_STATE_POWER_OFF) && + (genpd->state_idx == i)) + delta = ktime_sub(ktime_get(), genpd->accounting_time); + + msecs = ktime_to_ms( + ktime_add(genpd->states[i].idle_time, delta)); + seq_printf(s, "S%-13i %lld\n", i, msecs); + } + + genpd_unlock(genpd); + return ret; +} + +static int genpd_active_time_show(struct seq_file *s, void *data) +{ + struct generic_pm_domain *genpd = s->private; + ktime_t delta = 0; + int ret = 0; + + ret = genpd_lock_interruptible(genpd); + if (ret) + return -ERESTARTSYS; + + if (genpd->status == GPD_STATE_ACTIVE) + delta = ktime_sub(ktime_get(), genpd->accounting_time); + + seq_printf(s, "%lld ms\n", ktime_to_ms( + ktime_add(genpd->on_time, delta))); + + genpd_unlock(genpd); + return ret; +} + +static int genpd_total_idle_time_show(struct seq_file *s, void *data) +{ + struct generic_pm_domain *genpd = s->private; + ktime_t delta = 0, total = 0; + unsigned int i; + int ret = 0; + + ret = genpd_lock_interruptible(genpd); + if (ret) + return -ERESTARTSYS; + + for (i = 0; i < genpd->state_count; i++) { + + if ((genpd->status == GPD_STATE_POWER_OFF) && + (genpd->state_idx == i)) + delta = ktime_sub(ktime_get(), genpd->accounting_time); + + total = ktime_add(total, genpd->states[i].idle_time); + } + total = ktime_add(total, delta); + + seq_printf(s, "%lld ms\n", ktime_to_ms(total)); + + genpd_unlock(genpd); + return ret; +} + + +static int genpd_devices_show(struct seq_file *s, void *data) +{ + struct generic_pm_domain *genpd = s->private; + struct pm_domain_data *pm_data; + const char *kobj_path; + int ret = 0; + + ret = genpd_lock_interruptible(genpd); + if (ret) + return -ERESTARTSYS; + + list_for_each_entry(pm_data, &genpd->dev_list, list_node) { + kobj_path = kobject_get_path(&pm_data->dev->kobj, + genpd_is_irq_safe(genpd) ? + GFP_ATOMIC : GFP_KERNEL); + if (kobj_path == NULL) + continue; + + seq_printf(s, "%s\n", kobj_path); + kfree(kobj_path); + } + + genpd_unlock(genpd); + return ret; +} + +#define define_genpd_open_function(name) \ +static int genpd_##name##_open(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, genpd_##name##_show, inode->i_private); \ +} + +define_genpd_open_function(summary); +define_genpd_open_function(status); +define_genpd_open_function(sub_domains); +define_genpd_open_function(idle_states); +define_genpd_open_function(active_time); +define_genpd_open_function(total_idle_time); +define_genpd_open_function(devices); + +#define define_genpd_debugfs_fops(name) \ +static const struct file_operations genpd_##name##_fops = { \ + .open = genpd_##name##_open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ +} + +define_genpd_debugfs_fops(summary); +define_genpd_debugfs_fops(status); +define_genpd_debugfs_fops(sub_domains); +define_genpd_debugfs_fops(idle_states); +define_genpd_debugfs_fops(active_time); +define_genpd_debugfs_fops(total_idle_time); +define_genpd_debugfs_fops(devices); static int __init pm_genpd_debug_init(void) { struct dentry *d; + struct generic_pm_domain *genpd; pm_genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL); @@ -2372,10 +2570,29 @@ static int __init pm_genpd_debug_init(void) return -ENOMEM; d = debugfs_create_file("pm_genpd_summary", S_IRUGO, - pm_genpd_debugfs_dir, NULL, &pm_genpd_summary_fops); + pm_genpd_debugfs_dir, NULL, &genpd_summary_fops); if (!d) return -ENOMEM; + list_for_each_entry(genpd, &gpd_list, gpd_list_node) { + d = debugfs_create_dir(genpd->name, pm_genpd_debugfs_dir); + if (!d) + return -ENOMEM; + + debugfs_create_file("current_state", 0444, + d, genpd, &genpd_status_fops); + debugfs_create_file("sub_domains", 0444, + d, genpd, &genpd_sub_domains_fops); + debugfs_create_file("idle_states", 0444, + d, genpd, &genpd_idle_states_fops); + debugfs_create_file("active_time", 0444, + d, genpd, &genpd_active_time_fops); + debugfs_create_file("total_idle_time", 0444, + d, genpd, &genpd_total_idle_time_fops); + debugfs_create_file("devices", 0444, + d, genpd, &genpd_devices_fops); + } + return 0; } late_initcall(pm_genpd_debug_init); diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index c99f8730de82..ea1732ed7a9d 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -418,8 +418,7 @@ static void pm_dev_err(struct device *dev, pm_message_t state, const char *info, dev_name(dev), pm_verb(state.event), info, error); } -#ifdef CONFIG_PM_DEBUG -static void dpm_show_time(ktime_t starttime, pm_message_t state, +static void dpm_show_time(ktime_t starttime, pm_message_t state, int error, const char *info) { ktime_t calltime; @@ -432,14 +431,12 @@ static void dpm_show_time(ktime_t starttime, pm_message_t state, usecs = usecs64; if (usecs == 0) usecs = 1; - pr_info("PM: %s%s%s of devices complete after %ld.%03ld msecs\n", - info ?: "", info ? " " : "", pm_verb(state.event), - usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC); + + pm_pr_dbg("%s%s%s of devices %s after %ld.%03ld msecs\n", + info ?: "", info ? " " : "", pm_verb(state.event), + error ? "aborted" : "complete", + usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC); } -#else -static inline void dpm_show_time(ktime_t starttime, pm_message_t state, - const char *info) {} -#endif /* CONFIG_PM_DEBUG */ static int dpm_run_callback(pm_callback_t cb, struct device *dev, pm_message_t state, const char *info) @@ -602,14 +599,7 @@ static void async_resume_noirq(void *data, async_cookie_t cookie) put_device(dev); } -/** - * dpm_resume_noirq - Execute "noirq resume" callbacks for all devices. - * @state: PM transition of the system being carried out. - * - * Call the "noirq" resume handlers for all devices in dpm_noirq_list and - * enable device drivers to receive interrupts. - */ -void dpm_resume_noirq(pm_message_t state) +void dpm_noirq_resume_devices(pm_message_t state) { struct device *dev; ktime_t starttime = ktime_get(); @@ -654,11 +644,28 @@ void dpm_resume_noirq(pm_message_t state) } mutex_unlock(&dpm_list_mtx); async_synchronize_full(); - dpm_show_time(starttime, state, "noirq"); + dpm_show_time(starttime, state, 0, "noirq"); + trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false); +} + +void dpm_noirq_end(void) +{ resume_device_irqs(); device_wakeup_disarm_wake_irqs(); cpuidle_resume(); - trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false); +} + +/** + * dpm_resume_noirq - Execute "noirq resume" callbacks for all devices. + * @state: PM transition of the system being carried out. + * + * Invoke the "noirq" resume callbacks for all devices in dpm_noirq_list and + * allow device drivers' interrupt handlers to be called. + */ +void dpm_resume_noirq(pm_message_t state) +{ + dpm_noirq_resume_devices(state); + dpm_noirq_end(); } /** @@ -776,7 +783,7 @@ void dpm_resume_early(pm_message_t state) } mutex_unlock(&dpm_list_mtx); async_synchronize_full(); - dpm_show_time(starttime, state, "early"); + dpm_show_time(starttime, state, 0, "early"); trace_suspend_resume(TPS("dpm_resume_early"), state.event, false); } @@ -948,7 +955,7 @@ void dpm_resume(pm_message_t state) } mutex_unlock(&dpm_list_mtx); async_synchronize_full(); - dpm_show_time(starttime, state, NULL); + dpm_show_time(starttime, state, 0, NULL); cpufreq_resume(); trace_suspend_resume(TPS("dpm_resume"), state.event, false); @@ -1098,6 +1105,11 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a if (async_error) goto Complete; + if (pm_wakeup_pending()) { + async_error = -EBUSY; + goto Complete; + } + if (dev->power.syscore || dev->power.direct_complete) goto Complete; @@ -1158,22 +1170,19 @@ static int device_suspend_noirq(struct device *dev) return __device_suspend_noirq(dev, pm_transition, false); } -/** - * dpm_suspend_noirq - Execute "noirq suspend" callbacks for all devices. - * @state: PM transition of the system being carried out. - * - * Prevent device drivers from receiving interrupts and call the "noirq" suspend - * handlers for all non-sysdev devices. - */ -int dpm_suspend_noirq(pm_message_t state) +void dpm_noirq_begin(void) +{ + cpuidle_pause(); + device_wakeup_arm_wake_irqs(); + suspend_device_irqs(); +} + +int dpm_noirq_suspend_devices(pm_message_t state) { ktime_t starttime = ktime_get(); int error = 0; trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true); - cpuidle_pause(); - device_wakeup_arm_wake_irqs(); - suspend_device_irqs(); mutex_lock(&dpm_list_mtx); pm_transition = state; async_error = 0; @@ -1208,15 +1217,32 @@ int dpm_suspend_noirq(pm_message_t state) if (error) { suspend_stats.failed_suspend_noirq++; dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ); - dpm_resume_noirq(resume_event(state)); - } else { - dpm_show_time(starttime, state, "noirq"); } + dpm_show_time(starttime, state, error, "noirq"); trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, false); return error; } /** + * dpm_suspend_noirq - Execute "noirq suspend" callbacks for all devices. + * @state: PM transition of the system being carried out. + * + * Prevent device drivers' interrupt handlers from being called and invoke + * "noirq" suspend callbacks for all non-sysdev devices. + */ +int dpm_suspend_noirq(pm_message_t state) +{ + int ret; + + dpm_noirq_begin(); + ret = dpm_noirq_suspend_devices(state); + if (ret) + dpm_resume_noirq(resume_event(state)); + + return ret; +} + +/** * device_suspend_late - Execute a "late suspend" callback for given device. * @dev: Device to handle. * @state: PM transition of the system being carried out. @@ -1350,9 +1376,8 @@ int dpm_suspend_late(pm_message_t state) suspend_stats.failed_suspend_late++; dpm_save_failed_step(SUSPEND_SUSPEND_LATE); dpm_resume_early(resume_event(state)); - } else { - dpm_show_time(starttime, state, "late"); } + dpm_show_time(starttime, state, error, "late"); trace_suspend_resume(TPS("dpm_suspend_late"), state.event, false); return error; } @@ -1618,8 +1643,8 @@ int dpm_suspend(pm_message_t state) if (error) { suspend_stats.failed_suspend++; dpm_save_failed_step(SUSPEND_SUSPEND); - } else - dpm_show_time(starttime, state, NULL); + } + dpm_show_time(starttime, state, error, NULL); trace_suspend_resume(TPS("dpm_suspend"), state.event, false); return error; } diff --git a/drivers/base/power/opp/of.c b/drivers/base/power/opp/of.c index 57eec1ca0569..0b718886479b 100644 --- a/drivers/base/power/opp/of.c +++ b/drivers/base/power/opp/of.c @@ -248,15 +248,22 @@ void dev_pm_opp_of_remove_table(struct device *dev) } EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table); -/* Returns opp descriptor node for a device, caller must do of_node_put() */ -struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev) +/* Returns opp descriptor node for a device node, caller must + * do of_node_put() */ +static struct device_node *_opp_of_get_opp_desc_node(struct device_node *np) { /* * There should be only ONE phandle present in "operating-points-v2" * property. */ - return of_parse_phandle(dev->of_node, "operating-points-v2", 0); + return of_parse_phandle(np, "operating-points-v2", 0); +} + +/* Returns opp descriptor node for a device, caller must do of_node_put() */ +struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev) +{ + return _opp_of_get_opp_desc_node(dev->of_node); } EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_opp_desc_node); @@ -539,8 +546,12 @@ int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask) ret = dev_pm_opp_of_add_table(cpu_dev); if (ret) { - pr_err("%s: couldn't find opp table for cpu:%d, %d\n", - __func__, cpu, ret); + /* + * OPP may get registered dynamically, don't print error + * message here. + */ + pr_debug("%s: couldn't find opp table for cpu:%d, %d\n", + __func__, cpu, ret); /* Free all other OPPs */ dev_pm_opp_of_cpumask_remove_table(cpumask); @@ -572,8 +583,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table); int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask) { - struct device_node *np, *tmp_np; - struct device *tcpu_dev; + struct device_node *np, *tmp_np, *cpu_np; int cpu, ret = 0; /* Get OPP descriptor node */ @@ -593,19 +603,18 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, if (cpu == cpu_dev->id) continue; - tcpu_dev = get_cpu_device(cpu); - if (!tcpu_dev) { - dev_err(cpu_dev, "%s: failed to get cpu%d device\n", + cpu_np = of_get_cpu_node(cpu, NULL); + if (!cpu_np) { + dev_err(cpu_dev, "%s: failed to get cpu%d node\n", __func__, cpu); - ret = -ENODEV; + ret = -ENOENT; goto put_cpu_node; } /* Get OPP descriptor node */ - tmp_np = dev_pm_opp_of_get_opp_desc_node(tcpu_dev); + tmp_np = _opp_of_get_opp_desc_node(cpu_np); if (!tmp_np) { - dev_err(tcpu_dev, "%s: Couldn't find opp node.\n", - __func__); + pr_err("%pOF: Couldn't find opp node\n", cpu_np); ret = -ENOENT; goto put_cpu_node; } diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 144e6d8fafc8..cdd6f256da59 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -412,15 +412,17 @@ void device_set_wakeup_capable(struct device *dev, bool capable) if (!!dev->power.can_wakeup == !!capable) return; + dev->power.can_wakeup = capable; if (device_is_registered(dev) && !list_empty(&dev->power.entry)) { if (capable) { - if (wakeup_sysfs_add(dev)) - return; + int ret = wakeup_sysfs_add(dev); + + if (ret) + dev_info(dev, "Wakeup sysfs attributes not added\n"); } else { wakeup_sysfs_remove(dev); } } - dev->power.can_wakeup = capable; } EXPORT_SYMBOL_GPL(device_set_wakeup_capable); @@ -863,7 +865,7 @@ bool pm_wakeup_pending(void) void pm_system_wakeup(void) { atomic_inc(&pm_abort_suspend); - freeze_wake(); + s2idle_wake(); } EXPORT_SYMBOL_GPL(pm_system_wakeup); diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 2011fec2d6ad..bdce4488ded1 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -71,15 +71,6 @@ config ARM_HIGHBANK_CPUFREQ If in doubt, say N. -config ARM_DB8500_CPUFREQ - tristate "ST-Ericsson DB8500 cpufreq" if COMPILE_TEST && !ARCH_U8500 - default ARCH_U8500 - depends on HAS_IOMEM - depends on !CPU_THERMAL || THERMAL - help - This adds the CPUFreq driver for ST-Ericsson Ux500 (DB8500) SoC - series. - config ARM_IMX6Q_CPUFREQ tristate "Freescale i.MX6 cpufreq support" depends on ARCH_MXC @@ -96,14 +87,13 @@ config ARM_KIRKWOOD_CPUFREQ This adds the CPUFreq driver for Marvell Kirkwood SoCs. -config ARM_MT8173_CPUFREQ - tristate "Mediatek MT8173 CPUFreq support" +config ARM_MEDIATEK_CPUFREQ + tristate "CPU Frequency scaling support for MediaTek SoCs" depends on ARCH_MEDIATEK && REGULATOR - depends on ARM64 || (ARM_CPU_TOPOLOGY && COMPILE_TEST) depends on !CPU_THERMAL || THERMAL select PM_OPP help - This adds the CPUFreq driver support for Mediatek MT8173 SoC. + This adds the CPUFreq driver support for MediaTek SoCs. config ARM_OMAP2PLUS_CPUFREQ bool "TI OMAP2+" @@ -242,6 +232,11 @@ config ARM_STI_CPUFREQ this config option if you wish to add CPUFreq support for STi based SoCs. +config ARM_TANGO_CPUFREQ + bool + depends on CPUFREQ_DT && ARCH_TANGO + default y + config ARM_TEGRA20_CPUFREQ bool "Tegra20 CPUFreq support" depends on ARCH_TEGRA diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index ab3a42cd29ef..c7af9b2a255e 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -53,12 +53,11 @@ obj-$(CONFIG_ARM_DT_BL_CPUFREQ) += arm_big_little_dt.o obj-$(CONFIG_ARM_BRCMSTB_AVS_CPUFREQ) += brcmstb-avs-cpufreq.o obj-$(CONFIG_ARCH_DAVINCI) += davinci-cpufreq.o -obj-$(CONFIG_ARM_DB8500_CPUFREQ) += dbx500-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o -obj-$(CONFIG_ARM_MT8173_CPUFREQ) += mt8173-cpufreq.o +obj-$(CONFIG_ARM_MEDIATEK_CPUFREQ) += mediatek-cpufreq.o obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o @@ -75,6 +74,7 @@ obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o obj-$(CONFIG_ARM_SCPI_CPUFREQ) += scpi-cpufreq.o obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o obj-$(CONFIG_ARM_STI_CPUFREQ) += sti-cpufreq.o +obj-$(CONFIG_ARM_TANGO_CPUFREQ) += tango-cpufreq.o obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index ea6d62547b10..17504129fd77 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -483,11 +483,8 @@ static int bL_cpufreq_init(struct cpufreq_policy *policy) return ret; } - if (arm_bL_ops->get_transition_latency) - policy->cpuinfo.transition_latency = - arm_bL_ops->get_transition_latency(cpu_dev); - else - policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; + policy->cpuinfo.transition_latency = + arm_bL_ops->get_transition_latency(cpu_dev); if (is_bL_switching_enabled()) per_cpu(cpu_last_req_freq, policy->cpu) = clk_get_cpu_rate(policy->cpu); @@ -622,7 +619,8 @@ int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops) return -EBUSY; } - if (!ops || !strlen(ops->name) || !ops->init_opp_table) { + if (!ops || !strlen(ops->name) || !ops->init_opp_table || + !ops->get_transition_latency) { pr_err("%s: Invalid arm_bL_ops, exiting\n", __func__); return -ENODEV; } diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 10be285c9055..a1c3025f9df7 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -172,7 +172,6 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) return -EFAULT; } - cpumask_set_cpu(policy->cpu, policy->cpus); cpu->cur_policy = policy; /* Set policy->cur to max now. The governors will adjust later. */ diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index 1c262923fe58..a020da7940d6 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -9,11 +9,16 @@ #include <linux/err.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include "cpufreq-dt.h" -static const struct of_device_id machines[] __initconst = { +/* + * Machines for which the cpufreq device is *always* created, mostly used for + * platforms using "operating-points" (V1) property. + */ +static const struct of_device_id whitelist[] __initconst = { { .compatible = "allwinner,sun4i-a10", }, { .compatible = "allwinner,sun5i-a10s", }, { .compatible = "allwinner,sun5i-a13", }, @@ -22,7 +27,6 @@ static const struct of_device_id machines[] __initconst = { { .compatible = "allwinner,sun6i-a31s", }, { .compatible = "allwinner,sun7i-a20", }, { .compatible = "allwinner,sun8i-a23", }, - { .compatible = "allwinner,sun8i-a33", }, { .compatible = "allwinner,sun8i-a83t", }, { .compatible = "allwinner,sun8i-h3", }, @@ -32,7 +36,6 @@ static const struct of_device_id machines[] __initconst = { { .compatible = "arm,integrator-cp", }, { .compatible = "hisilicon,hi3660", }, - { .compatible = "hisilicon,hi6220", }, { .compatible = "fsl,imx27", }, { .compatible = "fsl,imx51", }, @@ -46,11 +49,8 @@ static const struct of_device_id machines[] __initconst = { { .compatible = "samsung,exynos3250", }, { .compatible = "samsung,exynos4210", }, { .compatible = "samsung,exynos4212", }, - { .compatible = "samsung,exynos4412", }, { .compatible = "samsung,exynos5250", }, #ifndef CONFIG_BL_SWITCHER - { .compatible = "samsung,exynos5420", }, - { .compatible = "samsung,exynos5433", }, { .compatible = "samsung,exynos5800", }, #endif @@ -67,6 +67,8 @@ static const struct of_device_id machines[] __initconst = { { .compatible = "renesas,r8a7792", }, { .compatible = "renesas,r8a7793", }, { .compatible = "renesas,r8a7794", }, + { .compatible = "renesas,r8a7795", }, + { .compatible = "renesas,r8a7796", }, { .compatible = "renesas,sh73a0", }, { .compatible = "rockchip,rk2928", }, @@ -76,17 +78,17 @@ static const struct of_device_id machines[] __initconst = { { .compatible = "rockchip,rk3188", }, { .compatible = "rockchip,rk3228", }, { .compatible = "rockchip,rk3288", }, + { .compatible = "rockchip,rk3328", }, { .compatible = "rockchip,rk3366", }, { .compatible = "rockchip,rk3368", }, { .compatible = "rockchip,rk3399", }, - { .compatible = "sigma,tango4" }, - - { .compatible = "socionext,uniphier-pro5", }, - { .compatible = "socionext,uniphier-pxs2", }, { .compatible = "socionext,uniphier-ld6b", }, - { .compatible = "socionext,uniphier-ld11", }, - { .compatible = "socionext,uniphier-ld20", }, + + { .compatible = "st-ericsson,u8500", }, + { .compatible = "st-ericsson,u8540", }, + { .compatible = "st-ericsson,u9500", }, + { .compatible = "st-ericsson,u9540", }, { .compatible = "ti,omap2", }, { .compatible = "ti,omap3", }, @@ -94,27 +96,56 @@ static const struct of_device_id machines[] __initconst = { { .compatible = "ti,omap5", }, { .compatible = "xlnx,zynq-7000", }, + { .compatible = "xlnx,zynqmp", }, - { .compatible = "zte,zx296718", }, + { } +}; +/* + * Machines for which the cpufreq device is *not* created, mostly used for + * platforms using "operating-points-v2" property. + */ +static const struct of_device_id blacklist[] __initconst = { { } }; +static bool __init cpu0_node_has_opp_v2_prop(void) +{ + struct device_node *np = of_cpu_device_node_get(0); + bool ret = false; + + if (of_get_property(np, "operating-points-v2", NULL)) + ret = true; + + of_node_put(np); + return ret; +} + static int __init cpufreq_dt_platdev_init(void) { struct device_node *np = of_find_node_by_path("/"); const struct of_device_id *match; + const void *data = NULL; if (!np) return -ENODEV; - match = of_match_node(machines, np); + match = of_match_node(whitelist, np); + if (match) { + data = match->data; + goto create_pdev; + } + + if (cpu0_node_has_opp_v2_prop() && !of_match_node(blacklist, np)) + goto create_pdev; + of_node_put(np); - if (!match) - return -ENODEV; + return -ENODEV; +create_pdev: + of_node_put(np); return PTR_ERR_OR_ZERO(platform_device_register_data(NULL, "cpufreq-dt", - -1, match->data, + -1, data, sizeof(struct cpufreq_dt_platform_data))); } device_initcall(cpufreq_dt_platdev_init); diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index fef3c2160691..d83ab94d041a 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -274,6 +274,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) transition_latency = CPUFREQ_ETERNAL; policy->cpuinfo.transition_latency = transition_latency; + policy->dvfs_possible_from_any_cpu = true; return 0; diff --git a/drivers/cpufreq/cpufreq-nforce2.c b/drivers/cpufreq/cpufreq-nforce2.c index 5503d491b016..dbf82f36d270 100644 --- a/drivers/cpufreq/cpufreq-nforce2.c +++ b/drivers/cpufreq/cpufreq-nforce2.c @@ -357,7 +357,6 @@ static int nforce2_cpu_init(struct cpufreq_policy *policy) /* cpuinfo and default policy values */ policy->min = policy->cpuinfo.min_freq = min_fsb * fid * 100; policy->max = policy->cpuinfo.max_freq = max_fsb * fid * 100; - policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; return 0; } @@ -369,6 +368,7 @@ static int nforce2_cpu_exit(struct cpufreq_policy *policy) static struct cpufreq_driver nforce2_driver = { .name = "nforce2", + .flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, .verify = nforce2_verify, .target = nforce2_target, .get = nforce2_get, diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 9bf97a366029..ea43b147a7fe 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -524,6 +524,32 @@ unsigned int cpufreq_driver_resolve_freq(struct cpufreq_policy *policy, } EXPORT_SYMBOL_GPL(cpufreq_driver_resolve_freq); +unsigned int cpufreq_policy_transition_delay_us(struct cpufreq_policy *policy) +{ + unsigned int latency; + + if (policy->transition_delay_us) + return policy->transition_delay_us; + + latency = policy->cpuinfo.transition_latency / NSEC_PER_USEC; + if (latency) { + /* + * For platforms that can change the frequency very fast (< 10 + * us), the above formula gives a decent transition delay. But + * for platforms where transition_latency is in milliseconds, it + * ends up giving unrealistic values. + * + * Cap the default transition delay to 10 ms, which seems to be + * a reasonable amount of time after which we should reevaluate + * the frequency. + */ + return min(latency * LATENCY_MULTIPLIER, (unsigned int)10000); + } + + return LATENCY_MULTIPLIER; +} +EXPORT_SYMBOL_GPL(cpufreq_policy_transition_delay_us); + /********************************************************************* * SYSFS INTERFACE * *********************************************************************/ @@ -1817,9 +1843,10 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier); * twice in parallel for the same policy and that it will never be called in * parallel with either ->target() or ->target_index() for the same policy. * - * If CPUFREQ_ENTRY_INVALID is returned by the driver's ->fast_switch() - * callback to indicate an error condition, the hardware configuration must be - * preserved. + * Returns the actual frequency set for the CPU. + * + * If 0 is returned by the driver's ->fast_switch() callback to indicate an + * error condition, the hardware configuration must be preserved. */ unsigned int cpufreq_driver_fast_switch(struct cpufreq_policy *policy, unsigned int target_freq) @@ -1988,13 +2015,13 @@ static int cpufreq_init_governor(struct cpufreq_policy *policy) if (!policy->governor) return -EINVAL; - if (policy->governor->max_transition_latency && - policy->cpuinfo.transition_latency > - policy->governor->max_transition_latency) { + /* Platform doesn't want dynamic frequency switching ? */ + if (policy->governor->dynamic_switching && + cpufreq_driver->flags & CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING) { struct cpufreq_governor *gov = cpufreq_fallback_governor(); if (gov) { - pr_warn("%s governor failed, too long transition latency of HW, fallback to %s governor\n", + pr_warn("Can't use %s governor as dynamic switching is disallowed. Fallback to %s governor\n", policy->governor->name, gov->name); policy->governor = gov; } else { diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 88220ff3e1c2..f20f20a77d4d 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -246,7 +246,6 @@ gov_show_one_common(sampling_rate); gov_show_one_common(sampling_down_factor); gov_show_one_common(up_threshold); gov_show_one_common(ignore_nice_load); -gov_show_one_common(min_sampling_rate); gov_show_one(cs, down_threshold); gov_show_one(cs, freq_step); @@ -254,12 +253,10 @@ gov_attr_rw(sampling_rate); gov_attr_rw(sampling_down_factor); gov_attr_rw(up_threshold); gov_attr_rw(ignore_nice_load); -gov_attr_ro(min_sampling_rate); gov_attr_rw(down_threshold); gov_attr_rw(freq_step); static struct attribute *cs_attributes[] = { - &min_sampling_rate.attr, &sampling_rate.attr, &sampling_down_factor.attr, &up_threshold.attr, @@ -297,10 +294,7 @@ static int cs_init(struct dbs_data *dbs_data) dbs_data->up_threshold = DEF_FREQUENCY_UP_THRESHOLD; dbs_data->sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR; dbs_data->ignore_nice_load = 0; - dbs_data->tuners = tuners; - dbs_data->min_sampling_rate = MIN_SAMPLING_RATE_RATIO * - jiffies_to_usecs(10); return 0; } diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index 47e24b5384b3..58d4f4e1ad6a 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -47,14 +47,11 @@ ssize_t store_sampling_rate(struct gov_attr_set *attr_set, const char *buf, { struct dbs_data *dbs_data = to_dbs_data(attr_set); struct policy_dbs_info *policy_dbs; - unsigned int rate; int ret; - ret = sscanf(buf, "%u", &rate); + ret = sscanf(buf, "%u", &dbs_data->sampling_rate); if (ret != 1) return -EINVAL; - dbs_data->sampling_rate = max(rate, dbs_data->min_sampling_rate); - /* * We are operating under dbs_data->mutex and so the list and its * entries can't be freed concurrently. @@ -275,6 +272,9 @@ static void dbs_update_util_handler(struct update_util_data *data, u64 time, struct policy_dbs_info *policy_dbs = cdbs->policy_dbs; u64 delta_ns, lst; + if (!cpufreq_can_do_remote_dvfs(policy_dbs->policy)) + return; + /* * The work may not be allowed to be queued up right now. * Possible reasons: @@ -392,7 +392,6 @@ int cpufreq_dbs_governor_init(struct cpufreq_policy *policy) struct dbs_governor *gov = dbs_governor_of(policy); struct dbs_data *dbs_data; struct policy_dbs_info *policy_dbs; - unsigned int latency; int ret = 0; /* State should be equivalent to EXIT */ @@ -431,16 +430,7 @@ int cpufreq_dbs_governor_init(struct cpufreq_policy *policy) if (ret) goto free_policy_dbs_info; - /* policy latency is in ns. Convert it to us first */ - latency = policy->cpuinfo.transition_latency / 1000; - if (latency == 0) - latency = 1; - - /* Bring kernel and HW constraints together */ - dbs_data->min_sampling_rate = max(dbs_data->min_sampling_rate, - MIN_LATENCY_MULTIPLIER * latency); - dbs_data->sampling_rate = max(dbs_data->min_sampling_rate, - LATENCY_MULTIPLIER * latency); + dbs_data->sampling_rate = cpufreq_policy_transition_delay_us(policy); if (!have_governor_per_policy()) gov->gdbs_data = dbs_data; diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_governor.h index 0236ec2cd654..8463f5def0f5 100644 --- a/drivers/cpufreq/cpufreq_governor.h +++ b/drivers/cpufreq/cpufreq_governor.h @@ -41,7 +41,6 @@ enum {OD_NORMAL_SAMPLE, OD_SUB_SAMPLE}; struct dbs_data { struct gov_attr_set attr_set; void *tuners; - unsigned int min_sampling_rate; unsigned int ignore_nice_load; unsigned int sampling_rate; unsigned int sampling_down_factor; @@ -160,7 +159,7 @@ void cpufreq_dbs_governor_limits(struct cpufreq_policy *policy); #define CPUFREQ_DBS_GOVERNOR_INITIALIZER(_name_) \ { \ .name = _name_, \ - .max_transition_latency = TRANSITION_LATENCY_LIMIT, \ + .dynamic_switching = true, \ .owner = THIS_MODULE, \ .init = cpufreq_dbs_governor_init, \ .exit = cpufreq_dbs_governor_exit, \ diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 3937acf7e026..6b423eebfd5d 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -319,7 +319,6 @@ gov_show_one_common(sampling_rate); gov_show_one_common(up_threshold); gov_show_one_common(sampling_down_factor); gov_show_one_common(ignore_nice_load); -gov_show_one_common(min_sampling_rate); gov_show_one_common(io_is_busy); gov_show_one(od, powersave_bias); @@ -329,10 +328,8 @@ gov_attr_rw(up_threshold); gov_attr_rw(sampling_down_factor); gov_attr_rw(ignore_nice_load); gov_attr_rw(powersave_bias); -gov_attr_ro(min_sampling_rate); static struct attribute *od_attributes[] = { - &min_sampling_rate.attr, &sampling_rate.attr, &up_threshold.attr, &sampling_down_factor.attr, @@ -373,17 +370,8 @@ static int od_init(struct dbs_data *dbs_data) if (idle_time != -1ULL) { /* Idle micro accounting is supported. Use finer thresholds */ dbs_data->up_threshold = MICRO_FREQUENCY_UP_THRESHOLD; - /* - * In nohz/micro accounting case we set the minimum frequency - * not depending on HZ, but fixed (very low). - */ - dbs_data->min_sampling_rate = MICRO_FREQUENCY_MIN_SAMPLE_RATE; } else { dbs_data->up_threshold = DEF_FREQUENCY_UP_THRESHOLD; - - /* For correct statistics, we need 10 ticks for each measure */ - dbs_data->min_sampling_rate = MIN_SAMPLING_RATE_RATIO * - jiffies_to_usecs(10); } dbs_data->sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR; diff --git a/drivers/cpufreq/dbx500-cpufreq.c b/drivers/cpufreq/dbx500-cpufreq.c deleted file mode 100644 index 4ee0431579c1..000000000000 --- a/drivers/cpufreq/dbx500-cpufreq.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) STMicroelectronics 2009 - * Copyright (C) ST-Ericsson SA 2010-2012 - * - * License Terms: GNU General Public License v2 - * Author: Sundar Iyer <sundar.iyer@stericsson.com> - * Author: Martin Persson <martin.persson@stericsson.com> - * Author: Jonas Aaberg <jonas.aberg@stericsson.com> - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/cpufreq.h> -#include <linux/cpu_cooling.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/platform_device.h> -#include <linux/clk.h> - -static struct cpufreq_frequency_table *freq_table; -static struct clk *armss_clk; -static struct thermal_cooling_device *cdev; - -static int dbx500_cpufreq_target(struct cpufreq_policy *policy, - unsigned int index) -{ - /* update armss clk frequency */ - return clk_set_rate(armss_clk, freq_table[index].frequency * 1000); -} - -static int dbx500_cpufreq_init(struct cpufreq_policy *policy) -{ - policy->clk = armss_clk; - return cpufreq_generic_init(policy, freq_table, 20 * 1000); -} - -static int dbx500_cpufreq_exit(struct cpufreq_policy *policy) -{ - if (!IS_ERR(cdev)) - cpufreq_cooling_unregister(cdev); - return 0; -} - -static void dbx500_cpufreq_ready(struct cpufreq_policy *policy) -{ - cdev = cpufreq_cooling_register(policy); - if (IS_ERR(cdev)) - pr_err("Failed to register cooling device %ld\n", PTR_ERR(cdev)); - else - pr_info("Cooling device registered: %s\n", cdev->type); -} - -static struct cpufreq_driver dbx500_cpufreq_driver = { - .flags = CPUFREQ_STICKY | CPUFREQ_CONST_LOOPS | - CPUFREQ_NEED_INITIAL_FREQ_CHECK, - .verify = cpufreq_generic_frequency_table_verify, - .target_index = dbx500_cpufreq_target, - .get = cpufreq_generic_get, - .init = dbx500_cpufreq_init, - .exit = dbx500_cpufreq_exit, - .ready = dbx500_cpufreq_ready, - .name = "DBX500", - .attr = cpufreq_generic_attr, -}; - -static int dbx500_cpufreq_probe(struct platform_device *pdev) -{ - struct cpufreq_frequency_table *pos; - - freq_table = dev_get_platdata(&pdev->dev); - if (!freq_table) { - pr_err("dbx500-cpufreq: Failed to fetch cpufreq table\n"); - return -ENODEV; - } - - armss_clk = clk_get(&pdev->dev, "armss"); - if (IS_ERR(armss_clk)) { - pr_err("dbx500-cpufreq: Failed to get armss clk\n"); - return PTR_ERR(armss_clk); - } - - pr_info("dbx500-cpufreq: Available frequencies:\n"); - cpufreq_for_each_entry(pos, freq_table) - pr_info(" %d Mhz\n", pos->frequency / 1000); - - return cpufreq_register_driver(&dbx500_cpufreq_driver); -} - -static struct platform_driver dbx500_cpufreq_plat_driver = { - .driver = { - .name = "cpufreq-ux500", - }, - .probe = dbx500_cpufreq_probe, -}; - -static int __init dbx500_cpufreq_register(void) -{ - return platform_driver_register(&dbx500_cpufreq_plat_driver); -} -device_initcall(dbx500_cpufreq_register); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("cpufreq driver for DBX500"); diff --git a/drivers/cpufreq/elanfreq.c b/drivers/cpufreq/elanfreq.c index bfce11cba1df..45e2ca62515e 100644 --- a/drivers/cpufreq/elanfreq.c +++ b/drivers/cpufreq/elanfreq.c @@ -165,9 +165,6 @@ static int elanfreq_cpu_init(struct cpufreq_policy *policy) if (pos->frequency > max_freq) pos->frequency = CPUFREQ_ENTRY_INVALID; - /* cpuinfo and default policy values */ - policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; - return cpufreq_table_validate_and_show(policy, elanfreq_table); } @@ -196,6 +193,7 @@ __setup("elanfreq=", elanfreq_setup); static struct cpufreq_driver elanfreq_driver = { .get = elanfreq_get_cpu_frequency, + .flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, .verify = cpufreq_generic_frequency_table_verify, .target_index = elanfreq_target, .init = elanfreq_cpu_init, diff --git a/drivers/cpufreq/gx-suspmod.c b/drivers/cpufreq/gx-suspmod.c index 3488c9c175eb..8f52a06664e3 100644 --- a/drivers/cpufreq/gx-suspmod.c +++ b/drivers/cpufreq/gx-suspmod.c @@ -428,7 +428,6 @@ static int cpufreq_gx_cpu_init(struct cpufreq_policy *policy) policy->max = maxfreq; policy->cpuinfo.min_freq = maxfreq / max_duration; policy->cpuinfo.max_freq = maxfreq; - policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; return 0; } @@ -438,6 +437,7 @@ static int cpufreq_gx_cpu_init(struct cpufreq_policy *policy) * MediaGX/Geode GX initialize cpufreq driver */ static struct cpufreq_driver gx_suspmod_driver = { + .flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, .get = gx_get_cpuspeed, .verify = cpufreq_gx_verify, .target = cpufreq_gx_target, diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index b6edd3ccaa55..14466a9b01c0 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -47,6 +47,7 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) struct dev_pm_opp *opp; unsigned long freq_hz, volt, volt_old; unsigned int old_freq, new_freq; + bool pll1_sys_temp_enabled = false; int ret; new_freq = freq_table[index].frequency; @@ -124,6 +125,10 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) if (freq_hz > clk_get_rate(pll2_pfd2_396m_clk)) { clk_set_rate(pll1_sys_clk, new_freq * 1000); clk_set_parent(pll1_sw_clk, pll1_sys_clk); + } else { + /* pll1_sys needs to be enabled for divider rate change to work. */ + pll1_sys_temp_enabled = true; + clk_prepare_enable(pll1_sys_clk); } } @@ -135,6 +140,10 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) return ret; } + /* PLL1 is only needed until after ARM-PODF is set. */ + if (pll1_sys_temp_enabled) + clk_disable_unprepare(pll1_sys_clk); + /* scaling down? scale voltage after frequency */ if (new_freq < old_freq) { ret = regulator_set_voltage_tol(arm_reg, volt, 0); diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 65ee4fcace1f..8f95265d5f52 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -37,8 +37,7 @@ #include <asm/cpufeature.h> #include <asm/intel-family.h> -#define INTEL_PSTATE_DEFAULT_SAMPLING_INTERVAL (10 * NSEC_PER_MSEC) -#define INTEL_PSTATE_HWP_SAMPLING_INTERVAL (50 * NSEC_PER_MSEC) +#define INTEL_PSTATE_SAMPLING_INTERVAL (10 * NSEC_PER_MSEC) #define INTEL_CPUFREQ_TRANSITION_LATENCY 20000 #define INTEL_CPUFREQ_TRANSITION_DELAY 500 @@ -173,28 +172,6 @@ struct vid_data { }; /** - * struct _pid - Stores PID data - * @setpoint: Target set point for busyness or performance - * @integral: Storage for accumulated error values - * @p_gain: PID proportional gain - * @i_gain: PID integral gain - * @d_gain: PID derivative gain - * @deadband: PID deadband - * @last_err: Last error storage for integral part of PID calculation - * - * Stores PID coefficients and last error for PID controller. - */ -struct _pid { - int setpoint; - int32_t integral; - int32_t p_gain; - int32_t i_gain; - int32_t d_gain; - int deadband; - int32_t last_err; -}; - -/** * struct global_params - Global parameters, mostly tunable via sysfs. * @no_turbo: Whether or not to use turbo P-states. * @turbo_disabled: Whethet or not turbo P-states are available at all, @@ -223,7 +200,6 @@ struct global_params { * @last_update: Time of the last update. * @pstate: Stores P state limits for this CPU * @vid: Stores VID limits for this CPU - * @pid: Stores PID parameters for this CPU * @last_sample_time: Last Sample time * @aperf_mperf_shift: Number of clock cycles after aperf, merf is incremented * This shift is a multiplier to mperf delta to @@ -258,7 +234,6 @@ struct cpudata { struct pstate_data pstate; struct vid_data vid; - struct _pid pid; u64 last_update; u64 last_sample_time; @@ -284,28 +259,6 @@ struct cpudata { static struct cpudata **all_cpu_data; /** - * struct pstate_adjust_policy - Stores static PID configuration data - * @sample_rate_ms: PID calculation sample rate in ms - * @sample_rate_ns: Sample rate calculation in ns - * @deadband: PID deadband - * @setpoint: PID Setpoint - * @p_gain_pct: PID proportional gain - * @i_gain_pct: PID integral gain - * @d_gain_pct: PID derivative gain - * - * Stores per CPU model static PID configuration data. - */ -struct pstate_adjust_policy { - int sample_rate_ms; - s64 sample_rate_ns; - int deadband; - int setpoint; - int p_gain_pct; - int d_gain_pct; - int i_gain_pct; -}; - -/** * struct pstate_funcs - Per CPU model specific callbacks * @get_max: Callback to get maximum non turbo effective P state * @get_max_physical: Callback to get maximum non turbo physical P state @@ -314,7 +267,6 @@ struct pstate_adjust_policy { * @get_scaling: Callback to get frequency scaling factor * @get_val: Callback to convert P state to actual MSR write value * @get_vid: Callback to get VID data for Atom platforms - * @update_util: Active mode utilization update callback. * * Core and Atom CPU models have different way to get P State limits. This * structure is used to store those callbacks. @@ -328,20 +280,9 @@ struct pstate_funcs { int (*get_aperf_mperf_shift)(void); u64 (*get_val)(struct cpudata*, int pstate); void (*get_vid)(struct cpudata *); - void (*update_util)(struct update_util_data *data, u64 time, - unsigned int flags); }; static struct pstate_funcs pstate_funcs __read_mostly; -static struct pstate_adjust_policy pid_params __read_mostly = { - .sample_rate_ms = 10, - .sample_rate_ns = 10 * NSEC_PER_MSEC, - .deadband = 0, - .setpoint = 97, - .p_gain_pct = 20, - .d_gain_pct = 0, - .i_gain_pct = 0, -}; static int hwp_active __read_mostly; static bool per_cpu_limits __read_mostly; @@ -509,56 +450,6 @@ static inline void intel_pstate_exit_perf_limits(struct cpufreq_policy *policy) } #endif -static signed int pid_calc(struct _pid *pid, int32_t busy) -{ - signed int result; - int32_t pterm, dterm, fp_error; - int32_t integral_limit; - - fp_error = pid->setpoint - busy; - - if (abs(fp_error) <= pid->deadband) - return 0; - - pterm = mul_fp(pid->p_gain, fp_error); - - pid->integral += fp_error; - - /* - * We limit the integral here so that it will never - * get higher than 30. This prevents it from becoming - * too large an input over long periods of time and allows - * it to get factored out sooner. - * - * The value of 30 was chosen through experimentation. - */ - integral_limit = int_tofp(30); - if (pid->integral > integral_limit) - pid->integral = integral_limit; - if (pid->integral < -integral_limit) - pid->integral = -integral_limit; - - dterm = mul_fp(pid->d_gain, fp_error - pid->last_err); - pid->last_err = fp_error; - - result = pterm + mul_fp(pid->integral, pid->i_gain) + dterm; - result = result + (1 << (FRAC_BITS-1)); - return (signed int)fp_toint(result); -} - -static inline void intel_pstate_pid_reset(struct cpudata *cpu) -{ - struct _pid *pid = &cpu->pid; - - pid->p_gain = percent_fp(pid_params.p_gain_pct); - pid->d_gain = percent_fp(pid_params.d_gain_pct); - pid->i_gain = percent_fp(pid_params.i_gain_pct); - pid->setpoint = int_tofp(pid_params.setpoint); - pid->last_err = pid->setpoint - int_tofp(100); - pid->deadband = int_tofp(pid_params.deadband); - pid->integral = 0; -} - static inline void update_turbo_state(void) { u64 misc_en; @@ -911,82 +802,6 @@ static void intel_pstate_update_policies(void) cpufreq_update_policy(cpu); } -/************************** debugfs begin ************************/ -static int pid_param_set(void *data, u64 val) -{ - unsigned int cpu; - - *(u32 *)data = val; - pid_params.sample_rate_ns = pid_params.sample_rate_ms * NSEC_PER_MSEC; - for_each_possible_cpu(cpu) - if (all_cpu_data[cpu]) - intel_pstate_pid_reset(all_cpu_data[cpu]); - - return 0; -} - -static int pid_param_get(void *data, u64 *val) -{ - *val = *(u32 *)data; - return 0; -} -DEFINE_SIMPLE_ATTRIBUTE(fops_pid_param, pid_param_get, pid_param_set, "%llu\n"); - -static struct dentry *debugfs_parent; - -struct pid_param { - char *name; - void *value; - struct dentry *dentry; -}; - -static struct pid_param pid_files[] = { - {"sample_rate_ms", &pid_params.sample_rate_ms, }, - {"d_gain_pct", &pid_params.d_gain_pct, }, - {"i_gain_pct", &pid_params.i_gain_pct, }, - {"deadband", &pid_params.deadband, }, - {"setpoint", &pid_params.setpoint, }, - {"p_gain_pct", &pid_params.p_gain_pct, }, - {NULL, NULL, } -}; - -static void intel_pstate_debug_expose_params(void) -{ - int i; - - debugfs_parent = debugfs_create_dir("pstate_snb", NULL); - if (IS_ERR_OR_NULL(debugfs_parent)) - return; - - for (i = 0; pid_files[i].name; i++) { - struct dentry *dentry; - - dentry = debugfs_create_file(pid_files[i].name, 0660, - debugfs_parent, pid_files[i].value, - &fops_pid_param); - if (!IS_ERR(dentry)) - pid_files[i].dentry = dentry; - } -} - -static void intel_pstate_debug_hide_params(void) -{ - int i; - - if (IS_ERR_OR_NULL(debugfs_parent)) - return; - - for (i = 0; pid_files[i].name; i++) { - debugfs_remove(pid_files[i].dentry); - pid_files[i].dentry = NULL; - } - - debugfs_remove(debugfs_parent); - debugfs_parent = NULL; -} - -/************************** debugfs end ************************/ - /************************** sysfs begin ************************/ #define show_one(file_name, object) \ static ssize_t show_##file_name \ @@ -1622,7 +1437,7 @@ static inline int32_t get_avg_pstate(struct cpudata *cpu) cpu->sample.core_avg_perf); } -static inline int32_t get_target_pstate_use_cpu_load(struct cpudata *cpu) +static inline int32_t get_target_pstate(struct cpudata *cpu) { struct sample *sample = &cpu->sample; int32_t busy_frac, boost; @@ -1660,44 +1475,6 @@ static inline int32_t get_target_pstate_use_cpu_load(struct cpudata *cpu) return target; } -static inline int32_t get_target_pstate_use_performance(struct cpudata *cpu) -{ - int32_t perf_scaled, max_pstate, current_pstate, sample_ratio; - u64 duration_ns; - - /* - * perf_scaled is the ratio of the average P-state during the last - * sampling period to the P-state requested last time (in percent). - * - * That measures the system's response to the previous P-state - * selection. - */ - max_pstate = cpu->pstate.max_pstate_physical; - current_pstate = cpu->pstate.current_pstate; - perf_scaled = mul_ext_fp(cpu->sample.core_avg_perf, - div_fp(100 * max_pstate, current_pstate)); - - /* - * Since our utilization update callback will not run unless we are - * in C0, check if the actual elapsed time is significantly greater (3x) - * than our sample interval. If it is, then we were idle for a long - * enough period of time to adjust our performance metric. - */ - duration_ns = cpu->sample.time - cpu->last_sample_time; - if ((s64)duration_ns > pid_params.sample_rate_ns * 3) { - sample_ratio = div_fp(pid_params.sample_rate_ns, duration_ns); - perf_scaled = mul_fp(perf_scaled, sample_ratio); - } else { - sample_ratio = div_fp(100 * (cpu->sample.mperf << cpu->aperf_mperf_shift), - cpu->sample.tsc); - if (sample_ratio < int_tofp(1)) - perf_scaled = 0; - } - - cpu->sample.busy_scaled = perf_scaled; - return cpu->pstate.current_pstate - pid_calc(&cpu->pid, perf_scaled); -} - static int intel_pstate_prepare_request(struct cpudata *cpu, int pstate) { int max_pstate = intel_pstate_get_base_pstate(cpu); @@ -1717,13 +1494,15 @@ static void intel_pstate_update_pstate(struct cpudata *cpu, int pstate) wrmsrl(MSR_IA32_PERF_CTL, pstate_funcs.get_val(cpu, pstate)); } -static void intel_pstate_adjust_pstate(struct cpudata *cpu, int target_pstate) +static void intel_pstate_adjust_pstate(struct cpudata *cpu) { int from = cpu->pstate.current_pstate; struct sample *sample; + int target_pstate; update_turbo_state(); + target_pstate = get_target_pstate(cpu); target_pstate = intel_pstate_prepare_request(cpu, target_pstate); trace_cpu_frequency(target_pstate * cpu->pstate.scaling, cpu->cpu); intel_pstate_update_pstate(cpu, target_pstate); @@ -1740,31 +1519,27 @@ static void intel_pstate_adjust_pstate(struct cpudata *cpu, int target_pstate) fp_toint(cpu->iowait_boost * 100)); } -static void intel_pstate_update_util_pid(struct update_util_data *data, - u64 time, unsigned int flags) -{ - struct cpudata *cpu = container_of(data, struct cpudata, update_util); - u64 delta_ns = time - cpu->sample.time; - - if ((s64)delta_ns < pid_params.sample_rate_ns) - return; - - if (intel_pstate_sample(cpu, time)) { - int target_pstate; - - target_pstate = get_target_pstate_use_performance(cpu); - intel_pstate_adjust_pstate(cpu, target_pstate); - } -} - static void intel_pstate_update_util(struct update_util_data *data, u64 time, unsigned int flags) { struct cpudata *cpu = container_of(data, struct cpudata, update_util); u64 delta_ns; + /* Don't allow remote callbacks */ + if (smp_processor_id() != cpu->cpu) + return; + if (flags & SCHED_CPUFREQ_IOWAIT) { cpu->iowait_boost = int_tofp(1); + cpu->last_update = time; + /* + * The last time the busy was 100% so P-state was max anyway + * so avoid overhead of computation. + */ + if (fp_toint(cpu->sample.busy_scaled) == 100) + return; + + goto set_pstate; } else if (cpu->iowait_boost) { /* Clear iowait_boost if the CPU may have been idle. */ delta_ns = time - cpu->last_update; @@ -1773,15 +1548,12 @@ static void intel_pstate_update_util(struct update_util_data *data, u64 time, } cpu->last_update = time; delta_ns = time - cpu->sample.time; - if ((s64)delta_ns < INTEL_PSTATE_DEFAULT_SAMPLING_INTERVAL) + if ((s64)delta_ns < INTEL_PSTATE_SAMPLING_INTERVAL) return; - if (intel_pstate_sample(cpu, time)) { - int target_pstate; - - target_pstate = get_target_pstate_use_cpu_load(cpu); - intel_pstate_adjust_pstate(cpu, target_pstate); - } +set_pstate: + if (intel_pstate_sample(cpu, time)) + intel_pstate_adjust_pstate(cpu); } static struct pstate_funcs core_funcs = { @@ -1791,7 +1563,6 @@ static struct pstate_funcs core_funcs = { .get_turbo = core_get_turbo_pstate, .get_scaling = core_get_scaling, .get_val = core_get_val, - .update_util = intel_pstate_update_util_pid, }; static const struct pstate_funcs silvermont_funcs = { @@ -1802,7 +1573,6 @@ static const struct pstate_funcs silvermont_funcs = { .get_val = atom_get_val, .get_scaling = silvermont_get_scaling, .get_vid = atom_get_vid, - .update_util = intel_pstate_update_util, }; static const struct pstate_funcs airmont_funcs = { @@ -1813,7 +1583,6 @@ static const struct pstate_funcs airmont_funcs = { .get_val = atom_get_val, .get_scaling = airmont_get_scaling, .get_vid = atom_get_vid, - .update_util = intel_pstate_update_util, }; static const struct pstate_funcs knl_funcs = { @@ -1824,7 +1593,6 @@ static const struct pstate_funcs knl_funcs = { .get_aperf_mperf_shift = knl_get_aperf_mperf_shift, .get_scaling = core_get_scaling, .get_val = core_get_val, - .update_util = intel_pstate_update_util_pid, }; static const struct pstate_funcs bxt_funcs = { @@ -1834,7 +1602,6 @@ static const struct pstate_funcs bxt_funcs = { .get_turbo = core_get_turbo_pstate, .get_scaling = core_get_scaling, .get_val = core_get_val, - .update_util = intel_pstate_update_util, }; #define ICPU(model, policy) \ @@ -1878,8 +1645,6 @@ static const struct x86_cpu_id intel_pstate_cpu_ee_disable_ids[] = { {} }; -static bool pid_in_use(void); - static int intel_pstate_init_cpu(unsigned int cpunum) { struct cpudata *cpu; @@ -1910,8 +1675,6 @@ static int intel_pstate_init_cpu(unsigned int cpunum) intel_pstate_disable_ee(cpunum); intel_pstate_hwp_enable(cpu); - } else if (pid_in_use()) { - intel_pstate_pid_reset(cpu); } intel_pstate_get_cpu_pstates(cpu); @@ -1934,7 +1697,7 @@ static void intel_pstate_set_update_util_hook(unsigned int cpu_num) /* Prevent intel_pstate_update_util() from using stale data. */ cpu->sample.time = 0; cpufreq_add_update_util_hook(cpu_num, &cpu->update_util, - pstate_funcs.update_util); + intel_pstate_update_util); cpu->update_util_set = true; } @@ -2132,7 +1895,6 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.max_freq *= cpu->pstate.scaling; intel_pstate_init_acpi_perf_limits(policy); - cpumask_set_cpu(policy->cpu, policy->cpus); policy->fast_switch_possible = true; @@ -2146,7 +1908,6 @@ static int intel_pstate_cpu_init(struct cpufreq_policy *policy) if (ret) return ret; - policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; if (IS_ENABLED(CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE)) policy->policy = CPUFREQ_POLICY_PERFORMANCE; else @@ -2261,12 +2022,6 @@ static struct cpufreq_driver intel_cpufreq = { static struct cpufreq_driver *default_driver = &intel_pstate; -static bool pid_in_use(void) -{ - return intel_pstate_driver == &intel_pstate && - pstate_funcs.update_util == intel_pstate_update_util_pid; -} - static void intel_pstate_driver_cleanup(void) { unsigned int cpu; @@ -2301,9 +2056,6 @@ static int intel_pstate_register_driver(struct cpufreq_driver *driver) global.min_perf_pct = min_perf_pct_min(); - if (pid_in_use()) - intel_pstate_debug_expose_params(); - return 0; } @@ -2312,9 +2064,6 @@ static int intel_pstate_unregister_driver(void) if (hwp_active) return -EBUSY; - if (pid_in_use()) - intel_pstate_debug_hide_params(); - cpufreq_unregister_driver(intel_pstate_driver); intel_pstate_driver_cleanup(); @@ -2382,24 +2131,6 @@ static int __init intel_pstate_msrs_not_valid(void) return 0; } -#ifdef CONFIG_ACPI -static void intel_pstate_use_acpi_profile(void) -{ - switch (acpi_gbl_FADT.preferred_profile) { - case PM_MOBILE: - case PM_TABLET: - case PM_APPLIANCE_PC: - case PM_DESKTOP: - case PM_WORKSTATION: - pstate_funcs.update_util = intel_pstate_update_util; - } -} -#else -static void intel_pstate_use_acpi_profile(void) -{ -} -#endif - static void __init copy_cpu_funcs(struct pstate_funcs *funcs) { pstate_funcs.get_max = funcs->get_max; @@ -2409,10 +2140,7 @@ static void __init copy_cpu_funcs(struct pstate_funcs *funcs) pstate_funcs.get_scaling = funcs->get_scaling; pstate_funcs.get_val = funcs->get_val; pstate_funcs.get_vid = funcs->get_vid; - pstate_funcs.update_util = funcs->update_util; pstate_funcs.get_aperf_mperf_shift = funcs->get_aperf_mperf_shift; - - intel_pstate_use_acpi_profile(); } #ifdef CONFIG_ACPI @@ -2556,9 +2284,7 @@ static int __init intel_pstate_init(void) if (x86_match_cpu(hwp_support_ids)) { copy_cpu_funcs(&core_funcs); - if (no_hwp) { - pstate_funcs.update_util = intel_pstate_update_util; - } else { + if (!no_hwp) { hwp_active++; intel_pstate.attr = hwp_cpufreq_attrs; goto hwp_cpu_matched; diff --git a/drivers/cpufreq/longrun.c b/drivers/cpufreq/longrun.c index 074971b12635..542aa9adba1a 100644 --- a/drivers/cpufreq/longrun.c +++ b/drivers/cpufreq/longrun.c @@ -270,7 +270,6 @@ static int longrun_cpu_init(struct cpufreq_policy *policy) /* cpuinfo and default policy values */ policy->cpuinfo.min_freq = longrun_low_freq; policy->cpuinfo.max_freq = longrun_high_freq; - policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; longrun_get_policy(policy); return 0; diff --git a/drivers/cpufreq/loongson2_cpufreq.c b/drivers/cpufreq/loongson2_cpufreq.c index 9ac27b22476c..da344696beed 100644 --- a/drivers/cpufreq/loongson2_cpufreq.c +++ b/drivers/cpufreq/loongson2_cpufreq.c @@ -114,7 +114,7 @@ static struct cpufreq_driver loongson2_cpufreq_driver = { .attr = cpufreq_generic_attr, }; -static struct platform_device_id platform_device_ids[] = { +static const struct platform_device_id platform_device_ids[] = { { .name = "loongson2_cpufreq", }, diff --git a/drivers/cpufreq/mt8173-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c index f9f00fb4bc3a..18c4bd9a5c65 100644 --- a/drivers/cpufreq/mt8173-cpufreq.c +++ b/drivers/cpufreq/mediatek-cpufreq.c @@ -507,7 +507,7 @@ static int mtk_cpufreq_exit(struct cpufreq_policy *policy) return 0; } -static struct cpufreq_driver mt8173_cpufreq_driver = { +static struct cpufreq_driver mtk_cpufreq_driver = { .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | CPUFREQ_HAVE_GOVERNOR_PER_POLICY, .verify = cpufreq_generic_frequency_table_verify, @@ -520,7 +520,7 @@ static struct cpufreq_driver mt8173_cpufreq_driver = { .attr = cpufreq_generic_attr, }; -static int mt8173_cpufreq_probe(struct platform_device *pdev) +static int mtk_cpufreq_probe(struct platform_device *pdev) { struct mtk_cpu_dvfs_info *info, *tmp; int cpu, ret; @@ -547,7 +547,7 @@ static int mt8173_cpufreq_probe(struct platform_device *pdev) list_add(&info->list_head, &dvfs_info_list); } - ret = cpufreq_register_driver(&mt8173_cpufreq_driver); + ret = cpufreq_register_driver(&mtk_cpufreq_driver); if (ret) { dev_err(&pdev->dev, "failed to register mtk cpufreq driver\n"); goto release_dvfs_info_list; @@ -564,15 +564,18 @@ release_dvfs_info_list: return ret; } -static struct platform_driver mt8173_cpufreq_platdrv = { +static struct platform_driver mtk_cpufreq_platdrv = { .driver = { - .name = "mt8173-cpufreq", + .name = "mtk-cpufreq", }, - .probe = mt8173_cpufreq_probe, + .probe = mtk_cpufreq_probe, }; /* List of machines supported by this driver */ -static const struct of_device_id mt8173_cpufreq_machines[] __initconst = { +static const struct of_device_id mtk_cpufreq_machines[] __initconst = { + { .compatible = "mediatek,mt2701", }, + { .compatible = "mediatek,mt7622", }, + { .compatible = "mediatek,mt7623", }, { .compatible = "mediatek,mt817x", }, { .compatible = "mediatek,mt8173", }, { .compatible = "mediatek,mt8176", }, @@ -580,7 +583,7 @@ static const struct of_device_id mt8173_cpufreq_machines[] __initconst = { { } }; -static int __init mt8173_cpufreq_driver_init(void) +static int __init mtk_cpufreq_driver_init(void) { struct device_node *np; const struct of_device_id *match; @@ -591,14 +594,14 @@ static int __init mt8173_cpufreq_driver_init(void) if (!np) return -ENODEV; - match = of_match_node(mt8173_cpufreq_machines, np); + match = of_match_node(mtk_cpufreq_machines, np); of_node_put(np); if (!match) { - pr_warn("Machine is not compatible with mt8173-cpufreq\n"); + pr_warn("Machine is not compatible with mtk-cpufreq\n"); return -ENODEV; } - err = platform_driver_register(&mt8173_cpufreq_platdrv); + err = platform_driver_register(&mtk_cpufreq_platdrv); if (err) return err; @@ -608,7 +611,7 @@ static int __init mt8173_cpufreq_driver_init(void) * and the device registration codes are put here to handle defer * probing. */ - pdev = platform_device_register_simple("mt8173-cpufreq", -1, NULL, 0); + pdev = platform_device_register_simple("mtk-cpufreq", -1, NULL, 0); if (IS_ERR(pdev)) { pr_err("failed to register mtk-cpufreq platform device\n"); return PTR_ERR(pdev); @@ -616,4 +619,4 @@ static int __init mt8173_cpufreq_driver_init(void) return 0; } -device_initcall(mt8173_cpufreq_driver_init); +device_initcall(mtk_cpufreq_driver_init); diff --git a/drivers/cpufreq/pmac32-cpufreq.c b/drivers/cpufreq/pmac32-cpufreq.c index ff44016ea031..61ae06ca008e 100644 --- a/drivers/cpufreq/pmac32-cpufreq.c +++ b/drivers/cpufreq/pmac32-cpufreq.c @@ -442,7 +442,8 @@ static struct cpufreq_driver pmac_cpufreq_driver = { .init = pmac_cpufreq_cpu_init, .suspend = pmac_cpufreq_suspend, .resume = pmac_cpufreq_resume, - .flags = CPUFREQ_PM_NO_WARN, + .flags = CPUFREQ_PM_NO_WARN | + CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, .attr = cpufreq_generic_attr, .name = "powermac", }; @@ -626,14 +627,16 @@ static int __init pmac_cpufreq_setup(void) if (!value) goto out; cur_freq = (*value) / 1000; - transition_latency = CPUFREQ_ETERNAL; /* Check for 7447A based MacRISC3 */ if (of_machine_is_compatible("MacRISC3") && of_get_property(cpunode, "dynamic-power-step", NULL) && PVR_VER(mfspr(SPRN_PVR)) == 0x8003) { pmac_cpufreq_init_7447A(cpunode); + + /* Allow dynamic switching */ transition_latency = 8000000; + pmac_cpufreq_driver.flags &= ~CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING; /* Check for other MacRISC3 machines */ } else if (of_machine_is_compatible("PowerBook3,4") || of_machine_is_compatible("PowerBook3,5") || diff --git a/drivers/cpufreq/pmac64-cpufreq.c b/drivers/cpufreq/pmac64-cpufreq.c index 267e0894c62d..be623dd7b9f2 100644 --- a/drivers/cpufreq/pmac64-cpufreq.c +++ b/drivers/cpufreq/pmac64-cpufreq.c @@ -516,7 +516,7 @@ static int __init g5_pm72_cpufreq_init(struct device_node *cpunode) goto bail; } - DBG("cpufreq: i2c clock chip found: %s\n", hwclock->full_name); + DBG("cpufreq: i2c clock chip found: %pOF\n", hwclock); /* Now get all the platform functions */ pfunc_cpu_getfreq = diff --git a/drivers/cpufreq/s5pv210-cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c index f82074eea779..5d31c2db12a3 100644 --- a/drivers/cpufreq/s5pv210-cpufreq.c +++ b/drivers/cpufreq/s5pv210-cpufreq.c @@ -602,6 +602,7 @@ static int s5pv210_cpufreq_probe(struct platform_device *pdev) } clk_base = of_iomap(np, 0); + of_node_put(np); if (!clk_base) { pr_err("%s: failed to map clock registers\n", __func__); return -EFAULT; @@ -612,6 +613,7 @@ static int s5pv210_cpufreq_probe(struct platform_device *pdev) if (id < 0 || id >= ARRAY_SIZE(dmc_base)) { pr_err("%s: failed to get alias of dmc node '%s'\n", __func__, np->name); + of_node_put(np); return id; } @@ -619,6 +621,7 @@ static int s5pv210_cpufreq_probe(struct platform_device *pdev) if (!dmc_base[id]) { pr_err("%s: failed to map dmc%d registers\n", __func__, id); + of_node_put(np); return -EFAULT; } } diff --git a/drivers/cpufreq/sa1100-cpufreq.c b/drivers/cpufreq/sa1100-cpufreq.c index 728eab77e8e0..e2d8a77c36d5 100644 --- a/drivers/cpufreq/sa1100-cpufreq.c +++ b/drivers/cpufreq/sa1100-cpufreq.c @@ -197,11 +197,12 @@ static int sa1100_target(struct cpufreq_policy *policy, unsigned int ppcr) static int __init sa1100_cpu_init(struct cpufreq_policy *policy) { - return cpufreq_generic_init(policy, sa11x0_freq_table, CPUFREQ_ETERNAL); + return cpufreq_generic_init(policy, sa11x0_freq_table, 0); } static struct cpufreq_driver sa1100_driver __refdata = { - .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | + CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, .verify = cpufreq_generic_frequency_table_verify, .target_index = sa1100_target, .get = sa11x0_getspeed, diff --git a/drivers/cpufreq/sa1110-cpufreq.c b/drivers/cpufreq/sa1110-cpufreq.c index 2bac9b6cfeea..66e5fb088ecc 100644 --- a/drivers/cpufreq/sa1110-cpufreq.c +++ b/drivers/cpufreq/sa1110-cpufreq.c @@ -306,13 +306,14 @@ static int sa1110_target(struct cpufreq_policy *policy, unsigned int ppcr) static int __init sa1110_cpu_init(struct cpufreq_policy *policy) { - return cpufreq_generic_init(policy, sa11x0_freq_table, CPUFREQ_ETERNAL); + return cpufreq_generic_init(policy, sa11x0_freq_table, 0); } /* sa1110_driver needs __refdata because it must remain after init registers * it with cpufreq_register_driver() */ static struct cpufreq_driver sa1110_driver __refdata = { - .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | + CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, .verify = cpufreq_generic_frequency_table_verify, .target_index = sa1110_target, .get = sa11x0_getspeed, diff --git a/drivers/cpufreq/sh-cpufreq.c b/drivers/cpufreq/sh-cpufreq.c index 719c3d9f07fb..28893d435cf5 100644 --- a/drivers/cpufreq/sh-cpufreq.c +++ b/drivers/cpufreq/sh-cpufreq.c @@ -137,8 +137,6 @@ static int sh_cpufreq_cpu_init(struct cpufreq_policy *policy) (clk_round_rate(cpuclk, ~0UL) + 500) / 1000; } - policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; - dev_info(dev, "CPU Frequencies - Minimum %u.%03u MHz, " "Maximum %u.%03u MHz.\n", policy->min / 1000, policy->min % 1000, @@ -159,6 +157,7 @@ static int sh_cpufreq_cpu_exit(struct cpufreq_policy *policy) static struct cpufreq_driver sh_cpufreq_driver = { .name = "sh", + .flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, .get = sh_cpufreq_get, .target = sh_cpufreq_target, .verify = sh_cpufreq_verify, diff --git a/drivers/cpufreq/speedstep-ich.c b/drivers/cpufreq/speedstep-ich.c index b86953a3ddc4..0412a246a785 100644 --- a/drivers/cpufreq/speedstep-ich.c +++ b/drivers/cpufreq/speedstep-ich.c @@ -207,7 +207,7 @@ static unsigned int speedstep_detect_chipset(void) * 8100 which use a pretty old revision of the 82815 * host bridge. Abort on these systems. */ - static struct pci_dev *hostbridge; + struct pci_dev *hostbridge; hostbridge = pci_get_subsys(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_MC, diff --git a/drivers/cpufreq/speedstep-lib.c b/drivers/cpufreq/speedstep-lib.c index 1b8062182c81..ccab452a4ef5 100644 --- a/drivers/cpufreq/speedstep-lib.c +++ b/drivers/cpufreq/speedstep-lib.c @@ -35,7 +35,7 @@ static int relaxed_check; static unsigned int pentium3_get_frequency(enum speedstep_processor processor) { /* See table 14 of p3_ds.pdf and table 22 of 29834003.pdf */ - struct { + static const struct { unsigned int ratio; /* Frequency Multiplier (x10) */ u8 bitmap; /* power on configuration bits [27, 25:22] (in MSR 0x2a) */ @@ -58,7 +58,7 @@ static unsigned int pentium3_get_frequency(enum speedstep_processor processor) }; /* PIII(-M) FSB settings: see table b1-b of 24547206.pdf */ - struct { + static const struct { unsigned int value; /* Front Side Bus speed in MHz */ u8 bitmap; /* power on configuration bits [18: 19] (in MSR 0x2a) */ diff --git a/drivers/cpufreq/speedstep-smi.c b/drivers/cpufreq/speedstep-smi.c index 37b30071c220..d23f24ccff38 100644 --- a/drivers/cpufreq/speedstep-smi.c +++ b/drivers/cpufreq/speedstep-smi.c @@ -266,7 +266,6 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy) pr_debug("workaround worked.\n"); } - policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; return cpufreq_table_validate_and_show(policy, speedstep_freqs); } @@ -290,6 +289,7 @@ static int speedstep_resume(struct cpufreq_policy *policy) static struct cpufreq_driver speedstep_driver = { .name = "speedstep-smi", + .flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, .verify = cpufreq_generic_frequency_table_verify, .target_index = speedstep_target, .init = speedstep_cpu_init, diff --git a/drivers/cpufreq/sti-cpufreq.c b/drivers/cpufreq/sti-cpufreq.c index d2d0430d09d4..47105735df12 100644 --- a/drivers/cpufreq/sti-cpufreq.c +++ b/drivers/cpufreq/sti-cpufreq.c @@ -65,8 +65,8 @@ static int sti_cpufreq_fetch_major(void) { ret = of_property_read_u32_index(np, "st,syscfg", MAJOR_ID_INDEX, &major_offset); if (ret) { - dev_err(dev, "No major number offset provided in %s [%d]\n", - np->full_name, ret); + dev_err(dev, "No major number offset provided in %pOF [%d]\n", + np, ret); return ret; } @@ -92,8 +92,8 @@ static int sti_cpufreq_fetch_minor(void) MINOR_ID_INDEX, &minor_offset); if (ret) { dev_err(dev, - "No minor number offset provided %s [%d]\n", - np->full_name, ret); + "No minor number offset provided %pOF [%d]\n", + np, ret); return ret; } diff --git a/drivers/cpufreq/tango-cpufreq.c b/drivers/cpufreq/tango-cpufreq.c new file mode 100644 index 000000000000..89a7f860bfe8 --- /dev/null +++ b/drivers/cpufreq/tango-cpufreq.c @@ -0,0 +1,38 @@ +#include <linux/of.h> +#include <linux/cpu.h> +#include <linux/clk.h> +#include <linux/pm_opp.h> +#include <linux/platform_device.h> + +static const struct of_device_id machines[] __initconst = { + { .compatible = "sigma,tango4" }, + { /* sentinel */ } +}; + +static int __init tango_cpufreq_init(void) +{ + struct device *cpu_dev = get_cpu_device(0); + unsigned long max_freq; + struct clk *cpu_clk; + void *res; + + if (!of_match_node(machines, of_root)) + return -ENODEV; + + cpu_clk = clk_get(cpu_dev, NULL); + if (IS_ERR(cpu_clk)) + return -ENODEV; + + max_freq = clk_get_rate(cpu_clk); + + dev_pm_opp_add(cpu_dev, max_freq / 1, 0); + dev_pm_opp_add(cpu_dev, max_freq / 2, 0); + dev_pm_opp_add(cpu_dev, max_freq / 3, 0); + dev_pm_opp_add(cpu_dev, max_freq / 5, 0); + dev_pm_opp_add(cpu_dev, max_freq / 9, 0); + + res = platform_device_register_data(NULL, "cpufreq-dt", -1, NULL, 0); + + return PTR_ERR_OR_ZERO(res); +} +device_initcall(tango_cpufreq_init); diff --git a/drivers/cpufreq/ti-cpufreq.c b/drivers/cpufreq/ti-cpufreq.c index a7b5658c0460..b29cd3398463 100644 --- a/drivers/cpufreq/ti-cpufreq.c +++ b/drivers/cpufreq/ti-cpufreq.c @@ -245,8 +245,6 @@ static int ti_cpufreq_init(void) if (ret) goto fail_put_node; - of_node_put(opp_data->opp_node); - ret = PTR_ERR_OR_ZERO(dev_pm_opp_set_supported_hw(opp_data->cpu_dev, version, VERSION_COUNT)); if (ret) { @@ -255,6 +253,8 @@ static int ti_cpufreq_init(void) goto fail_put_node; } + of_node_put(opp_data->opp_node); + register_cpufreq_dt: platform_device_register_simple("cpufreq-dt", -1, NULL, 0); diff --git a/drivers/cpufreq/unicore2-cpufreq.c b/drivers/cpufreq/unicore2-cpufreq.c index 6f9dfa80563a..db62d9844751 100644 --- a/drivers/cpufreq/unicore2-cpufreq.c +++ b/drivers/cpufreq/unicore2-cpufreq.c @@ -58,13 +58,12 @@ static int __init ucv2_cpu_init(struct cpufreq_policy *policy) policy->min = policy->cpuinfo.min_freq = 250000; policy->max = policy->cpuinfo.max_freq = 1000000; - policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; policy->clk = clk_get(NULL, "MAIN_CLK"); return PTR_ERR_OR_ZERO(policy->clk); } static struct cpufreq_driver ucv2_driver = { - .flags = CPUFREQ_STICKY, + .flags = CPUFREQ_STICKY | CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, .verify = ucv2_verify_speed, .target = ucv2_target, .get = cpufreq_generic_get, diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 3ba81b1dffad..0b67a05a7aae 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -5,6 +5,7 @@ obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o obj-$(CONFIG_DT_IDLE_STATES) += dt_idle_states.o +obj-$(CONFIG_ARCH_HAS_CPU_RELAX) += poll_state.o ################################################################################## # ARM SoC drivers diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 60bb64f4329d..484cc8909d5c 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -77,7 +77,7 @@ static int find_deepest_state(struct cpuidle_driver *drv, struct cpuidle_device *dev, unsigned int max_latency, unsigned int forbidden_flags, - bool freeze) + bool s2idle) { unsigned int latency_req = 0; int i, ret = 0; @@ -89,7 +89,7 @@ static int find_deepest_state(struct cpuidle_driver *drv, if (s->disabled || su->disable || s->exit_latency <= latency_req || s->exit_latency > max_latency || (s->flags & forbidden_flags) - || (freeze && !s->enter_freeze)) + || (s2idle && !s->enter_s2idle)) continue; latency_req = s->exit_latency; @@ -128,7 +128,7 @@ int cpuidle_find_deepest_state(struct cpuidle_driver *drv, } #ifdef CONFIG_SUSPEND -static void enter_freeze_proper(struct cpuidle_driver *drv, +static void enter_s2idle_proper(struct cpuidle_driver *drv, struct cpuidle_device *dev, int index) { /* @@ -143,7 +143,7 @@ static void enter_freeze_proper(struct cpuidle_driver *drv, * suspended is generally unsafe. */ stop_critical_timings(); - drv->states[index].enter_freeze(dev, drv, index); + drv->states[index].enter_s2idle(dev, drv, index); WARN_ON(!irqs_disabled()); /* * timekeeping_resume() that will be called by tick_unfreeze() for the @@ -155,25 +155,25 @@ static void enter_freeze_proper(struct cpuidle_driver *drv, } /** - * cpuidle_enter_freeze - Enter an idle state suitable for suspend-to-idle. + * cpuidle_enter_s2idle - Enter an idle state suitable for suspend-to-idle. * @drv: cpuidle driver for the given CPU. * @dev: cpuidle device for the given CPU. * - * If there are states with the ->enter_freeze callback, find the deepest of + * If there are states with the ->enter_s2idle callback, find the deepest of * them and enter it with frozen tick. */ -int cpuidle_enter_freeze(struct cpuidle_driver *drv, struct cpuidle_device *dev) +int cpuidle_enter_s2idle(struct cpuidle_driver *drv, struct cpuidle_device *dev) { int index; /* - * Find the deepest state with ->enter_freeze present, which guarantees + * Find the deepest state with ->enter_s2idle present, which guarantees * that interrupts won't be enabled when it exits and allows the tick to * be frozen safely. */ index = find_deepest_state(drv, dev, UINT_MAX, 0, true); if (index > 0) - enter_freeze_proper(drv, dev, index); + enter_s2idle_proper(drv, dev, index); return index; } diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index e53fb861beb0..dc32f34e68d9 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -179,36 +179,6 @@ static void __cpuidle_driver_init(struct cpuidle_driver *drv) } } -#ifdef CONFIG_ARCH_HAS_CPU_RELAX -static int __cpuidle poll_idle(struct cpuidle_device *dev, - struct cpuidle_driver *drv, int index) -{ - local_irq_enable(); - if (!current_set_polling_and_test()) { - while (!need_resched()) - cpu_relax(); - } - current_clr_polling(); - - return index; -} - -static void poll_idle_init(struct cpuidle_driver *drv) -{ - struct cpuidle_state *state = &drv->states[0]; - - snprintf(state->name, CPUIDLE_NAME_LEN, "POLL"); - snprintf(state->desc, CPUIDLE_DESC_LEN, "CPUIDLE CORE POLL IDLE"); - state->exit_latency = 0; - state->target_residency = 0; - state->power_usage = -1; - state->enter = poll_idle; - state->disabled = false; -} -#else -static void poll_idle_init(struct cpuidle_driver *drv) {} -#endif /* !CONFIG_ARCH_HAS_CPU_RELAX */ - /** * __cpuidle_register_driver: register the driver * @drv: a valid pointer to a struct cpuidle_driver @@ -246,8 +216,6 @@ static int __cpuidle_register_driver(struct cpuidle_driver *drv) on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer, (void *)1, 1); - poll_idle_init(drv); - return 0; } diff --git a/drivers/cpuidle/dt_idle_states.c b/drivers/cpuidle/dt_idle_states.c index ae8eb0359889..53342b7f1010 100644 --- a/drivers/cpuidle/dt_idle_states.c +++ b/drivers/cpuidle/dt_idle_states.c @@ -41,9 +41,9 @@ static int init_state_node(struct cpuidle_state *idle_state, /* * Since this is not a "coupled" state, it's safe to assume interrupts * won't be enabled when it exits allowing the tick to be frozen - * safely. So enter() can be also enter_freeze() callback. + * safely. So enter() can be also enter_s2idle() callback. */ - idle_state->enter_freeze = match_id->data; + idle_state->enter_s2idle = match_id->data; err = of_property_read_u32(state_node, "wakeup-latency-us", &idle_state->exit_latency); @@ -53,16 +53,16 @@ static int init_state_node(struct cpuidle_state *idle_state, err = of_property_read_u32(state_node, "entry-latency-us", &entry_latency); if (err) { - pr_debug(" * %s missing entry-latency-us property\n", - state_node->full_name); + pr_debug(" * %pOF missing entry-latency-us property\n", + state_node); return -EINVAL; } err = of_property_read_u32(state_node, "exit-latency-us", &exit_latency); if (err) { - pr_debug(" * %s missing exit-latency-us property\n", - state_node->full_name); + pr_debug(" * %pOF missing exit-latency-us property\n", + state_node); return -EINVAL; } /* @@ -75,8 +75,8 @@ static int init_state_node(struct cpuidle_state *idle_state, err = of_property_read_u32(state_node, "min-residency-us", &idle_state->target_residency); if (err) { - pr_debug(" * %s missing min-residency-us property\n", - state_node->full_name); + pr_debug(" * %pOF missing min-residency-us property\n", + state_node); return -EINVAL; } @@ -186,8 +186,8 @@ int dt_init_idle_driver(struct cpuidle_driver *drv, } if (!idle_state_valid(state_node, i, cpumask)) { - pr_warn("%s idle state not valid, bailing out\n", - state_node->full_name); + pr_warn("%pOF idle state not valid, bailing out\n", + state_node); err = -EINVAL; break; } @@ -200,8 +200,8 @@ int dt_init_idle_driver(struct cpuidle_driver *drv, idle_state = &drv->states[state_idx++]; err = init_state_node(idle_state, matches, state_node); if (err) { - pr_err("Parsing idle state node %s failed with err %d\n", - state_node->full_name, err); + pr_err("Parsing idle state node %pOF failed with err %d\n", + state_node, err); err = -EINVAL; break; } diff --git a/drivers/cpuidle/governors/ladder.c b/drivers/cpuidle/governors/ladder.c index ac321f09e717..ce1a2ffffb2a 100644 --- a/drivers/cpuidle/governors/ladder.c +++ b/drivers/cpuidle/governors/ladder.c @@ -69,6 +69,7 @@ static int ladder_select_state(struct cpuidle_driver *drv, struct ladder_device *ldev = this_cpu_ptr(&ladder_devices); struct ladder_device_state *last_state; int last_residency, last_idx = ldev->last_state_idx; + int first_idx = drv->states[0].flags & CPUIDLE_FLAG_POLLING ? 1 : 0; int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); /* Special case when user has set very strict latency requirement */ @@ -96,13 +97,13 @@ static int ladder_select_state(struct cpuidle_driver *drv, } /* consider demotion */ - if (last_idx > CPUIDLE_DRIVER_STATE_START && + if (last_idx > first_idx && (drv->states[last_idx].disabled || dev->states_usage[last_idx].disable || drv->states[last_idx].exit_latency > latency_req)) { int i; - for (i = last_idx - 1; i > CPUIDLE_DRIVER_STATE_START; i--) { + for (i = last_idx - 1; i > first_idx; i--) { if (drv->states[i].exit_latency <= latency_req) break; } @@ -110,7 +111,7 @@ static int ladder_select_state(struct cpuidle_driver *drv, return i; } - if (last_idx > CPUIDLE_DRIVER_STATE_START && + if (last_idx > first_idx && last_residency < last_state->threshold.demotion_time) { last_state->stats.demotion_count++; last_state->stats.promotion_count = 0; @@ -133,13 +134,14 @@ static int ladder_enable_device(struct cpuidle_driver *drv, struct cpuidle_device *dev) { int i; + int first_idx = drv->states[0].flags & CPUIDLE_FLAG_POLLING ? 1 : 0; struct ladder_device *ldev = &per_cpu(ladder_devices, dev->cpu); struct ladder_device_state *lstate; struct cpuidle_state *state; - ldev->last_state_idx = CPUIDLE_DRIVER_STATE_START; + ldev->last_state_idx = first_idx; - for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { + for (i = first_idx; i < drv->state_count; i++) { state = &drv->states[i]; lstate = &ldev->states[i]; @@ -151,7 +153,7 @@ static int ladder_enable_device(struct cpuidle_driver *drv, if (i < drv->state_count - 1) lstate->threshold.promotion_time = state->exit_latency; - if (i > CPUIDLE_DRIVER_STATE_START) + if (i > first_idx) lstate->threshold.demotion_time = state->exit_latency; } diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 61b64c2b2cb8..48eaf2879228 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -324,8 +324,9 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) expected_interval = get_typical_interval(data); expected_interval = min(expected_interval, data->next_timer_us); - if (CPUIDLE_DRIVER_STATE_START > 0) { - struct cpuidle_state *s = &drv->states[CPUIDLE_DRIVER_STATE_START]; + first_idx = 0; + if (drv->states[0].flags & CPUIDLE_FLAG_POLLING) { + struct cpuidle_state *s = &drv->states[1]; unsigned int polling_threshold; /* @@ -336,12 +337,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) polling_threshold = max_t(unsigned int, 20, s->target_residency); if (data->next_timer_us > polling_threshold && latency_req > s->exit_latency && !s->disabled && - !dev->states_usage[CPUIDLE_DRIVER_STATE_START].disable) - first_idx = CPUIDLE_DRIVER_STATE_START; - else - first_idx = CPUIDLE_DRIVER_STATE_START - 1; - } else { - first_idx = 0; + !dev->states_usage[1].disable) + first_idx = 1; } /* diff --git a/drivers/cpuidle/poll_state.c b/drivers/cpuidle/poll_state.c new file mode 100644 index 000000000000..7416b16287de --- /dev/null +++ b/drivers/cpuidle/poll_state.c @@ -0,0 +1,37 @@ +/* + * poll_state.c - Polling idle state + * + * This file is released under the GPLv2. + */ + +#include <linux/cpuidle.h> +#include <linux/sched.h> +#include <linux/sched/idle.h> + +static int __cpuidle poll_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + local_irq_enable(); + if (!current_set_polling_and_test()) { + while (!need_resched()) + cpu_relax(); + } + current_clr_polling(); + + return index; +} + +void cpuidle_poll_state_init(struct cpuidle_driver *drv) +{ + struct cpuidle_state *state = &drv->states[0]; + + snprintf(state->name, CPUIDLE_NAME_LEN, "POLL"); + snprintf(state->desc, CPUIDLE_DESC_LEN, "CPUIDLE CORE POLL IDLE"); + state->exit_latency = 0; + state->target_residency = 0; + state->power_usage = -1; + state->enter = poll_idle; + state->disabled = false; + state->flags = CPUIDLE_FLAG_POLLING; +} +EXPORT_SYMBOL_GPL(cpuidle_poll_state_init); diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 41254e702f1e..6a172d338f6d 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -1,6 +1,7 @@ menuconfig PM_DEVFREQ bool "Generic Dynamic Voltage and Frequency Scaling (DVFS) support" select SRCU + select PM_OPP help A device may have a list of frequencies and voltages available. devfreq, a generic DVFS framework can be registered for a device diff --git a/drivers/devfreq/devfreq-event.c b/drivers/devfreq/devfreq-event.c index 8648b32ebc89..d67242d87744 100644 --- a/drivers/devfreq/devfreq-event.c +++ b/drivers/devfreq/devfreq-event.c @@ -277,8 +277,8 @@ int devfreq_event_get_edev_count(struct device *dev) sizeof(u32)); if (count < 0) { dev_err(dev, - "failed to get the count of devfreq-event in %s node\n", - dev->of_node->full_name); + "failed to get the count of devfreq-event in %pOF node\n", + dev->of_node); return count; } diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index dea04871b50d..a1c4ee818614 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -564,7 +564,7 @@ struct devfreq *devfreq_add_device(struct device *dev, err = device_register(&devfreq->dev); if (err) { mutex_unlock(&devfreq->lock); - goto err_out; + goto err_dev; } devfreq->trans_table = devm_kzalloc(&devfreq->dev, @@ -610,6 +610,9 @@ err_init: mutex_unlock(&devfreq_list_lock); device_unregister(&devfreq->dev); +err_dev: + if (devfreq) + kfree(devfreq); err_out: return ERR_PTR(err); } diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h index a4f2fa1091e4..cfc50a61a90d 100644 --- a/drivers/devfreq/governor.h +++ b/drivers/devfreq/governor.h @@ -69,4 +69,8 @@ extern int devfreq_remove_governor(struct devfreq_governor *governor); extern int devfreq_update_status(struct devfreq *devfreq, unsigned long freq); +static inline int devfreq_update_stats(struct devfreq *df) +{ + return df->profile->get_dev_status(df->dev.parent, &df->last_status); +} #endif /* _GOVERNOR_H */ diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index e87ffb3c31a9..5dc7ea4b6bc4 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -97,7 +97,7 @@ static const struct idle_cpu *icpu; static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; static int intel_idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); -static void intel_idle_freeze(struct cpuidle_device *dev, +static void intel_idle_s2idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); static struct cpuidle_state *cpuidle_state_table; @@ -132,7 +132,7 @@ static struct cpuidle_state nehalem_cstates[] = { .exit_latency = 3, .target_residency = 6, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C1E", .desc = "MWAIT 0x01", @@ -140,7 +140,7 @@ static struct cpuidle_state nehalem_cstates[] = { .exit_latency = 10, .target_residency = 20, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C3", .desc = "MWAIT 0x10", @@ -148,7 +148,7 @@ static struct cpuidle_state nehalem_cstates[] = { .exit_latency = 20, .target_residency = 80, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6", .desc = "MWAIT 0x20", @@ -156,7 +156,7 @@ static struct cpuidle_state nehalem_cstates[] = { .exit_latency = 200, .target_residency = 800, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .enter = NULL } }; @@ -169,7 +169,7 @@ static struct cpuidle_state snb_cstates[] = { .exit_latency = 2, .target_residency = 2, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C1E", .desc = "MWAIT 0x01", @@ -177,7 +177,7 @@ static struct cpuidle_state snb_cstates[] = { .exit_latency = 10, .target_residency = 20, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C3", .desc = "MWAIT 0x10", @@ -185,7 +185,7 @@ static struct cpuidle_state snb_cstates[] = { .exit_latency = 80, .target_residency = 211, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6", .desc = "MWAIT 0x20", @@ -193,7 +193,7 @@ static struct cpuidle_state snb_cstates[] = { .exit_latency = 104, .target_residency = 345, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C7", .desc = "MWAIT 0x30", @@ -201,7 +201,7 @@ static struct cpuidle_state snb_cstates[] = { .exit_latency = 109, .target_residency = 345, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .enter = NULL } }; @@ -214,7 +214,7 @@ static struct cpuidle_state byt_cstates[] = { .exit_latency = 1, .target_residency = 1, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6N", .desc = "MWAIT 0x58", @@ -222,7 +222,7 @@ static struct cpuidle_state byt_cstates[] = { .exit_latency = 300, .target_residency = 275, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6S", .desc = "MWAIT 0x52", @@ -230,7 +230,7 @@ static struct cpuidle_state byt_cstates[] = { .exit_latency = 500, .target_residency = 560, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C7", .desc = "MWAIT 0x60", @@ -238,7 +238,7 @@ static struct cpuidle_state byt_cstates[] = { .exit_latency = 1200, .target_residency = 4000, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C7S", .desc = "MWAIT 0x64", @@ -246,7 +246,7 @@ static struct cpuidle_state byt_cstates[] = { .exit_latency = 10000, .target_residency = 20000, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .enter = NULL } }; @@ -259,7 +259,7 @@ static struct cpuidle_state cht_cstates[] = { .exit_latency = 1, .target_residency = 1, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6N", .desc = "MWAIT 0x58", @@ -267,7 +267,7 @@ static struct cpuidle_state cht_cstates[] = { .exit_latency = 80, .target_residency = 275, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6S", .desc = "MWAIT 0x52", @@ -275,7 +275,7 @@ static struct cpuidle_state cht_cstates[] = { .exit_latency = 200, .target_residency = 560, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C7", .desc = "MWAIT 0x60", @@ -283,7 +283,7 @@ static struct cpuidle_state cht_cstates[] = { .exit_latency = 1200, .target_residency = 4000, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C7S", .desc = "MWAIT 0x64", @@ -291,7 +291,7 @@ static struct cpuidle_state cht_cstates[] = { .exit_latency = 10000, .target_residency = 20000, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .enter = NULL } }; @@ -304,7 +304,7 @@ static struct cpuidle_state ivb_cstates[] = { .exit_latency = 1, .target_residency = 1, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C1E", .desc = "MWAIT 0x01", @@ -312,7 +312,7 @@ static struct cpuidle_state ivb_cstates[] = { .exit_latency = 10, .target_residency = 20, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C3", .desc = "MWAIT 0x10", @@ -320,7 +320,7 @@ static struct cpuidle_state ivb_cstates[] = { .exit_latency = 59, .target_residency = 156, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6", .desc = "MWAIT 0x20", @@ -328,7 +328,7 @@ static struct cpuidle_state ivb_cstates[] = { .exit_latency = 80, .target_residency = 300, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C7", .desc = "MWAIT 0x30", @@ -336,7 +336,7 @@ static struct cpuidle_state ivb_cstates[] = { .exit_latency = 87, .target_residency = 300, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .enter = NULL } }; @@ -349,7 +349,7 @@ static struct cpuidle_state ivt_cstates[] = { .exit_latency = 1, .target_residency = 1, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C1E", .desc = "MWAIT 0x01", @@ -357,7 +357,7 @@ static struct cpuidle_state ivt_cstates[] = { .exit_latency = 10, .target_residency = 80, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C3", .desc = "MWAIT 0x10", @@ -365,7 +365,7 @@ static struct cpuidle_state ivt_cstates[] = { .exit_latency = 59, .target_residency = 156, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6", .desc = "MWAIT 0x20", @@ -373,7 +373,7 @@ static struct cpuidle_state ivt_cstates[] = { .exit_latency = 82, .target_residency = 300, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .enter = NULL } }; @@ -386,7 +386,7 @@ static struct cpuidle_state ivt_cstates_4s[] = { .exit_latency = 1, .target_residency = 1, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C1E", .desc = "MWAIT 0x01", @@ -394,7 +394,7 @@ static struct cpuidle_state ivt_cstates_4s[] = { .exit_latency = 10, .target_residency = 250, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C3", .desc = "MWAIT 0x10", @@ -402,7 +402,7 @@ static struct cpuidle_state ivt_cstates_4s[] = { .exit_latency = 59, .target_residency = 300, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6", .desc = "MWAIT 0x20", @@ -410,7 +410,7 @@ static struct cpuidle_state ivt_cstates_4s[] = { .exit_latency = 84, .target_residency = 400, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .enter = NULL } }; @@ -423,7 +423,7 @@ static struct cpuidle_state ivt_cstates_8s[] = { .exit_latency = 1, .target_residency = 1, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C1E", .desc = "MWAIT 0x01", @@ -431,7 +431,7 @@ static struct cpuidle_state ivt_cstates_8s[] = { .exit_latency = 10, .target_residency = 500, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C3", .desc = "MWAIT 0x10", @@ -439,7 +439,7 @@ static struct cpuidle_state ivt_cstates_8s[] = { .exit_latency = 59, .target_residency = 600, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6", .desc = "MWAIT 0x20", @@ -447,7 +447,7 @@ static struct cpuidle_state ivt_cstates_8s[] = { .exit_latency = 88, .target_residency = 700, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .enter = NULL } }; @@ -460,7 +460,7 @@ static struct cpuidle_state hsw_cstates[] = { .exit_latency = 2, .target_residency = 2, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C1E", .desc = "MWAIT 0x01", @@ -468,7 +468,7 @@ static struct cpuidle_state hsw_cstates[] = { .exit_latency = 10, .target_residency = 20, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C3", .desc = "MWAIT 0x10", @@ -476,7 +476,7 @@ static struct cpuidle_state hsw_cstates[] = { .exit_latency = 33, .target_residency = 100, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6", .desc = "MWAIT 0x20", @@ -484,7 +484,7 @@ static struct cpuidle_state hsw_cstates[] = { .exit_latency = 133, .target_residency = 400, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C7s", .desc = "MWAIT 0x32", @@ -492,7 +492,7 @@ static struct cpuidle_state hsw_cstates[] = { .exit_latency = 166, .target_residency = 500, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C8", .desc = "MWAIT 0x40", @@ -500,7 +500,7 @@ static struct cpuidle_state hsw_cstates[] = { .exit_latency = 300, .target_residency = 900, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C9", .desc = "MWAIT 0x50", @@ -508,7 +508,7 @@ static struct cpuidle_state hsw_cstates[] = { .exit_latency = 600, .target_residency = 1800, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C10", .desc = "MWAIT 0x60", @@ -516,7 +516,7 @@ static struct cpuidle_state hsw_cstates[] = { .exit_latency = 2600, .target_residency = 7700, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .enter = NULL } }; @@ -528,7 +528,7 @@ static struct cpuidle_state bdw_cstates[] = { .exit_latency = 2, .target_residency = 2, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C1E", .desc = "MWAIT 0x01", @@ -536,7 +536,7 @@ static struct cpuidle_state bdw_cstates[] = { .exit_latency = 10, .target_residency = 20, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C3", .desc = "MWAIT 0x10", @@ -544,7 +544,7 @@ static struct cpuidle_state bdw_cstates[] = { .exit_latency = 40, .target_residency = 100, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6", .desc = "MWAIT 0x20", @@ -552,7 +552,7 @@ static struct cpuidle_state bdw_cstates[] = { .exit_latency = 133, .target_residency = 400, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C7s", .desc = "MWAIT 0x32", @@ -560,7 +560,7 @@ static struct cpuidle_state bdw_cstates[] = { .exit_latency = 166, .target_residency = 500, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C8", .desc = "MWAIT 0x40", @@ -568,7 +568,7 @@ static struct cpuidle_state bdw_cstates[] = { .exit_latency = 300, .target_residency = 900, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C9", .desc = "MWAIT 0x50", @@ -576,7 +576,7 @@ static struct cpuidle_state bdw_cstates[] = { .exit_latency = 600, .target_residency = 1800, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C10", .desc = "MWAIT 0x60", @@ -584,7 +584,7 @@ static struct cpuidle_state bdw_cstates[] = { .exit_latency = 2600, .target_residency = 7700, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .enter = NULL } }; @@ -597,7 +597,7 @@ static struct cpuidle_state skl_cstates[] = { .exit_latency = 2, .target_residency = 2, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C1E", .desc = "MWAIT 0x01", @@ -605,7 +605,7 @@ static struct cpuidle_state skl_cstates[] = { .exit_latency = 10, .target_residency = 20, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C3", .desc = "MWAIT 0x10", @@ -613,7 +613,7 @@ static struct cpuidle_state skl_cstates[] = { .exit_latency = 70, .target_residency = 100, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6", .desc = "MWAIT 0x20", @@ -621,7 +621,7 @@ static struct cpuidle_state skl_cstates[] = { .exit_latency = 85, .target_residency = 200, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C7s", .desc = "MWAIT 0x33", @@ -629,7 +629,7 @@ static struct cpuidle_state skl_cstates[] = { .exit_latency = 124, .target_residency = 800, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C8", .desc = "MWAIT 0x40", @@ -637,7 +637,7 @@ static struct cpuidle_state skl_cstates[] = { .exit_latency = 200, .target_residency = 800, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C9", .desc = "MWAIT 0x50", @@ -645,7 +645,7 @@ static struct cpuidle_state skl_cstates[] = { .exit_latency = 480, .target_residency = 5000, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C10", .desc = "MWAIT 0x60", @@ -653,7 +653,7 @@ static struct cpuidle_state skl_cstates[] = { .exit_latency = 890, .target_residency = 5000, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .enter = NULL } }; @@ -666,7 +666,7 @@ static struct cpuidle_state skx_cstates[] = { .exit_latency = 2, .target_residency = 2, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C1E", .desc = "MWAIT 0x01", @@ -674,7 +674,7 @@ static struct cpuidle_state skx_cstates[] = { .exit_latency = 10, .target_residency = 20, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6", .desc = "MWAIT 0x20", @@ -682,7 +682,7 @@ static struct cpuidle_state skx_cstates[] = { .exit_latency = 133, .target_residency = 600, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .enter = NULL } }; @@ -695,7 +695,7 @@ static struct cpuidle_state atom_cstates[] = { .exit_latency = 10, .target_residency = 20, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C2", .desc = "MWAIT 0x10", @@ -703,7 +703,7 @@ static struct cpuidle_state atom_cstates[] = { .exit_latency = 20, .target_residency = 80, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C4", .desc = "MWAIT 0x30", @@ -711,7 +711,7 @@ static struct cpuidle_state atom_cstates[] = { .exit_latency = 100, .target_residency = 400, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6", .desc = "MWAIT 0x52", @@ -719,7 +719,7 @@ static struct cpuidle_state atom_cstates[] = { .exit_latency = 140, .target_residency = 560, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .enter = NULL } }; @@ -731,7 +731,7 @@ static struct cpuidle_state tangier_cstates[] = { .exit_latency = 1, .target_residency = 4, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C4", .desc = "MWAIT 0x30", @@ -739,7 +739,7 @@ static struct cpuidle_state tangier_cstates[] = { .exit_latency = 100, .target_residency = 400, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6", .desc = "MWAIT 0x52", @@ -747,7 +747,7 @@ static struct cpuidle_state tangier_cstates[] = { .exit_latency = 140, .target_residency = 560, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C7", .desc = "MWAIT 0x60", @@ -755,7 +755,7 @@ static struct cpuidle_state tangier_cstates[] = { .exit_latency = 1200, .target_residency = 4000, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C9", .desc = "MWAIT 0x64", @@ -763,7 +763,7 @@ static struct cpuidle_state tangier_cstates[] = { .exit_latency = 10000, .target_residency = 20000, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .enter = NULL } }; @@ -775,7 +775,7 @@ static struct cpuidle_state avn_cstates[] = { .exit_latency = 2, .target_residency = 2, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6", .desc = "MWAIT 0x51", @@ -783,7 +783,7 @@ static struct cpuidle_state avn_cstates[] = { .exit_latency = 15, .target_residency = 45, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .enter = NULL } }; @@ -795,7 +795,7 @@ static struct cpuidle_state knl_cstates[] = { .exit_latency = 1, .target_residency = 2, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze }, + .enter_s2idle = intel_idle_s2idle }, { .name = "C6", .desc = "MWAIT 0x10", @@ -803,7 +803,7 @@ static struct cpuidle_state knl_cstates[] = { .exit_latency = 120, .target_residency = 500, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze }, + .enter_s2idle = intel_idle_s2idle }, { .enter = NULL } }; @@ -816,7 +816,7 @@ static struct cpuidle_state bxt_cstates[] = { .exit_latency = 2, .target_residency = 2, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C1E", .desc = "MWAIT 0x01", @@ -824,7 +824,7 @@ static struct cpuidle_state bxt_cstates[] = { .exit_latency = 10, .target_residency = 20, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6", .desc = "MWAIT 0x20", @@ -832,7 +832,7 @@ static struct cpuidle_state bxt_cstates[] = { .exit_latency = 133, .target_residency = 133, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C7s", .desc = "MWAIT 0x31", @@ -840,7 +840,7 @@ static struct cpuidle_state bxt_cstates[] = { .exit_latency = 155, .target_residency = 155, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C8", .desc = "MWAIT 0x40", @@ -848,7 +848,7 @@ static struct cpuidle_state bxt_cstates[] = { .exit_latency = 1000, .target_residency = 1000, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C9", .desc = "MWAIT 0x50", @@ -856,7 +856,7 @@ static struct cpuidle_state bxt_cstates[] = { .exit_latency = 2000, .target_residency = 2000, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C10", .desc = "MWAIT 0x60", @@ -864,7 +864,7 @@ static struct cpuidle_state bxt_cstates[] = { .exit_latency = 10000, .target_residency = 10000, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .enter = NULL } }; @@ -877,7 +877,7 @@ static struct cpuidle_state dnv_cstates[] = { .exit_latency = 2, .target_residency = 2, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C1E", .desc = "MWAIT 0x01", @@ -885,7 +885,7 @@ static struct cpuidle_state dnv_cstates[] = { .exit_latency = 10, .target_residency = 20, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .name = "C6", .desc = "MWAIT 0x20", @@ -893,7 +893,7 @@ static struct cpuidle_state dnv_cstates[] = { .exit_latency = 50, .target_residency = 500, .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, + .enter_s2idle = intel_idle_s2idle, }, { .enter = NULL } }; @@ -935,12 +935,12 @@ static __cpuidle int intel_idle(struct cpuidle_device *dev, } /** - * intel_idle_freeze - simplified "enter" callback routine for suspend-to-idle + * intel_idle_s2idle - simplified "enter" callback routine for suspend-to-idle * @dev: cpuidle_device * @drv: cpuidle driver * @index: state index */ -static void intel_idle_freeze(struct cpuidle_device *dev, +static void intel_idle_s2idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { unsigned long ecx = 1; /* break on interrupt flag */ @@ -1330,13 +1330,14 @@ static void __init intel_idle_cpuidle_driver_init(void) intel_idle_state_table_update(); + cpuidle_poll_state_init(drv); drv->state_count = 1; for (cstate = 0; cstate < CPUIDLE_STATE_MAX; ++cstate) { int num_substates, mwait_hint, mwait_cstate; if ((cpuidle_state_table[cstate].enter == NULL) && - (cpuidle_state_table[cstate].enter_freeze == NULL)) + (cpuidle_state_table[cstate].enter_s2idle == NULL)) break; if (cstate + 1 > max_cstate) { diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 5c739ac752e8..5970b8def548 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -33,7 +33,6 @@ #include <linux/mfd/abx500/ab8500.h> #include <linux/regulator/db8500-prcmu.h> #include <linux/regulator/machine.h> -#include <linux/cpufreq.h> #include <linux/platform_data/ux500_wdt.h> #include <linux/platform_data/db8500_thermal.h> #include "dbx500-prcmu-regs.h" @@ -1692,32 +1691,27 @@ static long round_clock_rate(u8 clock, unsigned long rate) return rounded_rate; } -/* CPU FREQ table, may be changed due to if MAX_OPP is supported. */ -static struct cpufreq_frequency_table db8500_cpufreq_table[] = { - { .frequency = 200000, .driver_data = ARM_EXTCLK,}, - { .frequency = 400000, .driver_data = ARM_50_OPP,}, - { .frequency = 800000, .driver_data = ARM_100_OPP,}, - { .frequency = CPUFREQ_TABLE_END,}, /* To be used for MAX_OPP. */ - { .frequency = CPUFREQ_TABLE_END,}, +static const unsigned long armss_freqs[] = { + 200000000, + 400000000, + 800000000, + 998400000 }; static long round_armss_rate(unsigned long rate) { - struct cpufreq_frequency_table *pos; - long freq = 0; - - /* cpufreq table frequencies is in KHz. */ - rate = rate / 1000; + unsigned long freq = 0; + int i; /* Find the corresponding arm opp from the cpufreq table. */ - cpufreq_for_each_entry(pos, db8500_cpufreq_table) { - freq = pos->frequency; - if (freq == rate) + for (i = 0; i < ARRAY_SIZE(armss_freqs); i++) { + freq = armss_freqs[i]; + if (rate <= freq) break; } /* Return the last valid value, even if a match was not found. */ - return freq * 1000; + return freq; } #define MIN_PLL_VCO_RATE 600000000ULL @@ -1854,21 +1848,23 @@ static void set_clock_rate(u8 clock, unsigned long rate) static int set_armss_rate(unsigned long rate) { - struct cpufreq_frequency_table *pos; - - /* cpufreq table frequencies is in KHz. */ - rate = rate / 1000; + unsigned long freq; + u8 opps[] = { ARM_EXTCLK, ARM_50_OPP, ARM_100_OPP, ARM_MAX_OPP }; + int i; /* Find the corresponding arm opp from the cpufreq table. */ - cpufreq_for_each_entry(pos, db8500_cpufreq_table) - if (pos->frequency == rate) + for (i = 0; i < ARRAY_SIZE(armss_freqs); i++) { + freq = armss_freqs[i]; + if (rate == freq) break; + } - if (pos->frequency != rate) + if (rate != freq) return -EINVAL; /* Set the new arm opp. */ - return db8500_prcmu_set_arm_opp(pos->driver_data); + pr_debug("SET ARM OPP 0x%02x\n", opps[i]); + return db8500_prcmu_set_arm_opp(opps[i]); } static int set_plldsi_rate(unsigned long rate) @@ -3049,12 +3045,6 @@ static const struct mfd_cell db8500_prcmu_devs[] = { .pdata_size = sizeof(db8500_regulators), }, { - .name = "cpufreq-ux500", - .of_compatible = "stericsson,cpufreq-ux500", - .platform_data = &db8500_cpufreq_table, - .pdata_size = sizeof(db8500_cpufreq_table), - }, - { .name = "cpuidle-dbx500", .of_compatible = "stericsson,cpuidle-dbx500", }, @@ -3067,14 +3057,6 @@ static const struct mfd_cell db8500_prcmu_devs[] = { }, }; -static void db8500_prcmu_update_cpufreq(void) -{ - if (prcmu_has_arm_maxopp()) { - db8500_cpufreq_table[3].frequency = 1000000; - db8500_cpufreq_table[3].driver_data = ARM_MAX_OPP; - } -} - static int db8500_prcmu_register_ab8500(struct device *parent) { struct device_node *np; @@ -3160,8 +3142,6 @@ static int db8500_prcmu_probe(struct platform_device *pdev) prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET); - db8500_prcmu_update_cpufreq(); - err = mfd_add_devices(&pdev->dev, 0, common_prcmu_devs, ARRAY_SIZE(common_prcmu_devs), NULL, 0, db8500_irq_domain); if (err) { diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index 8519e0f97bdd..a782c78e7c63 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c @@ -203,15 +203,26 @@ static void notify_handler(acpi_handle handle, u32 event, void *context) acpi_status status; if (priv->wakeup_mode) { + /* + * Needed for wakeup from suspend-to-idle to work on some + * platforms that don't expose the 5-button array, but still + * send notifies with the power button event code to this + * device object on power button actions while suspended. + */ + if (event == 0xce) + goto wakeup; + /* Wake up on 5-button array events only. */ if (event == 0xc0 || !priv->array) return; - if (sparse_keymap_entry_from_scancode(priv->array, event)) - pm_wakeup_hard_event(&device->dev); - else + if (!sparse_keymap_entry_from_scancode(priv->array, event)) { dev_info(&device->dev, "unknown event 0x%x\n", event); + return; + } +wakeup: + pm_wakeup_hard_event(&device->dev); return; } diff --git a/drivers/power/avs/rockchip-io-domain.c b/drivers/power/avs/rockchip-io-domain.c index 031a34372191..75f63e38a8d1 100644 --- a/drivers/power/avs/rockchip-io-domain.c +++ b/drivers/power/avs/rockchip-io-domain.c @@ -349,6 +349,36 @@ static const struct rockchip_iodomain_soc_data soc_data_rk3399_pmu = { .init = rk3399_pmu_iodomain_init, }; +static const struct rockchip_iodomain_soc_data soc_data_rv1108 = { + .grf_offset = 0x404, + .supply_names = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "vccio1", + "vccio2", + "vccio3", + "vccio5", + "vccio6", + }, + +}; + +static const struct rockchip_iodomain_soc_data soc_data_rv1108_pmu = { + .grf_offset = 0x104, + .supply_names = { + "pmu", + }, +}; + static const struct of_device_id rockchip_iodomain_match[] = { { .compatible = "rockchip,rk3188-io-voltage-domain", @@ -382,6 +412,14 @@ static const struct of_device_id rockchip_iodomain_match[] = { .compatible = "rockchip,rk3399-pmu-io-voltage-domain", .data = (void *)&soc_data_rk3399_pmu }, + { + .compatible = "rockchip,rv1108-io-voltage-domain", + .data = (void *)&soc_data_rv1108 + }, + { + .compatible = "rockchip,rv1108-pmu-io-voltage-domain", + .data = (void *)&soc_data_rv1108_pmu + }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, rockchip_iodomain_match); diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 9dd44dd4cdf6..14637a01ba2d 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -150,7 +150,7 @@ static void of_get_regulation_constraints(struct device_node *np, suspend_state = &constraints->state_disk; break; case PM_SUSPEND_ON: - case PM_SUSPEND_FREEZE: + case PM_SUSPEND_TO_IDLE: case PM_SUSPEND_STANDBY: default: continue; diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index f10a9b3761cd..537ff842ff73 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -127,6 +127,15 @@ struct cpufreq_policy { */ unsigned int transition_delay_us; + /* + * Remote DVFS flag (Not added to the driver structure as we don't want + * to access another structure from scheduler hotpath). + * + * Should be set if CPUs can do DVFS on behalf of other CPUs from + * different cpufreq policies. + */ + bool dvfs_possible_from_any_cpu; + /* Cached frequency lookup from cpufreq_driver_resolve_freq. */ unsigned int cached_target_freq; int cached_resolved_idx; @@ -370,6 +379,12 @@ struct cpufreq_driver { */ #define CPUFREQ_NEED_INITIAL_FREQ_CHECK (1 << 5) +/* + * Set by drivers to disallow use of governors with "dynamic_switching" flag + * set. + */ +#define CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING (1 << 6) + int cpufreq_register_driver(struct cpufreq_driver *driver_data); int cpufreq_unregister_driver(struct cpufreq_driver *driver_data); @@ -487,14 +502,8 @@ static inline unsigned long cpufreq_scale(unsigned long old, u_int div, * polling frequency is 1000 times the transition latency of the processor. The * ondemand governor will work on any processor with transition latency <= 10ms, * using appropriate sampling rate. - * - * For CPUs with transition latency > 10ms (mostly drivers with CPUFREQ_ETERNAL) - * the ondemand governor will not work. All times here are in us (microseconds). */ -#define MIN_SAMPLING_RATE_RATIO (2) #define LATENCY_MULTIPLIER (1000) -#define MIN_LATENCY_MULTIPLIER (20) -#define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000) struct cpufreq_governor { char name[CPUFREQ_NAME_LEN]; @@ -507,9 +516,8 @@ struct cpufreq_governor { char *buf); int (*store_setspeed) (struct cpufreq_policy *policy, unsigned int freq); - unsigned int max_transition_latency; /* HW must be able to switch to - next freq faster than this value in nano secs or we - will fallback to performance governor */ + /* For governors which change frequency dynamically by themselves */ + bool dynamic_switching; struct list_head governor_list; struct module *owner; }; @@ -525,6 +533,7 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, unsigned int relation); unsigned int cpufreq_driver_resolve_freq(struct cpufreq_policy *policy, unsigned int target_freq); +unsigned int cpufreq_policy_transition_delay_us(struct cpufreq_policy *policy); int cpufreq_register_governor(struct cpufreq_governor *governor); void cpufreq_unregister_governor(struct cpufreq_governor *governor); @@ -562,6 +571,17 @@ struct governor_attr { size_t count); }; +static inline bool cpufreq_can_do_remote_dvfs(struct cpufreq_policy *policy) +{ + /* + * Allow remote callbacks if: + * - dvfs_possible_from_any_cpu flag is set + * - the local and remote CPUs share cpufreq policy + */ + return policy->dvfs_possible_from_any_cpu || + cpumask_test_cpu(smp_processor_id(), policy->cpus); +} + /********************************************************************* * FREQUENCY TABLE HELPERS * *********************************************************************/ diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index fc1e5d7fc1c7..8f7788d23b57 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -52,17 +52,18 @@ struct cpuidle_state { int (*enter_dead) (struct cpuidle_device *dev, int index); /* - * CPUs execute ->enter_freeze with the local tick or entire timekeeping + * CPUs execute ->enter_s2idle with the local tick or entire timekeeping * suspended, so it must not re-enable interrupts at any point (even * temporarily) or attempt to change states of clock event devices. */ - void (*enter_freeze) (struct cpuidle_device *dev, + void (*enter_s2idle) (struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); }; /* Idle State Flags */ #define CPUIDLE_FLAG_NONE (0x00) +#define CPUIDLE_FLAG_POLLING (0x01) /* polling state */ #define CPUIDLE_FLAG_COUPLED (0x02) /* state applies to multiple cpus */ #define CPUIDLE_FLAG_TIMER_STOP (0x04) /* timer is stopped on this state */ @@ -197,14 +198,14 @@ static inline struct cpuidle_device *cpuidle_get_device(void) {return NULL; } #ifdef CONFIG_CPU_IDLE extern int cpuidle_find_deepest_state(struct cpuidle_driver *drv, struct cpuidle_device *dev); -extern int cpuidle_enter_freeze(struct cpuidle_driver *drv, +extern int cpuidle_enter_s2idle(struct cpuidle_driver *drv, struct cpuidle_device *dev); extern void cpuidle_use_deepest_state(bool enable); #else static inline int cpuidle_find_deepest_state(struct cpuidle_driver *drv, struct cpuidle_device *dev) {return -ENODEV; } -static inline int cpuidle_enter_freeze(struct cpuidle_driver *drv, +static inline int cpuidle_enter_s2idle(struct cpuidle_driver *drv, struct cpuidle_device *dev) {return -ENODEV; } static inline void cpuidle_use_deepest_state(bool enable) @@ -224,6 +225,12 @@ static inline void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, } #endif +#ifdef CONFIG_ARCH_HAS_CPU_RELAX +void cpuidle_poll_state_init(struct cpuidle_driver *drv); +#else +static inline void cpuidle_poll_state_init(struct cpuidle_driver *drv) {} +#endif + /****************************** * CPUIDLE GOVERNOR INTERFACE * ******************************/ @@ -250,12 +257,6 @@ static inline int cpuidle_register_governor(struct cpuidle_governor *gov) {return 0;} #endif -#ifdef CONFIG_ARCH_HAS_CPU_RELAX -#define CPUIDLE_DRIVER_STATE_START 1 -#else -#define CPUIDLE_DRIVER_STATE_START 0 -#endif - #define CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx) \ ({ \ int __ret; \ diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 6c220e4ebb6b..597294e0cc40 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -214,19 +214,6 @@ extern void devm_devfreq_unregister_notifier(struct device *dev, extern struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, int index); -/** - * devfreq_update_stats() - update the last_status pointer in struct devfreq - * @df: the devfreq instance whose status needs updating - * - * Governors are recommended to use this function along with last_status, - * which allows other entities to reuse the last_status without affecting - * the values fetched later by governors. - */ -static inline int devfreq_update_stats(struct devfreq *df) -{ - return df->profile->get_dev_status(df->dev.parent, &df->last_status); -} - #if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) /** * struct devfreq_simple_ondemand_data - void *data fed to struct devfreq diff --git a/include/linux/pm.h b/include/linux/pm.h index b8b4df09fd8f..47ded8aa8a5d 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -689,6 +689,8 @@ struct dev_pm_domain { extern void device_pm_lock(void); extern void dpm_resume_start(pm_message_t state); extern void dpm_resume_end(pm_message_t state); +extern void dpm_noirq_resume_devices(pm_message_t state); +extern void dpm_noirq_end(void); extern void dpm_resume_noirq(pm_message_t state); extern void dpm_resume_early(pm_message_t state); extern void dpm_resume(pm_message_t state); @@ -697,6 +699,8 @@ extern void dpm_complete(pm_message_t state); extern void device_pm_unlock(void); extern int dpm_suspend_end(pm_message_t state); extern int dpm_suspend_start(pm_message_t state); +extern void dpm_noirq_begin(void); +extern int dpm_noirq_suspend_devices(pm_message_t state); extern int dpm_suspend_noirq(pm_message_t state); extern int dpm_suspend_late(pm_message_t state); extern int dpm_suspend(pm_message_t state); diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 41004d97cefa..84f423d5633e 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -43,6 +43,7 @@ struct genpd_power_state { s64 power_on_latency_ns; s64 residency_ns; struct fwnode_handle *fwnode; + ktime_t idle_time; }; struct genpd_lock_ops; @@ -78,6 +79,8 @@ struct generic_pm_domain { unsigned int state_count; /* number of states */ unsigned int state_idx; /* state that genpd will go to when off */ void *free; /* Free the state that was allocated for default */ + ktime_t on_time; + ktime_t accounting_time; const struct genpd_lock_ops *lock_ops; union { struct mutex mlock; diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 0b1cf32edfd7..d10b7980799d 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -33,10 +33,10 @@ static inline void pm_restore_console(void) typedef int __bitwise suspend_state_t; #define PM_SUSPEND_ON ((__force suspend_state_t) 0) -#define PM_SUSPEND_FREEZE ((__force suspend_state_t) 1) +#define PM_SUSPEND_TO_IDLE ((__force suspend_state_t) 1) #define PM_SUSPEND_STANDBY ((__force suspend_state_t) 2) #define PM_SUSPEND_MEM ((__force suspend_state_t) 3) -#define PM_SUSPEND_MIN PM_SUSPEND_FREEZE +#define PM_SUSPEND_MIN PM_SUSPEND_TO_IDLE #define PM_SUSPEND_MAX ((__force suspend_state_t) 4) enum suspend_stat_step { @@ -186,7 +186,7 @@ struct platform_suspend_ops { void (*recover)(void); }; -struct platform_freeze_ops { +struct platform_s2idle_ops { int (*begin)(void); int (*prepare)(void); void (*wake)(void); @@ -196,6 +196,9 @@ struct platform_freeze_ops { }; #ifdef CONFIG_SUSPEND +extern suspend_state_t mem_sleep_current; +extern suspend_state_t mem_sleep_default; + /** * suspend_set_ops - set platform dependent suspend operations * @ops: The new suspend operations to set. @@ -234,22 +237,22 @@ static inline bool pm_resume_via_firmware(void) } /* Suspend-to-idle state machnine. */ -enum freeze_state { - FREEZE_STATE_NONE, /* Not suspended/suspending. */ - FREEZE_STATE_ENTER, /* Enter suspend-to-idle. */ - FREEZE_STATE_WAKE, /* Wake up from suspend-to-idle. */ +enum s2idle_states { + S2IDLE_STATE_NONE, /* Not suspended/suspending. */ + S2IDLE_STATE_ENTER, /* Enter suspend-to-idle. */ + S2IDLE_STATE_WAKE, /* Wake up from suspend-to-idle. */ }; -extern enum freeze_state __read_mostly suspend_freeze_state; +extern enum s2idle_states __read_mostly s2idle_state; -static inline bool idle_should_freeze(void) +static inline bool idle_should_enter_s2idle(void) { - return unlikely(suspend_freeze_state == FREEZE_STATE_ENTER); + return unlikely(s2idle_state == S2IDLE_STATE_ENTER); } extern void __init pm_states_init(void); -extern void freeze_set_ops(const struct platform_freeze_ops *ops); -extern void freeze_wake(void); +extern void s2idle_set_ops(const struct platform_s2idle_ops *ops); +extern void s2idle_wake(void); /** * arch_suspend_disable_irqs - disable IRQs for suspend @@ -281,10 +284,10 @@ static inline bool pm_resume_via_firmware(void) { return false; } static inline void suspend_set_ops(const struct platform_suspend_ops *ops) {} static inline int pm_suspend(suspend_state_t state) { return -ENOSYS; } -static inline bool idle_should_freeze(void) { return false; } +static inline bool idle_should_enter_s2idle(void) { return false; } static inline void __init pm_states_init(void) {} -static inline void freeze_set_ops(const struct platform_freeze_ops *ops) {} -static inline void freeze_wake(void) {} +static inline void s2idle_set_ops(const struct platform_s2idle_ops *ops) {} +static inline void s2idle_wake(void) {} #endif /* !CONFIG_SUSPEND */ /* struct pbe is used for creating lists of pages that should be restored @@ -427,6 +430,7 @@ extern int unregister_pm_notifier(struct notifier_block *nb); /* drivers/base/power/wakeup.c */ extern bool events_check_enabled; extern unsigned int pm_wakeup_irq; +extern suspend_state_t pm_suspend_target_state; extern bool pm_wakeup_pending(void); extern void pm_system_wakeup(void); @@ -491,10 +495,24 @@ static inline void unlock_system_sleep(void) {} #ifdef CONFIG_PM_SLEEP_DEBUG extern bool pm_print_times_enabled; +extern bool pm_debug_messages_on; +extern __printf(2, 3) void __pm_pr_dbg(bool defer, const char *fmt, ...); #else #define pm_print_times_enabled (false) +#define pm_debug_messages_on (false) + +#include <linux/printk.h> + +#define __pm_pr_dbg(defer, fmt, ...) \ + no_printk(KERN_DEBUG fmt, ##__VA_ARGS__) #endif +#define pm_pr_dbg(fmt, ...) \ + __pm_pr_dbg(false, fmt, ##__VA_ARGS__) + +#define pm_deferred_pr_dbg(fmt, ...) \ + __pm_pr_dbg(true, fmt, ##__VA_ARGS__) + #ifdef CONFIG_PM_AUTOSLEEP /* kernel/power/autosleep.c */ diff --git a/kernel/cpu_pm.c b/kernel/cpu_pm.c index 009cc9a17d95..67b02e138a47 100644 --- a/kernel/cpu_pm.c +++ b/kernel/cpu_pm.c @@ -22,15 +22,21 @@ #include <linux/spinlock.h> #include <linux/syscore_ops.h> -static DEFINE_RWLOCK(cpu_pm_notifier_lock); -static RAW_NOTIFIER_HEAD(cpu_pm_notifier_chain); +static ATOMIC_NOTIFIER_HEAD(cpu_pm_notifier_chain); static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls) { int ret; - ret = __raw_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL, + /* + * __atomic_notifier_call_chain has a RCU read critical section, which + * could be disfunctional in cpu idle. Copy RCU_NONIDLE code to let + * RCU know this. + */ + rcu_irq_enter_irqson(); + ret = __atomic_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL, nr_to_call, nr_calls); + rcu_irq_exit_irqson(); return notifier_to_errno(ret); } @@ -47,14 +53,7 @@ static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls) */ int cpu_pm_register_notifier(struct notifier_block *nb) { - unsigned long flags; - int ret; - - write_lock_irqsave(&cpu_pm_notifier_lock, flags); - ret = raw_notifier_chain_register(&cpu_pm_notifier_chain, nb); - write_unlock_irqrestore(&cpu_pm_notifier_lock, flags); - - return ret; + return atomic_notifier_chain_register(&cpu_pm_notifier_chain, nb); } EXPORT_SYMBOL_GPL(cpu_pm_register_notifier); @@ -69,14 +68,7 @@ EXPORT_SYMBOL_GPL(cpu_pm_register_notifier); */ int cpu_pm_unregister_notifier(struct notifier_block *nb) { - unsigned long flags; - int ret; - - write_lock_irqsave(&cpu_pm_notifier_lock, flags); - ret = raw_notifier_chain_unregister(&cpu_pm_notifier_chain, nb); - write_unlock_irqrestore(&cpu_pm_notifier_lock, flags); - - return ret; + return atomic_notifier_chain_unregister(&cpu_pm_notifier_chain, nb); } EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier); @@ -100,7 +92,6 @@ int cpu_pm_enter(void) int nr_calls; int ret = 0; - read_lock(&cpu_pm_notifier_lock); ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls); if (ret) /* @@ -108,7 +99,6 @@ int cpu_pm_enter(void) * PM entry who are notified earlier to prepare for it. */ cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL); - read_unlock(&cpu_pm_notifier_lock); return ret; } @@ -128,13 +118,7 @@ EXPORT_SYMBOL_GPL(cpu_pm_enter); */ int cpu_pm_exit(void) { - int ret; - - read_lock(&cpu_pm_notifier_lock); - ret = cpu_pm_notify(CPU_PM_EXIT, -1, NULL); - read_unlock(&cpu_pm_notifier_lock); - - return ret; + return cpu_pm_notify(CPU_PM_EXIT, -1, NULL); } EXPORT_SYMBOL_GPL(cpu_pm_exit); @@ -159,7 +143,6 @@ int cpu_cluster_pm_enter(void) int nr_calls; int ret = 0; - read_lock(&cpu_pm_notifier_lock); ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls); if (ret) /* @@ -167,7 +150,6 @@ int cpu_cluster_pm_enter(void) * PM entry who are notified earlier to prepare for it. */ cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL); - read_unlock(&cpu_pm_notifier_lock); return ret; } @@ -190,13 +172,7 @@ EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter); */ int cpu_cluster_pm_exit(void) { - int ret; - - read_lock(&cpu_pm_notifier_lock); - ret = cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL); - read_unlock(&cpu_pm_notifier_lock); - - return ret; + return cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL); } EXPORT_SYMBOL_GPL(cpu_cluster_pm_exit); diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index e1914c7b85b1..a5c36e9c56a6 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -651,7 +651,7 @@ static int load_image_and_restore(void) int error; unsigned int flags; - pr_debug("Loading hibernation image.\n"); + pm_pr_dbg("Loading hibernation image.\n"); lock_device_hotplug(); error = create_basic_memory_bitmaps(); @@ -681,7 +681,7 @@ int hibernate(void) bool snapshot_test = false; if (!hibernation_available()) { - pr_debug("Hibernation not available.\n"); + pm_pr_dbg("Hibernation not available.\n"); return -EPERM; } @@ -692,6 +692,7 @@ int hibernate(void) goto Unlock; } + pr_info("hibernation entry\n"); pm_prepare_console(); error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls); if (error) { @@ -727,7 +728,7 @@ int hibernate(void) else flags |= SF_CRC32_MODE; - pr_debug("Writing image.\n"); + pm_pr_dbg("Writing image.\n"); error = swsusp_write(flags); swsusp_free(); if (!error) { @@ -739,7 +740,7 @@ int hibernate(void) in_suspend = 0; pm_restore_gfp_mask(); } else { - pr_debug("Image restored successfully.\n"); + pm_pr_dbg("Image restored successfully.\n"); } Free_bitmaps: @@ -747,7 +748,7 @@ int hibernate(void) Thaw: unlock_device_hotplug(); if (snapshot_test) { - pr_debug("Checking hibernation image\n"); + pm_pr_dbg("Checking hibernation image\n"); error = swsusp_check(); if (!error) error = load_image_and_restore(); @@ -762,6 +763,8 @@ int hibernate(void) atomic_inc(&snapshot_device_available); Unlock: unlock_system_sleep(); + pr_info("hibernation exit\n"); + return error; } @@ -811,7 +814,7 @@ static int software_resume(void) goto Unlock; } - pr_debug("Checking hibernation image partition %s\n", resume_file); + pm_pr_dbg("Checking hibernation image partition %s\n", resume_file); if (resume_delay) { pr_info("Waiting %dsec before reading resume device ...\n", @@ -853,10 +856,10 @@ static int software_resume(void) } Check_image: - pr_debug("Hibernation image partition %d:%d present\n", + pm_pr_dbg("Hibernation image partition %d:%d present\n", MAJOR(swsusp_resume_device), MINOR(swsusp_resume_device)); - pr_debug("Looking for hibernation image.\n"); + pm_pr_dbg("Looking for hibernation image.\n"); error = swsusp_check(); if (error) goto Unlock; @@ -868,6 +871,7 @@ static int software_resume(void) goto Unlock; } + pr_info("resume from hibernation\n"); pm_prepare_console(); error = __pm_notifier_call_chain(PM_RESTORE_PREPARE, -1, &nr_calls); if (error) { @@ -875,7 +879,7 @@ static int software_resume(void) goto Close_Finish; } - pr_debug("Preparing processes for restore.\n"); + pm_pr_dbg("Preparing processes for restore.\n"); error = freeze_processes(); if (error) goto Close_Finish; @@ -884,11 +888,12 @@ static int software_resume(void) Finish: __pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL); pm_restore_console(); + pr_info("resume from hibernation failed (%d)\n", error); atomic_inc(&snapshot_device_available); /* For success case, the suspend path will release the lock */ Unlock: mutex_unlock(&pm_mutex); - pr_debug("Hibernation image not present or could not be loaded.\n"); + pm_pr_dbg("Hibernation image not present or could not be loaded.\n"); return error; Close_Finish: swsusp_close(FMODE_READ); @@ -1012,8 +1017,8 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr, error = -EINVAL; if (!error) - pr_debug("Hibernation mode set to '%s'\n", - hibernation_modes[mode]); + pm_pr_dbg("Hibernation mode set to '%s'\n", + hibernation_modes[mode]); unlock_system_sleep(); return error ? error : n; } diff --git a/kernel/power/main.c b/kernel/power/main.c index 42bd800a6755..3a2ca9066583 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -150,7 +150,7 @@ static ssize_t mem_sleep_store(struct kobject *kobj, struct kobj_attribute *attr power_attr(mem_sleep); #endif /* CONFIG_SUSPEND */ -#ifdef CONFIG_PM_DEBUG +#ifdef CONFIG_PM_SLEEP_DEBUG int pm_test_level = TEST_NONE; static const char * const pm_tests[__TEST_AFTER_LAST] = { @@ -211,7 +211,7 @@ static ssize_t pm_test_store(struct kobject *kobj, struct kobj_attribute *attr, } power_attr(pm_test); -#endif /* CONFIG_PM_DEBUG */ +#endif /* CONFIG_PM_SLEEP_DEBUG */ #ifdef CONFIG_DEBUG_FS static char *suspend_step_name(enum suspend_stat_step step) @@ -361,6 +361,61 @@ static ssize_t pm_wakeup_irq_show(struct kobject *kobj, power_attr_ro(pm_wakeup_irq); +bool pm_debug_messages_on __read_mostly; + +static ssize_t pm_debug_messages_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", pm_debug_messages_on); +} + +static ssize_t pm_debug_messages_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + if (val > 1) + return -EINVAL; + + pm_debug_messages_on = !!val; + return n; +} + +power_attr(pm_debug_messages); + +/** + * __pm_pr_dbg - Print a suspend debug message to the kernel log. + * @defer: Whether or not to use printk_deferred() to print the message. + * @fmt: Message format. + * + * The message will be emitted if enabled through the pm_debug_messages + * sysfs attribute. + */ +void __pm_pr_dbg(bool defer, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (!pm_debug_messages_on) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + if (defer) + printk_deferred(KERN_DEBUG "PM: %pV", &vaf); + else + printk(KERN_DEBUG "PM: %pV", &vaf); + + va_end(args); +} + #else /* !CONFIG_PM_SLEEP_DEBUG */ static inline void pm_print_times_init(void) {} #endif /* CONFIG_PM_SLEEP_DEBUG */ @@ -691,12 +746,11 @@ static struct attribute * g[] = { &wake_lock_attr.attr, &wake_unlock_attr.attr, #endif -#ifdef CONFIG_PM_DEBUG - &pm_test_attr.attr, -#endif #ifdef CONFIG_PM_SLEEP_DEBUG + &pm_test_attr.attr, &pm_print_times_attr.attr, &pm_wakeup_irq_attr.attr, + &pm_debug_messages_attr.attr, #endif #endif #ifdef CONFIG_FREEZER diff --git a/kernel/power/power.h b/kernel/power/power.h index 7fdc40d31b7d..1d2d761e3c25 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -192,7 +192,6 @@ extern void swsusp_show_speed(ktime_t, ktime_t, unsigned int, char *); extern const char * const pm_labels[]; extern const char *pm_states[]; extern const char *mem_sleep_states[]; -extern suspend_state_t mem_sleep_current; extern int suspend_devices_and_enter(suspend_state_t state); #else /* !CONFIG_SUSPEND */ @@ -245,7 +244,11 @@ enum { #define TEST_FIRST TEST_NONE #define TEST_MAX (__TEST_AFTER_LAST - 1) +#ifdef CONFIG_PM_SLEEP_DEBUG extern int pm_test_level; +#else +#define pm_test_level (TEST_NONE) +#endif #ifdef CONFIG_SUSPEND_FREEZER static inline int suspend_freeze_processes(void) diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 3ecf275d7e44..3e2b4f519009 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -8,6 +8,8 @@ * This file is released under the GPLv2. */ +#define pr_fmt(fmt) "PM: " fmt + #include <linux/string.h> #include <linux/delay.h> #include <linux/errno.h> @@ -33,53 +35,55 @@ #include "power.h" const char * const pm_labels[] = { - [PM_SUSPEND_FREEZE] = "freeze", + [PM_SUSPEND_TO_IDLE] = "freeze", [PM_SUSPEND_STANDBY] = "standby", [PM_SUSPEND_MEM] = "mem", }; const char *pm_states[PM_SUSPEND_MAX]; static const char * const mem_sleep_labels[] = { - [PM_SUSPEND_FREEZE] = "s2idle", + [PM_SUSPEND_TO_IDLE] = "s2idle", [PM_SUSPEND_STANDBY] = "shallow", [PM_SUSPEND_MEM] = "deep", }; const char *mem_sleep_states[PM_SUSPEND_MAX]; -suspend_state_t mem_sleep_current = PM_SUSPEND_FREEZE; -static suspend_state_t mem_sleep_default = PM_SUSPEND_MEM; +suspend_state_t mem_sleep_current = PM_SUSPEND_TO_IDLE; +suspend_state_t mem_sleep_default = PM_SUSPEND_MAX; +suspend_state_t pm_suspend_target_state; +EXPORT_SYMBOL_GPL(pm_suspend_target_state); unsigned int pm_suspend_global_flags; EXPORT_SYMBOL_GPL(pm_suspend_global_flags); static const struct platform_suspend_ops *suspend_ops; -static const struct platform_freeze_ops *freeze_ops; -static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head); +static const struct platform_s2idle_ops *s2idle_ops; +static DECLARE_WAIT_QUEUE_HEAD(s2idle_wait_head); -enum freeze_state __read_mostly suspend_freeze_state; -static DEFINE_SPINLOCK(suspend_freeze_lock); +enum s2idle_states __read_mostly s2idle_state; +static DEFINE_SPINLOCK(s2idle_lock); -void freeze_set_ops(const struct platform_freeze_ops *ops) +void s2idle_set_ops(const struct platform_s2idle_ops *ops) { lock_system_sleep(); - freeze_ops = ops; + s2idle_ops = ops; unlock_system_sleep(); } -static void freeze_begin(void) +static void s2idle_begin(void) { - suspend_freeze_state = FREEZE_STATE_NONE; + s2idle_state = S2IDLE_STATE_NONE; } -static void freeze_enter(void) +static void s2idle_enter(void) { - trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_FREEZE, true); + trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_TO_IDLE, true); - spin_lock_irq(&suspend_freeze_lock); + spin_lock_irq(&s2idle_lock); if (pm_wakeup_pending()) goto out; - suspend_freeze_state = FREEZE_STATE_ENTER; - spin_unlock_irq(&suspend_freeze_lock); + s2idle_state = S2IDLE_STATE_ENTER; + spin_unlock_irq(&s2idle_lock); get_online_cpus(); cpuidle_resume(); @@ -87,56 +91,75 @@ static void freeze_enter(void) /* Push all the CPUs into the idle loop. */ wake_up_all_idle_cpus(); /* Make the current CPU wait so it can enter the idle loop too. */ - wait_event(suspend_freeze_wait_head, - suspend_freeze_state == FREEZE_STATE_WAKE); + wait_event(s2idle_wait_head, + s2idle_state == S2IDLE_STATE_WAKE); cpuidle_pause(); put_online_cpus(); - spin_lock_irq(&suspend_freeze_lock); + spin_lock_irq(&s2idle_lock); out: - suspend_freeze_state = FREEZE_STATE_NONE; - spin_unlock_irq(&suspend_freeze_lock); + s2idle_state = S2IDLE_STATE_NONE; + spin_unlock_irq(&s2idle_lock); - trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_FREEZE, false); + trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_TO_IDLE, false); } static void s2idle_loop(void) { - pr_debug("PM: suspend-to-idle\n"); + pm_pr_dbg("suspend-to-idle\n"); + + for (;;) { + int error; + + dpm_noirq_begin(); + + /* + * Suspend-to-idle equals + * frozen processes + suspended devices + idle processors. + * Thus s2idle_enter() should be called right after + * all devices have been suspended. + */ + error = dpm_noirq_suspend_devices(PMSG_SUSPEND); + if (!error) + s2idle_enter(); + + dpm_noirq_resume_devices(PMSG_RESUME); + if (error && (error != -EBUSY || !pm_wakeup_pending())) { + dpm_noirq_end(); + break; + } - do { - freeze_enter(); + if (s2idle_ops && s2idle_ops->wake) + s2idle_ops->wake(); - if (freeze_ops && freeze_ops->wake) - freeze_ops->wake(); + dpm_noirq_end(); - dpm_resume_noirq(PMSG_RESUME); - if (freeze_ops && freeze_ops->sync) - freeze_ops->sync(); + if (s2idle_ops && s2idle_ops->sync) + s2idle_ops->sync(); if (pm_wakeup_pending()) break; pm_wakeup_clear(false); - } while (!dpm_suspend_noirq(PMSG_SUSPEND)); + } - pr_debug("PM: resume from suspend-to-idle\n"); + pm_pr_dbg("resume from suspend-to-idle\n"); } -void freeze_wake(void) +void s2idle_wake(void) { unsigned long flags; - spin_lock_irqsave(&suspend_freeze_lock, flags); - if (suspend_freeze_state > FREEZE_STATE_NONE) { - suspend_freeze_state = FREEZE_STATE_WAKE; - wake_up(&suspend_freeze_wait_head); + spin_lock_irqsave(&s2idle_lock, flags); + if (s2idle_state > S2IDLE_STATE_NONE) { + s2idle_state = S2IDLE_STATE_WAKE; + wake_up(&s2idle_wait_head); } - spin_unlock_irqrestore(&suspend_freeze_lock, flags); + spin_unlock_irqrestore(&s2idle_lock, flags); } -EXPORT_SYMBOL_GPL(freeze_wake); +EXPORT_SYMBOL_GPL(s2idle_wake); static bool valid_state(suspend_state_t state) { @@ -152,19 +175,19 @@ void __init pm_states_init(void) { /* "mem" and "freeze" are always present in /sys/power/state. */ pm_states[PM_SUSPEND_MEM] = pm_labels[PM_SUSPEND_MEM]; - pm_states[PM_SUSPEND_FREEZE] = pm_labels[PM_SUSPEND_FREEZE]; + pm_states[PM_SUSPEND_TO_IDLE] = pm_labels[PM_SUSPEND_TO_IDLE]; /* * Suspend-to-idle should be supported even without any suspend_ops, * initialize mem_sleep_states[] accordingly here. */ - mem_sleep_states[PM_SUSPEND_FREEZE] = mem_sleep_labels[PM_SUSPEND_FREEZE]; + mem_sleep_states[PM_SUSPEND_TO_IDLE] = mem_sleep_labels[PM_SUSPEND_TO_IDLE]; } static int __init mem_sleep_default_setup(char *str) { suspend_state_t state; - for (state = PM_SUSPEND_FREEZE; state <= PM_SUSPEND_MEM; state++) + for (state = PM_SUSPEND_TO_IDLE; state <= PM_SUSPEND_MEM; state++) if (mem_sleep_labels[state] && !strcmp(str, mem_sleep_labels[state])) { mem_sleep_default = state; @@ -193,7 +216,7 @@ void suspend_set_ops(const struct platform_suspend_ops *ops) } if (valid_state(PM_SUSPEND_MEM)) { mem_sleep_states[PM_SUSPEND_MEM] = mem_sleep_labels[PM_SUSPEND_MEM]; - if (mem_sleep_default == PM_SUSPEND_MEM) + if (mem_sleep_default >= PM_SUSPEND_MEM) mem_sleep_current = PM_SUSPEND_MEM; } @@ -216,49 +239,49 @@ EXPORT_SYMBOL_GPL(suspend_valid_only_mem); static bool sleep_state_supported(suspend_state_t state) { - return state == PM_SUSPEND_FREEZE || (suspend_ops && suspend_ops->enter); + return state == PM_SUSPEND_TO_IDLE || (suspend_ops && suspend_ops->enter); } static int platform_suspend_prepare(suspend_state_t state) { - return state != PM_SUSPEND_FREEZE && suspend_ops->prepare ? + return state != PM_SUSPEND_TO_IDLE && suspend_ops->prepare ? suspend_ops->prepare() : 0; } static int platform_suspend_prepare_late(suspend_state_t state) { - return state == PM_SUSPEND_FREEZE && freeze_ops && freeze_ops->prepare ? - freeze_ops->prepare() : 0; + return state == PM_SUSPEND_TO_IDLE && s2idle_ops && s2idle_ops->prepare ? + s2idle_ops->prepare() : 0; } static int platform_suspend_prepare_noirq(suspend_state_t state) { - return state != PM_SUSPEND_FREEZE && suspend_ops->prepare_late ? + return state != PM_SUSPEND_TO_IDLE && suspend_ops->prepare_late ? suspend_ops->prepare_late() : 0; } static void platform_resume_noirq(suspend_state_t state) { - if (state != PM_SUSPEND_FREEZE && suspend_ops->wake) + if (state != PM_SUSPEND_TO_IDLE && suspend_ops->wake) suspend_ops->wake(); } static void platform_resume_early(suspend_state_t state) { - if (state == PM_SUSPEND_FREEZE && freeze_ops && freeze_ops->restore) - freeze_ops->restore(); + if (state == PM_SUSPEND_TO_IDLE && s2idle_ops && s2idle_ops->restore) + s2idle_ops->restore(); } static void platform_resume_finish(suspend_state_t state) { - if (state != PM_SUSPEND_FREEZE && suspend_ops->finish) + if (state != PM_SUSPEND_TO_IDLE && suspend_ops->finish) suspend_ops->finish(); } static int platform_suspend_begin(suspend_state_t state) { - if (state == PM_SUSPEND_FREEZE && freeze_ops && freeze_ops->begin) - return freeze_ops->begin(); + if (state == PM_SUSPEND_TO_IDLE && s2idle_ops && s2idle_ops->begin) + return s2idle_ops->begin(); else if (suspend_ops && suspend_ops->begin) return suspend_ops->begin(state); else @@ -267,21 +290,21 @@ static int platform_suspend_begin(suspend_state_t state) static void platform_resume_end(suspend_state_t state) { - if (state == PM_SUSPEND_FREEZE && freeze_ops && freeze_ops->end) - freeze_ops->end(); + if (state == PM_SUSPEND_TO_IDLE && s2idle_ops && s2idle_ops->end) + s2idle_ops->end(); else if (suspend_ops && suspend_ops->end) suspend_ops->end(); } static void platform_recover(suspend_state_t state) { - if (state != PM_SUSPEND_FREEZE && suspend_ops->recover) + if (state != PM_SUSPEND_TO_IDLE && suspend_ops->recover) suspend_ops->recover(); } static bool platform_suspend_again(suspend_state_t state) { - return state != PM_SUSPEND_FREEZE && suspend_ops->suspend_again ? + return state != PM_SUSPEND_TO_IDLE && suspend_ops->suspend_again ? suspend_ops->suspend_again() : false; } @@ -370,16 +393,21 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) error = dpm_suspend_late(PMSG_SUSPEND); if (error) { - pr_err("PM: late suspend of devices failed\n"); + pr_err("late suspend of devices failed\n"); goto Platform_finish; } error = platform_suspend_prepare_late(state); if (error) goto Devices_early_resume; + if (state == PM_SUSPEND_TO_IDLE && pm_test_level != TEST_PLATFORM) { + s2idle_loop(); + goto Platform_early_resume; + } + error = dpm_suspend_noirq(PMSG_SUSPEND); if (error) { - pr_err("PM: noirq suspend of devices failed\n"); + pr_err("noirq suspend of devices failed\n"); goto Platform_early_resume; } error = platform_suspend_prepare_noirq(state); @@ -389,17 +417,6 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) if (suspend_test(TEST_PLATFORM)) goto Platform_wake; - /* - * PM_SUSPEND_FREEZE equals - * frozen processes + suspended devices + idle processors. - * Thus we should invoke freeze_enter() soon after - * all the devices are suspended. - */ - if (state == PM_SUSPEND_FREEZE) { - s2idle_loop(); - goto Platform_early_resume; - } - error = disable_nonboot_cpus(); if (error || suspend_test(TEST_CPUS)) goto Enable_cpus; @@ -456,6 +473,8 @@ int suspend_devices_and_enter(suspend_state_t state) if (!sleep_state_supported(state)) return -ENOSYS; + pm_suspend_target_state = state; + error = platform_suspend_begin(state); if (error) goto Close; @@ -464,7 +483,7 @@ int suspend_devices_and_enter(suspend_state_t state) suspend_test_start(); error = dpm_suspend_start(PMSG_SUSPEND); if (error) { - pr_err("PM: Some devices failed to suspend, or early wake event detected\n"); + pr_err("Some devices failed to suspend, or early wake event detected\n"); goto Recover_platform; } suspend_test_finish("suspend devices"); @@ -485,6 +504,7 @@ int suspend_devices_and_enter(suspend_state_t state) Close: platform_resume_end(state); + pm_suspend_target_state = PM_SUSPEND_ON; return error; Recover_platform: @@ -518,10 +538,10 @@ static int enter_state(suspend_state_t state) int error; trace_suspend_resume(TPS("suspend_enter"), state, true); - if (state == PM_SUSPEND_FREEZE) { + if (state == PM_SUSPEND_TO_IDLE) { #ifdef CONFIG_PM_DEBUG if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) { - pr_warn("PM: Unsupported test mode for suspend to idle, please choose none/freezer/devices/platform.\n"); + pr_warn("Unsupported test mode for suspend to idle, please choose none/freezer/devices/platform.\n"); return -EAGAIN; } #endif @@ -531,18 +551,18 @@ static int enter_state(suspend_state_t state) if (!mutex_trylock(&pm_mutex)) return -EBUSY; - if (state == PM_SUSPEND_FREEZE) - freeze_begin(); + if (state == PM_SUSPEND_TO_IDLE) + s2idle_begin(); #ifndef CONFIG_SUSPEND_SKIP_SYNC trace_suspend_resume(TPS("sync_filesystems"), 0, true); - pr_info("PM: Syncing filesystems ... "); + pr_info("Syncing filesystems ... "); sys_sync(); pr_cont("done.\n"); trace_suspend_resume(TPS("sync_filesystems"), 0, false); #endif - pr_debug("PM: Preparing system for sleep (%s)\n", pm_states[state]); + pm_pr_dbg("Preparing system for sleep (%s)\n", mem_sleep_labels[state]); pm_suspend_clear_flags(); error = suspend_prepare(state); if (error) @@ -552,13 +572,13 @@ static int enter_state(suspend_state_t state) goto Finish; trace_suspend_resume(TPS("suspend_enter"), state, false); - pr_debug("PM: Suspending system (%s)\n", pm_states[state]); + pm_pr_dbg("Suspending system (%s)\n", mem_sleep_labels[state]); pm_restrict_gfp_mask(); error = suspend_devices_and_enter(state); pm_restore_gfp_mask(); Finish: - pr_debug("PM: Finishing wakeup.\n"); + pm_pr_dbg("Finishing wakeup.\n"); suspend_finish(); Unlock: mutex_unlock(&pm_mutex); @@ -579,6 +599,7 @@ int pm_suspend(suspend_state_t state) if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX) return -EINVAL; + pr_info("suspend entry (%s)\n", mem_sleep_labels[state]); error = enter_state(state); if (error) { suspend_stats.fail++; @@ -586,6 +607,7 @@ int pm_suspend(suspend_state_t state) } else { suspend_stats.success++; } + pr_info("suspend exit\n"); return error; } EXPORT_SYMBOL(pm_suspend); diff --git a/kernel/power/suspend_test.c b/kernel/power/suspend_test.c index 5db217051232..6a897e8b2a88 100644 --- a/kernel/power/suspend_test.c +++ b/kernel/power/suspend_test.c @@ -104,9 +104,9 @@ repeat: printk(info_test, pm_states[state]); status = pm_suspend(state); if (status < 0) - state = PM_SUSPEND_FREEZE; + state = PM_SUSPEND_TO_IDLE; } - if (state == PM_SUSPEND_FREEZE) { + if (state == PM_SUSPEND_TO_IDLE) { printk(info_test, pm_states[state]); status = pm_suspend(state); } diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index 29a397067ffa..9209d83ecdcf 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -52,9 +52,11 @@ struct sugov_policy { struct sugov_cpu { struct update_util_data update_util; struct sugov_policy *sg_policy; + unsigned int cpu; - unsigned long iowait_boost; - unsigned long iowait_boost_max; + bool iowait_boost_pending; + unsigned int iowait_boost; + unsigned int iowait_boost_max; u64 last_update; /* The fields below are only needed when sharing a policy. */ @@ -76,6 +78,26 @@ static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time) { s64 delta_ns; + /* + * Since cpufreq_update_util() is called with rq->lock held for + * the @target_cpu, our per-cpu data is fully serialized. + * + * However, drivers cannot in general deal with cross-cpu + * requests, so while get_next_freq() will work, our + * sugov_update_commit() call may not for the fast switching platforms. + * + * Hence stop here for remote requests if they aren't supported + * by the hardware, as calculating the frequency is pointless if + * we cannot in fact act on it. + * + * For the slow switching platforms, the kthread is always scheduled on + * the right set of CPUs and any CPU can find the next frequency and + * schedule the kthread. + */ + if (sg_policy->policy->fast_switch_enabled && + !cpufreq_can_do_remote_dvfs(sg_policy->policy)) + return false; + if (sg_policy->work_in_progress) return false; @@ -106,7 +128,7 @@ static void sugov_update_commit(struct sugov_policy *sg_policy, u64 time, if (policy->fast_switch_enabled) { next_freq = cpufreq_driver_fast_switch(policy, next_freq); - if (next_freq == CPUFREQ_ENTRY_INVALID) + if (!next_freq) return; policy->cur = next_freq; @@ -154,12 +176,12 @@ static unsigned int get_next_freq(struct sugov_policy *sg_policy, return cpufreq_driver_resolve_freq(policy, freq); } -static void sugov_get_util(unsigned long *util, unsigned long *max) +static void sugov_get_util(unsigned long *util, unsigned long *max, int cpu) { - struct rq *rq = this_rq(); + struct rq *rq = cpu_rq(cpu); unsigned long cfs_max; - cfs_max = arch_scale_cpu_capacity(NULL, smp_processor_id()); + cfs_max = arch_scale_cpu_capacity(NULL, cpu); *util = min(rq->cfs.avg.util_avg, cfs_max); *max = cfs_max; @@ -169,30 +191,54 @@ static void sugov_set_iowait_boost(struct sugov_cpu *sg_cpu, u64 time, unsigned int flags) { if (flags & SCHED_CPUFREQ_IOWAIT) { - sg_cpu->iowait_boost = sg_cpu->iowait_boost_max; + if (sg_cpu->iowait_boost_pending) + return; + + sg_cpu->iowait_boost_pending = true; + + if (sg_cpu->iowait_boost) { + sg_cpu->iowait_boost <<= 1; + if (sg_cpu->iowait_boost > sg_cpu->iowait_boost_max) + sg_cpu->iowait_boost = sg_cpu->iowait_boost_max; + } else { + sg_cpu->iowait_boost = sg_cpu->sg_policy->policy->min; + } } else if (sg_cpu->iowait_boost) { s64 delta_ns = time - sg_cpu->last_update; /* Clear iowait_boost if the CPU apprears to have been idle. */ - if (delta_ns > TICK_NSEC) + if (delta_ns > TICK_NSEC) { sg_cpu->iowait_boost = 0; + sg_cpu->iowait_boost_pending = false; + } } } static void sugov_iowait_boost(struct sugov_cpu *sg_cpu, unsigned long *util, unsigned long *max) { - unsigned long boost_util = sg_cpu->iowait_boost; - unsigned long boost_max = sg_cpu->iowait_boost_max; + unsigned int boost_util, boost_max; - if (!boost_util) + if (!sg_cpu->iowait_boost) return; + if (sg_cpu->iowait_boost_pending) { + sg_cpu->iowait_boost_pending = false; + } else { + sg_cpu->iowait_boost >>= 1; + if (sg_cpu->iowait_boost < sg_cpu->sg_policy->policy->min) { + sg_cpu->iowait_boost = 0; + return; + } + } + + boost_util = sg_cpu->iowait_boost; + boost_max = sg_cpu->iowait_boost_max; + if (*util * boost_max < *max * boost_util) { *util = boost_util; *max = boost_max; } - sg_cpu->iowait_boost >>= 1; } #ifdef CONFIG_NO_HZ_COMMON @@ -229,7 +275,7 @@ static void sugov_update_single(struct update_util_data *hook, u64 time, if (flags & SCHED_CPUFREQ_RT_DL) { next_f = policy->cpuinfo.max_freq; } else { - sugov_get_util(&util, &max); + sugov_get_util(&util, &max, sg_cpu->cpu); sugov_iowait_boost(sg_cpu, &util, &max); next_f = get_next_freq(sg_policy, util, max); /* @@ -264,6 +310,7 @@ static unsigned int sugov_next_freq_shared(struct sugov_cpu *sg_cpu, u64 time) delta_ns = time - j_sg_cpu->last_update; if (delta_ns > TICK_NSEC) { j_sg_cpu->iowait_boost = 0; + j_sg_cpu->iowait_boost_pending = false; continue; } if (j_sg_cpu->flags & SCHED_CPUFREQ_RT_DL) @@ -290,7 +337,7 @@ static void sugov_update_shared(struct update_util_data *hook, u64 time, unsigned long util, max; unsigned int next_f; - sugov_get_util(&util, &max); + sugov_get_util(&util, &max, sg_cpu->cpu); raw_spin_lock(&sg_policy->update_lock); @@ -445,7 +492,11 @@ static int sugov_kthread_create(struct sugov_policy *sg_policy) } sg_policy->thread = thread; - kthread_bind_mask(thread, policy->related_cpus); + + /* Kthread is bound to all CPUs by default */ + if (!policy->dvfs_possible_from_any_cpu) + kthread_bind_mask(thread, policy->related_cpus); + init_irq_work(&sg_policy->irq_work, sugov_irq_work); mutex_init(&sg_policy->work_lock); @@ -528,16 +579,7 @@ static int sugov_init(struct cpufreq_policy *policy) goto stop_kthread; } - if (policy->transition_delay_us) { - tunables->rate_limit_us = policy->transition_delay_us; - } else { - unsigned int lat; - - tunables->rate_limit_us = LATENCY_MULTIPLIER; - lat = policy->cpuinfo.transition_latency / NSEC_PER_USEC; - if (lat) - tunables->rate_limit_us *= lat; - } + tunables->rate_limit_us = cpufreq_policy_transition_delay_us(policy); policy->governor_data = sg_policy; sg_policy->tunables = tunables; @@ -655,6 +697,7 @@ static void sugov_limits(struct cpufreq_policy *policy) static struct cpufreq_governor schedutil_gov = { .name = "schedutil", .owner = THIS_MODULE, + .dynamic_switching = true, .init = sugov_init, .exit = sugov_exit, .start = sugov_start, @@ -671,6 +714,11 @@ struct cpufreq_governor *cpufreq_default_governor(void) static int __init sugov_register(void) { + int cpu; + + for_each_possible_cpu(cpu) + per_cpu(sugov_cpu, cpu).cpu = cpu; + return cpufreq_register_governor(&schedutil_gov); } fs_initcall(sugov_register); diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index d05bd9457a40..9e38df7649f4 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -1136,7 +1136,7 @@ static void update_curr_dl(struct rq *rq) } /* kick cpufreq (see the comment in kernel/sched/sched.h). */ - cpufreq_update_this_cpu(rq, SCHED_CPUFREQ_DL); + cpufreq_update_util(rq, SCHED_CPUFREQ_DL); schedstat_set(curr->se.statistics.exec_max, max(curr->se.statistics.exec_max, delta_exec)); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 8d5868771cb3..8bc0a883d190 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -2803,7 +2803,9 @@ static inline void update_cfs_shares(struct sched_entity *se) static inline void cfs_rq_util_change(struct cfs_rq *cfs_rq) { - if (&this_rq()->cfs == cfs_rq) { + struct rq *rq = rq_of(cfs_rq); + + if (&rq->cfs == cfs_rq) { /* * There are a few boundary cases this might miss but it should * get called often enough that that should (hopefully) not be @@ -2820,7 +2822,7 @@ static inline void cfs_rq_util_change(struct cfs_rq *cfs_rq) * * See cpu_util(). */ - cpufreq_update_util(rq_of(cfs_rq), 0); + cpufreq_update_util(rq, 0); } } @@ -4897,7 +4899,7 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) * passed. */ if (p->in_iowait) - cpufreq_update_this_cpu(rq, SCHED_CPUFREQ_IOWAIT); + cpufreq_update_util(rq, SCHED_CPUFREQ_IOWAIT); for_each_sched_entity(se) { if (se->on_rq) diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index 6c23e30c0e5c..257f4f0b4532 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c @@ -158,7 +158,7 @@ static void cpuidle_idle_call(void) } /* - * Suspend-to-idle ("freeze") is a system state in which all user space + * Suspend-to-idle ("s2idle") is a system state in which all user space * has been frozen, all I/O devices have been suspended and the only * activity happens here and in iterrupts (if any). In that case bypass * the cpuidle governor and go stratight for the deepest idle state @@ -167,9 +167,9 @@ static void cpuidle_idle_call(void) * until a proper wakeup interrupt happens. */ - if (idle_should_freeze() || dev->use_deepest_state) { - if (idle_should_freeze()) { - entered_state = cpuidle_enter_freeze(drv, dev); + if (idle_should_enter_s2idle() || dev->use_deepest_state) { + if (idle_should_enter_s2idle()) { + entered_state = cpuidle_enter_s2idle(drv, dev); if (entered_state > 0) { local_irq_enable(); goto exit_idle; diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index 45caf937ef90..0af5ca9e3e3f 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -970,7 +970,7 @@ static void update_curr_rt(struct rq *rq) return; /* Kick cpufreq (see the comment in kernel/sched/sched.h). */ - cpufreq_update_this_cpu(rq, SCHED_CPUFREQ_RT); + cpufreq_update_util(rq, SCHED_CPUFREQ_RT); schedstat_set(curr->se.statistics.exec_max, max(curr->se.statistics.exec_max, delta_exec)); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index ab1c7f5409a0..6ed7962dc896 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -2074,19 +2074,13 @@ static inline void cpufreq_update_util(struct rq *rq, unsigned int flags) { struct update_util_data *data; - data = rcu_dereference_sched(*this_cpu_ptr(&cpufreq_update_util_data)); + data = rcu_dereference_sched(*per_cpu_ptr(&cpufreq_update_util_data, + cpu_of(rq))); if (data) data->func(data, rq_clock(rq), flags); } - -static inline void cpufreq_update_this_cpu(struct rq *rq, unsigned int flags) -{ - if (cpu_of(rq) == smp_processor_id()) - cpufreq_update_util(rq, flags); -} #else static inline void cpufreq_update_util(struct rq *rq, unsigned int flags) {} -static inline void cpufreq_update_this_cpu(struct rq *rq, unsigned int flags) {} #endif /* CONFIG_CPU_FREQ */ #ifdef arch_scale_freq_capacity diff --git a/kernel/time/timekeeping_debug.c b/kernel/time/timekeeping_debug.c index 38bc4d2208e8..0754cadfa9e6 100644 --- a/kernel/time/timekeeping_debug.c +++ b/kernel/time/timekeeping_debug.c @@ -19,6 +19,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/seq_file.h> +#include <linux/suspend.h> #include <linux/time.h> #include "timekeeping_internal.h" @@ -75,7 +76,7 @@ void tk_debug_account_sleep_time(struct timespec64 *t) int bin = min(fls(t->tv_sec), NUM_BINS-1); sleep_time_bin[bin]++; - printk_deferred(KERN_INFO "Suspended for %lld.%03lu seconds\n", - (s64)t->tv_sec, t->tv_nsec / NSEC_PER_MSEC); + pm_deferred_pr_dbg("Timekeeping suspended for %lld.%03lu seconds\n", + (s64)t->tv_sec, t->tv_nsec / NSEC_PER_MSEC); } diff --git a/tools/power/cpupower/utils/cpupower.c b/tools/power/cpupower/utils/cpupower.c index 9ea914378985..2dccf4998599 100644 --- a/tools/power/cpupower/utils/cpupower.c +++ b/tools/power/cpupower/utils/cpupower.c @@ -12,6 +12,7 @@ #include <string.h> #include <unistd.h> #include <errno.h> +#include <sched.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/utsname.h> @@ -31,6 +32,7 @@ static int cmd_help(int argc, const char **argv); */ struct cpupower_cpu_info cpupower_cpu_info; int run_as_root; +int base_cpu; /* Affected cpus chosen by -c/--cpu param */ struct bitmask *cpus_chosen; @@ -174,6 +176,7 @@ int main(int argc, const char *argv[]) unsigned int i, ret; struct stat statbuf; struct utsname uts; + char pathname[32]; cpus_chosen = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF)); @@ -198,17 +201,23 @@ int main(int argc, const char *argv[]) argv[0] = cmd = "help"; } - get_cpu_info(0, &cpupower_cpu_info); + base_cpu = sched_getcpu(); + if (base_cpu < 0) { + fprintf(stderr, _("No valid cpus found.\n")); + return EXIT_FAILURE; + } + + get_cpu_info(&cpupower_cpu_info); run_as_root = !geteuid(); if (run_as_root) { ret = uname(&uts); + sprintf(pathname, "/dev/cpu/%d/msr", base_cpu); if (!ret && !strcmp(uts.machine, "x86_64") && - stat("/dev/cpu/0/msr", &statbuf) != 0) { + stat(pathname, &statbuf) != 0) { if (system("modprobe msr") == -1) fprintf(stderr, _("MSR access not available.\n")); } } - for (i = 0; i < ARRAY_SIZE(commands); i++) { struct cmd_struct *p = commands + i; diff --git a/tools/power/cpupower/utils/helpers/cpuid.c b/tools/power/cpupower/utils/helpers/cpuid.c index 39c2c7d067bb..32d37c9be791 100644 --- a/tools/power/cpupower/utils/helpers/cpuid.c +++ b/tools/power/cpupower/utils/helpers/cpuid.c @@ -42,7 +42,7 @@ cpuid_func(edx); * * TBD: Should there be a cpuid alternative for this if /proc is not mounted? */ -int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info) +int get_cpu_info(struct cpupower_cpu_info *cpu_info) { FILE *fp; char value[64]; @@ -70,7 +70,7 @@ int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info) if (!strncmp(value, "processor\t: ", 12)) sscanf(value, "processor\t: %u", &proc); - if (proc != cpu) + if (proc != (unsigned int)base_cpu) continue; /* Get CPU vendor */ diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index 799a18be60aa..41da392be448 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -34,6 +34,7 @@ /* Internationalization ****************************/ extern int run_as_root; +extern int base_cpu; extern struct bitmask *cpus_chosen; /* Global verbose (-d) stuff *********************************/ @@ -87,11 +88,11 @@ struct cpupower_cpu_info { * * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo * - * Returns 0 on success or a negativ error code + * Returns 0 on success or a negative error code * Only used on x86, below global's struct values are zero/unknown on * other archs */ -extern int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info); +extern int get_cpu_info(struct cpupower_cpu_info *cpu_info); extern struct cpupower_cpu_info cpupower_cpu_info; /* cpuid and cpuinfo helpers **************************/ diff --git a/tools/power/cpupower/utils/helpers/misc.c b/tools/power/cpupower/utils/helpers/misc.c index 601d719d4e60..a5e7ddf19dbd 100644 --- a/tools/power/cpupower/utils/helpers/misc.c +++ b/tools/power/cpupower/utils/helpers/misc.c @@ -13,7 +13,7 @@ int cpufreq_has_boost_support(unsigned int cpu, int *support, int *active, *support = *active = *states = 0; - ret = get_cpu_info(0, &cpu_info); + ret = get_cpu_info(&cpu_info); if (ret) return ret; diff --git a/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c b/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c index ebeaba6571a3..f794d6bbb7e9 100644 --- a/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c +++ b/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c @@ -123,7 +123,7 @@ static int hsw_ext_start(void) previous_count[num][cpu] = val; } } - hsw_ext_get_count(TSC, &tsc_at_measure_start, 0); + hsw_ext_get_count(TSC, &tsc_at_measure_start, base_cpu); return 0; } @@ -132,7 +132,7 @@ static int hsw_ext_stop(void) unsigned long long val; int num, cpu; - hsw_ext_get_count(TSC, &tsc_at_measure_end, 0); + hsw_ext_get_count(TSC, &tsc_at_measure_end, base_cpu); for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { for (cpu = 0; cpu < cpu_count; cpu++) { diff --git a/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c index c83f1606970b..d7c2a6d13dea 100644 --- a/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c +++ b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c @@ -80,7 +80,8 @@ static int *is_valid; static int mperf_get_tsc(unsigned long long *tsc) { int ret; - ret = read_msr(0, MSR_TSC, tsc); + + ret = read_msr(base_cpu, MSR_TSC, tsc); if (ret) dprint("Reading TSC MSR failed, returning %llu\n", *tsc); return ret; diff --git a/tools/power/cpupower/utils/idle_monitor/nhm_idle.c b/tools/power/cpupower/utils/idle_monitor/nhm_idle.c index d2a91dd0d563..abf8cb5f7349 100644 --- a/tools/power/cpupower/utils/idle_monitor/nhm_idle.c +++ b/tools/power/cpupower/utils/idle_monitor/nhm_idle.c @@ -129,7 +129,7 @@ static int nhm_start(void) int num, cpu; unsigned long long dbg, val; - nhm_get_count(TSC, &tsc_at_measure_start, 0); + nhm_get_count(TSC, &tsc_at_measure_start, base_cpu); for (num = 0; num < NHM_CSTATE_COUNT; num++) { for (cpu = 0; cpu < cpu_count; cpu++) { @@ -137,7 +137,7 @@ static int nhm_start(void) previous_count[num][cpu] = val; } } - nhm_get_count(TSC, &dbg, 0); + nhm_get_count(TSC, &dbg, base_cpu); dprint("TSC diff: %llu\n", dbg - tsc_at_measure_start); return 0; } @@ -148,7 +148,7 @@ static int nhm_stop(void) unsigned long long dbg; int num, cpu; - nhm_get_count(TSC, &tsc_at_measure_end, 0); + nhm_get_count(TSC, &tsc_at_measure_end, base_cpu); for (num = 0; num < NHM_CSTATE_COUNT; num++) { for (cpu = 0; cpu < cpu_count; cpu++) { @@ -156,7 +156,7 @@ static int nhm_stop(void) current_count[num][cpu] = val; } } - nhm_get_count(TSC, &dbg, 0); + nhm_get_count(TSC, &dbg, base_cpu); dprint("TSC diff: %llu\n", dbg - tsc_at_measure_end); return 0; diff --git a/tools/power/cpupower/utils/idle_monitor/snb_idle.c b/tools/power/cpupower/utils/idle_monitor/snb_idle.c index efc8a69c9aba..a2b45219648d 100644 --- a/tools/power/cpupower/utils/idle_monitor/snb_idle.c +++ b/tools/power/cpupower/utils/idle_monitor/snb_idle.c @@ -120,7 +120,7 @@ static int snb_start(void) previous_count[num][cpu] = val; } } - snb_get_count(TSC, &tsc_at_measure_start, 0); + snb_get_count(TSC, &tsc_at_measure_start, base_cpu); return 0; } @@ -129,7 +129,7 @@ static int snb_stop(void) unsigned long long val; int num, cpu; - snb_get_count(TSC, &tsc_at_measure_end, 0); + snb_get_count(TSC, &tsc_at_measure_end, base_cpu); for (num = 0; num < SNB_CSTATE_COUNT; num++) { for (cpu = 0; cpu < cpu_count; cpu++) { diff --git a/tools/power/pm-graph/Makefile b/tools/power/pm-graph/Makefile index 4d0ccc89e6c6..32f40eacdafe 100644 --- a/tools/power/pm-graph/Makefile +++ b/tools/power/pm-graph/Makefile @@ -4,7 +4,7 @@ DESTDIR ?= all: @echo "Nothing to build" -install : +install : uninstall install -d $(DESTDIR)$(PREFIX)/lib/pm-graph install analyze_suspend.py $(DESTDIR)$(PREFIX)/lib/pm-graph install analyze_boot.py $(DESTDIR)$(PREFIX)/lib/pm-graph @@ -17,12 +17,15 @@ install : install sleepgraph.8 $(DESTDIR)$(PREFIX)/share/man/man8 uninstall : - rm $(DESTDIR)$(PREFIX)/share/man/man8/bootgraph.8 - rm $(DESTDIR)$(PREFIX)/share/man/man8/sleepgraph.8 + rm -f $(DESTDIR)$(PREFIX)/share/man/man8/bootgraph.8 + rm -f $(DESTDIR)$(PREFIX)/share/man/man8/sleepgraph.8 - rm $(DESTDIR)$(PREFIX)/bin/bootgraph - rm $(DESTDIR)$(PREFIX)/bin/sleepgraph + rm -f $(DESTDIR)$(PREFIX)/bin/bootgraph + rm -f $(DESTDIR)$(PREFIX)/bin/sleepgraph - rm $(DESTDIR)$(PREFIX)/lib/pm-graph/analyze_boot.py - rm $(DESTDIR)$(PREFIX)/lib/pm-graph/analyze_suspend.py - rmdir $(DESTDIR)$(PREFIX)/lib/pm-graph + rm -f $(DESTDIR)$(PREFIX)/lib/pm-graph/analyze_boot.py + rm -f $(DESTDIR)$(PREFIX)/lib/pm-graph/analyze_suspend.py + rm -f $(DESTDIR)$(PREFIX)/lib/pm-graph/*.pyc + if [ -d $(DESTDIR)$(PREFIX)/lib/pm-graph ] ; then \ + rmdir $(DESTDIR)$(PREFIX)/lib/pm-graph; \ + fi; diff --git a/tools/power/pm-graph/analyze_boot.py b/tools/power/pm-graph/analyze_boot.py index 3e1dcbbf1adc..e83df141a597 100755 --- a/tools/power/pm-graph/analyze_boot.py +++ b/tools/power/pm-graph/analyze_boot.py @@ -42,7 +42,7 @@ import analyze_suspend as aslib # store system values and test parameters class SystemValues(aslib.SystemValues): title = 'BootGraph' - version = 2.0 + version = '2.1' hostname = 'localhost' testtime = '' kernel = '' @@ -50,9 +50,14 @@ class SystemValues(aslib.SystemValues): ftracefile = '' htmlfile = 'bootgraph.html' outfile = '' - phoronix = False - addlogs = False + testdir = '' + testdirprefix = 'boot' + embedded = False + testlog = False + dmesglog = False + ftracelog = False useftrace = False + usecallgraph = False usedevsrc = True suspendmode = 'boot' max_graph_depth = 2 @@ -61,10 +66,12 @@ class SystemValues(aslib.SystemValues): manual = False iscronjob = False timeformat = '%.6f' + bootloader = 'grub' + blexec = [] def __init__(self): if('LOG_FILE' in os.environ and 'TEST_RESULTS_IDENTIFIER' in os.environ): - self.phoronix = True - self.addlogs = True + self.embedded = True + self.dmesglog = True self.outfile = os.environ['LOG_FILE'] self.htmlfile = os.environ['LOG_FILE'] self.hostname = platform.node() @@ -76,42 +83,80 @@ class SystemValues(aslib.SystemValues): self.kernel = self.kernelVersion(val) else: self.kernel = 'unknown' + self.testdir = datetime.now().strftime('boot-%y%m%d-%H%M%S') def kernelVersion(self, msg): return msg.split()[2] + def checkFtraceKernelVersion(self): + val = tuple(map(int, self.kernel.split('-')[0].split('.'))) + if val >= (4, 10, 0): + return True + return False def kernelParams(self): cmdline = 'initcall_debug log_buf_len=32M' if self.useftrace: - cmdline += ' trace_buf_size=128M trace_clock=global '\ + if self.cpucount > 0: + bs = min(self.memtotal / 2, 2*1024*1024) / self.cpucount + else: + bs = 131072 + cmdline += ' trace_buf_size=%dK trace_clock=global '\ 'trace_options=nooverwrite,funcgraph-abstime,funcgraph-cpu,'\ 'funcgraph-duration,funcgraph-proc,funcgraph-tail,'\ 'nofuncgraph-overhead,context-info,graph-time '\ 'ftrace=function_graph '\ 'ftrace_graph_max_depth=%d '\ 'ftrace_graph_filter=%s' % \ - (self.max_graph_depth, self.graph_filter) + (bs, self.max_graph_depth, self.graph_filter) return cmdline def setGraphFilter(self, val): - fp = open(self.tpath+'available_filter_functions') - master = fp.read().split('\n') - fp.close() + master = self.getBootFtraceFilterFunctions() + fs = '' for i in val.split(','): func = i.strip() + if func == '': + doError('badly formatted filter function string') + if '[' in func or ']' in func: + doError('loadable module functions not allowed - "%s"' % func) + if ' ' in func: + doError('spaces found in filter functions - "%s"' % func) if func not in master: doError('function "%s" not available for ftrace' % func) - self.graph_filter = val + if not fs: + fs = func + else: + fs += ','+func + if not fs: + doError('badly formatted filter function string') + self.graph_filter = fs + def getBootFtraceFilterFunctions(self): + self.rootCheck(True) + fp = open(self.tpath+'available_filter_functions') + fulllist = fp.read().split('\n') + fp.close() + list = [] + for i in fulllist: + if not i or ' ' in i or '[' in i or ']' in i: + continue + list.append(i) + return list + def myCronJob(self, line): + if '@reboot' not in line: + return False + if 'bootgraph' in line or 'analyze_boot.py' in line or '-cronjob' in line: + return True + return False def cronjobCmdString(self): cmdline = '%s -cronjob' % os.path.abspath(sys.argv[0]) args = iter(sys.argv[1:]) for arg in args: if arg in ['-h', '-v', '-cronjob', '-reboot']: continue - elif arg in ['-o', '-dmesg', '-ftrace', '-filter']: + elif arg in ['-o', '-dmesg', '-ftrace', '-func']: args.next() continue cmdline += ' '+arg if self.graph_filter != 'do_one_initcall': - cmdline += ' -filter "%s"' % self.graph_filter - cmdline += ' -o "%s"' % os.path.abspath(self.htmlfile) + cmdline += ' -func "%s"' % self.graph_filter + cmdline += ' -o "%s"' % os.path.abspath(self.testdir) return cmdline def manualRebootRequired(self): cmdline = self.kernelParams() @@ -121,6 +166,39 @@ class SystemValues(aslib.SystemValues): print '3. After reboot, re-run this tool with the same arguments but no command (w/o -reboot or -manual).\n' print 'CMDLINE="%s"' % cmdline sys.exit() + def getExec(self, cmd): + dirlist = ['/sbin', '/bin', '/usr/sbin', '/usr/bin', + '/usr/local/sbin', '/usr/local/bin'] + for path in dirlist: + cmdfull = os.path.join(path, cmd) + if os.path.exists(cmdfull): + return cmdfull + return '' + def blGrub(self): + blcmd = '' + for cmd in ['update-grub', 'grub-mkconfig', 'grub2-mkconfig']: + if blcmd: + break + blcmd = self.getExec(cmd) + if not blcmd: + doError('[GRUB] missing update command') + if not os.path.exists('/etc/default/grub'): + doError('[GRUB] missing /etc/default/grub') + if 'grub2' in blcmd: + cfg = '/boot/grub2/grub.cfg' + else: + cfg = '/boot/grub/grub.cfg' + if not os.path.exists(cfg): + doError('[GRUB] missing %s' % cfg) + if 'update-grub' in blcmd: + self.blexec = [blcmd] + else: + self.blexec = [blcmd, '-o', cfg] + def getBootLoader(self): + if self.bootloader == 'grub': + self.blGrub() + else: + doError('unknown boot loader: %s' % self.bootloader) sysvals = SystemValues() @@ -136,20 +214,23 @@ class Data(aslib.Data): idstr = '' html_device_id = 0 valid = False - initstart = 0.0 + tUserMode = 0.0 boottime = '' - phases = ['boot'] + phases = ['kernel', 'user'] do_one_initcall = False def __init__(self, num): self.testnumber = num self.idstr = 'a' self.dmesgtext = [] self.dmesg = { - 'boot': {'list': dict(), 'start': -1.0, 'end': -1.0, 'row': 0, 'color': '#dddddd'} + 'kernel': {'list': dict(), 'start': -1.0, 'end': -1.0, 'row': 0, + 'order': 0, 'color': 'linear-gradient(to bottom, #fff, #bcf)'}, + 'user': {'list': dict(), 'start': -1.0, 'end': -1.0, 'row': 0, + 'order': 1, 'color': '#fff'} } def deviceTopology(self): return '' - def newAction(self, phase, name, start, end, ret, ulen): + def newAction(self, phase, name, pid, start, end, ret, ulen): # new device callback for a specific phase self.html_device_id += 1 devid = '%s%d' % (self.idstr, self.html_device_id) @@ -163,41 +244,46 @@ class Data(aslib.Data): name = '%s[%d]' % (origname, i) i += 1 list[name] = {'name': name, 'start': start, 'end': end, - 'pid': 0, 'length': length, 'row': 0, 'id': devid, + 'pid': pid, 'length': length, 'row': 0, 'id': devid, 'ret': ret, 'ulen': ulen } return name - def deviceMatch(self, cg): + def deviceMatch(self, pid, cg): if cg.end - cg.start == 0: return True - list = self.dmesg['boot']['list'] - for devname in list: - dev = list[devname] - if cg.name == 'do_one_initcall': - if(cg.start <= dev['start'] and cg.end >= dev['end'] and dev['length'] > 0): - dev['ftrace'] = cg - self.do_one_initcall = True - return True - else: - if(cg.start > dev['start'] and cg.end < dev['end']): - if 'ftraces' not in dev: - dev['ftraces'] = [] - dev['ftraces'].append(cg) - return True + for p in data.phases: + list = self.dmesg[p]['list'] + for devname in list: + dev = list[devname] + if pid != dev['pid']: + continue + if cg.name == 'do_one_initcall': + if(cg.start <= dev['start'] and cg.end >= dev['end'] and dev['length'] > 0): + dev['ftrace'] = cg + self.do_one_initcall = True + return True + else: + if(cg.start > dev['start'] and cg.end < dev['end']): + if 'ftraces' not in dev: + dev['ftraces'] = [] + dev['ftraces'].append(cg) + return True return False # ----------------- FUNCTIONS -------------------- -# Function: loadKernelLog +# Function: parseKernelLog # Description: -# Load a raw kernel log from dmesg -def loadKernelLog(): +# parse a kernel log for boot data +def parseKernelLog(): + phase = 'kernel' data = Data(0) - data.dmesg['boot']['start'] = data.start = ktime = 0.0 + data.dmesg['kernel']['start'] = data.start = ktime = 0.0 sysvals.stamp = { 'time': datetime.now().strftime('%B %d %Y, %I:%M:%S %p'), 'host': sysvals.hostname, 'mode': 'boot', 'kernel': ''} + tp = aslib.TestProps() devtemp = dict() if(sysvals.dmesgfile): lf = open(sysvals.dmesgfile, 'r') @@ -205,6 +291,13 @@ def loadKernelLog(): lf = Popen('dmesg', stdout=PIPE).stdout for line in lf: line = line.replace('\r\n', '') + # grab the stamp and sysinfo + if re.match(tp.stampfmt, line): + tp.stamp = line + continue + elif re.match(tp.sysinfofmt, line): + tp.sysinfo = line + continue idx = line.find('[') if idx > 1: line = line[idx:] @@ -215,7 +308,6 @@ def loadKernelLog(): if(ktime > 120): break msg = m.group('msg') - data.end = data.initstart = ktime data.dmesgtext.append(line) if(ktime == 0.0 and re.match('^Linux version .*', msg)): if(not sysvals.stamp['kernel']): @@ -228,43 +320,39 @@ def loadKernelLog(): data.boottime = bt.strftime('%Y-%m-%d_%H:%M:%S') sysvals.stamp['time'] = bt.strftime('%B %d %Y, %I:%M:%S %p') continue - m = re.match('^calling *(?P<f>.*)\+.*', msg) + m = re.match('^calling *(?P<f>.*)\+.* @ (?P<p>[0-9]*)', msg) if(m): - devtemp[m.group('f')] = ktime + func = m.group('f') + pid = int(m.group('p')) + devtemp[func] = (ktime, pid) continue m = re.match('^initcall *(?P<f>.*)\+.* returned (?P<r>.*) after (?P<t>.*) usecs', msg) if(m): data.valid = True + data.end = ktime f, r, t = m.group('f', 'r', 't') if(f in devtemp): - data.newAction('boot', f, devtemp[f], ktime, int(r), int(t)) - data.end = ktime + start, pid = devtemp[f] + data.newAction(phase, f, pid, start, ktime, int(r), int(t)) del devtemp[f] continue if(re.match('^Freeing unused kernel memory.*', msg)): - break - - data.dmesg['boot']['end'] = data.end + data.tUserMode = ktime + data.dmesg['kernel']['end'] = ktime + data.dmesg['user']['start'] = ktime + phase = 'user' + + if tp.stamp: + sysvals.stamp = 0 + tp.parseStamp(data, sysvals) + data.dmesg['user']['end'] = data.end lf.close() return data -# Function: loadTraceLog +# Function: parseTraceLog # Description: # Check if trace is available and copy to a temp file -def loadTraceLog(data): - # load the data to a temp file if none given - if not sysvals.ftracefile: - lib = aslib.sysvals - aslib.rootCheck(True) - if not lib.verifyFtrace(): - doError('ftrace not available') - if lib.fgetVal('current_tracer').strip() != 'function_graph': - doError('ftrace not configured for a boot callgraph') - sysvals.ftracefile = '/tmp/boot_ftrace.%s.txt' % os.getpid() - call('cat '+lib.tpath+'trace > '+sysvals.ftracefile, shell=True) - if not sysvals.ftracefile: - doError('No trace data available') - +def parseTraceLog(data): # parse the trace log ftemp = dict() tp = aslib.TestProps() @@ -306,9 +394,29 @@ def loadTraceLog(data): print('Sanity check failed for %s-%d' % (proc, pid)) continue # match cg data to devices - if not data.deviceMatch(cg): + if not data.deviceMatch(pid, cg): print ' BAD: %s %s-%d [%f - %f]' % (cg.name, proc, pid, cg.start, cg.end) +# Function: retrieveLogs +# Description: +# Create copies of dmesg and/or ftrace for later processing +def retrieveLogs(): + # check ftrace is configured first + if sysvals.useftrace: + tracer = sysvals.fgetVal('current_tracer').strip() + if tracer != 'function_graph': + doError('ftrace not configured for a boot callgraph') + # create the folder and get dmesg + sysvals.systemInfo(aslib.dmidecode(sysvals.mempath)) + sysvals.initTestOutput('boot') + sysvals.writeDatafileHeader(sysvals.dmesgfile) + call('dmesg >> '+sysvals.dmesgfile, shell=True) + if not sysvals.useftrace: + return + # get ftrace + sysvals.writeDatafileHeader(sysvals.ftracefile) + call('cat '+sysvals.tpath+'trace >> '+sysvals.ftracefile, shell=True) + # Function: colorForName # Description: # Generate a repeatable color from a list for a given name @@ -353,18 +461,19 @@ def cgOverview(cg, minlen): # testruns: array of Data objects from parseKernelLog or parseTraceLog # Output: # True if the html file was created, false if it failed -def createBootGraph(data, embedded): +def createBootGraph(data): # html function templates html_srccall = '<div id={6} title="{5}" class="srccall" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;">{0}</div>\n' html_timetotal = '<table class="time1">\n<tr>'\ - '<td class="blue">Time from Kernel Boot to start of User Mode: <b>{0} ms</b></td>'\ + '<td class="blue">Init process starts @ <b>{0} ms</b></td>'\ + '<td class="blue">Last initcall ends @ <b>{1} ms</b></td>'\ '</tr>\n</table>\n' # device timeline devtl = aslib.Timeline(100, 20) # write the test title and general info header - devtl.createHeader(sysvals, 'noftrace') + devtl.createHeader(sysvals) # Generate the header for this timeline t0 = data.start @@ -373,84 +482,98 @@ def createBootGraph(data, embedded): if(tTotal == 0): print('ERROR: No timeline data') return False - boot_time = '%.0f'%(tTotal*1000) - devtl.html += html_timetotal.format(boot_time) + user_mode = '%.0f'%(data.tUserMode*1000) + last_init = '%.0f'%(tTotal*1000) + devtl.html += html_timetotal.format(user_mode, last_init) # determine the maximum number of rows we need to draw - phase = 'boot' - list = data.dmesg[phase]['list'] devlist = [] - for devname in list: - d = aslib.DevItem(0, phase, list[devname]) - devlist.append(d) - devtl.getPhaseRows(devlist) + for p in data.phases: + list = data.dmesg[p]['list'] + for devname in list: + d = aslib.DevItem(0, p, list[devname]) + devlist.append(d) + devtl.getPhaseRows(devlist, 0, 'start') devtl.calcTotalRows() # draw the timeline background devtl.createZoomBox() - boot = data.dmesg[phase] - length = boot['end']-boot['start'] - left = '%.3f' % (((boot['start']-t0)*100.0)/tTotal) - width = '%.3f' % ((length*100.0)/tTotal) - devtl.html += devtl.html_tblock.format(phase, left, width, devtl.scaleH) - devtl.html += devtl.html_phase.format('0', '100', \ - '%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \ - 'white', '') + devtl.html += devtl.html_tblock.format('boot', '0', '100', devtl.scaleH) + for p in data.phases: + phase = data.dmesg[p] + length = phase['end']-phase['start'] + left = '%.3f' % (((phase['start']-t0)*100.0)/tTotal) + width = '%.3f' % ((length*100.0)/tTotal) + devtl.html += devtl.html_phase.format(left, width, \ + '%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \ + phase['color'], '') # draw the device timeline num = 0 devstats = dict() - for devname in sorted(list): - cls, color = colorForName(devname) - dev = list[devname] - info = '@|%.3f|%.3f|%.3f|%d' % (dev['start']*1000.0, dev['end']*1000.0, - dev['ulen']/1000.0, dev['ret']) - devstats[dev['id']] = {'info':info} - dev['color'] = color - height = devtl.phaseRowHeight(0, phase, dev['row']) - top = '%.6f' % ((dev['row']*height) + devtl.scaleH) - left = '%.6f' % (((dev['start']-t0)*100)/tTotal) - width = '%.6f' % (((dev['end']-dev['start'])*100)/tTotal) - length = ' (%0.3f ms) ' % ((dev['end']-dev['start'])*1000) - devtl.html += devtl.html_device.format(dev['id'], - devname+length+'kernel_mode', left, top, '%.3f'%height, - width, devname, ' '+cls, '') - rowtop = devtl.phaseRowTop(0, phase, dev['row']) - height = '%.6f' % (devtl.rowH / 2) - top = '%.6f' % (rowtop + devtl.scaleH + (devtl.rowH / 2)) - if data.do_one_initcall: - if('ftrace' not in dev): + for phase in data.phases: + list = data.dmesg[phase]['list'] + for devname in sorted(list): + cls, color = colorForName(devname) + dev = list[devname] + info = '@|%.3f|%.3f|%.3f|%d' % (dev['start']*1000.0, dev['end']*1000.0, + dev['ulen']/1000.0, dev['ret']) + devstats[dev['id']] = {'info':info} + dev['color'] = color + height = devtl.phaseRowHeight(0, phase, dev['row']) + top = '%.6f' % ((dev['row']*height) + devtl.scaleH) + left = '%.6f' % (((dev['start']-t0)*100)/tTotal) + width = '%.6f' % (((dev['end']-dev['start'])*100)/tTotal) + length = ' (%0.3f ms) ' % ((dev['end']-dev['start'])*1000) + devtl.html += devtl.html_device.format(dev['id'], + devname+length+phase+'_mode', left, top, '%.3f'%height, + width, devname, ' '+cls, '') + rowtop = devtl.phaseRowTop(0, phase, dev['row']) + height = '%.6f' % (devtl.rowH / 2) + top = '%.6f' % (rowtop + devtl.scaleH + (devtl.rowH / 2)) + if data.do_one_initcall: + if('ftrace' not in dev): + continue + cg = dev['ftrace'] + large, stats = cgOverview(cg, 0.001) + devstats[dev['id']]['fstat'] = stats + for l in large: + left = '%f' % (((l.time-t0)*100)/tTotal) + width = '%f' % (l.length*100/tTotal) + title = '%s (%0.3fms)' % (l.name, l.length * 1000.0) + devtl.html += html_srccall.format(l.name, left, + top, height, width, title, 'x%d'%num) + num += 1 + continue + if('ftraces' not in dev): continue - cg = dev['ftrace'] - large, stats = cgOverview(cg, 0.001) - devstats[dev['id']]['fstat'] = stats - for l in large: - left = '%f' % (((l.time-t0)*100)/tTotal) - width = '%f' % (l.length*100/tTotal) - title = '%s (%0.3fms)' % (l.name, l.length * 1000.0) - devtl.html += html_srccall.format(l.name, left, - top, height, width, title, 'x%d'%num) + for cg in dev['ftraces']: + left = '%f' % (((cg.start-t0)*100)/tTotal) + width = '%f' % ((cg.end-cg.start)*100/tTotal) + cglen = (cg.end - cg.start) * 1000.0 + title = '%s (%0.3fms)' % (cg.name, cglen) + cg.id = 'x%d' % num + devtl.html += html_srccall.format(cg.name, left, + top, height, width, title, dev['id']+cg.id) num += 1 - continue - if('ftraces' not in dev): - continue - for cg in dev['ftraces']: - left = '%f' % (((cg.start-t0)*100)/tTotal) - width = '%f' % ((cg.end-cg.start)*100/tTotal) - cglen = (cg.end - cg.start) * 1000.0 - title = '%s (%0.3fms)' % (cg.name, cglen) - cg.id = 'x%d' % num - devtl.html += html_srccall.format(cg.name, left, - top, height, width, title, dev['id']+cg.id) - num += 1 # draw the time scale, try to make the number of labels readable - devtl.createTimeScale(t0, tMax, tTotal, phase) + devtl.createTimeScale(t0, tMax, tTotal, 'boot') devtl.html += '</div>\n' # timeline is finished devtl.html += '</div>\n</div>\n' + # draw a legend which describes the phases by color + devtl.html += '<div class="legend">\n' + pdelta = 20.0 + pmargin = 36.0 + for phase in data.phases: + order = '%.2f' % ((data.dmesg[phase]['order'] * pdelta) + pmargin) + devtl.html += devtl.html_legend.format(order, \ + data.dmesg[phase]['color'], phase+'_mode', phase[0]) + devtl.html += '</div>\n' + if(sysvals.outfile == sysvals.htmlfile): hf = open(sysvals.htmlfile, 'a') else: @@ -474,7 +597,7 @@ def createBootGraph(data, embedded): .fstat td {text-align:left;width:35px;}\n\ .srccall {position:absolute;font-size:10px;z-index:7;overflow:hidden;color:black;text-align:center;white-space:nowrap;border-radius:5px;border:1px solid black;background:linear-gradient(to bottom right,#CCC,#969696);}\n\ .srccall:hover {color:white;font-weight:bold;border:1px solid white;}\n' - if(not embedded): + if(not sysvals.embedded): aslib.addCSS(hf, sysvals, 1, False, extra) # write the device timeline @@ -495,9 +618,11 @@ def createBootGraph(data, embedded): html = \ '<div id="devicedetailtitle"></div>\n'\ '<div id="devicedetail" style="display:none;">\n'\ - '<div id="devicedetail0">\n'\ - '<div id="kernel_mode" class="phaselet" style="left:0%;width:100%;background:#DDDDDD"></div>\n'\ - '</div>\n</div>\n'\ + '<div id="devicedetail0">\n' + for p in data.phases: + phase = data.dmesg[p] + html += devtl.html_phaselet.format(p+'_mode', '0', '100', phase['color']) + html += '</div>\n</div>\n'\ '<script type="text/javascript">\n'+statinfo+\ '</script>\n' hf.write(html) @@ -507,21 +632,21 @@ def createBootGraph(data, embedded): aslib.addCallgraphs(sysvals, hf, data) # add the dmesg log as a hidden div - if sysvals.addlogs: + if sysvals.dmesglog: hf.write('<div id="dmesglog" style="display:none;">\n') for line in data.dmesgtext: line = line.replace('<', '<').replace('>', '>') hf.write(line) hf.write('</div>\n') - if(not embedded): + if(not sysvals.embedded): # write the footer and close aslib.addScriptCode(hf, [data]) hf.write('</body>\n</html>\n') else: # embedded out will be loaded in a page, skip the js hf.write('<div id=bounds style=display:none>%f,%f</div>' % \ - (data.start*1000, data.initstart*1000)) + (data.start*1000, data.end*1000)) hf.close() return True @@ -533,17 +658,20 @@ def updateCron(restore=False): if not restore: sysvals.rootUser(True) crondir = '/var/spool/cron/crontabs/' - cronfile = crondir+'root' - backfile = crondir+'root-analyze_boot-backup' + if not os.path.exists(crondir): + crondir = '/var/spool/cron/' if not os.path.exists(crondir): doError('%s not found' % crondir) - out = Popen(['which', 'crontab'], stdout=PIPE).stdout.read() - if not out: + cronfile = crondir+'root' + backfile = crondir+'root-analyze_boot-backup' + cmd = sysvals.getExec('crontab') + if not cmd: doError('crontab not found') # on restore: move the backup cron back into place if restore: if os.path.exists(backfile): shutil.move(backfile, cronfile) + call([cmd, cronfile]) return # backup current cron and install new one with reboot if os.path.exists(cronfile): @@ -556,13 +684,13 @@ def updateCron(restore=False): fp = open(backfile, 'r') op = open(cronfile, 'w') for line in fp: - if '@reboot' not in line: + if not sysvals.myCronJob(line): op.write(line) continue fp.close() op.write('@reboot python %s\n' % sysvals.cronjobCmdString()) op.close() - res = call('crontab %s' % cronfile, shell=True) + res = call([cmd, cronfile]) except Exception, e: print 'Exception: %s' % str(e) shutil.move(backfile, cronfile) @@ -577,25 +705,16 @@ def updateGrub(restore=False): # call update-grub on restore if restore: try: - call(['update-grub'], stderr=PIPE, stdout=PIPE, + call(sysvals.blexec, stderr=PIPE, stdout=PIPE, env={'PATH': '.:/sbin:/usr/sbin:/usr/bin:/sbin:/bin'}) except Exception, e: print 'Exception: %s\n' % str(e) return - # verify we can do this - sysvals.rootUser(True) - grubfile = '/etc/default/grub' - if not os.path.exists(grubfile): - print 'ERROR: Unable to set the kernel parameters via grub.\n' - sysvals.manualRebootRequired() - out = Popen(['which', 'update-grub'], stdout=PIPE).stdout.read() - if not out: - print 'ERROR: Unable to set the kernel parameters via grub.\n' - sysvals.manualRebootRequired() - # extract the option and create a grub config without it + sysvals.rootUser(True) tgtopt = 'GRUB_CMDLINE_LINUX_DEFAULT' cmdline = '' + grubfile = '/etc/default/grub' tempfile = '/etc/default/grub.analyze_boot' shutil.move(grubfile, tempfile) res = -1 @@ -622,7 +741,7 @@ def updateGrub(restore=False): # if the target option value is in quotes, strip them sp = '"' val = cmdline.strip() - if val[0] == '\'' or val[0] == '"': + if val and (val[0] == '\'' or val[0] == '"'): sp = val[0] val = val.strip(sp) cmdline = val @@ -633,7 +752,7 @@ def updateGrub(restore=False): # write out the updated target option op.write('\n%s=%s%s%s\n' % (tgtopt, sp, cmdline, sp)) op.close() - res = call('update-grub') + res = call(sysvals.blexec) os.remove(grubfile) except Exception, e: print 'Exception: %s' % str(e) @@ -641,10 +760,18 @@ def updateGrub(restore=False): # cleanup shutil.move(tempfile, grubfile) if res != 0: - doError('update-grub failed') + doError('update grub failed') -# Function: doError +# Function: updateKernelParams # Description: +# update boot conf for all kernels with our parameters +def updateKernelParams(restore=False): + # find the boot loader + sysvals.getBootLoader() + if sysvals.bootloader == 'grub': + updateGrub(restore) + +# Function: doError Description: # generic error function for catastrphic failures # Arguments: # msg: the error message to print @@ -660,7 +787,7 @@ def doError(msg, help=False): # print out the help text def printHelp(): print('') - print('%s v%.1f' % (sysvals.title, sysvals.version)) + print('%s v%s' % (sysvals.title, sysvals.version)) print('Usage: bootgraph <options> <command>') print('') print('Description:') @@ -669,13 +796,19 @@ def printHelp(): print(' the start of the init process.') print('') print(' If no specific command is given the tool reads the current dmesg') - print(' and/or ftrace log and outputs bootgraph.html') + print(' and/or ftrace log and creates a timeline') + print('') + print(' Generates output files in subdirectory: boot-yymmdd-HHMMSS') + print(' HTML output: <hostname>_boot.html') + print(' raw dmesg output: <hostname>_boot_dmesg.txt') + print(' raw ftrace output: <hostname>_boot_ftrace.txt') print('') print('Options:') print(' -h Print this help text') print(' -v Print the current tool version') print(' -addlogs Add the dmesg log to the html output') - print(' -o file Html timeline name (default: bootgraph.html)') + print(' -o name Overrides the output subdirectory name when running a new test') + print(' default: boot-{date}-{time}') print(' [advanced]') print(' -f Use ftrace to add function detail (default: disabled)') print(' -callgraph Add callgraph detail, can be very large (default: disabled)') @@ -683,13 +816,18 @@ def printHelp(): print(' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)') print(' -timeprec N Number of significant digits in timestamps (0:S, 3:ms, [6:us])') print(' -expandcg pre-expand the callgraph data in the html output (default: disabled)') - print(' -filter list Limit ftrace to comma-delimited list of functions (default: do_one_initcall)') - print(' [commands]') + print(' -func list Limit ftrace to comma-delimited list of functions (default: do_one_initcall)') + print(' -cgfilter S Filter the callgraph output in the timeline') + print(' -bl name Use the following boot loader for kernel params (default: grub)') print(' -reboot Reboot the machine automatically and generate a new timeline') - print(' -manual Show the requirements to generate a new timeline manually') - print(' -dmesg file Load a stored dmesg file (used with -ftrace)') - print(' -ftrace file Load a stored ftrace file (used with -dmesg)') + print(' -manual Show the steps to generate a new timeline manually (used with -reboot)') + print('') + print('Other commands:') print(' -flistall Print all functions capable of being captured in ftrace') + print(' -sysinfo Print out system info extracted from BIOS') + print(' [redo]') + print(' -dmesg file Create HTML output using dmesg input (used with -ftrace)') + print(' -ftrace file Create HTML output using ftrace input (used with -dmesg)') print('') return True @@ -698,14 +836,15 @@ def printHelp(): if __name__ == '__main__': # loop through the command line arguments cmd = '' - simplecmds = ['-updategrub', '-flistall'] + testrun = True + simplecmds = ['-sysinfo', '-kpupdate', '-flistall', '-checkbl'] args = iter(sys.argv[1:]) for arg in args: if(arg == '-h'): printHelp() sys.exit() elif(arg == '-v'): - print("Version %.1f" % sysvals.version) + print("Version %s" % sysvals.version) sys.exit() elif(arg in simplecmds): cmd = arg[1:] @@ -716,16 +855,32 @@ if __name__ == '__main__': sysvals.usecallgraph = True elif(arg == '-mincg'): sysvals.mincglen = aslib.getArgFloat('-mincg', args, 0.0, 10000.0) + elif(arg == '-cgfilter'): + try: + val = args.next() + except: + doError('No callgraph functions supplied', True) + sysvals.setDeviceFilter(val) + elif(arg == '-bl'): + try: + val = args.next() + except: + doError('No boot loader name supplied', True) + if val.lower() not in ['grub']: + doError('Unknown boot loader: %s' % val, True) + sysvals.bootloader = val.lower() elif(arg == '-timeprec'): sysvals.setPrecision(aslib.getArgInt('-timeprec', args, 0, 6)) elif(arg == '-maxdepth'): sysvals.max_graph_depth = aslib.getArgInt('-maxdepth', args, 0, 1000) - elif(arg == '-filter'): + elif(arg == '-func'): try: val = args.next() except: doError('No filter functions supplied', True) - aslib.rootCheck(True) + sysvals.useftrace = True + sysvals.usecallgraph = True + sysvals.rootCheck(True) sysvals.setGraphFilter(val) elif(arg == '-ftrace'): try: @@ -734,9 +889,10 @@ if __name__ == '__main__': doError('No ftrace file supplied', True) if(os.path.exists(val) == False): doError('%s does not exist' % val) + testrun = False sysvals.ftracefile = val elif(arg == '-addlogs'): - sysvals.addlogs = True + sysvals.dmesglog = True elif(arg == '-expandcg'): sysvals.cgexp = True elif(arg == '-dmesg'): @@ -748,18 +904,15 @@ if __name__ == '__main__': doError('%s does not exist' % val) if(sysvals.htmlfile == val or sysvals.outfile == val): doError('Output filename collision') + testrun = False sysvals.dmesgfile = val elif(arg == '-o'): try: val = args.next() except: - doError('No HTML filename supplied', True) - if(sysvals.dmesgfile == val or sysvals.ftracefile == val): - doError('Output filename collision') - sysvals.htmlfile = val + doError('No subdirectory name supplied', True) + sysvals.testdir = sysvals.setOutputFolder(val) elif(arg == '-reboot'): - if sysvals.iscronjob: - doError('-reboot and -cronjob are incompatible') sysvals.reboot = True elif(arg == '-manual'): sysvals.reboot = True @@ -767,58 +920,93 @@ if __name__ == '__main__': # remaining options are only for cron job use elif(arg == '-cronjob'): sysvals.iscronjob = True - if sysvals.reboot: - doError('-reboot and -cronjob are incompatible') else: doError('Invalid argument: '+arg, True) + # compatibility errors and access checks + if(sysvals.iscronjob and (sysvals.reboot or \ + sysvals.dmesgfile or sysvals.ftracefile or cmd)): + doError('-cronjob is meant for batch purposes only') + if(sysvals.reboot and (sysvals.dmesgfile or sysvals.ftracefile)): + doError('-reboot and -dmesg/-ftrace are incompatible') + if cmd or sysvals.reboot or sysvals.iscronjob or testrun: + sysvals.rootCheck(True) + if (testrun and sysvals.useftrace) or cmd == 'flistall': + if not sysvals.verifyFtrace(): + doError('Ftrace is not properly enabled') + + # run utility commands + sysvals.cpuInfo() if cmd != '': - if cmd == 'updategrub': - updateGrub() + if cmd == 'kpupdate': + updateKernelParams() elif cmd == 'flistall': - sysvals.getFtraceFilterFunctions(False) + for f in sysvals.getBootFtraceFilterFunctions(): + print f + elif cmd == 'checkbl': + sysvals.getBootLoader() + print 'Boot Loader: %s\n%s' % (sysvals.bootloader, sysvals.blexec) + elif(cmd == 'sysinfo'): + sysvals.printSystemInfo() sys.exit() - # update grub, setup a cronjob, and reboot + # reboot: update grub, setup a cronjob, and reboot if sysvals.reboot: + if (sysvals.useftrace or sysvals.usecallgraph) and \ + not sysvals.checkFtraceKernelVersion(): + doError('Ftrace functionality requires kernel v4.10 or newer') if not sysvals.manual: - updateGrub() + updateKernelParams() updateCron() call('reboot') else: sysvals.manualRebootRequired() sys.exit() - # disable the cronjob + # cronjob: remove the cronjob, grub changes, and disable ftrace if sysvals.iscronjob: updateCron(True) - updateGrub(True) + updateKernelParams(True) + try: + sysvals.fsetVal('0', 'tracing_on') + except: + pass - data = loadKernelLog() - if sysvals.useftrace: - loadTraceLog(data) - if sysvals.iscronjob: - try: - sysvals.fsetVal('0', 'tracing_on') - except: - pass + # testrun: generate copies of the logs + if testrun: + retrieveLogs() + else: + sysvals.setOutputFile() - if(sysvals.outfile and sysvals.phoronix): - fp = open(sysvals.outfile, 'w') - fp.write('pass %s initstart %.3f end %.3f boot %s\n' % - (data.valid, data.initstart*1000, data.end*1000, data.boottime)) - fp.close() - if(not data.valid): - if sysvals.dmesgfile: + # process the log data + if sysvals.dmesgfile: + data = parseKernelLog() + if(not data.valid): doError('No initcall data found in %s' % sysvals.dmesgfile) - else: - doError('No initcall data found, is initcall_debug enabled?') + if sysvals.useftrace and sysvals.ftracefile: + parseTraceLog(data) + else: + doError('dmesg file required') print(' Host: %s' % sysvals.hostname) print(' Test time: %s' % sysvals.testtime) print(' Boot time: %s' % data.boottime) print('Kernel Version: %s' % sysvals.kernel) print(' Kernel start: %.3f' % (data.start * 1000)) - print(' init start: %.3f' % (data.initstart * 1000)) + print('Usermode start: %.3f' % (data.tUserMode * 1000)) + print('Last Init Call: %.3f' % (data.end * 1000)) + + # handle embedded output logs + if(sysvals.outfile and sysvals.embedded): + fp = open(sysvals.outfile, 'w') + fp.write('pass %s initstart %.3f end %.3f boot %s\n' % + (data.valid, data.tUserMode*1000, data.end*1000, data.boottime)) + fp.close() + + createBootGraph(data) - createBootGraph(data, sysvals.phoronix) + # if running as root, change output dir owner to sudo_user + if testrun and os.path.isdir(sysvals.testdir) and \ + os.getuid() == 0 and 'SUDO_USER' in os.environ: + cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1' + call(cmd.format(os.environ['SUDO_USER'], sysvals.testdir), shell=True) diff --git a/tools/power/pm-graph/analyze_suspend.py b/tools/power/pm-graph/analyze_suspend.py index a9206e67fc1f..1b60fe203741 100755 --- a/tools/power/pm-graph/analyze_suspend.py +++ b/tools/power/pm-graph/analyze_suspend.py @@ -68,10 +68,12 @@ from subprocess import call, Popen, PIPE # store system values and test parameters class SystemValues: title = 'SleepGraph' - version = '4.6' + version = '4.7' ansi = False verbose = False - addlogs = False + testlog = True + dmesglog = False + ftracelog = False mindevlen = 0.0 mincglen = 0.0 cgphase = '' @@ -79,10 +81,11 @@ class SystemValues: max_graph_depth = 0 callloopmaxgap = 0.0001 callloopmaxlen = 0.005 + cpucount = 0 + memtotal = 204800 srgap = 0 cgexp = False - outdir = '' - testdir = '.' + testdir = '' tpath = '/sys/kernel/debug/tracing/' fpdtpath = '/sys/firmware/acpi/tables/FPDT' epath = '/sys/kernel/debug/tracing/events/power/' @@ -95,14 +98,17 @@ class SystemValues: testcommand = '' mempath = '/dev/mem' powerfile = '/sys/power/state' + mempowerfile = '/sys/power/mem_sleep' suspendmode = 'mem' + memmode = '' hostname = 'localhost' prefix = 'test' teststamp = '' + sysstamp = '' dmesgstart = 0.0 dmesgfile = '' ftracefile = '' - htmlfile = '' + htmlfile = 'output.html' embedded = False rtcwake = True rtcwaketime = 15 @@ -127,9 +133,6 @@ class SystemValues: devpropfmt = '# Device Properties: .*' tracertypefmt = '# tracer: (?P<t>.*)' firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$' - stampfmt = '# suspend-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\ - '(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\ - ' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$' tracefuncs = { 'sys_sync': dict(), 'pm_prepare_console': dict(), @@ -218,7 +221,7 @@ class SystemValues: # if this is a phoronix test run, set some default options if('LOG_FILE' in os.environ and 'TEST_RESULTS_IDENTIFIER' in os.environ): self.embedded = True - self.addlogs = True + self.dmesglog = self.ftracelog = True self.htmlfile = os.environ['LOG_FILE'] self.archargs = 'args_'+platform.machine() self.hostname = platform.node() @@ -233,6 +236,13 @@ class SystemValues: self.rtcpath = rtc if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()): self.ansi = True + self.testdir = datetime.now().strftime('suspend-%y%m%d-%H%M%S') + def rootCheck(self, fatal=True): + if(os.access(self.powerfile, os.W_OK)): + return True + if fatal: + doError('This command requires sysfs mount and root access') + return False def rootUser(self, fatal=False): if 'USER' in os.environ and os.environ['USER'] == 'root': return True @@ -249,30 +259,60 @@ class SystemValues: args['date'] = n.strftime('%y%m%d') args['time'] = n.strftime('%H%M%S') args['hostname'] = self.hostname - self.outdir = value.format(**args) + return value.format(**args) def setOutputFile(self): - if((self.htmlfile == '') and (self.dmesgfile != '')): + if self.dmesgfile != '': m = re.match('(?P<name>.*)_dmesg\.txt$', self.dmesgfile) if(m): self.htmlfile = m.group('name')+'.html' - if((self.htmlfile == '') and (self.ftracefile != '')): + if self.ftracefile != '': m = re.match('(?P<name>.*)_ftrace\.txt$', self.ftracefile) if(m): self.htmlfile = m.group('name')+'.html' - if(self.htmlfile == ''): - self.htmlfile = 'output.html' - def initTestOutput(self, subdir, testpath=''): + def systemInfo(self, info): + p = c = m = b = '' + if 'baseboard-manufacturer' in info: + m = info['baseboard-manufacturer'] + elif 'system-manufacturer' in info: + m = info['system-manufacturer'] + if 'baseboard-product-name' in info: + p = info['baseboard-product-name'] + elif 'system-product-name' in info: + p = info['system-product-name'] + if 'processor-version' in info: + c = info['processor-version'] + if 'bios-version' in info: + b = info['bios-version'] + self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | numcpu:%d | memsz:%d' % \ + (m, p, c, b, self.cpucount, self.memtotal) + def printSystemInfo(self): + self.rootCheck(True) + out = dmidecode(self.mempath, True) + fmt = '%-24s: %s' + for name in sorted(out): + print fmt % (name, out[name]) + print fmt % ('cpucount', ('%d' % self.cpucount)) + print fmt % ('memtotal', ('%d kB' % self.memtotal)) + def cpuInfo(self): + self.cpucount = 0 + fp = open('/proc/cpuinfo', 'r') + for line in fp: + if re.match('^processor[ \t]*:[ \t]*[0-9]*', line): + self.cpucount += 1 + fp.close() + fp = open('/proc/meminfo', 'r') + for line in fp: + m = re.match('^MemTotal:[ \t]*(?P<sz>[0-9]*) *kB', line) + if m: + self.memtotal = int(m.group('sz')) + break + fp.close() + def initTestOutput(self, name): self.prefix = self.hostname v = open('/proc/version', 'r').read().strip() kver = string.split(v)[2] - n = datetime.now() - testtime = n.strftime('suspend-%m%d%y-%H%M%S') - if not testpath: - testpath = n.strftime('suspend-%y%m%d-%H%M%S') - if(subdir != "."): - self.testdir = subdir+"/"+testpath - else: - self.testdir = testpath + fmt = name+'-%m%d%y-%H%M%S' + testtime = datetime.now().strftime(fmt) self.teststamp = \ '# '+testtime+' '+self.prefix+' '+self.suspendmode+' '+kver if(self.embedded): @@ -355,7 +395,7 @@ class SystemValues: continue self.tracefuncs[i] = dict() def getFtraceFilterFunctions(self, current): - rootCheck(True) + self.rootCheck(True) if not current: call('cat '+self.tpath+'available_filter_functions', shell=True) return @@ -453,7 +493,7 @@ class SystemValues: val += '\nr:%s_ret %s $retval\n' % (name, func) return val def addKprobes(self, output=False): - if len(sysvals.kprobes) < 1: + if len(self.kprobes) < 1: return if output: print(' kprobe functions in this kernel:') @@ -525,7 +565,7 @@ class SystemValues: fp.flush() fp.close() except: - pass + return False return True def fgetVal(self, path): file = self.tpath+path @@ -566,9 +606,15 @@ class SystemValues: self.cleanupFtrace() # set the trace clock to global self.fsetVal('global', 'trace_clock') - # set trace buffer to a huge value self.fsetVal('nop', 'current_tracer') - self.fsetVal('131073', 'buffer_size_kb') + # set trace buffer to a huge value + if self.usecallgraph or self.usedevsrc: + tgtsize = min(self.memtotal / 2, 2*1024*1024) + maxbuf = '%d' % (tgtsize / max(1, self.cpucount)) + if self.cpucount < 1 or not self.fsetVal(maxbuf, 'buffer_size_kb'): + self.fsetVal('131072', 'buffer_size_kb') + else: + self.fsetVal('16384', 'buffer_size_kb') # go no further if this is just a status check if testing: return @@ -641,6 +687,15 @@ class SystemValues: if not self.ansi: return str return '\x1B[%d;40m%s\x1B[m' % (color, str) + def writeDatafileHeader(self, filename, fwdata=[]): + fp = open(filename, 'w') + fp.write(self.teststamp+'\n') + fp.write(self.sysstamp+'\n') + if(self.suspendmode == 'mem' or self.suspendmode == 'command'): + for fw in fwdata: + if(fw): + fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1])) + fp.close() sysvals = SystemValues() suspendmodename = { @@ -1008,6 +1063,12 @@ class Data: else: self.trimTime(self.tSuspended, \ self.tResumed-self.tSuspended, False) + def getTimeValues(self): + sktime = (self.dmesg['suspend_machine']['end'] - \ + self.tKernSus) * 1000 + rktime = (self.dmesg['resume_complete']['end'] - \ + self.dmesg['resume_machine']['start']) * 1000 + return (sktime, rktime) def setPhase(self, phase, ktime, isbegin): if(isbegin): self.dmesg[phase]['start'] = ktime @@ -1517,7 +1578,7 @@ class FTraceCallGraph: prelinedep += 1 last = 0 lasttime = line.time - virtualfname = 'execution_misalignment' + virtualfname = 'missing_function_name' if len(self.list) > 0: last = self.list[-1] lasttime = last.time @@ -1773,24 +1834,30 @@ class Timeline: html_device = '<div id="{0}" title="{1}" class="thread{7}" style="left:{2}%;top:{3}px;height:{4}px;width:{5}%;{8}">{6}</div>\n' html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}px;height:{3}px;background:{4}">{5}</div>\n' html_phaselet = '<div id="{0}" class="phaselet" style="left:{1}%;width:{2}%;background:{3}"></div>\n' + html_legend = '<div id="p{3}" class="square" style="left:{0}%;background:{1}"> {2}</div>\n' def __init__(self, rowheight, scaleheight): self.rowH = rowheight self.scaleH = scaleheight self.html = '' - def createHeader(self, sv, suppress=''): + def createHeader(self, sv): if(not sv.stamp['time']): return self.html += '<div class="version"><a href="https://01.org/suspendresume">%s v%s</a></div>' \ % (sv.title, sv.version) - if sv.logmsg and 'log' not in suppress: - self.html += '<button id="showtest" class="logbtn">log</button>' - if sv.addlogs and 'dmesg' not in suppress: - self.html += '<button id="showdmesg" class="logbtn">dmesg</button>' - if sv.addlogs and sv.ftracefile and 'ftrace' not in suppress: - self.html += '<button id="showftrace" class="logbtn">ftrace</button>' + if sv.logmsg and sv.testlog: + self.html += '<button id="showtest" class="logbtn btnfmt">log</button>' + if sv.dmesglog: + self.html += '<button id="showdmesg" class="logbtn btnfmt">dmesg</button>' + if sv.ftracelog: + self.html += '<button id="showftrace" class="logbtn btnfmt">ftrace</button>' headline_stamp = '<div class="stamp">{0} {1} {2} {3}</div>\n' self.html += headline_stamp.format(sv.stamp['host'], sv.stamp['kernel'], sv.stamp['mode'], sv.stamp['time']) + if 'man' in sv.stamp and 'plat' in sv.stamp and 'cpu' in sv.stamp: + headline_sysinfo = '<div class="stamp sysinfo">{0} {1} <i>with</i> {2}</div>\n' + self.html += headline_sysinfo.format(sv.stamp['man'], + sv.stamp['plat'], sv.stamp['cpu']) + # Function: getDeviceRows # Description: # determine how may rows the device funcs will take @@ -1839,7 +1906,7 @@ class Timeline: # devlist: the list of devices/actions in a group of contiguous phases # Output: # The total number of rows needed to display this phase of the timeline - def getPhaseRows(self, devlist, row=0): + def getPhaseRows(self, devlist, row=0, sortby='length'): # clear all rows and set them to undefined remaining = len(devlist) rowdata = dict() @@ -1852,8 +1919,12 @@ class Timeline: if tp not in myphases: myphases.append(tp) dev['row'] = -1 - # sort by length 1st, then name 2nd - sortdict[item] = (float(dev['end']) - float(dev['start']), item.dev['name']) + if sortby == 'start': + # sort by start 1st, then length 2nd + sortdict[item] = (-1*float(dev['start']), float(dev['end']) - float(dev['start'])) + else: + # sort by length 1st, then name 2nd + sortdict[item] = (float(dev['end']) - float(dev['start']), item.dev['name']) if 'src' in dev: dev['devrows'] = self.getDeviceRows(dev['src']) # sort the devlist by length so that large items graph on top @@ -1995,8 +2066,13 @@ class Timeline: # A list of values describing the properties of these test runs class TestProps: stamp = '' + sysinfo = '' S0i3 = False fwdata = [] + stampfmt = '# [a-z]*-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\ + '(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\ + ' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$' + sysinfofmt = '^# sysinfo .*' ftrace_line_fmt_fg = \ '^ *(?P<time>[0-9\.]*) *\| *(?P<cpu>[0-9]*)\)'+\ ' *(?P<proc>.*)-(?P<pid>[0-9]*) *\|'+\ @@ -2019,6 +2095,36 @@ class TestProps: self.ftrace_line_fmt = self.ftrace_line_fmt_nop else: doError('Invalid tracer format: [%s]' % tracer) + def parseStamp(self, data, sv): + m = re.match(self.stampfmt, self.stamp) + data.stamp = {'time': '', 'host': '', 'mode': ''} + dt = datetime(int(m.group('y'))+2000, int(m.group('m')), + int(m.group('d')), int(m.group('H')), int(m.group('M')), + int(m.group('S'))) + data.stamp['time'] = dt.strftime('%B %d %Y, %I:%M:%S %p') + data.stamp['host'] = m.group('host') + data.stamp['mode'] = m.group('mode') + data.stamp['kernel'] = m.group('kernel') + if re.match(self.sysinfofmt, self.sysinfo): + for f in self.sysinfo.split('|'): + if '#' in f: + continue + tmp = f.strip().split(':', 1) + key = tmp[0] + val = tmp[1] + data.stamp[key] = val + sv.hostname = data.stamp['host'] + sv.suspendmode = data.stamp['mode'] + if sv.suspendmode == 'command' and sv.ftracefile != '': + modes = ['on', 'freeze', 'standby', 'mem'] + out = Popen(['grep', 'suspend_enter', sv.ftracefile], + stderr=PIPE, stdout=PIPE).stdout.read() + m = re.match('.* suspend_enter\[(?P<mode>.*)\]', out) + if m and m.group('mode') in ['1', '2', '3']: + sv.suspendmode = modes[int(m.group('mode'))] + data.stamp['mode'] = sv.suspendmode + if not sv.stamp: + sv.stamp = data.stamp # Class: TestRun # Description: @@ -2090,35 +2196,6 @@ def vprint(msg): if(sysvals.verbose): print(msg) -# Function: parseStamp -# Description: -# Pull in the stamp comment line from the data file(s), -# create the stamp, and add it to the global sysvals object -# Arguments: -# m: the valid re.match output for the stamp line -def parseStamp(line, data): - m = re.match(sysvals.stampfmt, line) - data.stamp = {'time': '', 'host': '', 'mode': ''} - dt = datetime(int(m.group('y'))+2000, int(m.group('m')), - int(m.group('d')), int(m.group('H')), int(m.group('M')), - int(m.group('S'))) - data.stamp['time'] = dt.strftime('%B %d %Y, %I:%M:%S %p') - data.stamp['host'] = m.group('host') - data.stamp['mode'] = m.group('mode') - data.stamp['kernel'] = m.group('kernel') - sysvals.hostname = data.stamp['host'] - sysvals.suspendmode = data.stamp['mode'] - if sysvals.suspendmode == 'command' and sysvals.ftracefile != '': - modes = ['on', 'freeze', 'standby', 'mem'] - out = Popen(['grep', 'suspend_enter', sysvals.ftracefile], - stderr=PIPE, stdout=PIPE).stdout.read() - m = re.match('.* suspend_enter\[(?P<mode>.*)\]', out) - if m and m.group('mode') in ['1', '2', '3']: - sysvals.suspendmode = modes[int(m.group('mode'))] - data.stamp['mode'] = sysvals.suspendmode - if not sysvals.stamp: - sysvals.stamp = data.stamp - # Function: doesTraceLogHaveTraceEvents # Description: # Quickly determine if the ftrace log has some or all of the trace events @@ -2136,11 +2213,6 @@ def doesTraceLogHaveTraceEvents(): sysvals.usekprobes = True out = Popen(['head', '-1', sysvals.ftracefile], stderr=PIPE, stdout=PIPE).stdout.read().replace('\n', '') - m = re.match(sysvals.stampfmt, out) - if m and m.group('mode') == 'command': - sysvals.usetraceeventsonly = True - sysvals.usetraceevents = True - return # figure out what level of trace events are supported sysvals.usetraceeventsonly = True sysvals.usetraceevents = False @@ -2182,11 +2254,13 @@ def appendIncompleteTraceLog(testruns): for line in tf: # remove any latent carriage returns line = line.replace('\r\n', '') - # grab the time stamp - m = re.match(sysvals.stampfmt, line) - if(m): + # grab the stamp and sysinfo + if re.match(tp.stampfmt, line): tp.stamp = line continue + elif re.match(tp.sysinfofmt, line): + tp.sysinfo = line + continue # determine the trace data type (required for further parsing) m = re.match(sysvals.tracertypefmt, line) if(m): @@ -2219,7 +2293,7 @@ def appendIncompleteTraceLog(testruns): # look for the suspend start marker if(t.startMarker()): data = testrun[testidx].data - parseStamp(tp.stamp, data) + tp.parseStamp(data, sysvals) data.setStart(t.time) continue if(not data): @@ -2389,11 +2463,13 @@ def parseTraceLog(): for line in tf: # remove any latent carriage returns line = line.replace('\r\n', '') - # stamp line: each stamp means a new test run - m = re.match(sysvals.stampfmt, line) - if(m): + # stamp and sysinfo lines + if re.match(tp.stampfmt, line): tp.stamp = line continue + elif re.match(tp.sysinfofmt, line): + tp.sysinfo = line + continue # firmware line: pull out any firmware data m = re.match(sysvals.firmwarefmt, line) if(m): @@ -2439,7 +2515,7 @@ def parseTraceLog(): testdata.append(data) testrun = TestRun(data) testruns.append(testrun) - parseStamp(tp.stamp, data) + tp.parseStamp(data, sysvals) data.setStart(t.time) data.tKernSus = t.time continue @@ -2820,10 +2896,13 @@ def loadKernelLog(justtext=False): idx = line.find('[') if idx > 1: line = line[idx:] - m = re.match(sysvals.stampfmt, line) - if(m): + # grab the stamp and sysinfo + if re.match(tp.stampfmt, line): tp.stamp = line continue + elif re.match(tp.sysinfofmt, line): + tp.sysinfo = line + continue m = re.match(sysvals.firmwarefmt, line) if(m): tp.fwdata.append((int(m.group('s')), int(m.group('r')))) @@ -2839,7 +2918,7 @@ def loadKernelLog(justtext=False): if(data): testruns.append(data) data = Data(len(testruns)) - parseStamp(tp.stamp, data) + tp.parseStamp(data, sysvals) if len(tp.fwdata) > data.testnumber: data.fwSuspend, data.fwResume = tp.fwdata[data.testnumber] if(data.fwSuspend > 0 or data.fwResume > 0): @@ -3170,6 +3249,8 @@ def addCallgraphs(sv, hf, data): continue list = data.dmesg[p]['list'] for devname in data.sortedDevices(p): + if len(sv.devicefilter) > 0 and devname not in sv.devicefilter: + continue dev = list[devname] color = 'white' if 'color' in data.dmesg[p]: @@ -3309,7 +3390,6 @@ def createHTML(testruns): html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">ERROR→</div>\n' html_traceevent = '<div title="{0}" class="traceevent{6}" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;{7}">{5}</div>\n' html_cpuexec = '<div class="jiffie" style="left:{0}%;top:{1}px;height:{2}px;width:{3}%;background:{4};"></div>\n' - html_legend = '<div id="p{3}" class="square" style="left:{0}%;background:{1}"> {2}</div>\n' html_timetotal = '<table class="time1">\n<tr>'\ '<td class="green" title="{3}">{2} Suspend Time: <b>{0} ms</b></td>'\ '<td class="yellow" title="{4}">{2} Resume Time: <b>{1} ms</b></td>'\ @@ -3346,10 +3426,7 @@ def createHTML(testruns): # Generate the header for this timeline for data in testruns: tTotal = data.end - data.start - sktime = (data.dmesg['suspend_machine']['end'] - \ - data.tKernSus) * 1000 - rktime = (data.dmesg['resume_complete']['end'] - \ - data.dmesg['resume_machine']['start']) * 1000 + sktime, rktime = data.getTimeValues() if(tTotal == 0): print('ERROR: No timeline data') sys.exit() @@ -3581,7 +3658,7 @@ def createHTML(testruns): id += tmp[1][0] order = '%.2f' % ((data.dmesg[phase]['order'] * pdelta) + pmargin) name = string.replace(phase, '_', ' ') - devtl.html += html_legend.format(order, \ + devtl.html += devtl.html_legend.format(order, \ data.dmesg[phase]['color'], name, id) devtl.html += '</div>\n' @@ -3628,10 +3705,10 @@ def createHTML(testruns): addCallgraphs(sysvals, hf, data) # add the test log as a hidden div - if sysvals.logmsg: + if sysvals.testlog and sysvals.logmsg: hf.write('<div id="testlog" style="display:none;">\n'+sysvals.logmsg+'</div>\n') # add the dmesg log as a hidden div - if sysvals.addlogs and sysvals.dmesgfile: + if sysvals.dmesglog and sysvals.dmesgfile: hf.write('<div id="dmesglog" style="display:none;">\n') lf = open(sysvals.dmesgfile, 'r') for line in lf: @@ -3640,7 +3717,7 @@ def createHTML(testruns): lf.close() hf.write('</div>\n') # add the ftrace log as a hidden div - if sysvals.addlogs and sysvals.ftracefile: + if sysvals.ftracelog and sysvals.ftracefile: hf.write('<div id="ftracelog" style="display:none;">\n') lf = open(sysvals.ftracefile, 'r') for line in lf: @@ -3701,6 +3778,7 @@ def addCSS(hf, sv, testcount=1, kerror=False, extra=''): <style type=\'text/css\'>\n\ body {overflow-y:scroll;}\n\ .stamp {width:100%;text-align:center;background:gray;line-height:30px;color:white;font:25px Arial;}\n\ + .stamp.sysinfo {font:10px Arial;}\n\ .callgraph {margin-top:30px;box-shadow:5px 5px 20px black;}\n\ .callgraph article * {padding-left:28px;}\n\ h1 {color:black;font:bold 30px Times;}\n\ @@ -3746,7 +3824,7 @@ def addCSS(hf, sv, testcount=1, kerror=False, extra=''): .legend {position:relative; width:100%; height:40px; text-align:center;margin-bottom:20px}\n\ .legend .square {position:absolute;cursor:pointer;top:10px; width:0px;height:20px;border:1px solid;padding-left:20px;}\n\ button {height:40px;width:200px;margin-bottom:20px;margin-top:20px;font-size:24px;}\n\ - .logbtn {position:relative;float:right;height:25px;width:50px;margin-top:3px;margin-bottom:0;font-size:10px;text-align:center;}\n\ + .btnfmt {position:relative;float:right;height:25px;width:auto;margin-top:3px;margin-bottom:0;font-size:10px;text-align:center;}\n\ .devlist {position:'+devlistpos+';width:190px;}\n\ a:link {color:white;text-decoration:none;}\n\ a:visited {color:white;}\n\ @@ -4084,8 +4162,6 @@ def addScriptCode(hf, testruns): ' win.document.write(title+"<pre>"+log.innerHTML+"</pre>");\n'\ ' win.document.close();\n'\ ' }\n'\ - ' function onClickPhase(e) {\n'\ - ' }\n'\ ' function onMouseDown(e) {\n'\ ' dragval[0] = e.clientX;\n'\ ' dragval[1] = document.getElementById("dmesgzoombox").scrollLeft;\n'\ @@ -4120,9 +4196,6 @@ def addScriptCode(hf, testruns): ' document.getElementById("zoomin").onclick = zoomTimeline;\n'\ ' document.getElementById("zoomout").onclick = zoomTimeline;\n'\ ' document.getElementById("zoomdef").onclick = zoomTimeline;\n'\ - ' var list = document.getElementsByClassName("square");\n'\ - ' for (var i = 0; i < list.length; i++)\n'\ - ' list[i].onclick = onClickPhase;\n'\ ' var list = document.getElementsByClassName("err");\n'\ ' for (var i = 0; i < list.length; i++)\n'\ ' list[i].onclick = errWindow;\n'\ @@ -4193,8 +4266,14 @@ def executeSuspend(): if sysvals.testcommand != '': call(sysvals.testcommand+' 2>&1', shell=True); else: + mode = sysvals.suspendmode + if sysvals.memmode and os.path.exists(sysvals.mempowerfile): + mode = 'mem' + pf = open(sysvals.mempowerfile, 'w') + pf.write(sysvals.memmode) + pf.close() pf = open(sysvals.powerfile, 'w') - pf.write(sysvals.suspendmode) + pf.write(mode) # execution will pause here try: pf.close() @@ -4219,24 +4298,15 @@ def executeSuspend(): pm.stop() sysvals.fsetVal('0', 'tracing_on') print('CAPTURING TRACE') - writeDatafileHeader(sysvals.ftracefile, fwdata) + sysvals.writeDatafileHeader(sysvals.ftracefile, fwdata) call('cat '+tp+'trace >> '+sysvals.ftracefile, shell=True) sysvals.fsetVal('', 'trace') devProps() # grab a copy of the dmesg output print('CAPTURING DMESG') - writeDatafileHeader(sysvals.dmesgfile, fwdata) + sysvals.writeDatafileHeader(sysvals.dmesgfile, fwdata) sysvals.getdmesg() -def writeDatafileHeader(filename, fwdata): - fp = open(filename, 'a') - fp.write(sysvals.teststamp+'\n') - if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'): - for fw in fwdata: - if(fw): - fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1])) - fp.close() - # Function: setUSBDevicesAuto # Description: # Set the autosuspend control parameter of all USB devices to auto @@ -4244,7 +4314,7 @@ def writeDatafileHeader(filename, fwdata): # to always-on since the kernel cant determine if the device can # properly autosuspend def setUSBDevicesAuto(): - rootCheck(True) + sysvals.rootCheck(True) for dirname, dirnames, filenames in os.walk('/sys/devices'): if(re.match('.*/usb[0-9]*.*', dirname) and 'idVendor' in filenames and 'idProduct' in filenames): @@ -4467,13 +4537,146 @@ def devProps(data=0): # Output: # A string list of the available modes def getModes(): - modes = '' + modes = [] if(os.path.exists(sysvals.powerfile)): fp = open(sysvals.powerfile, 'r') modes = string.split(fp.read()) fp.close() + if(os.path.exists(sysvals.mempowerfile)): + deep = False + fp = open(sysvals.mempowerfile, 'r') + for m in string.split(fp.read()): + memmode = m.strip('[]') + if memmode == 'deep': + deep = True + else: + modes.append('mem-%s' % memmode) + fp.close() + if 'mem' in modes and not deep: + modes.remove('mem') return modes +# Function: dmidecode +# Description: +# Read the bios tables and pull out system info +# Arguments: +# mempath: /dev/mem or custom mem path +# fatal: True to exit on error, False to return empty dict +# Output: +# A dict object with all available key/values +def dmidecode(mempath, fatal=False): + out = dict() + + # the list of values to retrieve, with hardcoded (type, idx) + info = { + 'bios-vendor': (0, 4), + 'bios-version': (0, 5), + 'bios-release-date': (0, 8), + 'system-manufacturer': (1, 4), + 'system-product-name': (1, 5), + 'system-version': (1, 6), + 'system-serial-number': (1, 7), + 'baseboard-manufacturer': (2, 4), + 'baseboard-product-name': (2, 5), + 'baseboard-version': (2, 6), + 'baseboard-serial-number': (2, 7), + 'chassis-manufacturer': (3, 4), + 'chassis-type': (3, 5), + 'chassis-version': (3, 6), + 'chassis-serial-number': (3, 7), + 'processor-manufacturer': (4, 7), + 'processor-version': (4, 16), + } + if(not os.path.exists(mempath)): + if(fatal): + doError('file does not exist: %s' % mempath) + return out + if(not os.access(mempath, os.R_OK)): + if(fatal): + doError('file is not readable: %s' % mempath) + return out + + # by default use legacy scan, but try to use EFI first + memaddr = 0xf0000 + memsize = 0x10000 + for ep in ['/sys/firmware/efi/systab', '/proc/efi/systab']: + if not os.path.exists(ep) or not os.access(ep, os.R_OK): + continue + fp = open(ep, 'r') + buf = fp.read() + fp.close() + i = buf.find('SMBIOS=') + if i >= 0: + try: + memaddr = int(buf[i+7:], 16) + memsize = 0x20 + except: + continue + + # read in the memory for scanning + fp = open(mempath, 'rb') + try: + fp.seek(memaddr) + buf = fp.read(memsize) + except: + if(fatal): + doError('DMI table is unreachable, sorry') + else: + return out + fp.close() + + # search for either an SM table or DMI table + i = base = length = num = 0 + while(i < memsize): + if buf[i:i+4] == '_SM_' and i < memsize - 16: + length = struct.unpack('H', buf[i+22:i+24])[0] + base, num = struct.unpack('IH', buf[i+24:i+30]) + break + elif buf[i:i+5] == '_DMI_': + length = struct.unpack('H', buf[i+6:i+8])[0] + base, num = struct.unpack('IH', buf[i+8:i+14]) + break + i += 16 + if base == 0 and length == 0 and num == 0: + if(fatal): + doError('Neither SMBIOS nor DMI were found') + else: + return out + + # read in the SM or DMI table + fp = open(mempath, 'rb') + try: + fp.seek(base) + buf = fp.read(length) + except: + if(fatal): + doError('DMI table is unreachable, sorry') + else: + return out + fp.close() + + # scan the table for the values we want + count = i = 0 + while(count < num and i <= len(buf) - 4): + type, size, handle = struct.unpack('BBH', buf[i:i+4]) + n = i + size + while n < len(buf) - 1: + if 0 == struct.unpack('H', buf[n:n+2])[0]: + break + n += 1 + data = buf[i+size:n+2].split('\0') + for name in info: + itype, idxadr = info[name] + if itype == type: + idx = struct.unpack('B', buf[i+idxadr])[0] + if idx > 0 and idx < len(data) - 1: + s = data[idx-1].strip() + if s and s.lower() != 'to be filled by o.e.m.': + out[name] = data[idx-1] + i = n + 2 + count += 1 + return out + # Function: getFPDT # Description: # Read the acpi bios tables and pull out FPDT, the firmware data @@ -4487,7 +4690,7 @@ def getFPDT(output): prectype[0] = 'Basic S3 Resume Performance Record' prectype[1] = 'Basic S3 Suspend Performance Record' - rootCheck(True) + sysvals.rootCheck(True) if(not os.path.exists(sysvals.fpdtpath)): if(output): doError('file does not exist: %s' % sysvals.fpdtpath) @@ -4617,7 +4820,7 @@ def statusCheck(probecheck=False): # check we have root access res = sysvals.colorText('NO (No features of this tool will work!)') - if(rootCheck(False)): + if(sysvals.rootCheck(False)): res = 'YES' print(' have root access: %s' % res) if(res != 'YES'): @@ -4716,16 +4919,6 @@ def doError(msg, help=False): print('ERROR: %s\n') % msg sys.exit() -# Function: rootCheck -# Description: -# quick check to see if we have root access -def rootCheck(fatal): - if(os.access(sysvals.powerfile, os.W_OK)): - return True - if fatal: - doError('This command requires sysfs mount and root access') - return False - # Function: getArgInt # Description: # pull out an integer argument from the command line with checks @@ -4779,6 +4972,7 @@ def processData(): if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)): appendIncompleteTraceLog(testruns) createHTML(testruns) + return testruns # Function: rerunTest # Description: @@ -4790,17 +4984,20 @@ def rerunTest(): doError('recreating this html output requires a dmesg file') sysvals.setOutputFile() vprint('Output file: %s' % sysvals.htmlfile) - if(os.path.exists(sysvals.htmlfile) and not os.access(sysvals.htmlfile, os.W_OK)): - doError('missing permission to write to %s' % sysvals.htmlfile) - processData() + if os.path.exists(sysvals.htmlfile): + if not os.path.isfile(sysvals.htmlfile): + doError('a directory already exists with this name: %s' % sysvals.htmlfile) + elif not os.access(sysvals.htmlfile, os.W_OK): + doError('missing permission to write to %s' % sysvals.htmlfile) + return processData() # Function: runTest # Description: # execute a suspend/resume, gather the logs, and generate the output -def runTest(subdir, testpath=''): +def runTest(): # prepare for the test sysvals.initFtrace() - sysvals.initTestOutput(subdir, testpath) + sysvals.initTestOutput('suspend') vprint('Output files:\n\t%s\n\t%s\n\t%s' % \ (sysvals.dmesgfile, sysvals.ftracefile, sysvals.htmlfile)) @@ -4897,7 +5094,7 @@ def configFromFile(file): if(opt.lower() == 'verbose'): sysvals.verbose = checkArgBool(value) elif(opt.lower() == 'addlogs'): - sysvals.addlogs = checkArgBool(value) + sysvals.dmesglog = sysvals.ftracelog = checkArgBool(value) elif(opt.lower() == 'dev'): sysvals.usedevsrc = checkArgBool(value) elif(opt.lower() == 'proc'): @@ -4947,7 +5144,7 @@ def configFromFile(file): elif(opt.lower() == 'mincg'): sysvals.mincglen = getArgFloat('-mincg', value, 0.0, 10000.0, False) elif(opt.lower() == 'output-dir'): - sysvals.setOutputFolder(value) + sysvals.testdir = sysvals.setOutputFolder(value) if sysvals.suspendmode == 'command' and not sysvals.testcommand: doError('No command supplied for mode "command"') @@ -5030,8 +5227,6 @@ def configFromFile(file): # Description: # print out the help text def printHelp(): - modes = getModes() - print('') print('%s v%s' % (sysvals.title, sysvals.version)) print('Usage: sudo sleepgraph <options> <commands>') @@ -5048,7 +5243,7 @@ def printHelp(): print(' If no specific command is given, the default behavior is to initiate') print(' a suspend/resume and capture the dmesg/ftrace output as an html timeline.') print('') - print(' Generates output files in subdirectory: suspend-mmddyy-HHMMSS') + print(' Generates output files in subdirectory: suspend-yymmdd-HHMMSS') print(' HTML output: <hostname>_<mode>.html') print(' raw dmesg output: <hostname>_<mode>_dmesg.txt') print(' raw ftrace output: <hostname>_<mode>_ftrace.txt') @@ -5058,8 +5253,9 @@ def printHelp(): print(' -v Print the current tool version') print(' -config fn Pull arguments and config options from file fn') print(' -verbose Print extra information during execution and analysis') - print(' -m mode Mode to initiate for suspend %s (default: %s)') % (modes, sysvals.suspendmode) - print(' -o subdir Override the output subdirectory') + print(' -m mode Mode to initiate for suspend (default: %s)') % (sysvals.suspendmode) + print(' -o name Overrides the output subdirectory name when running a new test') + print(' default: suspend-{date}-{time}') print(' -rtcwake t Wakeup t seconds after suspend, set t to "off" to disable (default: 15)') print(' -addlogs Add the dmesg and ftrace logs to the html output') print(' -srgap Add a visible gap in the timeline between sus/res (default: disabled)') @@ -5084,17 +5280,20 @@ def printHelp(): print(' -cgphase P Only show callgraph data for phase P (e.g. suspend_late)') print(' -cgtest N Only show callgraph data for test N (e.g. 0 or 1 in an x2 run)') print(' -timeprec N Number of significant digits in timestamps (0:S, [3:ms], 6:us)') - print(' [commands]') - print(' -ftrace ftracefile Create HTML output using ftrace input (used with -dmesg)') - print(' -dmesg dmesgfile Create HTML output using dmesg (used with -ftrace)') - print(' -summary directory Create a summary of all test in this dir') + print('') + print('Other commands:') print(' -modes List available suspend modes') print(' -status Test to see if the system is enabled to run this tool') print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table') + print(' -sysinfo Print out system info extracted from BIOS') print(' -usbtopo Print out the current USB topology with power info') print(' -usbauto Enable autosuspend for all connected USB devices') print(' -flist Print the list of functions currently being captured in ftrace') print(' -flistall Print all functions capable of being captured in ftrace') + print(' -summary directory Create a summary of all test in this dir') + print(' [redo]') + print(' -ftrace ftracefile Create HTML output using ftrace input (used with -dmesg)') + print(' -dmesg dmesgfile Create HTML output using dmesg (used with -ftrace)') print('') return True @@ -5102,9 +5301,9 @@ def printHelp(): # exec start (skipped if script is loaded as library) if __name__ == '__main__': cmd = '' - cmdarg = '' + outdir = '' multitest = {'run': False, 'count': 0, 'delay': 0} - simplecmds = ['-modes', '-fpdt', '-flist', '-flistall', '-usbtopo', '-usbauto', '-status'] + simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall', '-usbtopo', '-usbauto', '-status'] # loop through the command line arguments args = iter(sys.argv[1:]) for arg in args: @@ -5135,7 +5334,7 @@ if __name__ == '__main__': elif(arg == '-f'): sysvals.usecallgraph = True elif(arg == '-addlogs'): - sysvals.addlogs = True + sysvals.dmesglog = sysvals.ftracelog = True elif(arg == '-verbose'): sysvals.verbose = True elif(arg == '-proc'): @@ -5195,7 +5394,7 @@ if __name__ == '__main__': val = args.next() except: doError('No subdirectory name supplied', True) - sysvals.setOutputFolder(val) + outdir = sysvals.setOutputFolder(val) elif(arg == '-config'): try: val = args.next() @@ -5236,7 +5435,7 @@ if __name__ == '__main__': except: doError('No directory supplied', True) cmd = 'summary' - cmdarg = val + outdir = val sysvals.notestrun = True if(os.path.isdir(val) == False): doError('%s is not accesible' % val) @@ -5260,11 +5459,14 @@ if __name__ == '__main__': sysvals.mincglen = sysvals.mindevlen # just run a utility command and exit + sysvals.cpuInfo() if(cmd != ''): if(cmd == 'status'): statusCheck(True) elif(cmd == 'fpdt'): getFPDT(True) + elif(cmd == 'sysinfo'): + sysvals.printSystemInfo() elif(cmd == 'usbtopo'): detectUSB() elif(cmd == 'modes'): @@ -5276,7 +5478,7 @@ if __name__ == '__main__': elif(cmd == 'usbauto'): setUSBDevicesAuto() elif(cmd == 'summary'): - runSummary(cmdarg, True) + runSummary(outdir, True) sys.exit() # if instructed, re-analyze existing data files @@ -5289,21 +5491,43 @@ if __name__ == '__main__': print('Check FAILED, aborting the test run!') sys.exit() + # extract mem modes and convert + mode = sysvals.suspendmode + if 'mem' == mode[:3]: + if '-' in mode: + memmode = mode.split('-')[-1] + else: + memmode = 'deep' + if memmode == 'shallow': + mode = 'standby' + elif memmode == 's2idle': + mode = 'freeze' + else: + mode = 'mem' + sysvals.memmode = memmode + sysvals.suspendmode = mode + + sysvals.systemInfo(dmidecode(sysvals.mempath)) + if multitest['run']: # run multiple tests in a separate subdirectory - s = 'x%d' % multitest['count'] - if not sysvals.outdir: - sysvals.outdir = datetime.now().strftime('suspend-'+s+'-%m%d%y-%H%M%S') - if not os.path.isdir(sysvals.outdir): - os.mkdir(sysvals.outdir) + if not outdir: + s = 'suspend-x%d' % multitest['count'] + outdir = datetime.now().strftime(s+'-%y%m%d-%H%M%S') + if not os.path.isdir(outdir): + os.mkdir(outdir) for i in range(multitest['count']): if(i != 0): print('Waiting %d seconds...' % (multitest['delay'])) time.sleep(multitest['delay']) print('TEST (%d/%d) START' % (i+1, multitest['count'])) - runTest(sysvals.outdir) + fmt = 'suspend-%y%m%d-%H%M%S' + sysvals.testdir = os.path.join(outdir, datetime.now().strftime(fmt)) + runTest() print('TEST (%d/%d) COMPLETE' % (i+1, multitest['count'])) - runSummary(sysvals.outdir, False) + runSummary(outdir, False) else: + if outdir: + sysvals.testdir = outdir # run the test in the current directory - runTest('.', sysvals.outdir) + runTest() diff --git a/tools/power/pm-graph/bootgraph.8 b/tools/power/pm-graph/bootgraph.8 index 55272a67b0e7..dbdafcf546df 100644 --- a/tools/power/pm-graph/bootgraph.8 +++ b/tools/power/pm-graph/bootgraph.8 @@ -8,14 +8,23 @@ bootgraph \- Kernel boot timing analysis .RB [ COMMAND ] .SH DESCRIPTION \fBbootgraph \fP reads the dmesg log from kernel boot and -creates an html representation of the initcall timeline up to the start -of the init process. +creates an html representation of the initcall timeline. It graphs +every module init call found, through both kernel and user modes. The +timeline is split into two phases: kernel mode & user mode. kernel mode +represents a single process run on a single cpu with serial init calls. +Once user mode begins, the init process is called, and the init calls +start working in parallel. .PP If no specific command is given, the tool reads the current dmesg log and -outputs bootgraph.html. +outputs a new timeline. .PP The tool can also augment the timeline with ftrace data on custom target functions as well as full trace callgraphs. +.PP +Generates output files in subdirectory: boot-yymmdd-HHMMSS + html timeline : <hostname>_boot.html + raw dmesg file : <hostname>_boot_dmesg.txt + raw ftrace file : <hostname>_boot_ftrace.txt .SH OPTIONS .TP \fB-h\fR @@ -28,15 +37,18 @@ Print the current tool version Add the dmesg log to the html output. It will be viewable by clicking a button in the timeline. .TP -\fB-o \fIfile\fR -Override the HTML output filename (default: bootgraph.html) -.SS "Ftrace Debug" +\fB-o \fIname\fR +Overrides the output subdirectory name when running a new test. +Use {date}, {time}, {hostname} for current values. +.sp +e.g. boot-{hostname}-{date}-{time} +.SS "advanced" .TP \fB-f\fR Use ftrace to add function detail (default: disabled) .TP \fB-callgraph\fR -Use ftrace to create initcall callgraphs (default: disabled). If -filter +Use ftrace to create initcall callgraphs (default: disabled). If -func is not used there will be one callgraph per initcall. This can produce very large outputs, i.e. 10MB - 100MB. .TP @@ -50,16 +62,19 @@ This reduces the html file size as there can be many tiny callgraphs which are barely visible in the timeline. The value is a float: e.g. 0.001 represents 1 us. .TP +\fB-cgfilter \fI"func1,func2,..."\fR +Reduce callgraph output in the timeline by limiting it to a list of calls. The +argument can be a single function name or a comma delimited list. +(default: none) +.TP \fB-timeprec \fIn\fR Number of significant digits in timestamps (0:S, 3:ms, [6:us]) .TP \fB-expandcg\fR pre-expand the callgraph data in the html output (default: disabled) .TP -\fB-filter \fI"func1,func2,..."\fR +\fB-func \fI"func1,func2,..."\fR Instead of tracing each initcall, trace a custom list of functions (default: do_one_initcall) - -.SH COMMANDS .TP \fB-reboot\fR Reboot the machine and generate a new timeline automatically. Works in 4 steps. @@ -73,16 +88,23 @@ Show the requirements to generate a new timeline manually. Requires 3 steps. 1. append the string to the kernel command line via your native boot manager. 2. reboot the system 3. after startup, re-run the tool with the same arguments and no command + +.SH COMMANDS +.SS "rebuild" .TP \fB-dmesg \fIfile\fR Create HTML output from an existing dmesg file. .TP \fB-ftrace \fIfile\fR Create HTML output from an existing ftrace file (used with -dmesg). +.SS "other" .TP \fB-flistall\fR Print all ftrace functions capable of being captured. These are all the -possible values you can add to trace via the -filter argument. +possible values you can add to trace via the -func argument. +.TP +\fB-sysinfo\fR +Print out system info extracted from BIOS. Reads /dev/mem directly instead of going through dmidecode. .SH EXAMPLES Create a timeline using the current dmesg log. @@ -93,13 +115,13 @@ Create a timeline using the current dmesg and ftrace log. .IP \f(CW$ bootgraph -callgraph\fR .PP -Create a timeline using the current dmesg, add the log to the html and change the name. +Create a timeline using the current dmesg, add the log to the html and change the folder. .IP -\f(CW$ bootgraph -addlogs -o myboot.html\fR +\f(CW$ bootgraph -addlogs -o "myboot-{date}-{time}"\fR .PP Capture a new boot timeline by automatically rebooting the machine. .IP -\f(CW$ sudo bootgraph -reboot -addlogs -o latestboot.html\fR +\f(CW$ sudo bootgraph -reboot -addlogs -o "latest-{hostname)"\fR .PP Capture a new boot timeline with function trace data. .IP @@ -111,7 +133,7 @@ Capture a new boot timeline with trace & callgraph data. Skip callgraphs smaller .PP Capture a new boot timeline with callgraph data over custom functions. .IP -\f(CW$ sudo bootgraph -reboot -callgraph -filter "acpi_ps_parse_aml,msleep"\fR +\f(CW$ sudo bootgraph -reboot -callgraph -func "acpi_ps_parse_aml,msleep"\fR .PP Capture a brand new boot timeline with manual reboot. .IP @@ -123,6 +145,15 @@ Capture a brand new boot timeline with manual reboot. .IP \f(CW$ sudo bootgraph -callgraph # re-run the tool after restart\fR .PP +.SS "rebuild timeline from logs" +.PP +Rebuild the html from a previous run's logs, using the same options. +.IP +\f(CW$ bootgraph -dmesg dmesg.txt -ftrace ftrace.txt -callgraph\fR +.PP +Rebuild the html with different options. +.IP +\f(CW$ bootgraph -dmesg dmesg.txt -ftrace ftrace.txt -addlogs\fR .SH "SEE ALSO" dmesg(1), update-grub(8), crontab(1), reboot(8) diff --git a/tools/power/pm-graph/sleepgraph.8 b/tools/power/pm-graph/sleepgraph.8 index 610e72ebbc06..fbe7bd3eae8e 100644 --- a/tools/power/pm-graph/sleepgraph.8 +++ b/tools/power/pm-graph/sleepgraph.8 @@ -39,8 +39,9 @@ Pull arguments and config options from a file. \fB-m \fImode\fR Mode to initiate for suspend e.g. standby, freeze, mem (default: mem). .TP -\fB-o \fIsubdir\fR -Override the output subdirectory. Use {date}, {time}, {hostname} for current values. +\fB-o \fIname\fR +Overrides the output subdirectory name when running a new test. +Use {date}, {time}, {hostname} for current values. .sp e.g. suspend-{hostname}-{date}-{time} .TP @@ -52,7 +53,7 @@ disable rtcwake and require a user keypress to resume. Add the dmesg and ftrace logs to the html output. They will be viewable by clicking buttons in the timeline. -.SS "Advanced" +.SS "advanced" .TP \fB-cmd \fIstr\fR Run the timeline over a custom suspend command, e.g. pm-suspend. By default @@ -91,7 +92,7 @@ Include \fIt\fR ms delay after last resume (default: 0 ms). Execute \fIn\fR consecutive tests at \fId\fR seconds intervals. The outputs will be created in a new subdirectory with a summary page: suspend-xN-{date}-{time}. -.SS "Ftrace Debug" +.SS "ftrace debug" .TP \fB-f\fR Use ftrace to create device callgraphs (default: disabled). This can produce @@ -124,12 +125,6 @@ Number of significant digits in timestamps (0:S, [3:ms], 6:us). .SH COMMANDS .TP -\fB-ftrace \fIfile\fR -Create HTML output from an existing ftrace file. -.TP -\fB-dmesg \fIfile\fR -Create HTML output from an existing dmesg file. -.TP \fB-summary \fIindir\fR Create a summary page of all tests in \fIindir\fR. Creates summary.html in the current folder. The output page is a table of tests with @@ -146,6 +141,9 @@ with any options you intend to use to see if they will work. \fB-fpdt\fR Print out the contents of the ACPI Firmware Performance Data Table. .TP +\fB-sysinfo\fR +Print out system info extracted from BIOS. Reads /dev/mem directly instead of going through dmidecode. +.TP \fB-usbtopo\fR Print out the current USB topology with power info. .TP @@ -162,9 +160,16 @@ with -fadd they will also be checked. \fB-flistall\fR Print all ftrace functions capable of being captured. These are all the possible values you can add to trace via the -fadd argument. +.SS "rebuild" +.TP +\fB-ftrace \fIfile\fR +Create HTML output from an existing ftrace file. +.TP +\fB-dmesg \fIfile\fR +Create HTML output from an existing dmesg file. .SH EXAMPLES -.SS "Simple Commands" +.SS "simple commands" Check which suspend modes are currently supported. .IP \f(CW$ sleepgraph -modes\fR @@ -185,12 +190,8 @@ Generate a summary of all timelines in a particular folder. .IP \f(CW$ sleepgraph -summary ~/workspace/myresults/\fR .PP -Re-generate the html output from a previous run's dmesg and ftrace log. -.IP -\f(CW$ sleepgraph -dmesg myhost_mem_dmesg.txt -ftrace myhost_mem_ftrace.txt\fR -.PP -.SS "Capturing Simple Timelines" +.SS "capturing basic timelines" Execute a mem suspend with a 15 second wakeup. Include the logs in the html. .IP \f(CW$ sudo sleepgraph -rtcwake 15 -addlogs\fR @@ -204,7 +205,7 @@ Execute a freeze with no wakeup (require keypress). Change output folder name. \f(CW$ sudo sleepgraph -m freeze -rtcwake off -o "freeze-{hostname}-{date}-{time}"\fR .PP -.SS "Capturing Advanced Timelines" +.SS "capturing advanced timelines" Execute a suspend & include dev mode source calls, limit callbacks to 5ms or larger. .IP \f(CW$ sudo sleepgraph -m mem -rtcwake 15 -dev -mindev 5\fR @@ -222,8 +223,7 @@ Execute a suspend using a custom command. \f(CW$ sudo sleepgraph -cmd "echo mem > /sys/power/state" -rtcwake 15\fR .PP - -.SS "Capturing Timelines with Callgraph Data" +.SS "adding callgraph data" Add device callgraphs. Limit the trace depth and only show callgraphs 10ms or larger. .IP \f(CW$ sudo sleepgraph -m mem -rtcwake 15 -f -maxdepth 5 -mincg 10\fR @@ -235,6 +235,16 @@ Capture a full callgraph across all suspend, then filter the html by a single ph \f(CW$ sleepgraph -dmesg host_mem_dmesg.txt -ftrace host_mem_ftrace.txt -f -cgphase resume .PP +.SS "rebuild timeline from logs" +.PP +Rebuild the html from a previous run's logs, using the same options. +.IP +\f(CW$ sleepgraph -dmesg dmesg.txt -ftrace ftrace.txt -callgraph\fR +.PP +Rebuild the html with different options. +.IP +\f(CW$ sleepgraph -dmesg dmesg.txt -ftrace ftrace.txt -addlogs -srgap\fR + .SH "SEE ALSO" dmesg(1) .PP |