mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 08:10:54 +07:00
Merge branch 'timers/urgent' into WIP.timers
Pick up urgent fixes to avoid conflicts.
This commit is contained in:
commit
978267b643
@ -59,20 +59,28 @@ button driver uses the following 3 modes in order not to trigger issues.
|
||||
If the userspace hasn't been prepared to ignore the unreliable "opened"
|
||||
events and the unreliable initial state notification, Linux users can use
|
||||
the following kernel parameters to handle the possible issues:
|
||||
A. button.lid_init_state=open:
|
||||
A. button.lid_init_state=method:
|
||||
When this option is specified, the ACPI button driver reports the
|
||||
initial lid state using the returning value of the _LID control method
|
||||
and whether the "opened"/"closed" events are paired fully relies on the
|
||||
firmware implementation.
|
||||
This option can be used to fix some platforms where the returning value
|
||||
of the _LID control method is reliable but the initial lid state
|
||||
notification is missing.
|
||||
This option is the default behavior during the period the userspace
|
||||
isn't ready to handle the buggy AML tables.
|
||||
B. button.lid_init_state=open:
|
||||
When this option is specified, the ACPI button driver always reports the
|
||||
initial lid state as "opened" and whether the "opened"/"closed" events
|
||||
are paired fully relies on the firmware implementation.
|
||||
This may fix some platforms where the returning value of the _LID
|
||||
control method is not reliable and the initial lid state notification is
|
||||
missing.
|
||||
This option is the default behavior during the period the userspace
|
||||
isn't ready to handle the buggy AML tables.
|
||||
|
||||
If the userspace has been prepared to ignore the unreliable "opened" events
|
||||
and the unreliable initial state notification, Linux users should always
|
||||
use the following kernel parameter:
|
||||
B. button.lid_init_state=ignore:
|
||||
C. button.lid_init_state=ignore:
|
||||
When this option is specified, the ACPI button driver never reports the
|
||||
initial lid state and there is a compensation mechanism implemented to
|
||||
ensure that the reliable "closed" notifications can always be delievered
|
||||
|
@ -1,4 +1,5 @@
|
||||
.. |struct cpufreq_policy| replace:: :c:type:`struct cpufreq_policy <cpufreq_policy>`
|
||||
.. |intel_pstate| replace:: :doc:`intel_pstate <intel_pstate>`
|
||||
|
||||
=======================
|
||||
CPU Performance Scaling
|
||||
@ -75,7 +76,7 @@ feedback registers, as that information is typically specific to the hardware
|
||||
interface it comes from and may not be easily represented in an abstract,
|
||||
platform-independent way. For this reason, ``CPUFreq`` allows scaling drivers
|
||||
to bypass the governor layer and implement their own performance scaling
|
||||
algorithms. That is done by the ``intel_pstate`` scaling driver.
|
||||
algorithms. That is done by the |intel_pstate| scaling driver.
|
||||
|
||||
|
||||
``CPUFreq`` Policy Objects
|
||||
@ -174,13 +175,13 @@ necessary to restart the scaling governor so that it can take the new online CPU
|
||||
into account. That is achieved by invoking the governor's ``->stop`` and
|
||||
``->start()`` callbacks, in this order, for the entire policy.
|
||||
|
||||
As mentioned before, the ``intel_pstate`` scaling driver bypasses the scaling
|
||||
As mentioned before, the |intel_pstate| scaling driver bypasses the scaling
|
||||
governor layer of ``CPUFreq`` and provides its own P-state selection algorithms.
|
||||
Consequently, if ``intel_pstate`` is used, scaling governors are not attached to
|
||||
Consequently, if |intel_pstate| is used, scaling governors are not attached to
|
||||
new policy objects. Instead, the driver's ``->setpolicy()`` callback is invoked
|
||||
to register per-CPU utilization update callbacks for each policy. These
|
||||
callbacks are invoked by the CPU scheduler in the same way as for scaling
|
||||
governors, but in the ``intel_pstate`` case they both determine the P-state to
|
||||
governors, but in the |intel_pstate| case they both determine the P-state to
|
||||
use and change the hardware configuration accordingly in one go from scheduler
|
||||
context.
|
||||
|
||||
@ -257,7 +258,7 @@ are the following:
|
||||
|
||||
``scaling_available_governors``
|
||||
List of ``CPUFreq`` scaling governors present in the kernel that can
|
||||
be attached to this policy or (if the ``intel_pstate`` scaling driver is
|
||||
be attached to this policy or (if the |intel_pstate| scaling driver is
|
||||
in use) list of scaling algorithms provided by the driver that can be
|
||||
applied to this policy.
|
||||
|
||||
@ -274,7 +275,7 @@ are the following:
|
||||
the CPU is actually running at (due to hardware design and other
|
||||
limitations).
|
||||
|
||||
Some scaling drivers (e.g. ``intel_pstate``) attempt to provide
|
||||
Some scaling drivers (e.g. |intel_pstate|) attempt to provide
|
||||
information more precisely reflecting the current CPU frequency through
|
||||
this attribute, but that still may not be the exact current CPU
|
||||
frequency as seen by the hardware at the moment.
|
||||
@ -284,13 +285,13 @@ are the following:
|
||||
|
||||
``scaling_governor``
|
||||
The scaling governor currently attached to this policy or (if the
|
||||
``intel_pstate`` scaling driver is in use) the scaling algorithm
|
||||
|intel_pstate| scaling driver is in use) the scaling algorithm
|
||||
provided by the driver that is currently applied to this policy.
|
||||
|
||||
This attribute is read-write and writing to it will cause a new scaling
|
||||
governor to be attached to this policy or a new scaling algorithm
|
||||
provided by the scaling driver to be applied to it (in the
|
||||
``intel_pstate`` case), as indicated by the string written to this
|
||||
|intel_pstate| case), as indicated by the string written to this
|
||||
attribute (which must be one of the names listed by the
|
||||
``scaling_available_governors`` attribute described above).
|
||||
|
||||
@ -619,7 +620,7 @@ This file is located under :file:`/sys/devices/system/cpu/cpufreq/` and controls
|
||||
the "boost" setting for the whole system. It is not present if the underlying
|
||||
scaling driver does not support the frequency boost mechanism (or supports it,
|
||||
but provides a driver-specific interface for controlling it, like
|
||||
``intel_pstate``).
|
||||
|intel_pstate|).
|
||||
|
||||
If the value in this file is 1, the frequency boost mechanism is enabled. This
|
||||
means that either the hardware can be put into states in which it is able to
|
||||
|
@ -6,6 +6,7 @@ Power Management
|
||||
:maxdepth: 2
|
||||
|
||||
cpufreq
|
||||
intel_pstate
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
|
755
Documentation/admin-guide/pm/intel_pstate.rst
Normal file
755
Documentation/admin-guide/pm/intel_pstate.rst
Normal file
@ -0,0 +1,755 @@
|
||||
===============================================
|
||||
``intel_pstate`` CPU Performance Scaling Driver
|
||||
===============================================
|
||||
|
||||
::
|
||||
|
||||
Copyright (c) 2017 Intel Corp., Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
||||
|
||||
|
||||
General Information
|
||||
===================
|
||||
|
||||
``intel_pstate`` is a part of the
|
||||
:doc:`CPU performance scaling subsystem <cpufreq>` in the Linux kernel
|
||||
(``CPUFreq``). It is a scaling driver for the Sandy Bridge and later
|
||||
generations of Intel processors. Note, however, that some of those processors
|
||||
may not be supported. [To understand ``intel_pstate`` it is necessary to know
|
||||
how ``CPUFreq`` works in general, so this is the time to read :doc:`cpufreq` if
|
||||
you have not done that yet.]
|
||||
|
||||
For the processors supported by ``intel_pstate``, the P-state concept is broader
|
||||
than just an operating frequency or an operating performance point (see the
|
||||
`LinuxCon Europe 2015 presentation by Kristen Accardi <LCEU2015_>`_ for more
|
||||
information about that). For this reason, the representation of P-states used
|
||||
by ``intel_pstate`` internally follows the hardware specification (for details
|
||||
refer to `Intel® 64 and IA-32 Architectures Software Developer’s Manual
|
||||
Volume 3: System Programming Guide <SDM_>`_). However, the ``CPUFreq`` core
|
||||
uses frequencies for identifying operating performance points of CPUs and
|
||||
frequencies are involved in the user space interface exposed by it, so
|
||||
``intel_pstate`` maps its internal representation of P-states to frequencies too
|
||||
(fortunately, that mapping is unambiguous). At the same time, it would not be
|
||||
practical for ``intel_pstate`` to supply the ``CPUFreq`` core with a table of
|
||||
available frequencies due to the possible size of it, so the driver does not do
|
||||
that. Some functionality of the core is limited by that.
|
||||
|
||||
Since the hardware P-state selection interface used by ``intel_pstate`` is
|
||||
available at the logical CPU level, the driver always works with individual
|
||||
CPUs. Consequently, if ``intel_pstate`` is in use, every ``CPUFreq`` policy
|
||||
object corresponds to one logical CPU and ``CPUFreq`` policies are effectively
|
||||
equivalent to CPUs. In particular, this means that they become "inactive" every
|
||||
time the corresponding CPU is taken offline and need to be re-initialized when
|
||||
it goes back online.
|
||||
|
||||
``intel_pstate`` is not modular, so it cannot be unloaded, which means that the
|
||||
only way to pass early-configuration-time parameters to it is via the kernel
|
||||
command line. However, its configuration can be adjusted via ``sysfs`` to a
|
||||
great extent. In some configurations it even is possible to unregister it via
|
||||
``sysfs`` which allows another ``CPUFreq`` scaling driver to be loaded and
|
||||
registered (see `below <status_attr_>`_).
|
||||
|
||||
|
||||
Operation Modes
|
||||
===============
|
||||
|
||||
``intel_pstate`` can operate in three different modes: in the active mode with
|
||||
or without hardware-managed P-states support and in the passive mode. Which of
|
||||
them will be in effect depends on what kernel command line options are used and
|
||||
on the capabilities of the processor.
|
||||
|
||||
Active Mode
|
||||
-----------
|
||||
|
||||
This is the default operation mode of ``intel_pstate``. If it works in this
|
||||
mode, the ``scaling_driver`` policy attribute in ``sysfs`` for all ``CPUFreq``
|
||||
policies contains the string "intel_pstate".
|
||||
|
||||
In this mode the driver bypasses the scaling governors layer of ``CPUFreq`` and
|
||||
provides its own scaling algorithms for P-state selection. Those algorithms
|
||||
can be applied to ``CPUFreq`` policies in the same way as generic scaling
|
||||
governors (that is, through the ``scaling_governor`` policy attribute in
|
||||
``sysfs``). [Note that different P-state selection algorithms may be chosen for
|
||||
different policies, but that is not recommended.]
|
||||
|
||||
They are not generic scaling governors, but their names are the same as the
|
||||
names of some of those governors. Moreover, confusingly enough, they generally
|
||||
do not work in the same way as the generic governors they share the names with.
|
||||
For example, the ``powersave`` P-state selection algorithm provided by
|
||||
``intel_pstate`` is not a counterpart of the generic ``powersave`` governor
|
||||
(roughly, it corresponds to the ``schedutil`` and ``ondemand`` governors).
|
||||
|
||||
There are two P-state selection algorithms provided by ``intel_pstate`` in the
|
||||
active mode: ``powersave`` and ``performance``. The way they both operate
|
||||
depends on whether or not the hardware-managed P-states (HWP) feature has been
|
||||
enabled in the processor and possibly on the processor model.
|
||||
|
||||
Which of the P-state selection algorithms is used by default depends on the
|
||||
:c:macro:`CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE` kernel configuration option.
|
||||
Namely, if that option is set, the ``performance`` algorithm will be used by
|
||||
default, and the other one will be used by default if it is not set.
|
||||
|
||||
Active Mode With HWP
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If the processor supports the HWP feature, it will be enabled during the
|
||||
processor initialization and cannot be disabled after that. It is possible
|
||||
to avoid enabling it by passing the ``intel_pstate=no_hwp`` argument to the
|
||||
kernel in the command line.
|
||||
|
||||
If the HWP feature has been enabled, ``intel_pstate`` relies on the processor to
|
||||
select P-states by itself, but still it can give hints to the processor's
|
||||
internal P-state selection logic. What those hints are depends on which P-state
|
||||
selection algorithm has been applied to the given policy (or to the CPU it
|
||||
corresponds to).
|
||||
|
||||
Even though the P-state selection is carried out by the processor automatically,
|
||||
``intel_pstate`` registers utilization update callbacks with the CPU scheduler
|
||||
in this mode. However, they are not used for running a P-state selection
|
||||
algorithm, but for periodic updates of the current CPU frequency information to
|
||||
be made available from the ``scaling_cur_freq`` policy attribute in ``sysfs``.
|
||||
|
||||
HWP + ``performance``
|
||||
.....................
|
||||
|
||||
In this configuration ``intel_pstate`` will write 0 to the processor's
|
||||
Energy-Performance Preference (EPP) knob (if supported) or its
|
||||
Energy-Performance Bias (EPB) knob (otherwise), which means that the processor's
|
||||
internal P-state selection logic is expected to focus entirely on performance.
|
||||
|
||||
This will override the EPP/EPB setting coming from the ``sysfs`` interface
|
||||
(see `Energy vs Performance Hints`_ below).
|
||||
|
||||
Also, in this configuration the range of P-states available to the processor's
|
||||
internal P-state selection logic is always restricted to the upper boundary
|
||||
(that is, the maximum P-state that the driver is allowed to use).
|
||||
|
||||
HWP + ``powersave``
|
||||
...................
|
||||
|
||||
In this configuration ``intel_pstate`` will set the processor's
|
||||
Energy-Performance Preference (EPP) knob (if supported) or its
|
||||
Energy-Performance Bias (EPB) knob (otherwise) to whatever value it was
|
||||
previously set to via ``sysfs`` (or whatever default value it was
|
||||
set to by the platform firmware). This usually causes the processor's
|
||||
internal P-state selection logic to be less performance-focused.
|
||||
|
||||
Active Mode Without HWP
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is the default operation mode for processors that do not support the HWP
|
||||
feature. It also is used by default with the ``intel_pstate=no_hwp`` argument
|
||||
in the kernel command line. However, in this mode ``intel_pstate`` may refuse
|
||||
to work with the given processor if it does not recognize it. [Note that
|
||||
``intel_pstate`` will never refuse to work with any processor with the HWP
|
||||
feature enabled.]
|
||||
|
||||
In this mode ``intel_pstate`` registers utilization update callbacks with the
|
||||
CPU scheduler in order to run a P-state selection algorithm, either
|
||||
``powersave`` or ``performance``, depending on the ``scaling_cur_freq`` policy
|
||||
setting in ``sysfs``. The current CPU frequency information to be made
|
||||
available from the ``scaling_cur_freq`` policy attribute in ``sysfs`` is
|
||||
periodically updated by those utilization update callbacks too.
|
||||
|
||||
``performance``
|
||||
...............
|
||||
|
||||
Without HWP, this P-state selection algorithm is always the same regardless of
|
||||
the processor model and platform configuration.
|
||||
|
||||
It selects the maximum P-state it is allowed to use, subject to limits set via
|
||||
``sysfs``, every time the P-state selection computations are carried out by the
|
||||
driver's utilization update callback for the given CPU (that does not happen
|
||||
more often than every 10 ms), but the hardware configuration will not be changed
|
||||
if the new P-state is 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
|
||||
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
|
||||
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.
|
||||
|
||||
This is the default P-state selection algorithm if the
|
||||
:c:macro:`CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE` kernel configuration option
|
||||
is not set.
|
||||
|
||||
Passive Mode
|
||||
------------
|
||||
|
||||
This mode is used if the ``intel_pstate=passive`` argument is passed to the
|
||||
kernel in the command line (it implies the ``intel_pstate=no_hwp`` setting too).
|
||||
Like in the active mode without HWP support, in this mode ``intel_pstate`` may
|
||||
refuse to work with the given processor if it does not recognize it.
|
||||
|
||||
If the driver works in this mode, the ``scaling_driver`` policy attribute in
|
||||
``sysfs`` for all ``CPUFreq`` policies contains the string "intel_cpufreq".
|
||||
Then, the driver behaves like a regular ``CPUFreq`` scaling driver. That is,
|
||||
it is invoked by generic scaling governors when necessary to talk to the
|
||||
hardware in order to change the P-state of a CPU (in particular, the
|
||||
``schedutil`` governor can invoke it directly from scheduler context).
|
||||
|
||||
While in this mode, ``intel_pstate`` can be used with all of the (generic)
|
||||
scaling governors listed by the ``scaling_available_governors`` policy attribute
|
||||
in ``sysfs`` (and the P-state selection algorithms described above are not
|
||||
used). Then, it is responsible for the configuration of policy objects
|
||||
corresponding to CPUs and provides the ``CPUFreq`` core (and the scaling
|
||||
governors attached to the policy objects) with accurate information on the
|
||||
maximum and minimum operating frequencies supported by the hardware (including
|
||||
the so-called "turbo" frequency ranges). In other words, in the passive mode
|
||||
the entire range of available P-states is exposed by ``intel_pstate`` to the
|
||||
``CPUFreq`` core. However, in this mode the driver does not register
|
||||
utilization update callbacks with the CPU scheduler and the ``scaling_cur_freq``
|
||||
information comes from the ``CPUFreq`` core (and is the last frequency selected
|
||||
by the current scaling governor for the given policy).
|
||||
|
||||
|
||||
.. _turbo:
|
||||
|
||||
Turbo P-states Support
|
||||
======================
|
||||
|
||||
In the majority of cases, the entire range of P-states available to
|
||||
``intel_pstate`` can be divided into two sub-ranges that correspond to
|
||||
different types of processor behavior, above and below a boundary that
|
||||
will be referred to as the "turbo threshold" in what follows.
|
||||
|
||||
The P-states above the turbo threshold are referred to as "turbo P-states" and
|
||||
the whole sub-range of P-states they belong to is referred to as the "turbo
|
||||
range". These names are related to the Turbo Boost technology allowing a
|
||||
multicore processor to opportunistically increase the P-state of one or more
|
||||
cores if there is enough power to do that and if that is not going to cause the
|
||||
thermal envelope of the processor package to be exceeded.
|
||||
|
||||
Specifically, if software sets the P-state of a CPU core within the turbo range
|
||||
(that is, above the turbo threshold), the processor is permitted to take over
|
||||
performance scaling control for that core and put it into turbo P-states of its
|
||||
choice going forward. However, that permission is interpreted differently by
|
||||
different processor generations. Namely, the Sandy Bridge generation of
|
||||
processors will never use any P-states above the last one set by software for
|
||||
the given core, even if it is within the turbo range, whereas all of the later
|
||||
processor generations will take it as a license to use any P-states from the
|
||||
turbo range, even above the one set by software. In other words, on those
|
||||
processors setting any P-state from the turbo range will enable the processor
|
||||
to put the given core into all turbo P-states up to and including the maximum
|
||||
supported one as it sees fit.
|
||||
|
||||
One important property of turbo P-states is that they are not sustainable. More
|
||||
precisely, there is no guarantee that any CPUs will be able to stay in any of
|
||||
those states indefinitely, because the power distribution within the processor
|
||||
package may change over time or the thermal envelope it was designed for might
|
||||
be exceeded if a turbo P-state was used for too long.
|
||||
|
||||
In turn, the P-states below the turbo threshold generally are sustainable. In
|
||||
fact, if one of them is set by software, the processor is not expected to change
|
||||
it to a lower one unless in a thermal stress or a power limit violation
|
||||
situation (a higher P-state may still be used if it is set for another CPU in
|
||||
the same package at the same time, for example).
|
||||
|
||||
Some processors allow multiple cores to be in turbo P-states at the same time,
|
||||
but the maximum P-state that can be set for them generally depends on the number
|
||||
of cores running concurrently. The maximum turbo P-state that can be set for 3
|
||||
cores at the same time usually is lower than the analogous maximum P-state for
|
||||
2 cores, which in turn usually is lower than the maximum turbo P-state that can
|
||||
be set for 1 core. The one-core maximum turbo P-state is thus the maximum
|
||||
supported one overall.
|
||||
|
||||
The maximum supported turbo P-state, the turbo threshold (the maximum supported
|
||||
non-turbo P-state) and the minimum supported P-state are specific to the
|
||||
processor model and can be determined by reading the processor's model-specific
|
||||
registers (MSRs). Moreover, some processors support the Configurable TDP
|
||||
(Thermal Design Power) feature and, when that feature is enabled, the turbo
|
||||
threshold effectively becomes a configurable value that can be set by the
|
||||
platform firmware.
|
||||
|
||||
Unlike ``_PSS`` objects in the ACPI tables, ``intel_pstate`` always exposes
|
||||
the entire range of available P-states, including the whole turbo range, to the
|
||||
``CPUFreq`` core and (in the passive mode) to generic scaling governors. This
|
||||
generally causes turbo P-states to be set more often when ``intel_pstate`` is
|
||||
used relative to ACPI-based CPU performance scaling (see `below <acpi-cpufreq_>`_
|
||||
for more information).
|
||||
|
||||
Moreover, since ``intel_pstate`` always knows what the real turbo threshold is
|
||||
(even if the Configurable TDP feature is enabled in the processor), its
|
||||
``no_turbo`` attribute in ``sysfs`` (described `below <no_turbo_attr_>`_) should
|
||||
work as expected in all cases (that is, if set to disable turbo P-states, it
|
||||
always should prevent ``intel_pstate`` from using them).
|
||||
|
||||
|
||||
Processor Support
|
||||
=================
|
||||
|
||||
To handle a given processor ``intel_pstate`` requires a number of different
|
||||
pieces of information on it to be known, including:
|
||||
|
||||
* The minimum supported P-state.
|
||||
|
||||
* The maximum supported `non-turbo P-state <turbo_>`_.
|
||||
|
||||
* Whether or not turbo P-states are supported at all.
|
||||
|
||||
* The maximum supported `one-core turbo P-state <turbo_>`_ (if turbo P-states
|
||||
are supported).
|
||||
|
||||
* The scaling formula to translate the driver's internal representation
|
||||
of P-states into frequencies and the other way around.
|
||||
|
||||
Generally, ways to obtain that information are specific to the processor model
|
||||
or family. Although it often is possible to obtain all of it from the processor
|
||||
itself (using model-specific registers), there are cases in which hardware
|
||||
manuals need to be consulted to get to it too.
|
||||
|
||||
For this reason, there is a list of supported processors in ``intel_pstate`` and
|
||||
the driver initialization will fail if the detected processor is not in that
|
||||
list, unless it supports the `HWP feature <Active Mode_>`_. [The interface to
|
||||
obtain all of the information listed above is the same for all of the processors
|
||||
supporting the HWP feature, which is why they all are supported by
|
||||
``intel_pstate``.]
|
||||
|
||||
|
||||
User Space Interface in ``sysfs``
|
||||
=================================
|
||||
|
||||
Global Attributes
|
||||
-----------------
|
||||
|
||||
``intel_pstate`` exposes several global attributes (files) in ``sysfs`` to
|
||||
control its functionality at the system level. They are located in the
|
||||
``/sys/devices/system/cpu/cpufreq/intel_pstate/`` directory and affect all
|
||||
CPUs.
|
||||
|
||||
Some of them are not present if the ``intel_pstate=per_cpu_perf_limits``
|
||||
argument is passed to the kernel in the command line.
|
||||
|
||||
``max_perf_pct``
|
||||
Maximum P-state the driver is allowed to set in percent of the
|
||||
maximum supported performance level (the highest supported `turbo
|
||||
P-state <turbo_>`_).
|
||||
|
||||
This attribute will not be exposed if the
|
||||
``intel_pstate=per_cpu_perf_limits`` argument is present in the kernel
|
||||
command line.
|
||||
|
||||
``min_perf_pct``
|
||||
Minimum P-state the driver is allowed to set in percent of the
|
||||
maximum supported performance level (the highest supported `turbo
|
||||
P-state <turbo_>`_).
|
||||
|
||||
This attribute will not be exposed if the
|
||||
``intel_pstate=per_cpu_perf_limits`` argument is present in the kernel
|
||||
command line.
|
||||
|
||||
``num_pstates``
|
||||
Number of P-states supported by the processor (between 0 and 255
|
||||
inclusive) including both turbo and non-turbo P-states (see
|
||||
`Turbo P-states Support`_).
|
||||
|
||||
The value of this attribute is not affected by the ``no_turbo``
|
||||
setting described `below <no_turbo_attr_>`_.
|
||||
|
||||
This attribute is read-only.
|
||||
|
||||
``turbo_pct``
|
||||
Ratio of the `turbo range <turbo_>`_ size to the size of the entire
|
||||
range of supported P-states, in percent.
|
||||
|
||||
This attribute is read-only.
|
||||
|
||||
.. _no_turbo_attr:
|
||||
|
||||
``no_turbo``
|
||||
If set (equal to 1), the driver is not allowed to set any turbo P-states
|
||||
(see `Turbo P-states Support`_). If unset (equalt to 0, which is the
|
||||
default), turbo P-states can be set by the driver.
|
||||
[Note that ``intel_pstate`` does not support the general ``boost``
|
||||
attribute (supported by some other scaling drivers) which is replaced
|
||||
by this one.]
|
||||
|
||||
This attrubute does not affect the maximum supported frequency value
|
||||
supplied to the ``CPUFreq`` core and exposed via the policy interface,
|
||||
but it affects the maximum possible value of per-policy P-state limits
|
||||
(see `Interpretation of Policy Attributes`_ below for details).
|
||||
|
||||
.. _status_attr:
|
||||
|
||||
``status``
|
||||
Operation mode of the driver: "active", "passive" or "off".
|
||||
|
||||
"active"
|
||||
The driver is functional and in the `active mode
|
||||
<Active Mode_>`_.
|
||||
|
||||
"passive"
|
||||
The driver is functional and in the `passive mode
|
||||
<Passive Mode_>`_.
|
||||
|
||||
"off"
|
||||
The driver is not functional (it is not registered as a scaling
|
||||
driver with the ``CPUFreq`` core).
|
||||
|
||||
This attribute can be written to in order to change the driver's
|
||||
operation mode or to unregister it. The string written to it must be
|
||||
one of the possible values of it and, if successful, the write will
|
||||
cause the driver to switch over to the operation mode represented by
|
||||
that string - or to be unregistered in the "off" case. [Actually,
|
||||
switching over from the active mode to the passive mode or the other
|
||||
way around causes the driver to be unregistered and registered again
|
||||
with a different set of callbacks, so all of its settings (the global
|
||||
as well as the per-policy ones) are then reset to their default
|
||||
values, possibly depending on the target operation mode.]
|
||||
|
||||
That only is supported in some configurations, though (for example, if
|
||||
the `HWP feature is enabled in the processor <Active Mode With HWP_>`_,
|
||||
the operation mode of the driver cannot be changed), and if it is not
|
||||
supported in the current configuration, writes to this attribute with
|
||||
fail with an appropriate error.
|
||||
|
||||
Interpretation of Policy Attributes
|
||||
-----------------------------------
|
||||
|
||||
The interpretation of some ``CPUFreq`` policy attributes described in
|
||||
:doc:`cpufreq` is special with ``intel_pstate`` as the current scaling driver
|
||||
and it generally depends on the driver's `operation mode <Operation Modes_>`_.
|
||||
|
||||
First of all, the values of the ``cpuinfo_max_freq``, ``cpuinfo_min_freq`` and
|
||||
``scaling_cur_freq`` attributes are produced by applying a processor-specific
|
||||
multiplier to the internal P-state representation used by ``intel_pstate``.
|
||||
Also, the values of the ``scaling_max_freq`` and ``scaling_min_freq``
|
||||
attributes are capped by the frequency corresponding to the maximum P-state that
|
||||
the driver is allowed to set.
|
||||
|
||||
If the ``no_turbo`` `global attribute <no_turbo_attr_>`_ is set, the driver is
|
||||
not allowed to use turbo P-states, so the maximum value of ``scaling_max_freq``
|
||||
and ``scaling_min_freq`` is limited to the maximum non-turbo P-state frequency.
|
||||
Accordingly, setting ``no_turbo`` causes ``scaling_max_freq`` and
|
||||
``scaling_min_freq`` to go down to that value if they were above it before.
|
||||
However, the old values of ``scaling_max_freq`` and ``scaling_min_freq`` will be
|
||||
restored after unsetting ``no_turbo``, unless these attributes have been written
|
||||
to after ``no_turbo`` was set.
|
||||
|
||||
If ``no_turbo`` is not set, the maximum possible value of ``scaling_max_freq``
|
||||
and ``scaling_min_freq`` corresponds to the maximum supported turbo P-state,
|
||||
which also is the value of ``cpuinfo_max_freq`` in either case.
|
||||
|
||||
Next, the following policy attributes have special meaning if
|
||||
``intel_pstate`` works in the `active mode <Active Mode_>`_:
|
||||
|
||||
``scaling_available_governors``
|
||||
List of P-state selection algorithms provided by ``intel_pstate``.
|
||||
|
||||
``scaling_governor``
|
||||
P-state selection algorithm provided by ``intel_pstate`` currently in
|
||||
use with the given policy.
|
||||
|
||||
``scaling_cur_freq``
|
||||
Frequency of the average P-state of the CPU represented by the given
|
||||
policy for the time interval between the last two invocations of the
|
||||
driver's utilization update callback by the CPU scheduler for that CPU.
|
||||
|
||||
The meaning of these attributes in the `passive mode <Passive Mode_>`_ is the
|
||||
same as for other scaling drivers.
|
||||
|
||||
Additionally, the value of the ``scaling_driver`` attribute for ``intel_pstate``
|
||||
depends on the operation mode of the driver. Namely, it is either
|
||||
"intel_pstate" (in the `active mode <Active Mode_>`_) or "intel_cpufreq" (in the
|
||||
`passive mode <Passive Mode_>`_).
|
||||
|
||||
Coordination of P-State Limits
|
||||
------------------------------
|
||||
|
||||
``intel_pstate`` allows P-state limits to be set in two ways: with the help of
|
||||
the ``max_perf_pct`` and ``min_perf_pct`` `global attributes
|
||||
<Global Attributes_>`_ or via the ``scaling_max_freq`` and ``scaling_min_freq``
|
||||
``CPUFreq`` policy attributes. The coordination between those limits is based
|
||||
on the following rules, regardless of the current operation mode of the driver:
|
||||
|
||||
1. All CPUs are affected by the global limits (that is, none of them can be
|
||||
requested to run faster than the global maximum and none of them can be
|
||||
requested to run slower than the global minimum).
|
||||
|
||||
2. Each individual CPU is affected by its own per-policy limits (that is, it
|
||||
cannot be requested to run faster than its own per-policy maximum and it
|
||||
cannot be requested to run slower than its own per-policy minimum).
|
||||
|
||||
3. The global and per-policy limits can be set independently.
|
||||
|
||||
If the `HWP feature is enabled in the processor <Active Mode With HWP_>`_, the
|
||||
resulting effective values are written into its registers whenever the limits
|
||||
change in order to request its internal P-state selection logic to always set
|
||||
P-states within these limits. Otherwise, the limits are taken into account by
|
||||
scaling governors (in the `passive mode <Passive Mode_>`_) and by the driver
|
||||
every time before setting a new P-state for a CPU.
|
||||
|
||||
Additionally, if the ``intel_pstate=per_cpu_perf_limits`` command line argument
|
||||
is passed to the kernel, ``max_perf_pct`` and ``min_perf_pct`` are not exposed
|
||||
at all and the only way to set the limits is by using the policy attributes.
|
||||
|
||||
|
||||
Energy vs Performance Hints
|
||||
---------------------------
|
||||
|
||||
If ``intel_pstate`` works in the `active mode with the HWP feature enabled
|
||||
<Active Mode With HWP_>`_ in the processor, additional attributes are present
|
||||
in every ``CPUFreq`` policy directory in ``sysfs``. They are intended to allow
|
||||
user space to help ``intel_pstate`` to adjust the processor's internal P-state
|
||||
selection logic by focusing it on performance or on energy-efficiency, or
|
||||
somewhere between the two extremes:
|
||||
|
||||
``energy_performance_preference``
|
||||
Current value of the energy vs performance hint for the given policy
|
||||
(or the CPU represented by it).
|
||||
|
||||
The hint can be changed by writing to this attribute.
|
||||
|
||||
``energy_performance_available_preferences``
|
||||
List of strings that can be written to the
|
||||
``energy_performance_preference`` attribute.
|
||||
|
||||
They represent different energy vs performance hints and should be
|
||||
self-explanatory, except that ``default`` represents whatever hint
|
||||
value was set by the platform firmware.
|
||||
|
||||
Strings written to the ``energy_performance_preference`` attribute are
|
||||
internally translated to integer values written to the processor's
|
||||
Energy-Performance Preference (EPP) knob (if supported) or its
|
||||
Energy-Performance Bias (EPB) knob.
|
||||
|
||||
[Note that tasks may by migrated from one CPU to another by the scheduler's
|
||||
load-balancing algorithm and if different energy vs performance hints are
|
||||
set for those CPUs, that may lead to undesirable outcomes. To avoid such
|
||||
issues it is better to set the same energy vs performance hint for all CPUs
|
||||
or to pin every task potentially sensitive to them to a specific CPU.]
|
||||
|
||||
.. _acpi-cpufreq:
|
||||
|
||||
``intel_pstate`` vs ``acpi-cpufreq``
|
||||
====================================
|
||||
|
||||
On the majority of systems supported by ``intel_pstate``, the ACPI tables
|
||||
provided by the platform firmware contain ``_PSS`` objects returning information
|
||||
that can be used for CPU performance scaling (refer to the `ACPI specification`_
|
||||
for details on the ``_PSS`` objects and the format of the information returned
|
||||
by them).
|
||||
|
||||
The information returned by the ACPI ``_PSS`` objects is used by the
|
||||
``acpi-cpufreq`` scaling driver. On systems supported by ``intel_pstate``
|
||||
the ``acpi-cpufreq`` driver uses the same hardware CPU performance scaling
|
||||
interface, but the set of P-states it can use is limited by the ``_PSS``
|
||||
output.
|
||||
|
||||
On those systems each ``_PSS`` object returns a list of P-states supported by
|
||||
the corresponding CPU which basically is a subset of the P-states range that can
|
||||
be used by ``intel_pstate`` on the same system, with one exception: the whole
|
||||
`turbo range <turbo_>`_ is represented by one item in it (the topmost one). By
|
||||
convention, the frequency returned by ``_PSS`` for that item is greater by 1 MHz
|
||||
than the frequency of the highest non-turbo P-state listed by it, but the
|
||||
corresponding P-state representation (following the hardware specification)
|
||||
returned for it matches the maximum supported turbo P-state (or is the
|
||||
special value 255 meaning essentially "go as high as you can get").
|
||||
|
||||
The list of P-states returned by ``_PSS`` is reflected by the table of
|
||||
available frequencies supplied by ``acpi-cpufreq`` to the ``CPUFreq`` core and
|
||||
scaling governors and the minimum and maximum supported frequencies reported by
|
||||
it come from that list as well. In particular, given the special representation
|
||||
of the turbo range described above, this means that the maximum supported
|
||||
frequency reported by ``acpi-cpufreq`` is higher by 1 MHz than the frequency
|
||||
of the highest supported non-turbo P-state listed by ``_PSS`` which, of course,
|
||||
affects decisions made by the scaling governors, except for ``powersave`` and
|
||||
``performance``.
|
||||
|
||||
For example, if a given governor attempts to select a frequency proportional to
|
||||
estimated CPU load and maps the load of 100% to the maximum supported frequency
|
||||
(possibly multiplied by a constant), then it will tend to choose P-states below
|
||||
the turbo threshold if ``acpi-cpufreq`` is used as the scaling driver, because
|
||||
in that case the turbo range corresponds to a small fraction of the frequency
|
||||
band it can use (1 MHz vs 1 GHz or more). In consequence, it will only go to
|
||||
the turbo range for the highest loads and the other loads above 50% that might
|
||||
benefit from running at turbo frequencies will be given non-turbo P-states
|
||||
instead.
|
||||
|
||||
One more issue related to that may appear on systems supporting the
|
||||
`Configurable TDP feature <turbo_>`_ allowing the platform firmware to set the
|
||||
turbo threshold. Namely, if that is not coordinated with the lists of P-states
|
||||
returned by ``_PSS`` properly, there may be more than one item corresponding to
|
||||
a turbo P-state in those lists and there may be a problem with avoiding the
|
||||
turbo range (if desirable or necessary). Usually, to avoid using turbo
|
||||
P-states overall, ``acpi-cpufreq`` simply avoids using the topmost state listed
|
||||
by ``_PSS``, but that is not sufficient when there are other turbo P-states in
|
||||
the list returned by it.
|
||||
|
||||
Apart from the above, ``acpi-cpufreq`` works like ``intel_pstate`` in the
|
||||
`passive mode <Passive Mode_>`_, except that the number of P-states it can set
|
||||
is limited to the ones listed by the ACPI ``_PSS`` objects.
|
||||
|
||||
|
||||
Kernel Command Line Options for ``intel_pstate``
|
||||
================================================
|
||||
|
||||
Several kernel command line options can be used to pass early-configuration-time
|
||||
parameters to ``intel_pstate`` in order to enforce specific behavior of it. All
|
||||
of them have to be prepended with the ``intel_pstate=`` prefix.
|
||||
|
||||
``disable``
|
||||
Do not register ``intel_pstate`` as the scaling driver even if the
|
||||
processor is supported by it.
|
||||
|
||||
``passive``
|
||||
Register ``intel_pstate`` in the `passive mode <Passive Mode_>`_ to
|
||||
start with.
|
||||
|
||||
This option implies the ``no_hwp`` one described below.
|
||||
|
||||
``force``
|
||||
Register ``intel_pstate`` as the scaling driver instead of
|
||||
``acpi-cpufreq`` even if the latter is preferred on the given system.
|
||||
|
||||
This may prevent some platform features (such as thermal controls and
|
||||
power capping) that rely on the availability of ACPI P-states
|
||||
information from functioning as expected, so it should be used with
|
||||
caution.
|
||||
|
||||
This option does not work with processors that are not supported by
|
||||
``intel_pstate`` and on platforms where the ``pcc-cpufreq`` scaling
|
||||
driver is used instead of ``acpi-cpufreq``.
|
||||
|
||||
``no_hwp``
|
||||
Do not enable the `hardware-managed P-states (HWP) feature
|
||||
<Active Mode With HWP_>`_ even if it is supported by the processor.
|
||||
|
||||
``hwp_only``
|
||||
Register ``intel_pstate`` as the scaling driver only if the
|
||||
`hardware-managed P-states (HWP) feature <Active Mode With HWP_>`_ is
|
||||
supported by the processor.
|
||||
|
||||
``support_acpi_ppc``
|
||||
Take ACPI ``_PPC`` performance limits into account.
|
||||
|
||||
If the preferred power management profile in the FADT (Fixed ACPI
|
||||
Description Table) is set to "Enterprise Server" or "Performance
|
||||
Server", the ACPI ``_PPC`` limits are taken into account by default
|
||||
and this option has no effect.
|
||||
|
||||
``per_cpu_perf_limits``
|
||||
Use per-logical-CPU P-State limits (see `Coordination of P-state
|
||||
Limits`_ for details).
|
||||
|
||||
|
||||
Diagnostics and Tuning
|
||||
======================
|
||||
|
||||
Trace Events
|
||||
------------
|
||||
|
||||
There are two static trace events that can be used for ``intel_pstate``
|
||||
diagnostics. One of them is the ``cpu_frequency`` trace event generally used
|
||||
by ``CPUFreq``, and the other one is the ``pstate_sample`` trace event specific
|
||||
to ``intel_pstate``. Both of them are triggered by ``intel_pstate`` only if
|
||||
it works in the `active mode <Active Mode_>`_.
|
||||
|
||||
The following sequence of shell commands can be used to enable them and see
|
||||
their output (if the kernel is generally configured to support event tracing)::
|
||||
|
||||
# cd /sys/kernel/debug/tracing/
|
||||
# echo 1 > events/power/pstate_sample/enable
|
||||
# echo 1 > events/power/cpu_frequency/enable
|
||||
# cat trace
|
||||
gnome-terminal--4510 [001] ..s. 1177.680733: pstate_sample: core_busy=107 scaled=94 from=26 to=26 mperf=1143818 aperf=1230607 tsc=29838618 freq=2474476
|
||||
cat-5235 [002] ..s. 1177.681723: cpu_frequency: state=2900000 cpu_id=2
|
||||
|
||||
If ``intel_pstate`` works in the `passive mode <Passive Mode_>`_, the
|
||||
``cpu_frequency`` trace event will be triggered either by the ``schedutil``
|
||||
scaling governor (for the policies it is attached to), or by the ``CPUFreq``
|
||||
core (for the policies with other scaling governors).
|
||||
|
||||
``ftrace``
|
||||
----------
|
||||
|
||||
The ``ftrace`` interface can be used for low-level diagnostics of
|
||||
``intel_pstate``. For example, to check how often the function to set a
|
||||
P-state is called, the ``ftrace`` filter can be set to to
|
||||
:c:func:`intel_pstate_set_pstate`::
|
||||
|
||||
# cd /sys/kernel/debug/tracing/
|
||||
# cat available_filter_functions | grep -i pstate
|
||||
intel_pstate_set_pstate
|
||||
intel_pstate_cpu_init
|
||||
...
|
||||
# echo intel_pstate_set_pstate > set_ftrace_filter
|
||||
# echo function > current_tracer
|
||||
# cat trace | head -15
|
||||
# tracer: function
|
||||
#
|
||||
# entries-in-buffer/entries-written: 80/80 #P:4
|
||||
#
|
||||
# _-----=> irqs-off
|
||||
# / _----=> need-resched
|
||||
# | / _---=> hardirq/softirq
|
||||
# || / _--=> preempt-depth
|
||||
# ||| / delay
|
||||
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
|
||||
# | | | |||| | |
|
||||
Xorg-3129 [000] ..s. 2537.644844: intel_pstate_set_pstate <-intel_pstate_timer_func
|
||||
gnome-terminal--4510 [002] ..s. 2537.649844: intel_pstate_set_pstate <-intel_pstate_timer_func
|
||||
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
|
@ -1,281 +0,0 @@
|
||||
Intel P-State driver
|
||||
--------------------
|
||||
|
||||
This driver provides an interface to control the P-State selection for the
|
||||
SandyBridge+ Intel processors.
|
||||
|
||||
The following document explains P-States:
|
||||
http://events.linuxfoundation.org/sites/events/files/slides/LinuxConEurope_2015.pdf
|
||||
As stated in the document, P-State doesn’t exactly mean a frequency. However, for
|
||||
the sake of the relationship with cpufreq, P-State and frequency are used
|
||||
interchangeably.
|
||||
|
||||
Understanding the cpufreq core governors and policies are important before
|
||||
discussing more details about the Intel P-State driver. Based on what callbacks
|
||||
a cpufreq driver provides to the cpufreq core, it can support two types of
|
||||
drivers:
|
||||
- with target_index() callback: In this mode, the drivers using cpufreq core
|
||||
simply provide the minimum and maximum frequency limits and an additional
|
||||
interface target_index() to set the current frequency. The cpufreq subsystem
|
||||
has a number of scaling governors ("performance", "powersave", "ondemand",
|
||||
etc.). Depending on which governor is in use, cpufreq core will call for
|
||||
transitions to a specific frequency using target_index() callback.
|
||||
- setpolicy() callback: In this mode, drivers do not provide target_index()
|
||||
callback, so cpufreq core can't request a transition to a specific frequency.
|
||||
The driver provides minimum and maximum frequency limits and callbacks to set a
|
||||
policy. The policy in cpufreq sysfs is referred to as the "scaling governor".
|
||||
The cpufreq core can request the driver to operate in any of the two policies:
|
||||
"performance" and "powersave". The driver decides which frequency to use based
|
||||
on the above policy selection considering minimum and maximum frequency limits.
|
||||
|
||||
The Intel P-State driver falls under the latter category, which implements the
|
||||
setpolicy() callback. This driver decides what P-State to use based on the
|
||||
requested policy from the cpufreq core. If the processor is capable of
|
||||
selecting its next P-State internally, then the driver will offload this
|
||||
responsibility to the processor (aka HWP: Hardware P-States). If not, the
|
||||
driver implements algorithms to select the next P-State.
|
||||
|
||||
Since these policies are implemented in the driver, they are not same as the
|
||||
cpufreq scaling governors implementation, even if they have the same name in
|
||||
the cpufreq sysfs (scaling_governors). For example the "performance" policy is
|
||||
similar to cpufreq’s "performance" governor, but "powersave" is completely
|
||||
different than the cpufreq "powersave" governor. The strategy here is similar
|
||||
to cpufreq "ondemand", where the requested P-State is related to the system load.
|
||||
|
||||
Sysfs Interface
|
||||
|
||||
In addition to the frequency-controlling interfaces provided by the cpufreq
|
||||
core, the driver provides its own sysfs files to control the P-State selection.
|
||||
These files have been added to /sys/devices/system/cpu/intel_pstate/.
|
||||
Any changes made to these files are applicable to all CPUs (even in a
|
||||
multi-package system, Refer to later section on placing "Per-CPU limits").
|
||||
|
||||
max_perf_pct: Limits the maximum P-State that will be requested by
|
||||
the driver. It states it as a percentage of the available performance. The
|
||||
available (P-State) performance may be reduced by the no_turbo
|
||||
setting described below.
|
||||
|
||||
min_perf_pct: Limits the minimum P-State that will be requested by
|
||||
the driver. It states it as a percentage of the max (non-turbo)
|
||||
performance level.
|
||||
|
||||
no_turbo: Limits the driver to selecting P-State below the turbo
|
||||
frequency range.
|
||||
|
||||
turbo_pct: Displays the percentage of the total performance that
|
||||
is supported by hardware that is in the turbo range. This number
|
||||
is independent of whether turbo has been disabled or not.
|
||||
|
||||
num_pstates: Displays the number of P-States that are supported
|
||||
by hardware. This number is independent of whether turbo has
|
||||
been disabled or not.
|
||||
|
||||
For example, if a system has these parameters:
|
||||
Max 1 core turbo ratio: 0x21 (Max 1 core ratio is the maximum P-State)
|
||||
Max non turbo ratio: 0x17
|
||||
Minimum ratio : 0x08 (Here the ratio is called max efficiency ratio)
|
||||
|
||||
Sysfs will show :
|
||||
max_perf_pct:100, which corresponds to 1 core ratio
|
||||
min_perf_pct:24, max_efficiency_ratio / max 1 Core ratio
|
||||
no_turbo:0, turbo is not disabled
|
||||
num_pstates:26 = (max 1 Core ratio - Max Efficiency Ratio + 1)
|
||||
turbo_pct:39 = (max 1 core ratio - max non turbo ratio) / num_pstates
|
||||
|
||||
Refer to "Intel® 64 and IA-32 Architectures Software Developer’s Manual
|
||||
Volume 3: System Programming Guide" to understand ratios.
|
||||
|
||||
There is one more sysfs attribute in /sys/devices/system/cpu/intel_pstate/
|
||||
that can be used for controlling the operation mode of the driver:
|
||||
|
||||
status: Three settings are possible:
|
||||
"off" - The driver is not in use at this time.
|
||||
"active" - The driver works as a P-state governor (default).
|
||||
"passive" - The driver works as a regular cpufreq one and collaborates
|
||||
with the generic cpufreq governors (it sets P-states as
|
||||
requested by those governors).
|
||||
The current setting is returned by reads from this attribute. Writing one
|
||||
of the above strings to it changes the operation mode as indicated by that
|
||||
string, if possible. If HW-managed P-states (HWP) are enabled, it is not
|
||||
possible to change the driver's operation mode and attempts to write to
|
||||
this attribute will fail.
|
||||
|
||||
cpufreq sysfs for Intel P-State
|
||||
|
||||
Since this driver registers with cpufreq, cpufreq sysfs is also presented.
|
||||
There are some important differences, which need to be considered.
|
||||
|
||||
scaling_cur_freq: This displays the real frequency which was used during
|
||||
the last sample period instead of what is requested. Some other cpufreq driver,
|
||||
like acpi-cpufreq, displays what is requested (Some changes are on the
|
||||
way to fix this for acpi-cpufreq driver). The same is true for frequencies
|
||||
displayed at /proc/cpuinfo.
|
||||
|
||||
scaling_governor: This displays current active policy. Since each CPU has a
|
||||
cpufreq sysfs, it is possible to set a scaling governor to each CPU. But this
|
||||
is not possible with Intel P-States, as there is one common policy for all
|
||||
CPUs. Here, the last requested policy will be applicable to all CPUs. It is
|
||||
suggested that one use the cpupower utility to change policy to all CPUs at the
|
||||
same time.
|
||||
|
||||
scaling_setspeed: This attribute can never be used with Intel P-State.
|
||||
|
||||
scaling_max_freq/scaling_min_freq: This interface can be used similarly to
|
||||
the max_perf_pct/min_perf_pct of Intel P-State sysfs. However since frequencies
|
||||
are converted to nearest possible P-State, this is prone to rounding errors.
|
||||
This method is not preferred to limit performance.
|
||||
|
||||
affected_cpus: Not used
|
||||
related_cpus: Not used
|
||||
|
||||
For contemporary Intel processors, the frequency is controlled by the
|
||||
processor itself and the P-State exposed to software is related to
|
||||
performance levels. The idea that frequency can be set to a single
|
||||
frequency is fictional for Intel Core processors. Even if the scaling
|
||||
driver selects a single P-State, the actual frequency the processor
|
||||
will run at is selected by the processor itself.
|
||||
|
||||
Per-CPU limits
|
||||
|
||||
The kernel command line option "intel_pstate=per_cpu_perf_limits" forces
|
||||
the intel_pstate driver to use per-CPU performance limits. When it is set,
|
||||
the sysfs control interface described above is subject to limitations.
|
||||
- The following controls are not available for both read and write
|
||||
/sys/devices/system/cpu/intel_pstate/max_perf_pct
|
||||
/sys/devices/system/cpu/intel_pstate/min_perf_pct
|
||||
- The following controls can be used to set performance limits, as far as the
|
||||
architecture of the processor permits:
|
||||
/sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq
|
||||
/sys/devices/system/cpu/cpu*/cpufreq/scaling_min_freq
|
||||
/sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
|
||||
- User can still observe turbo percent and number of P-States from
|
||||
/sys/devices/system/cpu/intel_pstate/turbo_pct
|
||||
/sys/devices/system/cpu/intel_pstate/num_pstates
|
||||
- User can read write system wide turbo status
|
||||
/sys/devices/system/cpu/no_turbo
|
||||
|
||||
Support of energy performance hints
|
||||
It is possible to provide hints to the HWP algorithms in the processor
|
||||
to be more performance centric to more energy centric. When the driver
|
||||
is using HWP, two additional cpufreq sysfs attributes are presented for
|
||||
each logical CPU.
|
||||
These attributes are:
|
||||
- energy_performance_available_preferences
|
||||
- energy_performance_preference
|
||||
|
||||
To get list of supported hints:
|
||||
$ cat energy_performance_available_preferences
|
||||
default performance balance_performance balance_power power
|
||||
|
||||
The current preference can be read or changed via cpufreq sysfs
|
||||
attribute "energy_performance_preference". Reading from this attribute
|
||||
will display current effective setting. User can write any of the valid
|
||||
preference string to this attribute. User can always restore to power-on
|
||||
default by writing "default".
|
||||
|
||||
Since threads can migrate to different CPUs, this is possible that the
|
||||
new CPU may have different energy performance preference than the previous
|
||||
one. To avoid such issues, either threads can be pinned to specific CPUs
|
||||
or set the same energy performance preference value to all CPUs.
|
||||
|
||||
Tuning Intel P-State driver
|
||||
|
||||
When the performance can be tuned using PID (Proportional Integral
|
||||
Derivative) controller, debugfs files are provided for adjusting performance.
|
||||
They are presented under:
|
||||
/sys/kernel/debug/pstate_snb/
|
||||
|
||||
The PID tunable parameters are:
|
||||
deadband
|
||||
d_gain_pct
|
||||
i_gain_pct
|
||||
p_gain_pct
|
||||
sample_rate_ms
|
||||
setpoint
|
||||
|
||||
To adjust these parameters, some understanding of driver implementation is
|
||||
necessary. There are some tweeks described here, but be very careful. Adjusting
|
||||
them requires expert level understanding of power and performance relationship.
|
||||
These limits are only useful when the "powersave" policy is active.
|
||||
|
||||
-To make the system more responsive to load changes, sample_rate_ms can
|
||||
be adjusted (current default is 10ms).
|
||||
-To make the system use higher performance, even if the load is lower, setpoint
|
||||
can be adjusted to a lower number. This will also lead to faster ramp up time
|
||||
to reach the maximum P-State.
|
||||
If there are no derivative and integral coefficients, The next P-State will be
|
||||
equal to:
|
||||
current P-State - ((setpoint - current cpu load) * p_gain_pct)
|
||||
|
||||
For example, if the current PID parameters are (Which are defaults for the core
|
||||
processors like SandyBridge):
|
||||
deadband = 0
|
||||
d_gain_pct = 0
|
||||
i_gain_pct = 0
|
||||
p_gain_pct = 20
|
||||
sample_rate_ms = 10
|
||||
setpoint = 97
|
||||
|
||||
If the current P-State = 0x08 and current load = 100, this will result in the
|
||||
next P-State = 0x08 - ((97 - 100) * 0.2) = 8.6 (rounded to 9). Here the P-State
|
||||
goes up by only 1. If during next sample interval the current load doesn't
|
||||
change and still 100, then P-State goes up by one again. This process will
|
||||
continue as long as the load is more than the setpoint until the maximum P-State
|
||||
is reached.
|
||||
|
||||
For the same load at setpoint = 60, this will result in the next P-State
|
||||
= 0x08 - ((60 - 100) * 0.2) = 16
|
||||
So by changing the setpoint from 97 to 60, there is an increase of the
|
||||
next P-State from 9 to 16. So this will make processor execute at higher
|
||||
P-State for the same CPU load. If the load continues to be more than the
|
||||
setpoint during next sample intervals, then P-State will go up again till the
|
||||
maximum P-State is reached. But the ramp up time to reach the maximum P-State
|
||||
will be much faster when the setpoint is 60 compared to 97.
|
||||
|
||||
Debugging Intel P-State driver
|
||||
|
||||
Event tracing
|
||||
To debug P-State transition, the Linux event tracing interface can be used.
|
||||
There are two specific events, which can be enabled (Provided the kernel
|
||||
configs related to event tracing are enabled).
|
||||
|
||||
# cd /sys/kernel/debug/tracing/
|
||||
# echo 1 > events/power/pstate_sample/enable
|
||||
# echo 1 > events/power/cpu_frequency/enable
|
||||
# cat trace
|
||||
gnome-terminal--4510 [001] ..s. 1177.680733: pstate_sample: core_busy=107
|
||||
scaled=94 from=26 to=26 mperf=1143818 aperf=1230607 tsc=29838618
|
||||
freq=2474476
|
||||
cat-5235 [002] ..s. 1177.681723: cpu_frequency: state=2900000 cpu_id=2
|
||||
|
||||
|
||||
Using ftrace
|
||||
|
||||
If function level tracing is required, the Linux ftrace interface can be used.
|
||||
For example if we want to check how often a function to set a P-State is
|
||||
called, we can set ftrace filter to intel_pstate_set_pstate.
|
||||
|
||||
# cd /sys/kernel/debug/tracing/
|
||||
# cat available_filter_functions | grep -i pstate
|
||||
intel_pstate_set_pstate
|
||||
intel_pstate_cpu_init
|
||||
...
|
||||
|
||||
# echo intel_pstate_set_pstate > set_ftrace_filter
|
||||
# echo function > current_tracer
|
||||
# cat trace | head -15
|
||||
# tracer: function
|
||||
#
|
||||
# entries-in-buffer/entries-written: 80/80 #P:4
|
||||
#
|
||||
# _-----=> irqs-off
|
||||
# / _----=> need-resched
|
||||
# | / _---=> hardirq/softirq
|
||||
# || / _--=> preempt-depth
|
||||
# ||| / delay
|
||||
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
|
||||
# | | | |||| | |
|
||||
Xorg-3129 [000] ..s. 2537.644844: intel_pstate_set_pstate <-intel_pstate_timer_func
|
||||
gnome-terminal--4510 [002] ..s. 2537.649844: intel_pstate_set_pstate <-intel_pstate_timer_func
|
||||
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
|
@ -36,7 +36,7 @@ Optional properties:
|
||||
control gpios
|
||||
|
||||
- threshold: allows setting the "click"-threshold in the range
|
||||
from 20 to 80.
|
||||
from 0 to 80.
|
||||
|
||||
- gain: allows setting the sensitivity in the range from 0 to
|
||||
31. Note that lower values indicate higher
|
||||
|
@ -16,6 +16,11 @@ Required properties:
|
||||
- reg: Base address of PMIC on Hi6220 SoC.
|
||||
- interrupt-controller: Hi655x has internal IRQs (has own IRQ domain).
|
||||
- pmic-gpios: The GPIO used by PMIC IRQ.
|
||||
- #clock-cells: From common clock binding; shall be set to 0
|
||||
|
||||
Optional properties:
|
||||
- clock-output-names: From common clock binding to override the
|
||||
default output clock name
|
||||
|
||||
Example:
|
||||
pmic: pmic@f8000000 {
|
||||
@ -24,4 +29,5 @@ Example:
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
pmic-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
|
||||
#clock-cells = <0>;
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ Optional properties:
|
||||
"ext_clock" (External clock provided to the card).
|
||||
- post-power-on-delay-ms : Delay in ms after powering the card and
|
||||
de-asserting the reset-gpios (if any)
|
||||
- power-off-delay-us : Delay in us after asserting the reset-gpios (if any)
|
||||
during power off of the card.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -15,6 +15,10 @@ Optional properties:
|
||||
- phy-reset-active-high : If present then the reset sequence using the GPIO
|
||||
specified in the "phy-reset-gpios" property is reversed (H=reset state,
|
||||
L=operation state).
|
||||
- phy-reset-post-delay : Post reset delay in milliseconds. If present then
|
||||
a delay of phy-reset-post-delay milliseconds will be observed after the
|
||||
phy-reset-gpios has been toggled. Can be omitted thus no delay is
|
||||
observed. Delay is in range of 1ms to 1000ms. Other delays are invalid.
|
||||
- phy-supply : regulator that powers the Ethernet PHY.
|
||||
- phy-handle : phandle to the PHY device connected to this device.
|
||||
- fixed-link : Assume a fixed link. See fixed-link.txt in the same directory.
|
||||
|
@ -247,7 +247,6 @@ bias-bus-hold - latch weakly
|
||||
bias-pull-up - pull up the pin
|
||||
bias-pull-down - pull down the pin
|
||||
bias-pull-pin-default - use pin-default pull state
|
||||
bi-directional - pin supports simultaneous input/output operations
|
||||
drive-push-pull - drive actively high and low
|
||||
drive-open-drain - drive with open drain
|
||||
drive-open-source - drive with open source
|
||||
@ -260,7 +259,6 @@ input-debounce - debounce mode with debound time X
|
||||
power-source - select between different power supplies
|
||||
low-power-enable - enable low power mode
|
||||
low-power-disable - disable low power mode
|
||||
output-enable - enable output on pin regardless of output value
|
||||
output-low - set the pin to output mode with low level
|
||||
output-high - set the pin to output mode with high level
|
||||
slew-rate - set the slew rate
|
||||
|
@ -15,7 +15,7 @@ It has been tested with the following devices:
|
||||
The driver allows configuration of the touch screen via a set of sysfs files:
|
||||
|
||||
/sys/class/input/eventX/device/device/threshold:
|
||||
allows setting the "click"-threshold in the range from 20 to 80.
|
||||
allows setting the "click"-threshold in the range from 0 to 80.
|
||||
|
||||
/sys/class/input/eventX/device/device/gain:
|
||||
allows setting the sensitivity in the range from 0 to 31. Note that
|
||||
|
@ -16,6 +16,8 @@ ALC880
|
||||
6-jack in back, 2-jack in front
|
||||
6stack-digout
|
||||
6-jack with a SPDIF out
|
||||
6stack-automute
|
||||
6-jack with headphone jack detection
|
||||
|
||||
ALC260
|
||||
======
|
||||
@ -62,6 +64,8 @@ lenovo-dock
|
||||
Enables docking station I/O for some Lenovos
|
||||
hp-gpio-led
|
||||
GPIO LED support on HP laptops
|
||||
hp-dock-gpio-mic1-led
|
||||
HP dock with mic LED support
|
||||
dell-headset-multi
|
||||
Headset jack, which can also be used as mic-in
|
||||
dell-headset-dock
|
||||
@ -72,6 +76,12 @@ alc283-sense-combo
|
||||
Combo jack sensing on ALC283
|
||||
tpt440-dock
|
||||
Pin configs for Lenovo Thinkpad Dock support
|
||||
tpt440
|
||||
Lenovo Thinkpad T440s setup
|
||||
tpt460
|
||||
Lenovo Thinkpad T460/560 setup
|
||||
dual-codecs
|
||||
Lenovo laptops with dual codecs
|
||||
|
||||
ALC66x/67x/892
|
||||
==============
|
||||
@ -97,6 +107,8 @@ inv-dmic
|
||||
Inverted internal mic workaround
|
||||
dell-headset-multi
|
||||
Headset jack, which can also be used as mic-in
|
||||
dual-codecs
|
||||
Lenovo laptops with dual codecs
|
||||
|
||||
ALC680
|
||||
======
|
||||
@ -114,6 +126,8 @@ inv-dmic
|
||||
Inverted internal mic workaround
|
||||
no-primary-hp
|
||||
VAIO Z/VGC-LN51JGB workaround (for fixed speaker DAC)
|
||||
dual-codecs
|
||||
ALC1220 dual codecs for Gaming mobos
|
||||
|
||||
ALC861/660
|
||||
==========
|
||||
@ -206,65 +220,47 @@ auto
|
||||
|
||||
Conexant 5045
|
||||
=============
|
||||
laptop-hpsense
|
||||
Laptop with HP sense (old model laptop)
|
||||
laptop-micsense
|
||||
Laptop with Mic sense (old model fujitsu)
|
||||
laptop-hpmicsense
|
||||
Laptop with HP and Mic senses
|
||||
benq
|
||||
Benq R55E
|
||||
laptop-hp530
|
||||
HP 530 laptop
|
||||
test
|
||||
for testing/debugging purpose, almost all controls can be
|
||||
adjusted. Appearing only when compiled with $CONFIG_SND_DEBUG=y
|
||||
cap-mix-amp
|
||||
Fix max input level on mixer widget
|
||||
toshiba-p105
|
||||
Toshiba P105 quirk
|
||||
hp-530
|
||||
HP 530 quirk
|
||||
|
||||
Conexant 5047
|
||||
=============
|
||||
laptop
|
||||
Basic Laptop config
|
||||
laptop-hp
|
||||
Laptop config for some HP models (subdevice 30A5)
|
||||
laptop-eapd
|
||||
Laptop config with EAPD support
|
||||
test
|
||||
for testing/debugging purpose, almost all controls can be
|
||||
adjusted. Appearing only when compiled with $CONFIG_SND_DEBUG=y
|
||||
cap-mix-amp
|
||||
Fix max input level on mixer widget
|
||||
|
||||
Conexant 5051
|
||||
=============
|
||||
laptop
|
||||
Basic Laptop config (default)
|
||||
hp
|
||||
HP Spartan laptop
|
||||
hp-dv6736
|
||||
HP dv6736
|
||||
hp-f700
|
||||
HP Compaq Presario F700
|
||||
ideapad
|
||||
Lenovo IdeaPad laptop
|
||||
toshiba
|
||||
Toshiba Satellite M300
|
||||
lenovo-x200
|
||||
Lenovo X200 quirk
|
||||
|
||||
Conexant 5066
|
||||
=============
|
||||
laptop
|
||||
Basic Laptop config (default)
|
||||
hp-laptop
|
||||
HP laptops, e g G60
|
||||
asus
|
||||
Asus K52JU, Lenovo G560
|
||||
dell-laptop
|
||||
Dell laptops
|
||||
dell-vostro
|
||||
Dell Vostro
|
||||
olpc-xo-1_5
|
||||
OLPC XO 1.5
|
||||
ideapad
|
||||
Lenovo IdeaPad U150
|
||||
stereo-dmic
|
||||
Workaround for inverted stereo digital mic
|
||||
gpio1
|
||||
Enable GPIO1 pin
|
||||
headphone-mic-pin
|
||||
Enable headphone mic NID 0x18 without detection
|
||||
tp410
|
||||
Thinkpad T400 & co quirks
|
||||
thinkpad
|
||||
Lenovo Thinkpad
|
||||
Thinkpad mute/mic LED quirk
|
||||
lemote-a1004
|
||||
Lemote A1004 quirk
|
||||
lemote-a1205
|
||||
Lemote A1205 quirk
|
||||
olpc-xo
|
||||
OLPC XO quirk
|
||||
mute-led-eapd
|
||||
Mute LED control via EAPD
|
||||
hp-dock
|
||||
HP dock support
|
||||
mute-led-gpio
|
||||
Mute LED control via GPIO
|
||||
|
||||
STAC9200
|
||||
========
|
||||
@ -444,6 +440,8 @@ dell-eq
|
||||
Dell desktops/laptops
|
||||
alienware
|
||||
Alienware M17x
|
||||
asus-mobo
|
||||
Pin configs for ASUS mobo with 5.1/SPDIF out
|
||||
auto
|
||||
BIOS setup (default)
|
||||
|
||||
@ -477,6 +475,8 @@ hp-envy-ts-bass
|
||||
Pin fixup for HP Envy TS bass speaker (NID 0x10)
|
||||
hp-bnb13-eq
|
||||
Hardware equalizer setup for HP laptops
|
||||
hp-envy-ts-bass
|
||||
HP Envy TS bass support
|
||||
auto
|
||||
BIOS setup (default)
|
||||
|
||||
@ -496,10 +496,22 @@ auto
|
||||
|
||||
Cirrus Logic CS4206/4207
|
||||
========================
|
||||
mbp53
|
||||
MacBook Pro 5,3
|
||||
mbp55
|
||||
MacBook Pro 5,5
|
||||
imac27
|
||||
IMac 27 Inch
|
||||
imac27_122
|
||||
iMac 12,2
|
||||
apple
|
||||
Generic Apple quirk
|
||||
mbp101
|
||||
MacBookPro 10,1
|
||||
mbp81
|
||||
MacBookPro 8,1
|
||||
mba42
|
||||
MacBookAir 4,2
|
||||
auto
|
||||
BIOS setup (default)
|
||||
|
||||
@ -509,6 +521,10 @@ mba6
|
||||
MacBook Air 6,1 and 6,2
|
||||
gpio0
|
||||
Enable GPIO 0 amp
|
||||
mbp11
|
||||
MacBookPro 11,2
|
||||
macmini
|
||||
MacMini 7,1
|
||||
auto
|
||||
BIOS setup (default)
|
||||
|
||||
|
@ -7143,7 +7143,7 @@ S: Maintained
|
||||
F: drivers/media/platform/rcar_jpu.c
|
||||
|
||||
JSM Neo PCI based serial card
|
||||
M: Gabriel Krisman Bertazi <krisman@linux.vnet.ibm.com>
|
||||
M: Guilherme G. Piccoli <gpiccoli@linux.vnet.ibm.com>
|
||||
L: linux-serial@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/tty/serial/jsm/
|
||||
|
2
Makefile
2
Makefile
@ -1,7 +1,7 @@
|
||||
VERSION = 4
|
||||
PATCHLEVEL = 12
|
||||
SUBLEVEL = 0
|
||||
EXTRAVERSION = -rc2
|
||||
EXTRAVERSION = -rc3
|
||||
NAME = Fearless Coyote
|
||||
|
||||
# *DOCUMENTATION*
|
||||
|
@ -81,6 +81,45 @@ reboot-mode {
|
||||
};
|
||||
};
|
||||
|
||||
reg_sys_5v: regulator@0 {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "SYS_5V";
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
reg_vdd_3v3: regulator@1 {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "VDD_3V3";
|
||||
regulator-min-microvolt = <3300000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
vin-supply = <®_sys_5v>;
|
||||
};
|
||||
|
||||
reg_5v_hub: regulator@2 {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "5V_HUB";
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
regulator-boot-on;
|
||||
gpio = <&gpio0 7 0>;
|
||||
regulator-always-on;
|
||||
vin-supply = <®_sys_5v>;
|
||||
};
|
||||
|
||||
wl1835_pwrseq: wl1835-pwrseq {
|
||||
compatible = "mmc-pwrseq-simple";
|
||||
/* WLAN_EN GPIO */
|
||||
reset-gpios = <&gpio0 5 GPIO_ACTIVE_LOW>;
|
||||
clocks = <&pmic>;
|
||||
clock-names = "ext_clock";
|
||||
power-off-delay-us = <10>;
|
||||
};
|
||||
|
||||
soc {
|
||||
spi0: spi@f7106000 {
|
||||
status = "ok";
|
||||
@ -256,11 +295,31 @@ gpio15: gpio@f702b000 {
|
||||
|
||||
/* GPIO blocks 16 thru 19 do not appear to be routed to pins */
|
||||
|
||||
dwmmc_2: dwmmc2@f723f000 {
|
||||
ti,non-removable;
|
||||
dwmmc_0: dwmmc0@f723d000 {
|
||||
cap-mmc-highspeed;
|
||||
non-removable;
|
||||
/* WL_EN */
|
||||
vmmc-supply = <&wlan_en_reg>;
|
||||
bus-width = <0x8>;
|
||||
vmmc-supply = <&ldo19>;
|
||||
};
|
||||
|
||||
dwmmc_1: dwmmc1@f723e000 {
|
||||
card-detect-delay = <200>;
|
||||
cap-sd-highspeed;
|
||||
sd-uhs-sdr12;
|
||||
sd-uhs-sdr25;
|
||||
sd-uhs-sdr50;
|
||||
vqmmc-supply = <&ldo7>;
|
||||
vmmc-supply = <&ldo10>;
|
||||
bus-width = <0x4>;
|
||||
disable-wp;
|
||||
cd-gpios = <&gpio1 0 1>;
|
||||
};
|
||||
|
||||
dwmmc_2: dwmmc2@f723f000 {
|
||||
bus-width = <0x4>;
|
||||
non-removable;
|
||||
vmmc-supply = <®_vdd_3v3>;
|
||||
mmc-pwrseq = <&wl1835_pwrseq>;
|
||||
|
||||
#address-cells = <0x1>;
|
||||
#size-cells = <0x0>;
|
||||
@ -272,18 +331,6 @@ wlcore: wlcore@2 {
|
||||
interrupts = <3 IRQ_TYPE_EDGE_RISING>;
|
||||
};
|
||||
};
|
||||
|
||||
wlan_en_reg: regulator@1 {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "wlan-en-regulator";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
/* WLAN_EN GPIO */
|
||||
gpio = <&gpio0 5 0>;
|
||||
/* WLAN card specific delay */
|
||||
startup-delay-us = <70000>;
|
||||
enable-active-high;
|
||||
};
|
||||
};
|
||||
|
||||
leds {
|
||||
@ -330,6 +377,7 @@ bt_active_led {
|
||||
pmic: pmic@f8000000 {
|
||||
compatible = "hisilicon,hi655x-pmic";
|
||||
reg = <0x0 0xf8000000 0x0 0x1000>;
|
||||
#clock-cells = <0>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
pmic-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
|
||||
|
@ -725,20 +725,10 @@ i2c2: i2c@f7102000 {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
fixed_5v_hub: regulator@0 {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "fixed_5v_hub";
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
regulator-boot-on;
|
||||
gpio = <&gpio0 7 0>;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
usb_phy: usbphy {
|
||||
compatible = "hisilicon,hi6220-usb-phy";
|
||||
#phy-cells = <0>;
|
||||
phy-supply = <&fixed_5v_hub>;
|
||||
phy-supply = <®_5v_hub>;
|
||||
hisilicon,peripheral-syscon = <&sys_ctrl>;
|
||||
};
|
||||
|
||||
@ -766,17 +756,12 @@ mailbox: mailbox@f7510000 {
|
||||
|
||||
dwmmc_0: dwmmc0@f723d000 {
|
||||
compatible = "hisilicon,hi6220-dw-mshc";
|
||||
num-slots = <0x1>;
|
||||
cap-mmc-highspeed;
|
||||
non-removable;
|
||||
reg = <0x0 0xf723d000 0x0 0x1000>;
|
||||
interrupts = <0x0 0x48 0x4>;
|
||||
clocks = <&sys_ctrl 2>, <&sys_ctrl 1>;
|
||||
clock-names = "ciu", "biu";
|
||||
resets = <&sys_ctrl PERIPH_RSTDIS0_MMC0>;
|
||||
reset-names = "reset";
|
||||
bus-width = <0x8>;
|
||||
vmmc-supply = <&ldo19>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&emmc_pmx_func &emmc_clk_cfg_func
|
||||
&emmc_cfg_func &emmc_rst_cfg_func>;
|
||||
@ -784,13 +769,7 @@ dwmmc_0: dwmmc0@f723d000 {
|
||||
|
||||
dwmmc_1: dwmmc1@f723e000 {
|
||||
compatible = "hisilicon,hi6220-dw-mshc";
|
||||
num-slots = <0x1>;
|
||||
card-detect-delay = <200>;
|
||||
hisilicon,peripheral-syscon = <&ao_ctrl>;
|
||||
cap-sd-highspeed;
|
||||
sd-uhs-sdr12;
|
||||
sd-uhs-sdr25;
|
||||
sd-uhs-sdr50;
|
||||
reg = <0x0 0xf723e000 0x0 0x1000>;
|
||||
interrupts = <0x0 0x49 0x4>;
|
||||
#address-cells = <0x1>;
|
||||
@ -799,11 +778,6 @@ dwmmc_1: dwmmc1@f723e000 {
|
||||
clock-names = "ciu", "biu";
|
||||
resets = <&sys_ctrl PERIPH_RSTDIS0_MMC1>;
|
||||
reset-names = "reset";
|
||||
vqmmc-supply = <&ldo7>;
|
||||
vmmc-supply = <&ldo10>;
|
||||
bus-width = <0x4>;
|
||||
disable-wp;
|
||||
cd-gpios = <&gpio1 0 1>;
|
||||
pinctrl-names = "default", "idle";
|
||||
pinctrl-0 = <&sd_pmx_func &sd_clk_cfg_func &sd_cfg_func>;
|
||||
pinctrl-1 = <&sd_pmx_idle &sd_clk_cfg_idle &sd_cfg_idle>;
|
||||
@ -811,15 +785,12 @@ dwmmc_1: dwmmc1@f723e000 {
|
||||
|
||||
dwmmc_2: dwmmc2@f723f000 {
|
||||
compatible = "hisilicon,hi6220-dw-mshc";
|
||||
num-slots = <0x1>;
|
||||
reg = <0x0 0xf723f000 0x0 0x1000>;
|
||||
interrupts = <0x0 0x4a 0x4>;
|
||||
clocks = <&sys_ctrl HI6220_MMC2_CIUCLK>, <&sys_ctrl HI6220_MMC2_CLK>;
|
||||
clock-names = "ciu", "biu";
|
||||
resets = <&sys_ctrl PERIPH_RSTDIS0_MMC2>;
|
||||
reset-names = "reset";
|
||||
bus-width = <0x4>;
|
||||
broken-cd;
|
||||
pinctrl-names = "default", "idle";
|
||||
pinctrl-0 = <&sdio_pmx_func &sdio_clk_cfg_func &sdio_cfg_func>;
|
||||
pinctrl-1 = <&sdio_pmx_idle &sdio_clk_cfg_idle &sdio_cfg_idle>;
|
||||
|
@ -23,9 +23,9 @@
|
||||
#define ACPI_MADT_GICC_LENGTH \
|
||||
(acpi_gbl_FADT.header.revision < 6 ? 76 : 80)
|
||||
|
||||
#define BAD_MADT_GICC_ENTRY(entry, end) \
|
||||
(!(entry) || (unsigned long)(entry) + sizeof(*(entry)) > (end) || \
|
||||
(entry)->header.length != ACPI_MADT_GICC_LENGTH)
|
||||
#define BAD_MADT_GICC_ENTRY(entry, end) \
|
||||
(!(entry) || (entry)->header.length != ACPI_MADT_GICC_LENGTH || \
|
||||
(unsigned long)(entry) + ACPI_MADT_GICC_LENGTH > (end))
|
||||
|
||||
/* Basic configuration for ACPI */
|
||||
#ifdef CONFIG_ACPI
|
||||
|
@ -191,8 +191,10 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
|
||||
return NULL;
|
||||
|
||||
root_ops = kzalloc_node(sizeof(*root_ops), GFP_KERNEL, node);
|
||||
if (!root_ops)
|
||||
if (!root_ops) {
|
||||
kfree(ri);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ri->cfg = pci_acpi_setup_ecam_mapping(root);
|
||||
if (!ri->cfg) {
|
||||
|
@ -16,5 +16,11 @@ static inline cycles_t get_cycles(void)
|
||||
#define vxtime_lock() do {} while (0)
|
||||
#define vxtime_unlock() do {} while (0)
|
||||
|
||||
/* This attribute is used in include/linux/jiffies.h alongside with
|
||||
* __cacheline_aligned_in_smp. It is assumed that __cacheline_aligned_in_smp
|
||||
* for frv does not contain another section specification.
|
||||
*/
|
||||
#define __jiffy_arch_data __attribute__((__section__(".data")))
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -120,7 +120,6 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long usp,
|
||||
struct thread_info *ti = task_thread_info(p);
|
||||
struct pt_regs *childregs, *regs = current_pt_regs();
|
||||
unsigned long childksp;
|
||||
p->set_child_tid = p->clear_child_tid = NULL;
|
||||
|
||||
childksp = (unsigned long)task_stack_page(p) + THREAD_SIZE - 32;
|
||||
|
||||
|
@ -167,8 +167,6 @@ copy_thread(unsigned long clone_flags, unsigned long usp,
|
||||
|
||||
top_of_kernel_stack = sp;
|
||||
|
||||
p->set_child_tid = p->clear_child_tid = NULL;
|
||||
|
||||
/* Locate userspace context on stack... */
|
||||
sp -= STACK_FRAME_OVERHEAD; /* redzone */
|
||||
sp -= sizeof(struct pt_regs);
|
||||
|
@ -46,6 +46,8 @@
|
||||
#define PPC_FEATURE2_HTM_NOSC 0x01000000
|
||||
#define PPC_FEATURE2_ARCH_3_00 0x00800000 /* ISA 3.00 */
|
||||
#define PPC_FEATURE2_HAS_IEEE128 0x00400000 /* VSX IEEE Binary Float 128-bit */
|
||||
#define PPC_FEATURE2_DARN 0x00200000 /* darn random number insn */
|
||||
#define PPC_FEATURE2_SCV 0x00100000 /* scv syscall */
|
||||
|
||||
/*
|
||||
* IMPORTANT!
|
||||
|
@ -124,7 +124,8 @@ extern void __restore_cpu_e6500(void);
|
||||
#define COMMON_USER_POWER9 COMMON_USER_POWER8
|
||||
#define COMMON_USER2_POWER9 (COMMON_USER2_POWER8 | \
|
||||
PPC_FEATURE2_ARCH_3_00 | \
|
||||
PPC_FEATURE2_HAS_IEEE128)
|
||||
PPC_FEATURE2_HAS_IEEE128 | \
|
||||
PPC_FEATURE2_DARN )
|
||||
|
||||
#ifdef CONFIG_PPC_BOOK3E_64
|
||||
#define COMMON_USER_BOOKE (COMMON_USER_PPC64 | PPC_FEATURE_BOOKE)
|
||||
|
@ -161,7 +161,9 @@ static struct ibm_pa_feature {
|
||||
{ .pabyte = 0, .pabit = 3, .cpu_features = CPU_FTR_CTRL },
|
||||
{ .pabyte = 0, .pabit = 6, .cpu_features = CPU_FTR_NOEXECUTE },
|
||||
{ .pabyte = 1, .pabit = 2, .mmu_features = MMU_FTR_CI_LARGE_PAGE },
|
||||
#ifdef CONFIG_PPC_RADIX_MMU
|
||||
{ .pabyte = 40, .pabit = 0, .mmu_features = MMU_FTR_TYPE_RADIX },
|
||||
#endif
|
||||
{ .pabyte = 1, .pabit = 1, .invert = 1, .cpu_features = CPU_FTR_NODSISRALIGN },
|
||||
{ .pabyte = 5, .pabit = 0, .cpu_features = CPU_FTR_REAL_LE,
|
||||
.cpu_user_ftrs = PPC_FEATURE_TRUE_LE },
|
||||
|
@ -197,7 +197,9 @@ static int __spu_trap_data_map(struct spu *spu, unsigned long ea, u64 dsisr)
|
||||
(REGION_ID(ea) != USER_REGION_ID)) {
|
||||
|
||||
spin_unlock(&spu->register_lock);
|
||||
ret = hash_page(ea, _PAGE_PRESENT | _PAGE_READ, 0x300, dsisr);
|
||||
ret = hash_page(ea,
|
||||
_PAGE_PRESENT | _PAGE_READ | _PAGE_PRIVILEGED,
|
||||
0x300, dsisr);
|
||||
spin_lock(&spu->register_lock);
|
||||
|
||||
if (!ret) {
|
||||
|
@ -714,7 +714,7 @@ static void pnv_npu2_release_context(struct kref *kref)
|
||||
void pnv_npu2_destroy_context(struct npu_context *npu_context,
|
||||
struct pci_dev *gpdev)
|
||||
{
|
||||
struct pnv_phb *nphb, *phb;
|
||||
struct pnv_phb *nphb;
|
||||
struct npu *npu;
|
||||
struct pci_dev *npdev = pnv_pci_get_npu_dev(gpdev, 0);
|
||||
struct device_node *nvlink_dn;
|
||||
@ -728,13 +728,12 @@ void pnv_npu2_destroy_context(struct npu_context *npu_context,
|
||||
|
||||
nphb = pci_bus_to_host(npdev->bus)->private_data;
|
||||
npu = &nphb->npu;
|
||||
phb = pci_bus_to_host(gpdev->bus)->private_data;
|
||||
nvlink_dn = of_parse_phandle(npdev->dev.of_node, "ibm,nvlink", 0);
|
||||
if (WARN_ON(of_property_read_u32(nvlink_dn, "ibm,npu-link-index",
|
||||
&nvlink_index)))
|
||||
return;
|
||||
npu_context->npdev[npu->index][nvlink_index] = NULL;
|
||||
opal_npu_destroy_context(phb->opal_id, npu_context->mm->context.id,
|
||||
opal_npu_destroy_context(nphb->opal_id, npu_context->mm->context.id,
|
||||
PCI_DEVID(gpdev->bus->number, gpdev->devfn));
|
||||
kref_put(&npu_context->kref, pnv_npu2_release_context);
|
||||
}
|
||||
|
@ -360,7 +360,7 @@ config SMP
|
||||
Management" code will be disabled if you say Y here.
|
||||
|
||||
See also <file:Documentation/x86/i386/IO-APIC.txt>,
|
||||
<file:Documentation/nmi_watchdog.txt> and the SMP-HOWTO available at
|
||||
<file:Documentation/lockup-watchdogs.txt> and the SMP-HOWTO available at
|
||||
<http://www.tldp.org/docs.html#howto>.
|
||||
|
||||
If you don't know what to do here, say N.
|
||||
|
@ -159,7 +159,7 @@ ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||
# If '-Os' is enabled, disable it and print a warning.
|
||||
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
|
||||
undefine CONFIG_CC_OPTIMIZE_FOR_SIZE
|
||||
$(warning Disabling CONFIG_CC_OPTIMIZE_FOR_SIZE. Your compiler does not have -mfentry so you cannot optimize for size with CONFIG_FUNCTION_GRAPH_TRACER.)
|
||||
$(warning Disabling CONFIG_CC_OPTIMIZE_FOR_SIZE. Your compiler does not have -mfentry so you cannot optimize for size with CONFIG_FUNCTION_GRAPH_TRACER.)
|
||||
endif
|
||||
|
||||
endif
|
||||
|
@ -94,7 +94,7 @@ vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o
|
||||
quiet_cmd_check_data_rel = DATAREL $@
|
||||
define cmd_check_data_rel
|
||||
for obj in $(filter %.o,$^); do \
|
||||
readelf -S $$obj | grep -qF .rel.local && { \
|
||||
${CROSS_COMPILE}readelf -S $$obj | grep -qF .rel.local && { \
|
||||
echo "error: $$obj has data relocations!" >&2; \
|
||||
exit 1; \
|
||||
} || true; \
|
||||
|
@ -251,6 +251,23 @@ ENTRY(__switch_to_asm)
|
||||
jmp __switch_to
|
||||
END(__switch_to_asm)
|
||||
|
||||
/*
|
||||
* The unwinder expects the last frame on the stack to always be at the same
|
||||
* offset from the end of the page, which allows it to validate the stack.
|
||||
* Calling schedule_tail() directly would break that convention because its an
|
||||
* asmlinkage function so its argument has to be pushed on the stack. This
|
||||
* wrapper creates a proper "end of stack" frame header before the call.
|
||||
*/
|
||||
ENTRY(schedule_tail_wrapper)
|
||||
FRAME_BEGIN
|
||||
|
||||
pushl %eax
|
||||
call schedule_tail
|
||||
popl %eax
|
||||
|
||||
FRAME_END
|
||||
ret
|
||||
ENDPROC(schedule_tail_wrapper)
|
||||
/*
|
||||
* A newly forked process directly context switches into this address.
|
||||
*
|
||||
@ -259,24 +276,15 @@ END(__switch_to_asm)
|
||||
* edi: kernel thread arg
|
||||
*/
|
||||
ENTRY(ret_from_fork)
|
||||
FRAME_BEGIN /* help unwinder find end of stack */
|
||||
|
||||
/*
|
||||
* schedule_tail() is asmlinkage so we have to put its 'prev' argument
|
||||
* on the stack.
|
||||
*/
|
||||
pushl %eax
|
||||
call schedule_tail
|
||||
popl %eax
|
||||
call schedule_tail_wrapper
|
||||
|
||||
testl %ebx, %ebx
|
||||
jnz 1f /* kernel threads are uncommon */
|
||||
|
||||
2:
|
||||
/* When we fork, we trace the syscall return in the child, too. */
|
||||
leal FRAME_OFFSET(%esp), %eax
|
||||
movl %esp, %eax
|
||||
call syscall_return_slowpath
|
||||
FRAME_END
|
||||
jmp restore_all
|
||||
|
||||
/* kernel thread */
|
||||
|
@ -36,7 +36,6 @@
|
||||
#include <asm/smap.h>
|
||||
#include <asm/pgtable_types.h>
|
||||
#include <asm/export.h>
|
||||
#include <asm/frame.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
.code64
|
||||
@ -406,19 +405,17 @@ END(__switch_to_asm)
|
||||
* r12: kernel thread arg
|
||||
*/
|
||||
ENTRY(ret_from_fork)
|
||||
FRAME_BEGIN /* help unwinder find end of stack */
|
||||
movq %rax, %rdi
|
||||
call schedule_tail /* rdi: 'prev' task parameter */
|
||||
call schedule_tail /* rdi: 'prev' task parameter */
|
||||
|
||||
testq %rbx, %rbx /* from kernel_thread? */
|
||||
jnz 1f /* kernel threads are uncommon */
|
||||
testq %rbx, %rbx /* from kernel_thread? */
|
||||
jnz 1f /* kernel threads are uncommon */
|
||||
|
||||
2:
|
||||
leaq FRAME_OFFSET(%rsp),%rdi /* pt_regs pointer */
|
||||
movq %rsp, %rdi
|
||||
call syscall_return_slowpath /* returns with IRQs disabled */
|
||||
TRACE_IRQS_ON /* user mode is traced as IRQS on */
|
||||
SWAPGS
|
||||
FRAME_END
|
||||
jmp restore_regs_and_iret
|
||||
|
||||
1:
|
||||
|
@ -266,6 +266,7 @@ static inline int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *s
|
||||
#endif
|
||||
|
||||
int mce_available(struct cpuinfo_x86 *c);
|
||||
bool mce_is_memory_error(struct mce *m);
|
||||
|
||||
DECLARE_PER_CPU(unsigned, mce_exception_count);
|
||||
DECLARE_PER_CPU(unsigned, mce_poll_count);
|
||||
|
@ -409,8 +409,13 @@ void __init_or_module noinline apply_alternatives(struct alt_instr *start,
|
||||
memcpy(insnbuf, replacement, a->replacementlen);
|
||||
insnbuf_sz = a->replacementlen;
|
||||
|
||||
/* 0xe8 is a relative jump; fix the offset. */
|
||||
if (*insnbuf == 0xe8 && a->replacementlen == 5) {
|
||||
/*
|
||||
* 0xe8 is a relative jump; fix the offset.
|
||||
*
|
||||
* Instruction length is checked before the opcode to avoid
|
||||
* accessing uninitialized bytes for zero-length replacements.
|
||||
*/
|
||||
if (a->replacementlen == 5 && *insnbuf == 0xe8) {
|
||||
*(s32 *)(insnbuf + 1) += replacement - instr;
|
||||
DPRINTK("Fix CALL offset: 0x%x, CALL 0x%lx",
|
||||
*(s32 *)(insnbuf + 1),
|
||||
|
@ -499,16 +499,14 @@ static int mce_usable_address(struct mce *m)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool memory_error(struct mce *m)
|
||||
bool mce_is_memory_error(struct mce *m)
|
||||
{
|
||||
struct cpuinfo_x86 *c = &boot_cpu_data;
|
||||
|
||||
if (c->x86_vendor == X86_VENDOR_AMD) {
|
||||
if (m->cpuvendor == X86_VENDOR_AMD) {
|
||||
/* ErrCodeExt[20:16] */
|
||||
u8 xec = (m->status >> 16) & 0x1f;
|
||||
|
||||
return (xec == 0x0 || xec == 0x8);
|
||||
} else if (c->x86_vendor == X86_VENDOR_INTEL) {
|
||||
} else if (m->cpuvendor == X86_VENDOR_INTEL) {
|
||||
/*
|
||||
* Intel SDM Volume 3B - 15.9.2 Compound Error Codes
|
||||
*
|
||||
@ -529,6 +527,7 @@ static bool memory_error(struct mce *m)
|
||||
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mce_is_memory_error);
|
||||
|
||||
static bool cec_add_mce(struct mce *m)
|
||||
{
|
||||
@ -536,7 +535,7 @@ static bool cec_add_mce(struct mce *m)
|
||||
return false;
|
||||
|
||||
/* We eat only correctable DRAM errors with usable addresses. */
|
||||
if (memory_error(m) &&
|
||||
if (mce_is_memory_error(m) &&
|
||||
!(m->status & MCI_STATUS_UC) &&
|
||||
mce_usable_address(m))
|
||||
if (!cec_add_elem(m->addr >> PAGE_SHIFT))
|
||||
@ -713,7 +712,7 @@ bool machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
|
||||
|
||||
severity = mce_severity(&m, mca_cfg.tolerant, NULL, false);
|
||||
|
||||
if (severity == MCE_DEFERRED_SEVERITY && memory_error(&m))
|
||||
if (severity == MCE_DEFERRED_SEVERITY && mce_is_memory_error(&m))
|
||||
if (m.status & MCI_STATUS_ADDRV)
|
||||
m.severity = severity;
|
||||
|
||||
|
@ -320,7 +320,7 @@ void load_ucode_amd_ap(unsigned int cpuid_1_eax)
|
||||
}
|
||||
|
||||
static enum ucode_state
|
||||
load_microcode_amd(int cpu, u8 family, const u8 *data, size_t size);
|
||||
load_microcode_amd(bool save, u8 family, const u8 *data, size_t size);
|
||||
|
||||
int __init save_microcode_in_initrd_amd(unsigned int cpuid_1_eax)
|
||||
{
|
||||
@ -338,8 +338,7 @@ int __init save_microcode_in_initrd_amd(unsigned int cpuid_1_eax)
|
||||
if (!desc.mc)
|
||||
return -EINVAL;
|
||||
|
||||
ret = load_microcode_amd(smp_processor_id(), x86_family(cpuid_1_eax),
|
||||
desc.data, desc.size);
|
||||
ret = load_microcode_amd(true, x86_family(cpuid_1_eax), desc.data, desc.size);
|
||||
if (ret != UCODE_OK)
|
||||
return -EINVAL;
|
||||
|
||||
@ -675,7 +674,7 @@ static enum ucode_state __load_microcode_amd(u8 family, const u8 *data,
|
||||
}
|
||||
|
||||
static enum ucode_state
|
||||
load_microcode_amd(int cpu, u8 family, const u8 *data, size_t size)
|
||||
load_microcode_amd(bool save, u8 family, const u8 *data, size_t size)
|
||||
{
|
||||
enum ucode_state ret;
|
||||
|
||||
@ -689,8 +688,8 @@ load_microcode_amd(int cpu, u8 family, const u8 *data, size_t size)
|
||||
|
||||
#ifdef CONFIG_X86_32
|
||||
/* save BSP's matching patch for early load */
|
||||
if (cpu_data(cpu).cpu_index == boot_cpu_data.cpu_index) {
|
||||
struct ucode_patch *p = find_patch(cpu);
|
||||
if (save) {
|
||||
struct ucode_patch *p = find_patch(0);
|
||||
if (p) {
|
||||
memset(amd_ucode_patch, 0, PATCH_MAX_SIZE);
|
||||
memcpy(amd_ucode_patch, p->data, min_t(u32, ksize(p->data),
|
||||
@ -722,11 +721,12 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device,
|
||||
{
|
||||
char fw_name[36] = "amd-ucode/microcode_amd.bin";
|
||||
struct cpuinfo_x86 *c = &cpu_data(cpu);
|
||||
bool bsp = c->cpu_index == boot_cpu_data.cpu_index;
|
||||
enum ucode_state ret = UCODE_NFOUND;
|
||||
const struct firmware *fw;
|
||||
|
||||
/* reload ucode container only on the boot cpu */
|
||||
if (!refresh_fw || c->cpu_index != boot_cpu_data.cpu_index)
|
||||
if (!refresh_fw || !bsp)
|
||||
return UCODE_OK;
|
||||
|
||||
if (c->x86 >= 0x15)
|
||||
@ -743,7 +743,7 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device,
|
||||
goto fw_release;
|
||||
}
|
||||
|
||||
ret = load_microcode_amd(cpu, c->x86, fw->data, fw->size);
|
||||
ret = load_microcode_amd(bsp, c->x86, fw->data, fw->size);
|
||||
|
||||
fw_release:
|
||||
release_firmware(fw);
|
||||
|
@ -689,8 +689,12 @@ static inline void *alloc_tramp(unsigned long size)
|
||||
{
|
||||
return module_alloc(size);
|
||||
}
|
||||
static inline void tramp_free(void *tramp)
|
||||
static inline void tramp_free(void *tramp, int size)
|
||||
{
|
||||
int npages = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
|
||||
set_memory_nx((unsigned long)tramp, npages);
|
||||
set_memory_rw((unsigned long)tramp, npages);
|
||||
module_memfree(tramp);
|
||||
}
|
||||
#else
|
||||
@ -699,7 +703,7 @@ static inline void *alloc_tramp(unsigned long size)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline void tramp_free(void *tramp) { }
|
||||
static inline void tramp_free(void *tramp, int size) { }
|
||||
#endif
|
||||
|
||||
/* Defined as markers to the end of the ftrace default trampolines */
|
||||
@ -771,7 +775,7 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size)
|
||||
/* Copy ftrace_caller onto the trampoline memory */
|
||||
ret = probe_kernel_read(trampoline, (void *)start_offset, size);
|
||||
if (WARN_ON(ret < 0)) {
|
||||
tramp_free(trampoline);
|
||||
tramp_free(trampoline, *tramp_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -797,7 +801,7 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size)
|
||||
|
||||
/* Are we pointing to the reference? */
|
||||
if (WARN_ON(memcmp(op_ptr.op, op_ref, 3) != 0)) {
|
||||
tramp_free(trampoline);
|
||||
tramp_free(trampoline, *tramp_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -839,7 +843,7 @@ void arch_ftrace_update_trampoline(struct ftrace_ops *ops)
|
||||
unsigned long offset;
|
||||
unsigned long ip;
|
||||
unsigned int size;
|
||||
int ret;
|
||||
int ret, npages;
|
||||
|
||||
if (ops->trampoline) {
|
||||
/*
|
||||
@ -848,11 +852,14 @@ void arch_ftrace_update_trampoline(struct ftrace_ops *ops)
|
||||
*/
|
||||
if (!(ops->flags & FTRACE_OPS_FL_ALLOC_TRAMP))
|
||||
return;
|
||||
npages = PAGE_ALIGN(ops->trampoline_size) >> PAGE_SHIFT;
|
||||
set_memory_rw(ops->trampoline, npages);
|
||||
} else {
|
||||
ops->trampoline = create_trampoline(ops, &size);
|
||||
if (!ops->trampoline)
|
||||
return;
|
||||
ops->trampoline_size = size;
|
||||
npages = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
}
|
||||
|
||||
offset = calc_trampoline_call_offset(ops->flags & FTRACE_OPS_FL_SAVE_REGS);
|
||||
@ -863,6 +870,7 @@ void arch_ftrace_update_trampoline(struct ftrace_ops *ops)
|
||||
/* Do a safe modify in case the trampoline is executing */
|
||||
new = ftrace_call_replace(ip, (unsigned long)func);
|
||||
ret = update_ftrace_func(ip, new);
|
||||
set_memory_ro(ops->trampoline, npages);
|
||||
|
||||
/* The update should never fail */
|
||||
WARN_ON(ret);
|
||||
@ -939,7 +947,7 @@ void arch_ftrace_trampoline_free(struct ftrace_ops *ops)
|
||||
if (!ops || !(ops->flags & FTRACE_OPS_FL_ALLOC_TRAMP))
|
||||
return;
|
||||
|
||||
tramp_free((void *)ops->trampoline);
|
||||
tramp_free((void *)ops->trampoline, ops->trampoline_size);
|
||||
ops->trampoline = 0;
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,7 @@
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/frame.h>
|
||||
#include <linux/kasan.h>
|
||||
#include <linux/moduleloader.h>
|
||||
|
||||
#include <asm/text-patching.h>
|
||||
#include <asm/cacheflush.h>
|
||||
@ -417,6 +418,14 @@ static void prepare_boost(struct kprobe *p, struct insn *insn)
|
||||
}
|
||||
}
|
||||
|
||||
/* Recover page to RW mode before releasing it */
|
||||
void free_insn_page(void *page)
|
||||
{
|
||||
set_memory_nx((unsigned long)page & PAGE_MASK, 1);
|
||||
set_memory_rw((unsigned long)page & PAGE_MASK, 1);
|
||||
module_memfree(page);
|
||||
}
|
||||
|
||||
static int arch_copy_kprobe(struct kprobe *p)
|
||||
{
|
||||
struct insn insn;
|
||||
|
@ -78,7 +78,7 @@ void __show_regs(struct pt_regs *regs, int all)
|
||||
|
||||
printk(KERN_DEFAULT "EIP: %pS\n", (void *)regs->ip);
|
||||
printk(KERN_DEFAULT "EFLAGS: %08lx CPU: %d\n", regs->flags,
|
||||
smp_processor_id());
|
||||
raw_smp_processor_id());
|
||||
|
||||
printk(KERN_DEFAULT "EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
|
||||
regs->ax, regs->bx, regs->cx, regs->dx);
|
||||
|
@ -980,8 +980,6 @@ void __init setup_arch(char **cmdline_p)
|
||||
*/
|
||||
x86_configure_nx();
|
||||
|
||||
simple_udelay_calibration();
|
||||
|
||||
parse_early_param();
|
||||
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
@ -1041,6 +1039,8 @@ void __init setup_arch(char **cmdline_p)
|
||||
*/
|
||||
init_hypervisor_platform();
|
||||
|
||||
simple_udelay_calibration();
|
||||
|
||||
x86_init.resources.probe_roms();
|
||||
|
||||
/* after parse_early_param, so could debug it */
|
||||
|
@ -104,6 +104,11 @@ static inline unsigned long *last_frame(struct unwind_state *state)
|
||||
return (unsigned long *)task_pt_regs(state->task) - 2;
|
||||
}
|
||||
|
||||
static bool is_last_frame(struct unwind_state *state)
|
||||
{
|
||||
return state->bp == last_frame(state);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_X86_32
|
||||
#define GCC_REALIGN_WORDS 3
|
||||
#else
|
||||
@ -115,16 +120,15 @@ static inline unsigned long *last_aligned_frame(struct unwind_state *state)
|
||||
return last_frame(state) - GCC_REALIGN_WORDS;
|
||||
}
|
||||
|
||||
static bool is_last_task_frame(struct unwind_state *state)
|
||||
static bool is_last_aligned_frame(struct unwind_state *state)
|
||||
{
|
||||
unsigned long *last_bp = last_frame(state);
|
||||
unsigned long *aligned_bp = last_aligned_frame(state);
|
||||
|
||||
/*
|
||||
* We have to check for the last task frame at two different locations
|
||||
* because gcc can occasionally decide to realign the stack pointer and
|
||||
* change the offset of the stack frame in the prologue of a function
|
||||
* called by head/entry code. Examples:
|
||||
* GCC can occasionally decide to realign the stack pointer and change
|
||||
* the offset of the stack frame in the prologue of a function called
|
||||
* by head/entry code. Examples:
|
||||
*
|
||||
* <start_secondary>:
|
||||
* push %edi
|
||||
@ -141,11 +145,38 @@ static bool is_last_task_frame(struct unwind_state *state)
|
||||
* push %rbp
|
||||
* mov %rsp,%rbp
|
||||
*
|
||||
* Note that after aligning the stack, it pushes a duplicate copy of
|
||||
* the return address before pushing the frame pointer.
|
||||
* After aligning the stack, it pushes a duplicate copy of the return
|
||||
* address before pushing the frame pointer.
|
||||
*/
|
||||
return (state->bp == last_bp ||
|
||||
(state->bp == aligned_bp && *(aligned_bp+1) == *(last_bp+1)));
|
||||
return (state->bp == aligned_bp && *(aligned_bp + 1) == *(last_bp + 1));
|
||||
}
|
||||
|
||||
static bool is_last_ftrace_frame(struct unwind_state *state)
|
||||
{
|
||||
unsigned long *last_bp = last_frame(state);
|
||||
unsigned long *last_ftrace_bp = last_bp - 3;
|
||||
|
||||
/*
|
||||
* When unwinding from an ftrace handler of a function called by entry
|
||||
* code, the stack layout of the last frame is:
|
||||
*
|
||||
* bp
|
||||
* parent ret addr
|
||||
* bp
|
||||
* function ret addr
|
||||
* parent ret addr
|
||||
* pt_regs
|
||||
* -----------------
|
||||
*/
|
||||
return (state->bp == last_ftrace_bp &&
|
||||
*state->bp == *(state->bp + 2) &&
|
||||
*(state->bp + 1) == *(state->bp + 4));
|
||||
}
|
||||
|
||||
static bool is_last_task_frame(struct unwind_state *state)
|
||||
{
|
||||
return is_last_frame(state) || is_last_aligned_frame(state) ||
|
||||
is_last_ftrace_frame(state);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1495,8 +1495,10 @@ EXPORT_SYMBOL_GPL(kvm_lapic_hv_timer_in_use);
|
||||
|
||||
static void cancel_hv_timer(struct kvm_lapic *apic)
|
||||
{
|
||||
preempt_disable();
|
||||
kvm_x86_ops->cancel_hv_timer(apic->vcpu);
|
||||
apic->lapic_timer.hv_timer_in_use = false;
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
static bool start_hv_timer(struct kvm_lapic *apic)
|
||||
@ -1934,7 +1936,8 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event)
|
||||
for (i = 0; i < KVM_APIC_LVT_NUM; i++)
|
||||
kvm_lapic_set_reg(apic, APIC_LVTT + 0x10 * i, APIC_LVT_MASKED);
|
||||
apic_update_lvtt(apic);
|
||||
if (kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_LINT0_REENABLED))
|
||||
if (kvm_vcpu_is_reset_bsp(vcpu) &&
|
||||
kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_LINT0_REENABLED))
|
||||
kvm_lapic_set_reg(apic, APIC_LVT0,
|
||||
SET_APIC_DELIVERY_MODE(0, APIC_MODE_EXTINT));
|
||||
apic_manage_nmi_watchdog(apic, kvm_lapic_get_reg(apic, APIC_LVT0));
|
||||
|
@ -1807,7 +1807,7 @@ static void svm_get_segment(struct kvm_vcpu *vcpu,
|
||||
* AMD's VMCB does not have an explicit unusable field, so emulate it
|
||||
* for cross vendor migration purposes by "not present"
|
||||
*/
|
||||
var->unusable = !var->present || (var->type == 0);
|
||||
var->unusable = !var->present;
|
||||
|
||||
switch (seg) {
|
||||
case VCPU_SREG_TR:
|
||||
@ -1840,6 +1840,7 @@ static void svm_get_segment(struct kvm_vcpu *vcpu,
|
||||
*/
|
||||
if (var->unusable)
|
||||
var->db = 0;
|
||||
/* This is symmetric with svm_set_segment() */
|
||||
var->dpl = to_svm(vcpu)->vmcb->save.cpl;
|
||||
break;
|
||||
}
|
||||
@ -1980,18 +1981,14 @@ static void svm_set_segment(struct kvm_vcpu *vcpu,
|
||||
s->base = var->base;
|
||||
s->limit = var->limit;
|
||||
s->selector = var->selector;
|
||||
if (var->unusable)
|
||||
s->attrib = 0;
|
||||
else {
|
||||
s->attrib = (var->type & SVM_SELECTOR_TYPE_MASK);
|
||||
s->attrib |= (var->s & 1) << SVM_SELECTOR_S_SHIFT;
|
||||
s->attrib |= (var->dpl & 3) << SVM_SELECTOR_DPL_SHIFT;
|
||||
s->attrib |= (var->present & 1) << SVM_SELECTOR_P_SHIFT;
|
||||
s->attrib |= (var->avl & 1) << SVM_SELECTOR_AVL_SHIFT;
|
||||
s->attrib |= (var->l & 1) << SVM_SELECTOR_L_SHIFT;
|
||||
s->attrib |= (var->db & 1) << SVM_SELECTOR_DB_SHIFT;
|
||||
s->attrib |= (var->g & 1) << SVM_SELECTOR_G_SHIFT;
|
||||
}
|
||||
s->attrib = (var->type & SVM_SELECTOR_TYPE_MASK);
|
||||
s->attrib |= (var->s & 1) << SVM_SELECTOR_S_SHIFT;
|
||||
s->attrib |= (var->dpl & 3) << SVM_SELECTOR_DPL_SHIFT;
|
||||
s->attrib |= ((var->present & 1) && !var->unusable) << SVM_SELECTOR_P_SHIFT;
|
||||
s->attrib |= (var->avl & 1) << SVM_SELECTOR_AVL_SHIFT;
|
||||
s->attrib |= (var->l & 1) << SVM_SELECTOR_L_SHIFT;
|
||||
s->attrib |= (var->db & 1) << SVM_SELECTOR_DB_SHIFT;
|
||||
s->attrib |= (var->g & 1) << SVM_SELECTOR_G_SHIFT;
|
||||
|
||||
/*
|
||||
* This is always accurate, except if SYSRET returned to a segment
|
||||
@ -2000,7 +1997,8 @@ static void svm_set_segment(struct kvm_vcpu *vcpu,
|
||||
* would entail passing the CPL to userspace and back.
|
||||
*/
|
||||
if (seg == VCPU_SREG_SS)
|
||||
svm->vmcb->save.cpl = (s->attrib >> SVM_SELECTOR_DPL_SHIFT) & 3;
|
||||
/* This is symmetric with svm_get_segment() */
|
||||
svm->vmcb->save.cpl = (var->dpl & 3);
|
||||
|
||||
mark_dirty(svm->vmcb, VMCB_SEG);
|
||||
}
|
||||
|
@ -6914,97 +6914,21 @@ static int get_vmx_mem_address(struct kvm_vcpu *vcpu,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function performs the various checks including
|
||||
* - if it's 4KB aligned
|
||||
* - No bits beyond the physical address width are set
|
||||
* - Returns 0 on success or else 1
|
||||
* (Intel SDM Section 30.3)
|
||||
*/
|
||||
static int nested_vmx_check_vmptr(struct kvm_vcpu *vcpu, int exit_reason,
|
||||
gpa_t *vmpointer)
|
||||
static int nested_vmx_get_vmptr(struct kvm_vcpu *vcpu, gpa_t *vmpointer)
|
||||
{
|
||||
gva_t gva;
|
||||
gpa_t vmptr;
|
||||
struct x86_exception e;
|
||||
struct page *page;
|
||||
struct vcpu_vmx *vmx = to_vmx(vcpu);
|
||||
int maxphyaddr = cpuid_maxphyaddr(vcpu);
|
||||
|
||||
if (get_vmx_mem_address(vcpu, vmcs_readl(EXIT_QUALIFICATION),
|
||||
vmcs_read32(VMX_INSTRUCTION_INFO), false, &gva))
|
||||
return 1;
|
||||
|
||||
if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, &vmptr,
|
||||
sizeof(vmptr), &e)) {
|
||||
if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, vmpointer,
|
||||
sizeof(*vmpointer), &e)) {
|
||||
kvm_inject_page_fault(vcpu, &e);
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (exit_reason) {
|
||||
case EXIT_REASON_VMON:
|
||||
/*
|
||||
* SDM 3: 24.11.5
|
||||
* The first 4 bytes of VMXON region contain the supported
|
||||
* VMCS revision identifier
|
||||
*
|
||||
* Note - IA32_VMX_BASIC[48] will never be 1
|
||||
* for the nested case;
|
||||
* which replaces physical address width with 32
|
||||
*
|
||||
*/
|
||||
if (!PAGE_ALIGNED(vmptr) || (vmptr >> maxphyaddr)) {
|
||||
nested_vmx_failInvalid(vcpu);
|
||||
return kvm_skip_emulated_instruction(vcpu);
|
||||
}
|
||||
|
||||
page = nested_get_page(vcpu, vmptr);
|
||||
if (page == NULL) {
|
||||
nested_vmx_failInvalid(vcpu);
|
||||
return kvm_skip_emulated_instruction(vcpu);
|
||||
}
|
||||
if (*(u32 *)kmap(page) != VMCS12_REVISION) {
|
||||
kunmap(page);
|
||||
nested_release_page_clean(page);
|
||||
nested_vmx_failInvalid(vcpu);
|
||||
return kvm_skip_emulated_instruction(vcpu);
|
||||
}
|
||||
kunmap(page);
|
||||
nested_release_page_clean(page);
|
||||
vmx->nested.vmxon_ptr = vmptr;
|
||||
break;
|
||||
case EXIT_REASON_VMCLEAR:
|
||||
if (!PAGE_ALIGNED(vmptr) || (vmptr >> maxphyaddr)) {
|
||||
nested_vmx_failValid(vcpu,
|
||||
VMXERR_VMCLEAR_INVALID_ADDRESS);
|
||||
return kvm_skip_emulated_instruction(vcpu);
|
||||
}
|
||||
|
||||
if (vmptr == vmx->nested.vmxon_ptr) {
|
||||
nested_vmx_failValid(vcpu,
|
||||
VMXERR_VMCLEAR_VMXON_POINTER);
|
||||
return kvm_skip_emulated_instruction(vcpu);
|
||||
}
|
||||
break;
|
||||
case EXIT_REASON_VMPTRLD:
|
||||
if (!PAGE_ALIGNED(vmptr) || (vmptr >> maxphyaddr)) {
|
||||
nested_vmx_failValid(vcpu,
|
||||
VMXERR_VMPTRLD_INVALID_ADDRESS);
|
||||
return kvm_skip_emulated_instruction(vcpu);
|
||||
}
|
||||
|
||||
if (vmptr == vmx->nested.vmxon_ptr) {
|
||||
nested_vmx_failValid(vcpu,
|
||||
VMXERR_VMPTRLD_VMXON_POINTER);
|
||||
return kvm_skip_emulated_instruction(vcpu);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return 1; /* shouldn't happen */
|
||||
}
|
||||
|
||||
if (vmpointer)
|
||||
*vmpointer = vmptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -7066,6 +6990,8 @@ static int enter_vmx_operation(struct kvm_vcpu *vcpu)
|
||||
static int handle_vmon(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int ret;
|
||||
gpa_t vmptr;
|
||||
struct page *page;
|
||||
struct vcpu_vmx *vmx = to_vmx(vcpu);
|
||||
const u64 VMXON_NEEDED_FEATURES = FEATURE_CONTROL_LOCKED
|
||||
| FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX;
|
||||
@ -7095,9 +7021,37 @@ static int handle_vmon(struct kvm_vcpu *vcpu)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (nested_vmx_check_vmptr(vcpu, EXIT_REASON_VMON, NULL))
|
||||
if (nested_vmx_get_vmptr(vcpu, &vmptr))
|
||||
return 1;
|
||||
|
||||
|
||||
/*
|
||||
* SDM 3: 24.11.5
|
||||
* The first 4 bytes of VMXON region contain the supported
|
||||
* VMCS revision identifier
|
||||
*
|
||||
* Note - IA32_VMX_BASIC[48] will never be 1 for the nested case;
|
||||
* which replaces physical address width with 32
|
||||
*/
|
||||
if (!PAGE_ALIGNED(vmptr) || (vmptr >> cpuid_maxphyaddr(vcpu))) {
|
||||
nested_vmx_failInvalid(vcpu);
|
||||
return kvm_skip_emulated_instruction(vcpu);
|
||||
}
|
||||
|
||||
page = nested_get_page(vcpu, vmptr);
|
||||
if (page == NULL) {
|
||||
nested_vmx_failInvalid(vcpu);
|
||||
return kvm_skip_emulated_instruction(vcpu);
|
||||
}
|
||||
if (*(u32 *)kmap(page) != VMCS12_REVISION) {
|
||||
kunmap(page);
|
||||
nested_release_page_clean(page);
|
||||
nested_vmx_failInvalid(vcpu);
|
||||
return kvm_skip_emulated_instruction(vcpu);
|
||||
}
|
||||
kunmap(page);
|
||||
nested_release_page_clean(page);
|
||||
|
||||
vmx->nested.vmxon_ptr = vmptr;
|
||||
ret = enter_vmx_operation(vcpu);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -7213,9 +7167,19 @@ static int handle_vmclear(struct kvm_vcpu *vcpu)
|
||||
if (!nested_vmx_check_permission(vcpu))
|
||||
return 1;
|
||||
|
||||
if (nested_vmx_check_vmptr(vcpu, EXIT_REASON_VMCLEAR, &vmptr))
|
||||
if (nested_vmx_get_vmptr(vcpu, &vmptr))
|
||||
return 1;
|
||||
|
||||
if (!PAGE_ALIGNED(vmptr) || (vmptr >> cpuid_maxphyaddr(vcpu))) {
|
||||
nested_vmx_failValid(vcpu, VMXERR_VMCLEAR_INVALID_ADDRESS);
|
||||
return kvm_skip_emulated_instruction(vcpu);
|
||||
}
|
||||
|
||||
if (vmptr == vmx->nested.vmxon_ptr) {
|
||||
nested_vmx_failValid(vcpu, VMXERR_VMCLEAR_VMXON_POINTER);
|
||||
return kvm_skip_emulated_instruction(vcpu);
|
||||
}
|
||||
|
||||
if (vmptr == vmx->nested.current_vmptr)
|
||||
nested_release_vmcs12(vmx);
|
||||
|
||||
@ -7545,9 +7509,19 @@ static int handle_vmptrld(struct kvm_vcpu *vcpu)
|
||||
if (!nested_vmx_check_permission(vcpu))
|
||||
return 1;
|
||||
|
||||
if (nested_vmx_check_vmptr(vcpu, EXIT_REASON_VMPTRLD, &vmptr))
|
||||
if (nested_vmx_get_vmptr(vcpu, &vmptr))
|
||||
return 1;
|
||||
|
||||
if (!PAGE_ALIGNED(vmptr) || (vmptr >> cpuid_maxphyaddr(vcpu))) {
|
||||
nested_vmx_failValid(vcpu, VMXERR_VMPTRLD_INVALID_ADDRESS);
|
||||
return kvm_skip_emulated_instruction(vcpu);
|
||||
}
|
||||
|
||||
if (vmptr == vmx->nested.vmxon_ptr) {
|
||||
nested_vmx_failValid(vcpu, VMXERR_VMPTRLD_VMXON_POINTER);
|
||||
return kvm_skip_emulated_instruction(vcpu);
|
||||
}
|
||||
|
||||
if (vmx->nested.current_vmptr != vmptr) {
|
||||
struct vmcs12 *new_vmcs12;
|
||||
struct page *page;
|
||||
@ -7913,11 +7887,13 @@ static bool nested_vmx_exit_handled_cr(struct kvm_vcpu *vcpu,
|
||||
{
|
||||
unsigned long exit_qualification = vmcs_readl(EXIT_QUALIFICATION);
|
||||
int cr = exit_qualification & 15;
|
||||
int reg = (exit_qualification >> 8) & 15;
|
||||
unsigned long val = kvm_register_readl(vcpu, reg);
|
||||
int reg;
|
||||
unsigned long val;
|
||||
|
||||
switch ((exit_qualification >> 4) & 3) {
|
||||
case 0: /* mov to cr */
|
||||
reg = (exit_qualification >> 8) & 15;
|
||||
val = kvm_register_readl(vcpu, reg);
|
||||
switch (cr) {
|
||||
case 0:
|
||||
if (vmcs12->cr0_guest_host_mask &
|
||||
@ -7972,6 +7948,7 @@ static bool nested_vmx_exit_handled_cr(struct kvm_vcpu *vcpu,
|
||||
* lmsw can change bits 1..3 of cr0, and only set bit 0 of
|
||||
* cr0. Other attempted changes are ignored, with no exit.
|
||||
*/
|
||||
val = (exit_qualification >> LMSW_SOURCE_DATA_SHIFT) & 0x0f;
|
||||
if (vmcs12->cr0_guest_host_mask & 0xe &
|
||||
(val ^ vmcs12->cr0_read_shadow))
|
||||
return true;
|
||||
|
@ -8394,10 +8394,13 @@ static inline bool kvm_vcpu_has_events(struct kvm_vcpu *vcpu)
|
||||
if (vcpu->arch.pv.pv_unhalted)
|
||||
return true;
|
||||
|
||||
if (atomic_read(&vcpu->arch.nmi_queued))
|
||||
if (kvm_test_request(KVM_REQ_NMI, vcpu) ||
|
||||
(vcpu->arch.nmi_pending &&
|
||||
kvm_x86_ops->nmi_allowed(vcpu)))
|
||||
return true;
|
||||
|
||||
if (kvm_test_request(KVM_REQ_SMI, vcpu))
|
||||
if (kvm_test_request(KVM_REQ_SMI, vcpu) ||
|
||||
(vcpu->arch.smi_pending && !is_smm(vcpu)))
|
||||
return true;
|
||||
|
||||
if (kvm_arch_interrupt_allowed(vcpu) &&
|
||||
|
@ -186,7 +186,7 @@ static void cpa_flush_range(unsigned long start, int numpages, int cache)
|
||||
unsigned int i, level;
|
||||
unsigned long addr;
|
||||
|
||||
BUG_ON(irqs_disabled());
|
||||
BUG_ON(irqs_disabled() && !early_boot_irqs_disabled);
|
||||
WARN_ON(PAGE_ALIGN(start) != start);
|
||||
|
||||
on_each_cpu(__cpa_flush_range, NULL, 1);
|
||||
|
@ -828,9 +828,11 @@ static void __init kexec_enter_virtual_mode(void)
|
||||
|
||||
/*
|
||||
* We don't do virtual mode, since we don't do runtime services, on
|
||||
* non-native EFI
|
||||
* non-native EFI. With efi=old_map, we don't do runtime services in
|
||||
* kexec kernel because in the initial boot something else might
|
||||
* have been mapped at these virtual addresses.
|
||||
*/
|
||||
if (!efi_is_native()) {
|
||||
if (!efi_is_native() || efi_enabled(EFI_OLD_MEMMAP)) {
|
||||
efi_memmap_unmap();
|
||||
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
|
||||
return;
|
||||
|
@ -71,11 +71,13 @@ static void __init early_code_mapping_set_exec(int executable)
|
||||
|
||||
pgd_t * __init efi_call_phys_prolog(void)
|
||||
{
|
||||
unsigned long vaddress;
|
||||
pgd_t *save_pgd;
|
||||
unsigned long vaddr, addr_pgd, addr_p4d, addr_pud;
|
||||
pgd_t *save_pgd, *pgd_k, *pgd_efi;
|
||||
p4d_t *p4d, *p4d_k, *p4d_efi;
|
||||
pud_t *pud;
|
||||
|
||||
int pgd;
|
||||
int n_pgds;
|
||||
int n_pgds, i, j;
|
||||
|
||||
if (!efi_enabled(EFI_OLD_MEMMAP)) {
|
||||
save_pgd = (pgd_t *)read_cr3();
|
||||
@ -88,10 +90,49 @@ pgd_t * __init efi_call_phys_prolog(void)
|
||||
n_pgds = DIV_ROUND_UP((max_pfn << PAGE_SHIFT), PGDIR_SIZE);
|
||||
save_pgd = kmalloc_array(n_pgds, sizeof(*save_pgd), GFP_KERNEL);
|
||||
|
||||
/*
|
||||
* Build 1:1 identity mapping for efi=old_map usage. Note that
|
||||
* PAGE_OFFSET is PGDIR_SIZE aligned when KASLR is disabled, while
|
||||
* it is PUD_SIZE ALIGNED with KASLR enabled. So for a given physical
|
||||
* address X, the pud_index(X) != pud_index(__va(X)), we can only copy
|
||||
* PUD entry of __va(X) to fill in pud entry of X to build 1:1 mapping.
|
||||
* This means here we can only reuse the PMD tables of the direct mapping.
|
||||
*/
|
||||
for (pgd = 0; pgd < n_pgds; pgd++) {
|
||||
save_pgd[pgd] = *pgd_offset_k(pgd * PGDIR_SIZE);
|
||||
vaddress = (unsigned long)__va(pgd * PGDIR_SIZE);
|
||||
set_pgd(pgd_offset_k(pgd * PGDIR_SIZE), *pgd_offset_k(vaddress));
|
||||
addr_pgd = (unsigned long)(pgd * PGDIR_SIZE);
|
||||
vaddr = (unsigned long)__va(pgd * PGDIR_SIZE);
|
||||
pgd_efi = pgd_offset_k(addr_pgd);
|
||||
save_pgd[pgd] = *pgd_efi;
|
||||
|
||||
p4d = p4d_alloc(&init_mm, pgd_efi, addr_pgd);
|
||||
if (!p4d) {
|
||||
pr_err("Failed to allocate p4d table!\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < PTRS_PER_P4D; i++) {
|
||||
addr_p4d = addr_pgd + i * P4D_SIZE;
|
||||
p4d_efi = p4d + p4d_index(addr_p4d);
|
||||
|
||||
pud = pud_alloc(&init_mm, p4d_efi, addr_p4d);
|
||||
if (!pud) {
|
||||
pr_err("Failed to allocate pud table!\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (j = 0; j < PTRS_PER_PUD; j++) {
|
||||
addr_pud = addr_p4d + j * PUD_SIZE;
|
||||
|
||||
if (addr_pud > (max_pfn << PAGE_SHIFT))
|
||||
break;
|
||||
|
||||
vaddr = (unsigned long)__va(addr_pud);
|
||||
|
||||
pgd_k = pgd_offset_k(vaddr);
|
||||
p4d_k = p4d_offset(pgd_k, vaddr);
|
||||
pud[j] = *pud_offset(p4d_k, vaddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
out:
|
||||
__flush_tlb_all();
|
||||
@ -104,8 +145,11 @@ void __init efi_call_phys_epilog(pgd_t *save_pgd)
|
||||
/*
|
||||
* After the lock is released, the original page table is restored.
|
||||
*/
|
||||
int pgd_idx;
|
||||
int pgd_idx, i;
|
||||
int nr_pgds;
|
||||
pgd_t *pgd;
|
||||
p4d_t *p4d;
|
||||
pud_t *pud;
|
||||
|
||||
if (!efi_enabled(EFI_OLD_MEMMAP)) {
|
||||
write_cr3((unsigned long)save_pgd);
|
||||
@ -115,9 +159,28 @@ void __init efi_call_phys_epilog(pgd_t *save_pgd)
|
||||
|
||||
nr_pgds = DIV_ROUND_UP((max_pfn << PAGE_SHIFT) , PGDIR_SIZE);
|
||||
|
||||
for (pgd_idx = 0; pgd_idx < nr_pgds; pgd_idx++)
|
||||
for (pgd_idx = 0; pgd_idx < nr_pgds; pgd_idx++) {
|
||||
pgd = pgd_offset_k(pgd_idx * PGDIR_SIZE);
|
||||
set_pgd(pgd_offset_k(pgd_idx * PGDIR_SIZE), save_pgd[pgd_idx]);
|
||||
|
||||
if (!(pgd_val(*pgd) & _PAGE_PRESENT))
|
||||
continue;
|
||||
|
||||
for (i = 0; i < PTRS_PER_P4D; i++) {
|
||||
p4d = p4d_offset(pgd,
|
||||
pgd_idx * PGDIR_SIZE + i * P4D_SIZE);
|
||||
|
||||
if (!(p4d_val(*p4d) & _PAGE_PRESENT))
|
||||
continue;
|
||||
|
||||
pud = (pud_t *)p4d_page_vaddr(*p4d);
|
||||
pud_free(&init_mm, pud);
|
||||
}
|
||||
|
||||
p4d = (p4d_t *)pgd_page_vaddr(*pgd);
|
||||
p4d_free(&init_mm, p4d);
|
||||
}
|
||||
|
||||
kfree(save_pgd);
|
||||
|
||||
__flush_tlb_all();
|
||||
|
@ -360,6 +360,9 @@ void __init efi_free_boot_services(void)
|
||||
free_bootmem_late(start, size);
|
||||
}
|
||||
|
||||
if (!num_entries)
|
||||
return;
|
||||
|
||||
new_size = efi.memmap.desc_size * num_entries;
|
||||
new_phys = efi_memmap_alloc(num_entries);
|
||||
if (!new_phys) {
|
||||
|
@ -74,7 +74,7 @@ static void blkg_free(struct blkcg_gq *blkg)
|
||||
blkcg_policy[i]->pd_free_fn(blkg->pd[i]);
|
||||
|
||||
if (blkg->blkcg != &blkcg_root)
|
||||
blk_exit_rl(&blkg->rl);
|
||||
blk_exit_rl(blkg->q, &blkg->rl);
|
||||
|
||||
blkg_rwstat_exit(&blkg->stat_ios);
|
||||
blkg_rwstat_exit(&blkg->stat_bytes);
|
||||
|
@ -648,13 +648,19 @@ int blk_init_rl(struct request_list *rl, struct request_queue *q,
|
||||
if (!rl->rq_pool)
|
||||
return -ENOMEM;
|
||||
|
||||
if (rl != &q->root_rl)
|
||||
WARN_ON_ONCE(!blk_get_queue(q));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void blk_exit_rl(struct request_list *rl)
|
||||
void blk_exit_rl(struct request_queue *q, struct request_list *rl)
|
||||
{
|
||||
if (rl->rq_pool)
|
||||
if (rl->rq_pool) {
|
||||
mempool_destroy(rl->rq_pool);
|
||||
if (rl != &q->root_rl)
|
||||
blk_put_queue(q);
|
||||
}
|
||||
}
|
||||
|
||||
struct request_queue *blk_alloc_queue(gfp_t gfp_mask)
|
||||
|
@ -628,25 +628,6 @@ void blk_mq_delay_kick_requeue_list(struct request_queue *q,
|
||||
}
|
||||
EXPORT_SYMBOL(blk_mq_delay_kick_requeue_list);
|
||||
|
||||
void blk_mq_abort_requeue_list(struct request_queue *q)
|
||||
{
|
||||
unsigned long flags;
|
||||
LIST_HEAD(rq_list);
|
||||
|
||||
spin_lock_irqsave(&q->requeue_lock, flags);
|
||||
list_splice_init(&q->requeue_list, &rq_list);
|
||||
spin_unlock_irqrestore(&q->requeue_lock, flags);
|
||||
|
||||
while (!list_empty(&rq_list)) {
|
||||
struct request *rq;
|
||||
|
||||
rq = list_first_entry(&rq_list, struct request, queuelist);
|
||||
list_del_init(&rq->queuelist);
|
||||
blk_mq_end_request(rq, -EIO);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(blk_mq_abort_requeue_list);
|
||||
|
||||
struct request *blk_mq_tag_to_rq(struct blk_mq_tags *tags, unsigned int tag)
|
||||
{
|
||||
if (tag < tags->nr_tags) {
|
||||
@ -2660,7 +2641,8 @@ int blk_mq_update_nr_requests(struct request_queue *q, unsigned int nr)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, int nr_hw_queues)
|
||||
static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set,
|
||||
int nr_hw_queues)
|
||||
{
|
||||
struct request_queue *q;
|
||||
|
||||
@ -2684,6 +2666,13 @@ void blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, int nr_hw_queues)
|
||||
list_for_each_entry(q, &set->tag_list, tag_set_list)
|
||||
blk_mq_unfreeze_queue(q);
|
||||
}
|
||||
|
||||
void blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, int nr_hw_queues)
|
||||
{
|
||||
mutex_lock(&set->tag_list_lock);
|
||||
__blk_mq_update_nr_hw_queues(set, nr_hw_queues);
|
||||
mutex_unlock(&set->tag_list_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_mq_update_nr_hw_queues);
|
||||
|
||||
/* Enable polling stats and return whether they were already enabled. */
|
||||
|
@ -809,7 +809,7 @@ static void blk_release_queue(struct kobject *kobj)
|
||||
|
||||
blk_free_queue_stats(q->stats);
|
||||
|
||||
blk_exit_rl(&q->root_rl);
|
||||
blk_exit_rl(q, &q->root_rl);
|
||||
|
||||
if (q->queue_tags)
|
||||
__blk_queue_free_tags(q);
|
||||
@ -887,10 +887,10 @@ int blk_register_queue(struct gendisk *disk)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (q->mq_ops)
|
||||
if (q->mq_ops) {
|
||||
__blk_mq_register_dev(dev, q);
|
||||
|
||||
blk_mq_debugfs_register(q);
|
||||
blk_mq_debugfs_register(q);
|
||||
}
|
||||
|
||||
kobject_uevent(&q->kobj, KOBJ_ADD);
|
||||
|
||||
|
@ -22,11 +22,11 @@ static int throtl_quantum = 32;
|
||||
#define DFL_THROTL_SLICE_HD (HZ / 10)
|
||||
#define DFL_THROTL_SLICE_SSD (HZ / 50)
|
||||
#define MAX_THROTL_SLICE (HZ)
|
||||
#define DFL_IDLE_THRESHOLD_SSD (1000L) /* 1 ms */
|
||||
#define DFL_IDLE_THRESHOLD_HD (100L * 1000) /* 100 ms */
|
||||
#define MAX_IDLE_TIME (5L * 1000 * 1000) /* 5 s */
|
||||
/* default latency target is 0, eg, guarantee IO latency by default */
|
||||
#define DFL_LATENCY_TARGET (0)
|
||||
#define MIN_THROTL_BPS (320 * 1024)
|
||||
#define MIN_THROTL_IOPS (10)
|
||||
#define DFL_LATENCY_TARGET (-1L)
|
||||
#define DFL_IDLE_THRESHOLD (0)
|
||||
|
||||
#define SKIP_LATENCY (((u64)1) << BLK_STAT_RES_SHIFT)
|
||||
|
||||
@ -157,6 +157,7 @@ struct throtl_grp {
|
||||
unsigned long last_check_time;
|
||||
|
||||
unsigned long latency_target; /* us */
|
||||
unsigned long latency_target_conf; /* us */
|
||||
/* When did we start a new slice */
|
||||
unsigned long slice_start[2];
|
||||
unsigned long slice_end[2];
|
||||
@ -165,6 +166,7 @@ struct throtl_grp {
|
||||
unsigned long checked_last_finish_time; /* ns / 1024 */
|
||||
unsigned long avg_idletime; /* ns / 1024 */
|
||||
unsigned long idletime_threshold; /* us */
|
||||
unsigned long idletime_threshold_conf; /* us */
|
||||
|
||||
unsigned int bio_cnt; /* total bios */
|
||||
unsigned int bad_bio_cnt; /* bios exceeding latency threshold */
|
||||
@ -201,8 +203,6 @@ struct throtl_data
|
||||
unsigned int limit_index;
|
||||
bool limit_valid[LIMIT_CNT];
|
||||
|
||||
unsigned long dft_idletime_threshold; /* us */
|
||||
|
||||
unsigned long low_upgrade_time;
|
||||
unsigned long low_downgrade_time;
|
||||
|
||||
@ -294,8 +294,14 @@ static uint64_t tg_bps_limit(struct throtl_grp *tg, int rw)
|
||||
|
||||
td = tg->td;
|
||||
ret = tg->bps[rw][td->limit_index];
|
||||
if (ret == 0 && td->limit_index == LIMIT_LOW)
|
||||
return tg->bps[rw][LIMIT_MAX];
|
||||
if (ret == 0 && td->limit_index == LIMIT_LOW) {
|
||||
/* intermediate node or iops isn't 0 */
|
||||
if (!list_empty(&blkg->blkcg->css.children) ||
|
||||
tg->iops[rw][td->limit_index])
|
||||
return U64_MAX;
|
||||
else
|
||||
return MIN_THROTL_BPS;
|
||||
}
|
||||
|
||||
if (td->limit_index == LIMIT_MAX && tg->bps[rw][LIMIT_LOW] &&
|
||||
tg->bps[rw][LIMIT_LOW] != tg->bps[rw][LIMIT_MAX]) {
|
||||
@ -315,10 +321,17 @@ static unsigned int tg_iops_limit(struct throtl_grp *tg, int rw)
|
||||
|
||||
if (cgroup_subsys_on_dfl(io_cgrp_subsys) && !blkg->parent)
|
||||
return UINT_MAX;
|
||||
|
||||
td = tg->td;
|
||||
ret = tg->iops[rw][td->limit_index];
|
||||
if (ret == 0 && tg->td->limit_index == LIMIT_LOW)
|
||||
return tg->iops[rw][LIMIT_MAX];
|
||||
if (ret == 0 && tg->td->limit_index == LIMIT_LOW) {
|
||||
/* intermediate node or bps isn't 0 */
|
||||
if (!list_empty(&blkg->blkcg->css.children) ||
|
||||
tg->bps[rw][td->limit_index])
|
||||
return UINT_MAX;
|
||||
else
|
||||
return MIN_THROTL_IOPS;
|
||||
}
|
||||
|
||||
if (td->limit_index == LIMIT_MAX && tg->iops[rw][LIMIT_LOW] &&
|
||||
tg->iops[rw][LIMIT_LOW] != tg->iops[rw][LIMIT_MAX]) {
|
||||
@ -482,6 +495,9 @@ static struct blkg_policy_data *throtl_pd_alloc(gfp_t gfp, int node)
|
||||
/* LIMIT_LOW will have default value 0 */
|
||||
|
||||
tg->latency_target = DFL_LATENCY_TARGET;
|
||||
tg->latency_target_conf = DFL_LATENCY_TARGET;
|
||||
tg->idletime_threshold = DFL_IDLE_THRESHOLD;
|
||||
tg->idletime_threshold_conf = DFL_IDLE_THRESHOLD;
|
||||
|
||||
return &tg->pd;
|
||||
}
|
||||
@ -510,8 +526,6 @@ static void throtl_pd_init(struct blkg_policy_data *pd)
|
||||
if (cgroup_subsys_on_dfl(io_cgrp_subsys) && blkg->parent)
|
||||
sq->parent_sq = &blkg_to_tg(blkg->parent)->service_queue;
|
||||
tg->td = td;
|
||||
|
||||
tg->idletime_threshold = td->dft_idletime_threshold;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1349,7 +1363,7 @@ static int tg_print_conf_uint(struct seq_file *sf, void *v)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tg_conf_updated(struct throtl_grp *tg)
|
||||
static void tg_conf_updated(struct throtl_grp *tg, bool global)
|
||||
{
|
||||
struct throtl_service_queue *sq = &tg->service_queue;
|
||||
struct cgroup_subsys_state *pos_css;
|
||||
@ -1367,8 +1381,26 @@ static void tg_conf_updated(struct throtl_grp *tg)
|
||||
* restrictions in the whole hierarchy and allows them to bypass
|
||||
* blk-throttle.
|
||||
*/
|
||||
blkg_for_each_descendant_pre(blkg, pos_css, tg_to_blkg(tg))
|
||||
tg_update_has_rules(blkg_to_tg(blkg));
|
||||
blkg_for_each_descendant_pre(blkg, pos_css,
|
||||
global ? tg->td->queue->root_blkg : tg_to_blkg(tg)) {
|
||||
struct throtl_grp *this_tg = blkg_to_tg(blkg);
|
||||
struct throtl_grp *parent_tg;
|
||||
|
||||
tg_update_has_rules(this_tg);
|
||||
/* ignore root/second level */
|
||||
if (!cgroup_subsys_on_dfl(io_cgrp_subsys) || !blkg->parent ||
|
||||
!blkg->parent->parent)
|
||||
continue;
|
||||
parent_tg = blkg_to_tg(blkg->parent);
|
||||
/*
|
||||
* make sure all children has lower idle time threshold and
|
||||
* higher latency target
|
||||
*/
|
||||
this_tg->idletime_threshold = min(this_tg->idletime_threshold,
|
||||
parent_tg->idletime_threshold);
|
||||
this_tg->latency_target = max(this_tg->latency_target,
|
||||
parent_tg->latency_target);
|
||||
}
|
||||
|
||||
/*
|
||||
* We're already holding queue_lock and know @tg is valid. Let's
|
||||
@ -1413,7 +1445,7 @@ static ssize_t tg_set_conf(struct kernfs_open_file *of,
|
||||
else
|
||||
*(unsigned int *)((void *)tg + of_cft(of)->private) = v;
|
||||
|
||||
tg_conf_updated(tg);
|
||||
tg_conf_updated(tg, false);
|
||||
ret = 0;
|
||||
out_finish:
|
||||
blkg_conf_finish(&ctx);
|
||||
@ -1497,34 +1529,34 @@ static u64 tg_prfill_limit(struct seq_file *sf, struct blkg_policy_data *pd,
|
||||
tg->iops_conf[READ][off] == iops_dft &&
|
||||
tg->iops_conf[WRITE][off] == iops_dft &&
|
||||
(off != LIMIT_LOW ||
|
||||
(tg->idletime_threshold == tg->td->dft_idletime_threshold &&
|
||||
tg->latency_target == DFL_LATENCY_TARGET)))
|
||||
(tg->idletime_threshold_conf == DFL_IDLE_THRESHOLD &&
|
||||
tg->latency_target_conf == DFL_LATENCY_TARGET)))
|
||||
return 0;
|
||||
|
||||
if (tg->bps_conf[READ][off] != bps_dft)
|
||||
if (tg->bps_conf[READ][off] != U64_MAX)
|
||||
snprintf(bufs[0], sizeof(bufs[0]), "%llu",
|
||||
tg->bps_conf[READ][off]);
|
||||
if (tg->bps_conf[WRITE][off] != bps_dft)
|
||||
if (tg->bps_conf[WRITE][off] != U64_MAX)
|
||||
snprintf(bufs[1], sizeof(bufs[1]), "%llu",
|
||||
tg->bps_conf[WRITE][off]);
|
||||
if (tg->iops_conf[READ][off] != iops_dft)
|
||||
if (tg->iops_conf[READ][off] != UINT_MAX)
|
||||
snprintf(bufs[2], sizeof(bufs[2]), "%u",
|
||||
tg->iops_conf[READ][off]);
|
||||
if (tg->iops_conf[WRITE][off] != iops_dft)
|
||||
if (tg->iops_conf[WRITE][off] != UINT_MAX)
|
||||
snprintf(bufs[3], sizeof(bufs[3]), "%u",
|
||||
tg->iops_conf[WRITE][off]);
|
||||
if (off == LIMIT_LOW) {
|
||||
if (tg->idletime_threshold == ULONG_MAX)
|
||||
if (tg->idletime_threshold_conf == ULONG_MAX)
|
||||
strcpy(idle_time, " idle=max");
|
||||
else
|
||||
snprintf(idle_time, sizeof(idle_time), " idle=%lu",
|
||||
tg->idletime_threshold);
|
||||
tg->idletime_threshold_conf);
|
||||
|
||||
if (tg->latency_target == ULONG_MAX)
|
||||
if (tg->latency_target_conf == ULONG_MAX)
|
||||
strcpy(latency_time, " latency=max");
|
||||
else
|
||||
snprintf(latency_time, sizeof(latency_time),
|
||||
" latency=%lu", tg->latency_target);
|
||||
" latency=%lu", tg->latency_target_conf);
|
||||
}
|
||||
|
||||
seq_printf(sf, "%s rbps=%s wbps=%s riops=%s wiops=%s%s%s\n",
|
||||
@ -1563,8 +1595,8 @@ static ssize_t tg_set_limit(struct kernfs_open_file *of,
|
||||
v[2] = tg->iops_conf[READ][index];
|
||||
v[3] = tg->iops_conf[WRITE][index];
|
||||
|
||||
idle_time = tg->idletime_threshold;
|
||||
latency_time = tg->latency_target;
|
||||
idle_time = tg->idletime_threshold_conf;
|
||||
latency_time = tg->latency_target_conf;
|
||||
while (true) {
|
||||
char tok[27]; /* wiops=18446744073709551616 */
|
||||
char *p;
|
||||
@ -1623,17 +1655,33 @@ static ssize_t tg_set_limit(struct kernfs_open_file *of,
|
||||
tg->iops_conf[READ][LIMIT_MAX]);
|
||||
tg->iops[WRITE][LIMIT_LOW] = min(tg->iops_conf[WRITE][LIMIT_LOW],
|
||||
tg->iops_conf[WRITE][LIMIT_MAX]);
|
||||
tg->idletime_threshold_conf = idle_time;
|
||||
tg->latency_target_conf = latency_time;
|
||||
|
||||
if (index == LIMIT_LOW) {
|
||||
blk_throtl_update_limit_valid(tg->td);
|
||||
if (tg->td->limit_valid[LIMIT_LOW])
|
||||
tg->td->limit_index = LIMIT_LOW;
|
||||
tg->idletime_threshold = (idle_time == ULONG_MAX) ?
|
||||
ULONG_MAX : idle_time;
|
||||
tg->latency_target = (latency_time == ULONG_MAX) ?
|
||||
ULONG_MAX : latency_time;
|
||||
/* force user to configure all settings for low limit */
|
||||
if (!(tg->bps[READ][LIMIT_LOW] || tg->iops[READ][LIMIT_LOW] ||
|
||||
tg->bps[WRITE][LIMIT_LOW] || tg->iops[WRITE][LIMIT_LOW]) ||
|
||||
tg->idletime_threshold_conf == DFL_IDLE_THRESHOLD ||
|
||||
tg->latency_target_conf == DFL_LATENCY_TARGET) {
|
||||
tg->bps[READ][LIMIT_LOW] = 0;
|
||||
tg->bps[WRITE][LIMIT_LOW] = 0;
|
||||
tg->iops[READ][LIMIT_LOW] = 0;
|
||||
tg->iops[WRITE][LIMIT_LOW] = 0;
|
||||
tg->idletime_threshold = DFL_IDLE_THRESHOLD;
|
||||
tg->latency_target = DFL_LATENCY_TARGET;
|
||||
} else if (index == LIMIT_LOW) {
|
||||
tg->idletime_threshold = tg->idletime_threshold_conf;
|
||||
tg->latency_target = tg->latency_target_conf;
|
||||
}
|
||||
tg_conf_updated(tg);
|
||||
|
||||
blk_throtl_update_limit_valid(tg->td);
|
||||
if (tg->td->limit_valid[LIMIT_LOW]) {
|
||||
if (index == LIMIT_LOW)
|
||||
tg->td->limit_index = LIMIT_LOW;
|
||||
} else
|
||||
tg->td->limit_index = LIMIT_MAX;
|
||||
tg_conf_updated(tg, index == LIMIT_LOW &&
|
||||
tg->td->limit_valid[LIMIT_LOW]);
|
||||
ret = 0;
|
||||
out_finish:
|
||||
blkg_conf_finish(&ctx);
|
||||
@ -1722,17 +1770,25 @@ static bool throtl_tg_is_idle(struct throtl_grp *tg)
|
||||
/*
|
||||
* cgroup is idle if:
|
||||
* - single idle is too long, longer than a fixed value (in case user
|
||||
* configure a too big threshold) or 4 times of slice
|
||||
* configure a too big threshold) or 4 times of idletime threshold
|
||||
* - average think time is more than threshold
|
||||
* - IO latency is largely below threshold
|
||||
*/
|
||||
unsigned long time = jiffies_to_usecs(4 * tg->td->throtl_slice);
|
||||
unsigned long time;
|
||||
bool ret;
|
||||
|
||||
time = min_t(unsigned long, MAX_IDLE_TIME, time);
|
||||
return (ktime_get_ns() >> 10) - tg->last_finish_time > time ||
|
||||
tg->avg_idletime > tg->idletime_threshold ||
|
||||
(tg->latency_target && tg->bio_cnt &&
|
||||
time = min_t(unsigned long, MAX_IDLE_TIME, 4 * tg->idletime_threshold);
|
||||
ret = tg->latency_target == DFL_LATENCY_TARGET ||
|
||||
tg->idletime_threshold == DFL_IDLE_THRESHOLD ||
|
||||
(ktime_get_ns() >> 10) - tg->last_finish_time > time ||
|
||||
tg->avg_idletime > tg->idletime_threshold ||
|
||||
(tg->latency_target && tg->bio_cnt &&
|
||||
tg->bad_bio_cnt * 5 < tg->bio_cnt);
|
||||
throtl_log(&tg->service_queue,
|
||||
"avg_idle=%ld, idle_threshold=%ld, bad_bio=%d, total_bio=%d, is_idle=%d, scale=%d",
|
||||
tg->avg_idletime, tg->idletime_threshold, tg->bad_bio_cnt,
|
||||
tg->bio_cnt, ret, tg->td->scale);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool throtl_tg_can_upgrade(struct throtl_grp *tg)
|
||||
@ -1828,6 +1884,7 @@ static void throtl_upgrade_state(struct throtl_data *td)
|
||||
struct cgroup_subsys_state *pos_css;
|
||||
struct blkcg_gq *blkg;
|
||||
|
||||
throtl_log(&td->service_queue, "upgrade to max");
|
||||
td->limit_index = LIMIT_MAX;
|
||||
td->low_upgrade_time = jiffies;
|
||||
td->scale = 0;
|
||||
@ -1850,6 +1907,7 @@ static void throtl_downgrade_state(struct throtl_data *td, int new)
|
||||
{
|
||||
td->scale /= 2;
|
||||
|
||||
throtl_log(&td->service_queue, "downgrade, scale %d", td->scale);
|
||||
if (td->scale) {
|
||||
td->low_upgrade_time = jiffies - td->scale * td->throtl_slice;
|
||||
return;
|
||||
@ -2023,6 +2081,11 @@ static void throtl_update_latency_buckets(struct throtl_data *td)
|
||||
td->avg_buckets[i].valid = true;
|
||||
last_latency = td->avg_buckets[i].latency;
|
||||
}
|
||||
|
||||
for (i = 0; i < LATENCY_BUCKET_SIZE; i++)
|
||||
throtl_log(&td->service_queue,
|
||||
"Latency bucket %d: latency=%ld, valid=%d", i,
|
||||
td->avg_buckets[i].latency, td->avg_buckets[i].valid);
|
||||
}
|
||||
#else
|
||||
static inline void throtl_update_latency_buckets(struct throtl_data *td)
|
||||
@ -2354,19 +2417,14 @@ void blk_throtl_exit(struct request_queue *q)
|
||||
void blk_throtl_register_queue(struct request_queue *q)
|
||||
{
|
||||
struct throtl_data *td;
|
||||
struct cgroup_subsys_state *pos_css;
|
||||
struct blkcg_gq *blkg;
|
||||
|
||||
td = q->td;
|
||||
BUG_ON(!td);
|
||||
|
||||
if (blk_queue_nonrot(q)) {
|
||||
if (blk_queue_nonrot(q))
|
||||
td->throtl_slice = DFL_THROTL_SLICE_SSD;
|
||||
td->dft_idletime_threshold = DFL_IDLE_THRESHOLD_SSD;
|
||||
} else {
|
||||
else
|
||||
td->throtl_slice = DFL_THROTL_SLICE_HD;
|
||||
td->dft_idletime_threshold = DFL_IDLE_THRESHOLD_HD;
|
||||
}
|
||||
#ifndef CONFIG_BLK_DEV_THROTTLING_LOW
|
||||
/* if no low limit, use previous default */
|
||||
td->throtl_slice = DFL_THROTL_SLICE_HD;
|
||||
@ -2375,18 +2433,6 @@ void blk_throtl_register_queue(struct request_queue *q)
|
||||
td->track_bio_latency = !q->mq_ops && !q->request_fn;
|
||||
if (!td->track_bio_latency)
|
||||
blk_stat_enable_accounting(q);
|
||||
|
||||
/*
|
||||
* some tg are created before queue is fully initialized, eg, nonrot
|
||||
* isn't initialized yet
|
||||
*/
|
||||
rcu_read_lock();
|
||||
blkg_for_each_descendant_post(blkg, pos_css, q->root_blkg) {
|
||||
struct throtl_grp *tg = blkg_to_tg(blkg);
|
||||
|
||||
tg->idletime_threshold = td->dft_idletime_threshold;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_THROTTLING_LOW
|
||||
|
@ -59,7 +59,7 @@ void blk_free_flush_queue(struct blk_flush_queue *q);
|
||||
|
||||
int blk_init_rl(struct request_list *rl, struct request_queue *q,
|
||||
gfp_t gfp_mask);
|
||||
void blk_exit_rl(struct request_list *rl);
|
||||
void blk_exit_rl(struct request_queue *q, struct request_list *rl);
|
||||
void blk_rq_bio_prep(struct request_queue *q, struct request *rq,
|
||||
struct bio *bio);
|
||||
void blk_queue_bypass_start(struct request_queue *q);
|
||||
|
@ -38,9 +38,13 @@ static const u64 cfq_target_latency = (u64)NSEC_PER_SEC * 3/10; /* 300 ms */
|
||||
static const int cfq_hist_divisor = 4;
|
||||
|
||||
/*
|
||||
* offset from end of service tree
|
||||
* offset from end of queue service tree for idle class
|
||||
*/
|
||||
#define CFQ_IDLE_DELAY (NSEC_PER_SEC / 5)
|
||||
/* offset from end of group service tree under time slice mode */
|
||||
#define CFQ_SLICE_MODE_GROUP_DELAY (NSEC_PER_SEC / 5)
|
||||
/* offset from end of group service under IOPS mode */
|
||||
#define CFQ_IOPS_MODE_GROUP_DELAY (HZ / 5)
|
||||
|
||||
/*
|
||||
* below this threshold, we consider thinktime immediate
|
||||
@ -1362,6 +1366,14 @@ cfq_group_service_tree_add(struct cfq_rb_root *st, struct cfq_group *cfqg)
|
||||
cfqg->vfraction = max_t(unsigned, vfr, 1);
|
||||
}
|
||||
|
||||
static inline u64 cfq_get_cfqg_vdisktime_delay(struct cfq_data *cfqd)
|
||||
{
|
||||
if (!iops_mode(cfqd))
|
||||
return CFQ_SLICE_MODE_GROUP_DELAY;
|
||||
else
|
||||
return CFQ_IOPS_MODE_GROUP_DELAY;
|
||||
}
|
||||
|
||||
static void
|
||||
cfq_group_notify_queue_add(struct cfq_data *cfqd, struct cfq_group *cfqg)
|
||||
{
|
||||
@ -1381,7 +1393,8 @@ cfq_group_notify_queue_add(struct cfq_data *cfqd, struct cfq_group *cfqg)
|
||||
n = rb_last(&st->rb);
|
||||
if (n) {
|
||||
__cfqg = rb_entry_cfqg(n);
|
||||
cfqg->vdisktime = __cfqg->vdisktime + CFQ_IDLE_DELAY;
|
||||
cfqg->vdisktime = __cfqg->vdisktime +
|
||||
cfq_get_cfqg_vdisktime_delay(cfqd);
|
||||
} else
|
||||
cfqg->vdisktime = st->min_vdisktime;
|
||||
cfq_group_service_tree_add(st, cfqg);
|
||||
|
@ -320,8 +320,10 @@ struct hd_struct *add_partition(struct gendisk *disk, int partno,
|
||||
|
||||
if (info) {
|
||||
struct partition_meta_info *pinfo = alloc_part_info(disk);
|
||||
if (!pinfo)
|
||||
if (!pinfo) {
|
||||
err = -ENOMEM;
|
||||
goto out_free_stats;
|
||||
}
|
||||
memcpy(pinfo, info, sizeof(*info));
|
||||
p->info = pinfo;
|
||||
}
|
||||
|
@ -300,6 +300,8 @@ static void parse_bsd(struct parsed_partitions *state,
|
||||
continue;
|
||||
bsd_start = le32_to_cpu(p->p_offset);
|
||||
bsd_size = le32_to_cpu(p->p_size);
|
||||
if (memcmp(flavour, "bsd\0", 4) == 0)
|
||||
bsd_start += offset;
|
||||
if (offset == bsd_start && size == bsd_size)
|
||||
/* full parent partition, we have it already */
|
||||
continue;
|
||||
|
@ -764,6 +764,44 @@ static int crypto_init_skcipher_ops_ablkcipher(struct crypto_tfm *tfm)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skcipher_setkey_unaligned(struct crypto_skcipher *tfm,
|
||||
const u8 *key, unsigned int keylen)
|
||||
{
|
||||
unsigned long alignmask = crypto_skcipher_alignmask(tfm);
|
||||
struct skcipher_alg *cipher = crypto_skcipher_alg(tfm);
|
||||
u8 *buffer, *alignbuffer;
|
||||
unsigned long absize;
|
||||
int ret;
|
||||
|
||||
absize = keylen + alignmask;
|
||||
buffer = kmalloc(absize, GFP_ATOMIC);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1);
|
||||
memcpy(alignbuffer, key, keylen);
|
||||
ret = cipher->setkey(tfm, alignbuffer, keylen);
|
||||
kzfree(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int skcipher_setkey(struct crypto_skcipher *tfm, const u8 *key,
|
||||
unsigned int keylen)
|
||||
{
|
||||
struct skcipher_alg *cipher = crypto_skcipher_alg(tfm);
|
||||
unsigned long alignmask = crypto_skcipher_alignmask(tfm);
|
||||
|
||||
if (keylen < cipher->min_keysize || keylen > cipher->max_keysize) {
|
||||
crypto_skcipher_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((unsigned long)key & alignmask)
|
||||
return skcipher_setkey_unaligned(tfm, key, keylen);
|
||||
|
||||
return cipher->setkey(tfm, key, keylen);
|
||||
}
|
||||
|
||||
static void crypto_skcipher_exit_tfm(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct crypto_skcipher *skcipher = __crypto_skcipher_cast(tfm);
|
||||
@ -784,7 +822,7 @@ static int crypto_skcipher_init_tfm(struct crypto_tfm *tfm)
|
||||
tfm->__crt_alg->cra_type == &crypto_givcipher_type)
|
||||
return crypto_init_skcipher_ops_ablkcipher(tfm);
|
||||
|
||||
skcipher->setkey = alg->setkey;
|
||||
skcipher->setkey = skcipher_setkey;
|
||||
skcipher->encrypt = alg->encrypt;
|
||||
skcipher->decrypt = alg->decrypt;
|
||||
skcipher->ivsize = alg->ivsize;
|
||||
|
@ -418,11 +418,7 @@ acpi_tb_get_table(struct acpi_table_desc *table_desc,
|
||||
|
||||
table_desc->validation_count++;
|
||||
if (table_desc->validation_count == 0) {
|
||||
ACPI_ERROR((AE_INFO,
|
||||
"Table %p, Validation count is zero after increment\n",
|
||||
table_desc));
|
||||
table_desc->validation_count--;
|
||||
return_ACPI_STATUS(AE_LIMIT);
|
||||
}
|
||||
|
||||
*out_table = table_desc->pointer;
|
||||
|
@ -57,6 +57,7 @@
|
||||
|
||||
#define ACPI_BUTTON_LID_INIT_IGNORE 0x00
|
||||
#define ACPI_BUTTON_LID_INIT_OPEN 0x01
|
||||
#define ACPI_BUTTON_LID_INIT_METHOD 0x02
|
||||
|
||||
#define _COMPONENT ACPI_BUTTON_COMPONENT
|
||||
ACPI_MODULE_NAME("button");
|
||||
@ -112,7 +113,7 @@ struct acpi_button {
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier);
|
||||
static struct acpi_device *lid_device;
|
||||
static u8 lid_init_state = ACPI_BUTTON_LID_INIT_OPEN;
|
||||
static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
|
||||
|
||||
static unsigned long lid_report_interval __read_mostly = 500;
|
||||
module_param(lid_report_interval, ulong, 0644);
|
||||
@ -376,6 +377,9 @@ static void acpi_lid_initialize_state(struct acpi_device *device)
|
||||
case ACPI_BUTTON_LID_INIT_OPEN:
|
||||
(void)acpi_lid_notify_state(device, 1);
|
||||
break;
|
||||
case ACPI_BUTTON_LID_INIT_METHOD:
|
||||
(void)acpi_lid_update_state(device);
|
||||
break;
|
||||
case ACPI_BUTTON_LID_INIT_IGNORE:
|
||||
default:
|
||||
break;
|
||||
@ -560,6 +564,9 @@ static int param_set_lid_init_state(const char *val, struct kernel_param *kp)
|
||||
if (!strncmp(val, "open", sizeof("open") - 1)) {
|
||||
lid_init_state = ACPI_BUTTON_LID_INIT_OPEN;
|
||||
pr_info("Notify initial lid state as open\n");
|
||||
} else if (!strncmp(val, "method", sizeof("method") - 1)) {
|
||||
lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
|
||||
pr_info("Notify initial lid state with _LID return value\n");
|
||||
} else if (!strncmp(val, "ignore", sizeof("ignore") - 1)) {
|
||||
lid_init_state = ACPI_BUTTON_LID_INIT_IGNORE;
|
||||
pr_info("Do not notify initial lid state\n");
|
||||
@ -573,6 +580,8 @@ static int param_get_lid_init_state(char *buffer, struct kernel_param *kp)
|
||||
switch (lid_init_state) {
|
||||
case ACPI_BUTTON_LID_INIT_OPEN:
|
||||
return sprintf(buffer, "open");
|
||||
case ACPI_BUTTON_LID_INIT_METHOD:
|
||||
return sprintf(buffer, "method");
|
||||
case ACPI_BUTTON_LID_INIT_IGNORE:
|
||||
return sprintf(buffer, "ignore");
|
||||
default:
|
||||
|
@ -26,7 +26,7 @@ static int nfit_handle_mce(struct notifier_block *nb, unsigned long val,
|
||||
struct nfit_spa *nfit_spa;
|
||||
|
||||
/* We only care about memory errors */
|
||||
if (!(mce->status & MCACOD))
|
||||
if (!mce_is_memory_error(mce))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
/*
|
||||
|
@ -333,14 +333,17 @@ static ssize_t acpi_table_show(struct file *filp, struct kobject *kobj,
|
||||
container_of(bin_attr, struct acpi_table_attr, attr);
|
||||
struct acpi_table_header *table_header = NULL;
|
||||
acpi_status status;
|
||||
ssize_t rc;
|
||||
|
||||
status = acpi_get_table(table_attr->name, table_attr->instance,
|
||||
&table_header);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
return memory_read_from_buffer(buf, count, &offset,
|
||||
table_header, table_header->length);
|
||||
rc = memory_read_from_buffer(buf, count, &offset, table_header,
|
||||
table_header->length);
|
||||
acpi_put_table(table_header);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int acpi_table_attr_init(struct kobject *tables_obj,
|
||||
|
@ -512,13 +512,12 @@ static bool wakeup_source_not_registered(struct wakeup_source *ws)
|
||||
/**
|
||||
* wakup_source_activate - Mark given wakeup source as active.
|
||||
* @ws: Wakeup source to handle.
|
||||
* @hard: If set, abort suspends in progress and wake up from suspend-to-idle.
|
||||
*
|
||||
* Update the @ws' statistics and, if @ws has just been activated, notify the PM
|
||||
* core of the event by incrementing the counter of of wakeup events being
|
||||
* processed.
|
||||
*/
|
||||
static void wakeup_source_activate(struct wakeup_source *ws, bool hard)
|
||||
static void wakeup_source_activate(struct wakeup_source *ws)
|
||||
{
|
||||
unsigned int cec;
|
||||
|
||||
@ -526,9 +525,6 @@ static void wakeup_source_activate(struct wakeup_source *ws, bool hard)
|
||||
"unregistered wakeup source\n"))
|
||||
return;
|
||||
|
||||
if (hard)
|
||||
pm_system_wakeup();
|
||||
|
||||
ws->active = true;
|
||||
ws->active_count++;
|
||||
ws->last_time = ktime_get();
|
||||
@ -554,7 +550,10 @@ static void wakeup_source_report_event(struct wakeup_source *ws, bool hard)
|
||||
ws->wakeup_count++;
|
||||
|
||||
if (!ws->active)
|
||||
wakeup_source_activate(ws, hard);
|
||||
wakeup_source_activate(ws);
|
||||
|
||||
if (hard)
|
||||
pm_system_wakeup();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -937,14 +937,6 @@ static int nbd_reconnect_socket(struct nbd_device *nbd, unsigned long arg)
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
/* Reset all properties of an NBD device */
|
||||
static void nbd_reset(struct nbd_device *nbd)
|
||||
{
|
||||
nbd->config = NULL;
|
||||
nbd->tag_set.timeout = 0;
|
||||
queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, nbd->disk->queue);
|
||||
}
|
||||
|
||||
static void nbd_bdev_reset(struct block_device *bdev)
|
||||
{
|
||||
if (bdev->bd_openers > 1)
|
||||
@ -1029,7 +1021,11 @@ static void nbd_config_put(struct nbd_device *nbd)
|
||||
}
|
||||
kfree(config->socks);
|
||||
}
|
||||
nbd_reset(nbd);
|
||||
kfree(nbd->config);
|
||||
nbd->config = NULL;
|
||||
|
||||
nbd->tag_set.timeout = 0;
|
||||
queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, nbd->disk->queue);
|
||||
|
||||
mutex_unlock(&nbd->config_lock);
|
||||
nbd_put(nbd);
|
||||
@ -1483,7 +1479,6 @@ static int nbd_dev_add(int index)
|
||||
disk->fops = &nbd_fops;
|
||||
disk->private_data = nbd;
|
||||
sprintf(disk->disk_name, "nbd%d", index);
|
||||
nbd_reset(nbd);
|
||||
add_disk(disk);
|
||||
nbd_total_devices++;
|
||||
return index;
|
||||
|
@ -4023,6 +4023,7 @@ static void rbd_queue_workfn(struct work_struct *work)
|
||||
|
||||
switch (req_op(rq)) {
|
||||
case REQ_OP_DISCARD:
|
||||
case REQ_OP_WRITE_ZEROES:
|
||||
op_type = OBJ_OP_DISCARD;
|
||||
break;
|
||||
case REQ_OP_WRITE:
|
||||
@ -4420,6 +4421,7 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
|
||||
q->limits.discard_granularity = segment_size;
|
||||
q->limits.discard_alignment = segment_size;
|
||||
blk_queue_max_discard_sectors(q, segment_size / SECTOR_SIZE);
|
||||
blk_queue_max_write_zeroes_sectors(q, segment_size / SECTOR_SIZE);
|
||||
|
||||
if (!ceph_test_opt(rbd_dev->rbd_client->client, NOCRC))
|
||||
q->backing_dev_info->capabilities |= BDI_CAP_STABLE_WRITES;
|
||||
|
@ -374,7 +374,7 @@ static ssize_t cm4040_write(struct file *filp, const char __user *buf,
|
||||
|
||||
rc = write_sync_reg(SCR_HOST_TO_READER_START, dev);
|
||||
if (rc <= 0) {
|
||||
DEBUGP(5, dev, "write_sync_reg c=%.2Zx\n", rc);
|
||||
DEBUGP(5, dev, "write_sync_reg c=%.2zx\n", rc);
|
||||
DEBUGP(2, dev, "<- cm4040_write (failed)\n");
|
||||
if (rc == -ERESTARTSYS)
|
||||
return rc;
|
||||
@ -387,7 +387,7 @@ static ssize_t cm4040_write(struct file *filp, const char __user *buf,
|
||||
for (i = 0; i < bytes_to_write; i++) {
|
||||
rc = wait_for_bulk_out_ready(dev);
|
||||
if (rc <= 0) {
|
||||
DEBUGP(5, dev, "wait_for_bulk_out_ready rc=%.2Zx\n",
|
||||
DEBUGP(5, dev, "wait_for_bulk_out_ready rc=%.2zx\n",
|
||||
rc);
|
||||
DEBUGP(2, dev, "<- cm4040_write (failed)\n");
|
||||
if (rc == -ERESTARTSYS)
|
||||
@ -403,7 +403,7 @@ static ssize_t cm4040_write(struct file *filp, const char __user *buf,
|
||||
rc = write_sync_reg(SCR_HOST_TO_READER_DONE, dev);
|
||||
|
||||
if (rc <= 0) {
|
||||
DEBUGP(5, dev, "write_sync_reg c=%.2Zx\n", rc);
|
||||
DEBUGP(5, dev, "write_sync_reg c=%.2zx\n", rc);
|
||||
DEBUGP(2, dev, "<- cm4040_write (failed)\n");
|
||||
if (rc == -ERESTARTSYS)
|
||||
return rc;
|
||||
|
@ -1097,12 +1097,16 @@ static void add_interrupt_bench(cycles_t start)
|
||||
static __u32 get_reg(struct fast_pool *f, struct pt_regs *regs)
|
||||
{
|
||||
__u32 *ptr = (__u32 *) regs;
|
||||
unsigned long flags;
|
||||
|
||||
if (regs == NULL)
|
||||
return 0;
|
||||
local_irq_save(flags);
|
||||
if (f->reg_idx >= sizeof(struct pt_regs) / sizeof(__u32))
|
||||
f->reg_idx = 0;
|
||||
return *(ptr + f->reg_idx++);
|
||||
ptr += f->reg_idx++;
|
||||
local_irq_restore(flags);
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
void add_interrupt_randomness(int irq, int irq_flags)
|
||||
|
@ -71,6 +71,15 @@ 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
|
||||
|
@ -53,7 +53,7 @@ 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_UX500_SOC_DB8500) += dbx500-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
|
||||
|
@ -2468,6 +2468,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
|
||||
if (!(cpufreq_driver->flags & CPUFREQ_STICKY) &&
|
||||
list_empty(&cpufreq_policy_list)) {
|
||||
/* if all ->init() calls failed, unregister */
|
||||
ret = -ENODEV;
|
||||
pr_debug("%s: No CPU initialized for driver %s\n", __func__,
|
||||
driver_data->name);
|
||||
goto err_if_unreg;
|
||||
|
@ -127,7 +127,12 @@ static int kirkwood_cpufreq_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(priv.cpu_clk);
|
||||
}
|
||||
|
||||
clk_prepare_enable(priv.cpu_clk);
|
||||
err = clk_prepare_enable(priv.cpu_clk);
|
||||
if (err) {
|
||||
dev_err(priv.dev, "Unable to prepare cpuclk\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
kirkwood_freq_table[0].frequency = clk_get_rate(priv.cpu_clk) / 1000;
|
||||
|
||||
priv.ddr_clk = of_clk_get_by_name(np, "ddrclk");
|
||||
@ -137,7 +142,11 @@ static int kirkwood_cpufreq_probe(struct platform_device *pdev)
|
||||
goto out_cpu;
|
||||
}
|
||||
|
||||
clk_prepare_enable(priv.ddr_clk);
|
||||
err = clk_prepare_enable(priv.ddr_clk);
|
||||
if (err) {
|
||||
dev_err(priv.dev, "Unable to prepare ddrclk\n");
|
||||
goto out_cpu;
|
||||
}
|
||||
kirkwood_freq_table[1].frequency = clk_get_rate(priv.ddr_clk) / 1000;
|
||||
|
||||
priv.powersave_clk = of_clk_get_by_name(np, "powersave");
|
||||
@ -146,7 +155,11 @@ static int kirkwood_cpufreq_probe(struct platform_device *pdev)
|
||||
err = PTR_ERR(priv.powersave_clk);
|
||||
goto out_ddr;
|
||||
}
|
||||
clk_prepare_enable(priv.powersave_clk);
|
||||
err = clk_prepare_enable(priv.powersave_clk);
|
||||
if (err) {
|
||||
dev_err(priv.dev, "Unable to prepare powersave clk\n");
|
||||
goto out_ddr;
|
||||
}
|
||||
|
||||
of_node_put(np);
|
||||
np = NULL;
|
||||
|
@ -201,6 +201,7 @@ struct ep93xx_dma_engine {
|
||||
struct dma_device dma_dev;
|
||||
bool m2m;
|
||||
int (*hw_setup)(struct ep93xx_dma_chan *);
|
||||
void (*hw_synchronize)(struct ep93xx_dma_chan *);
|
||||
void (*hw_shutdown)(struct ep93xx_dma_chan *);
|
||||
void (*hw_submit)(struct ep93xx_dma_chan *);
|
||||
int (*hw_interrupt)(struct ep93xx_dma_chan *);
|
||||
@ -323,6 +324,8 @@ static int m2p_hw_setup(struct ep93xx_dma_chan *edmac)
|
||||
| M2P_CONTROL_ENABLE;
|
||||
m2p_set_control(edmac, control);
|
||||
|
||||
edmac->buffer = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -331,21 +334,27 @@ static inline u32 m2p_channel_state(struct ep93xx_dma_chan *edmac)
|
||||
return (readl(edmac->regs + M2P_STATUS) >> 4) & 0x3;
|
||||
}
|
||||
|
||||
static void m2p_hw_shutdown(struct ep93xx_dma_chan *edmac)
|
||||
static void m2p_hw_synchronize(struct ep93xx_dma_chan *edmac)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 control;
|
||||
|
||||
spin_lock_irqsave(&edmac->lock, flags);
|
||||
control = readl(edmac->regs + M2P_CONTROL);
|
||||
control &= ~(M2P_CONTROL_STALLINT | M2P_CONTROL_NFBINT);
|
||||
m2p_set_control(edmac, control);
|
||||
spin_unlock_irqrestore(&edmac->lock, flags);
|
||||
|
||||
while (m2p_channel_state(edmac) >= M2P_STATE_ON)
|
||||
cpu_relax();
|
||||
schedule();
|
||||
}
|
||||
|
||||
static void m2p_hw_shutdown(struct ep93xx_dma_chan *edmac)
|
||||
{
|
||||
m2p_set_control(edmac, 0);
|
||||
|
||||
while (m2p_channel_state(edmac) == M2P_STATE_STALL)
|
||||
cpu_relax();
|
||||
while (m2p_channel_state(edmac) != M2P_STATE_IDLE)
|
||||
dev_warn(chan2dev(edmac), "M2P: Not yet IDLE\n");
|
||||
}
|
||||
|
||||
static void m2p_fill_desc(struct ep93xx_dma_chan *edmac)
|
||||
@ -1160,6 +1169,26 @@ ep93xx_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ep93xx_dma_synchronize - Synchronizes the termination of transfers to the
|
||||
* current context.
|
||||
* @chan: channel
|
||||
*
|
||||
* Synchronizes the DMA channel termination to the current context. When this
|
||||
* function returns it is guaranteed that all transfers for previously issued
|
||||
* descriptors have stopped and and it is safe to free the memory associated
|
||||
* with them. Furthermore it is guaranteed that all complete callback functions
|
||||
* for a previously submitted descriptor have finished running and it is safe to
|
||||
* free resources accessed from within the complete callbacks.
|
||||
*/
|
||||
static void ep93xx_dma_synchronize(struct dma_chan *chan)
|
||||
{
|
||||
struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
|
||||
|
||||
if (edmac->edma->hw_synchronize)
|
||||
edmac->edma->hw_synchronize(edmac);
|
||||
}
|
||||
|
||||
/**
|
||||
* ep93xx_dma_terminate_all - terminate all transactions
|
||||
* @chan: channel
|
||||
@ -1323,6 +1352,7 @@ static int __init ep93xx_dma_probe(struct platform_device *pdev)
|
||||
dma_dev->device_prep_slave_sg = ep93xx_dma_prep_slave_sg;
|
||||
dma_dev->device_prep_dma_cyclic = ep93xx_dma_prep_dma_cyclic;
|
||||
dma_dev->device_config = ep93xx_dma_slave_config;
|
||||
dma_dev->device_synchronize = ep93xx_dma_synchronize;
|
||||
dma_dev->device_terminate_all = ep93xx_dma_terminate_all;
|
||||
dma_dev->device_issue_pending = ep93xx_dma_issue_pending;
|
||||
dma_dev->device_tx_status = ep93xx_dma_tx_status;
|
||||
@ -1340,6 +1370,7 @@ static int __init ep93xx_dma_probe(struct platform_device *pdev)
|
||||
} else {
|
||||
dma_cap_set(DMA_PRIVATE, dma_dev->cap_mask);
|
||||
|
||||
edma->hw_synchronize = m2p_hw_synchronize;
|
||||
edma->hw_setup = m2p_hw_setup;
|
||||
edma->hw_shutdown = m2p_hw_shutdown;
|
||||
edma->hw_submit = m2p_hw_submit;
|
||||
|
@ -161,6 +161,7 @@ struct mv_xor_v2_device {
|
||||
struct mv_xor_v2_sw_desc *sw_desq;
|
||||
int desc_size;
|
||||
unsigned int npendings;
|
||||
unsigned int hw_queue_idx;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -213,18 +214,6 @@ static void mv_xor_v2_set_data_buffers(struct mv_xor_v2_device *xor_dev,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the next available index in the DESQ.
|
||||
*/
|
||||
static int mv_xor_v2_get_desq_write_ptr(struct mv_xor_v2_device *xor_dev)
|
||||
{
|
||||
/* read the index for the next available descriptor in the DESQ */
|
||||
u32 reg = readl(xor_dev->dma_base + MV_XOR_V2_DMA_DESQ_ALLOC_OFF);
|
||||
|
||||
return ((reg >> MV_XOR_V2_DMA_DESQ_ALLOC_WRPTR_SHIFT)
|
||||
& MV_XOR_V2_DMA_DESQ_ALLOC_WRPTR_MASK);
|
||||
}
|
||||
|
||||
/*
|
||||
* notify the engine of new descriptors, and update the available index.
|
||||
*/
|
||||
@ -257,22 +246,6 @@ static int mv_xor_v2_set_desc_size(struct mv_xor_v2_device *xor_dev)
|
||||
return MV_XOR_V2_EXT_DESC_SIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the IMSG threshold
|
||||
*/
|
||||
static inline
|
||||
void mv_xor_v2_set_imsg_thrd(struct mv_xor_v2_device *xor_dev, int thrd_val)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = readl(xor_dev->dma_base + MV_XOR_V2_DMA_IMSG_THRD_OFF);
|
||||
|
||||
reg &= (~MV_XOR_V2_DMA_IMSG_THRD_MASK << MV_XOR_V2_DMA_IMSG_THRD_SHIFT);
|
||||
reg |= (thrd_val << MV_XOR_V2_DMA_IMSG_THRD_SHIFT);
|
||||
|
||||
writel(reg, xor_dev->dma_base + MV_XOR_V2_DMA_IMSG_THRD_OFF);
|
||||
}
|
||||
|
||||
static irqreturn_t mv_xor_v2_interrupt_handler(int irq, void *data)
|
||||
{
|
||||
struct mv_xor_v2_device *xor_dev = data;
|
||||
@ -288,12 +261,6 @@ static irqreturn_t mv_xor_v2_interrupt_handler(int irq, void *data)
|
||||
if (!ndescs)
|
||||
return IRQ_NONE;
|
||||
|
||||
/*
|
||||
* Update IMSG threshold, to disable new IMSG interrupts until
|
||||
* end of the tasklet
|
||||
*/
|
||||
mv_xor_v2_set_imsg_thrd(xor_dev, MV_XOR_V2_DESC_NUM);
|
||||
|
||||
/* schedule a tasklet to handle descriptors callbacks */
|
||||
tasklet_schedule(&xor_dev->irq_tasklet);
|
||||
|
||||
@ -306,7 +273,6 @@ static irqreturn_t mv_xor_v2_interrupt_handler(int irq, void *data)
|
||||
static dma_cookie_t
|
||||
mv_xor_v2_tx_submit(struct dma_async_tx_descriptor *tx)
|
||||
{
|
||||
int desq_ptr;
|
||||
void *dest_hw_desc;
|
||||
dma_cookie_t cookie;
|
||||
struct mv_xor_v2_sw_desc *sw_desc =
|
||||
@ -322,15 +288,15 @@ mv_xor_v2_tx_submit(struct dma_async_tx_descriptor *tx)
|
||||
spin_lock_bh(&xor_dev->lock);
|
||||
cookie = dma_cookie_assign(tx);
|
||||
|
||||
/* get the next available slot in the DESQ */
|
||||
desq_ptr = mv_xor_v2_get_desq_write_ptr(xor_dev);
|
||||
|
||||
/* copy the HW descriptor from the SW descriptor to the DESQ */
|
||||
dest_hw_desc = xor_dev->hw_desq_virt + desq_ptr;
|
||||
dest_hw_desc = xor_dev->hw_desq_virt + xor_dev->hw_queue_idx;
|
||||
|
||||
memcpy(dest_hw_desc, &sw_desc->hw_desc, xor_dev->desc_size);
|
||||
|
||||
xor_dev->npendings++;
|
||||
xor_dev->hw_queue_idx++;
|
||||
if (xor_dev->hw_queue_idx >= MV_XOR_V2_DESC_NUM)
|
||||
xor_dev->hw_queue_idx = 0;
|
||||
|
||||
spin_unlock_bh(&xor_dev->lock);
|
||||
|
||||
@ -344,6 +310,7 @@ static struct mv_xor_v2_sw_desc *
|
||||
mv_xor_v2_prep_sw_desc(struct mv_xor_v2_device *xor_dev)
|
||||
{
|
||||
struct mv_xor_v2_sw_desc *sw_desc;
|
||||
bool found = false;
|
||||
|
||||
/* Lock the channel */
|
||||
spin_lock_bh(&xor_dev->lock);
|
||||
@ -355,19 +322,23 @@ mv_xor_v2_prep_sw_desc(struct mv_xor_v2_device *xor_dev)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* get a free SW descriptor from the SW DESQ */
|
||||
sw_desc = list_first_entry(&xor_dev->free_sw_desc,
|
||||
struct mv_xor_v2_sw_desc, free_list);
|
||||
list_for_each_entry(sw_desc, &xor_dev->free_sw_desc, free_list) {
|
||||
if (async_tx_test_ack(&sw_desc->async_tx)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
spin_unlock_bh(&xor_dev->lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_del(&sw_desc->free_list);
|
||||
|
||||
/* Release the channel */
|
||||
spin_unlock_bh(&xor_dev->lock);
|
||||
|
||||
/* set the async tx descriptor */
|
||||
dma_async_tx_descriptor_init(&sw_desc->async_tx, &xor_dev->dmachan);
|
||||
sw_desc->async_tx.tx_submit = mv_xor_v2_tx_submit;
|
||||
async_tx_ack(&sw_desc->async_tx);
|
||||
|
||||
return sw_desc;
|
||||
}
|
||||
|
||||
@ -389,6 +360,8 @@ mv_xor_v2_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest,
|
||||
__func__, len, &src, &dest, flags);
|
||||
|
||||
sw_desc = mv_xor_v2_prep_sw_desc(xor_dev);
|
||||
if (!sw_desc)
|
||||
return NULL;
|
||||
|
||||
sw_desc->async_tx.flags = flags;
|
||||
|
||||
@ -443,6 +416,8 @@ mv_xor_v2_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src,
|
||||
__func__, src_cnt, len, &dest, flags);
|
||||
|
||||
sw_desc = mv_xor_v2_prep_sw_desc(xor_dev);
|
||||
if (!sw_desc)
|
||||
return NULL;
|
||||
|
||||
sw_desc->async_tx.flags = flags;
|
||||
|
||||
@ -491,6 +466,8 @@ mv_xor_v2_prep_dma_interrupt(struct dma_chan *chan, unsigned long flags)
|
||||
container_of(chan, struct mv_xor_v2_device, dmachan);
|
||||
|
||||
sw_desc = mv_xor_v2_prep_sw_desc(xor_dev);
|
||||
if (!sw_desc)
|
||||
return NULL;
|
||||
|
||||
/* set the HW descriptor */
|
||||
hw_descriptor = &sw_desc->hw_desc;
|
||||
@ -554,7 +531,6 @@ static void mv_xor_v2_tasklet(unsigned long data)
|
||||
{
|
||||
struct mv_xor_v2_device *xor_dev = (struct mv_xor_v2_device *) data;
|
||||
int pending_ptr, num_of_pending, i;
|
||||
struct mv_xor_v2_descriptor *next_pending_hw_desc = NULL;
|
||||
struct mv_xor_v2_sw_desc *next_pending_sw_desc = NULL;
|
||||
|
||||
dev_dbg(xor_dev->dmadev.dev, "%s %d\n", __func__, __LINE__);
|
||||
@ -562,17 +538,10 @@ static void mv_xor_v2_tasklet(unsigned long data)
|
||||
/* get the pending descriptors parameters */
|
||||
num_of_pending = mv_xor_v2_get_pending_params(xor_dev, &pending_ptr);
|
||||
|
||||
/* next HW descriptor */
|
||||
next_pending_hw_desc = xor_dev->hw_desq_virt + pending_ptr;
|
||||
|
||||
/* loop over free descriptors */
|
||||
for (i = 0; i < num_of_pending; i++) {
|
||||
|
||||
if (pending_ptr > MV_XOR_V2_DESC_NUM)
|
||||
pending_ptr = 0;
|
||||
|
||||
if (next_pending_sw_desc != NULL)
|
||||
next_pending_hw_desc++;
|
||||
struct mv_xor_v2_descriptor *next_pending_hw_desc =
|
||||
xor_dev->hw_desq_virt + pending_ptr;
|
||||
|
||||
/* get the SW descriptor related to the HW descriptor */
|
||||
next_pending_sw_desc =
|
||||
@ -608,15 +577,14 @@ static void mv_xor_v2_tasklet(unsigned long data)
|
||||
|
||||
/* increment the next descriptor */
|
||||
pending_ptr++;
|
||||
if (pending_ptr >= MV_XOR_V2_DESC_NUM)
|
||||
pending_ptr = 0;
|
||||
}
|
||||
|
||||
if (num_of_pending != 0) {
|
||||
/* free the descriptores */
|
||||
mv_xor_v2_free_desc_from_desq(xor_dev, num_of_pending);
|
||||
}
|
||||
|
||||
/* Update IMSG threshold, to enable new IMSG interrupts */
|
||||
mv_xor_v2_set_imsg_thrd(xor_dev, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -648,9 +616,6 @@ static int mv_xor_v2_descq_init(struct mv_xor_v2_device *xor_dev)
|
||||
writel((xor_dev->hw_desq & 0xFFFF00000000) >> 32,
|
||||
xor_dev->dma_base + MV_XOR_V2_DMA_DESQ_BAHR_OFF);
|
||||
|
||||
/* enable the DMA engine */
|
||||
writel(0, xor_dev->dma_base + MV_XOR_V2_DMA_DESQ_STOP_OFF);
|
||||
|
||||
/*
|
||||
* This is a temporary solution, until we activate the
|
||||
* SMMU. Set the attributes for reading & writing data buffers
|
||||
@ -694,6 +659,9 @@ static int mv_xor_v2_descq_init(struct mv_xor_v2_device *xor_dev)
|
||||
reg |= MV_XOR_V2_GLOB_PAUSE_AXI_TIME_DIS_VAL;
|
||||
writel(reg, xor_dev->glob_base + MV_XOR_V2_GLOB_PAUSE);
|
||||
|
||||
/* enable the DMA engine */
|
||||
writel(0, xor_dev->dma_base + MV_XOR_V2_DMA_DESQ_STOP_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -725,6 +693,10 @@ static int mv_xor_v2_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, xor_dev);
|
||||
|
||||
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
xor_dev->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(xor_dev->clk) && PTR_ERR(xor_dev->clk) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
@ -785,8 +757,15 @@ static int mv_xor_v2_probe(struct platform_device *pdev)
|
||||
|
||||
/* add all SW descriptors to the free list */
|
||||
for (i = 0; i < MV_XOR_V2_DESC_NUM; i++) {
|
||||
xor_dev->sw_desq[i].idx = i;
|
||||
list_add(&xor_dev->sw_desq[i].free_list,
|
||||
struct mv_xor_v2_sw_desc *sw_desc =
|
||||
xor_dev->sw_desq + i;
|
||||
sw_desc->idx = i;
|
||||
dma_async_tx_descriptor_init(&sw_desc->async_tx,
|
||||
&xor_dev->dmachan);
|
||||
sw_desc->async_tx.tx_submit = mv_xor_v2_tx_submit;
|
||||
async_tx_ack(&sw_desc->async_tx);
|
||||
|
||||
list_add(&sw_desc->free_list,
|
||||
&xor_dev->free_sw_desc);
|
||||
}
|
||||
|
||||
|
@ -3008,7 +3008,8 @@ static int pl330_remove(struct amba_device *adev)
|
||||
|
||||
for (i = 0; i < AMBA_NR_IRQS; i++) {
|
||||
irq = adev->irq[i];
|
||||
devm_free_irq(&adev->dev, irq, pl330);
|
||||
if (irq)
|
||||
devm_free_irq(&adev->dev, irq, pl330);
|
||||
}
|
||||
|
||||
dma_async_device_unregister(&pl330->ddma);
|
||||
|
@ -1287,6 +1287,9 @@ static unsigned int rcar_dmac_chan_get_residue(struct rcar_dmac_chan *chan,
|
||||
if (desc->hwdescs.use) {
|
||||
dptr = (rcar_dmac_chan_read(chan, RCAR_DMACHCRB) &
|
||||
RCAR_DMACHCRB_DPTR_MASK) >> RCAR_DMACHCRB_DPTR_SHIFT;
|
||||
if (dptr == 0)
|
||||
dptr = desc->nchunks;
|
||||
dptr--;
|
||||
WARN_ON(dptr >= desc->nchunks);
|
||||
} else {
|
||||
running = desc->running;
|
||||
|
@ -117,7 +117,7 @@ struct usb_dmac {
|
||||
#define USB_DMASWR 0x0008
|
||||
#define USB_DMASWR_SWR (1 << 0)
|
||||
#define USB_DMAOR 0x0060
|
||||
#define USB_DMAOR_AE (1 << 2)
|
||||
#define USB_DMAOR_AE (1 << 1)
|
||||
#define USB_DMAOR_DME (1 << 0)
|
||||
|
||||
#define USB_DMASAR 0x0000
|
||||
|
@ -47,6 +47,7 @@ DEFINE_DMI_ATTR_WITH_SHOW(product_name, 0444, DMI_PRODUCT_NAME);
|
||||
DEFINE_DMI_ATTR_WITH_SHOW(product_version, 0444, DMI_PRODUCT_VERSION);
|
||||
DEFINE_DMI_ATTR_WITH_SHOW(product_serial, 0400, DMI_PRODUCT_SERIAL);
|
||||
DEFINE_DMI_ATTR_WITH_SHOW(product_uuid, 0400, DMI_PRODUCT_UUID);
|
||||
DEFINE_DMI_ATTR_WITH_SHOW(product_family, 0400, DMI_PRODUCT_FAMILY);
|
||||
DEFINE_DMI_ATTR_WITH_SHOW(board_vendor, 0444, DMI_BOARD_VENDOR);
|
||||
DEFINE_DMI_ATTR_WITH_SHOW(board_name, 0444, DMI_BOARD_NAME);
|
||||
DEFINE_DMI_ATTR_WITH_SHOW(board_version, 0444, DMI_BOARD_VERSION);
|
||||
@ -191,6 +192,7 @@ static void __init dmi_id_init_attr_table(void)
|
||||
ADD_DMI_ATTR(product_version, DMI_PRODUCT_VERSION);
|
||||
ADD_DMI_ATTR(product_serial, DMI_PRODUCT_SERIAL);
|
||||
ADD_DMI_ATTR(product_uuid, DMI_PRODUCT_UUID);
|
||||
ADD_DMI_ATTR(product_family, DMI_PRODUCT_FAMILY);
|
||||
ADD_DMI_ATTR(board_vendor, DMI_BOARD_VENDOR);
|
||||
ADD_DMI_ATTR(board_name, DMI_BOARD_NAME);
|
||||
ADD_DMI_ATTR(board_version, DMI_BOARD_VERSION);
|
||||
|
@ -430,6 +430,7 @@ static void __init dmi_decode(const struct dmi_header *dm, void *dummy)
|
||||
dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6);
|
||||
dmi_save_ident(dm, DMI_PRODUCT_SERIAL, 7);
|
||||
dmi_save_uuid(dm, DMI_PRODUCT_UUID, 8);
|
||||
dmi_save_ident(dm, DMI_PRODUCT_FAMILY, 26);
|
||||
break;
|
||||
case 2: /* Base Board Information */
|
||||
dmi_save_ident(dm, DMI_BOARD_VENDOR, 4);
|
||||
|
@ -36,6 +36,9 @@ void __init efi_bgrt_init(struct acpi_table_header *table)
|
||||
if (acpi_disabled)
|
||||
return;
|
||||
|
||||
if (!efi_enabled(EFI_BOOT))
|
||||
return;
|
||||
|
||||
if (table->length < sizeof(bgrt_tab)) {
|
||||
pr_notice("Ignoring BGRT: invalid length %u (expected %zu)\n",
|
||||
table->length, sizeof(bgrt_tab));
|
||||
|
@ -53,6 +53,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry,
|
||||
if (sscanf(name, "dump-type%u-%u-%d-%lu-%c",
|
||||
&record->type, &part, &cnt, &time, &data_type) == 5) {
|
||||
record->id = generic_id(time, part, cnt);
|
||||
record->part = part;
|
||||
record->count = cnt;
|
||||
record->time.tv_sec = time;
|
||||
record->time.tv_nsec = 0;
|
||||
@ -64,6 +65,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry,
|
||||
} else if (sscanf(name, "dump-type%u-%u-%d-%lu",
|
||||
&record->type, &part, &cnt, &time) == 4) {
|
||||
record->id = generic_id(time, part, cnt);
|
||||
record->part = part;
|
||||
record->count = cnt;
|
||||
record->time.tv_sec = time;
|
||||
record->time.tv_nsec = 0;
|
||||
@ -77,6 +79,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry,
|
||||
* multiple logs, remains.
|
||||
*/
|
||||
record->id = generic_id(time, part, 0);
|
||||
record->part = part;
|
||||
record->count = 0;
|
||||
record->time.tv_sec = time;
|
||||
record->time.tv_nsec = 0;
|
||||
@ -241,9 +244,15 @@ static int efi_pstore_write(struct pstore_record *record)
|
||||
efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
|
||||
int i, ret = 0;
|
||||
|
||||
record->time.tv_sec = get_seconds();
|
||||
record->time.tv_nsec = 0;
|
||||
|
||||
record->id = generic_id(record->time.tv_sec, record->part,
|
||||
record->count);
|
||||
|
||||
snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lu-%c",
|
||||
record->type, record->part, record->count,
|
||||
get_seconds(), record->compressed ? 'C' : 'D');
|
||||
record->time.tv_sec, record->compressed ? 'C' : 'D');
|
||||
|
||||
for (i = 0; i < DUMP_NAME_LEN; i++)
|
||||
efi_name[i] = name[i];
|
||||
@ -255,7 +264,6 @@ static int efi_pstore_write(struct pstore_record *record)
|
||||
if (record->reason == KMSG_DUMP_OOPS)
|
||||
efivar_run_worker();
|
||||
|
||||
record->id = record->part;
|
||||
return ret;
|
||||
};
|
||||
|
||||
@ -287,7 +295,7 @@ static int efi_pstore_erase_func(struct efivar_entry *entry, void *data)
|
||||
* holding multiple logs, remains.
|
||||
*/
|
||||
snprintf(name_old, sizeof(name_old), "dump-type%u-%u-%lu",
|
||||
ed->record->type, (unsigned int)ed->record->id,
|
||||
ed->record->type, ed->record->part,
|
||||
ed->record->time.tv_sec);
|
||||
|
||||
for (i = 0; i < DUMP_NAME_LEN; i++)
|
||||
@ -320,10 +328,7 @@ static int efi_pstore_erase(struct pstore_record *record)
|
||||
char name[DUMP_NAME_LEN];
|
||||
efi_char16_t efi_name[DUMP_NAME_LEN];
|
||||
int found, i;
|
||||
unsigned int part;
|
||||
|
||||
do_div(record->id, 1000);
|
||||
part = do_div(record->id, 100);
|
||||
snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lu",
|
||||
record->type, record->part, record->count,
|
||||
record->time.tv_sec);
|
||||
|
@ -16,10 +16,10 @@
|
||||
|
||||
/* BIOS variables */
|
||||
static const efi_guid_t efi_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
|
||||
static const efi_char16_t const efi_SecureBoot_name[] = {
|
||||
static const efi_char16_t efi_SecureBoot_name[] = {
|
||||
'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', 0
|
||||
};
|
||||
static const efi_char16_t const efi_SetupMode_name[] = {
|
||||
static const efi_char16_t efi_SetupMode_name[] = {
|
||||
'S', 'e', 't', 'u', 'p', 'M', 'o', 'd', 'e', 0
|
||||
};
|
||||
|
||||
|
@ -425,10 +425,15 @@ bool amdgpu_fbdev_robj_is_fb(struct amdgpu_device *adev, struct amdgpu_bo *robj)
|
||||
|
||||
void amdgpu_fbdev_restore_mode(struct amdgpu_device *adev)
|
||||
{
|
||||
struct amdgpu_fbdev *afbdev = adev->mode_info.rfbdev;
|
||||
struct amdgpu_fbdev *afbdev;
|
||||
struct drm_fb_helper *fb_helper;
|
||||
int ret;
|
||||
|
||||
if (!adev)
|
||||
return;
|
||||
|
||||
afbdev = adev->mode_info.rfbdev;
|
||||
|
||||
if (!afbdev)
|
||||
return;
|
||||
|
||||
|
@ -634,7 +634,7 @@ int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job)
|
||||
mutex_unlock(&id_mgr->lock);
|
||||
}
|
||||
|
||||
if (gds_switch_needed) {
|
||||
if (ring->funcs->emit_gds_switch && gds_switch_needed) {
|
||||
id->gds_base = job->gds_base;
|
||||
id->gds_size = job->gds_size;
|
||||
id->gws_base = job->gws_base;
|
||||
@ -672,6 +672,7 @@ void amdgpu_vm_reset_id(struct amdgpu_device *adev, unsigned vmhub,
|
||||
struct amdgpu_vm_id_manager *id_mgr = &adev->vm_manager.id_mgr[vmhub];
|
||||
struct amdgpu_vm_id *id = &id_mgr->ids[vmid];
|
||||
|
||||
atomic64_set(&id->owner, 0);
|
||||
id->gds_base = 0;
|
||||
id->gds_size = 0;
|
||||
id->gws_base = 0;
|
||||
@ -680,6 +681,26 @@ void amdgpu_vm_reset_id(struct amdgpu_device *adev, unsigned vmhub,
|
||||
id->oa_size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_vm_reset_all_id - reset VMID to zero
|
||||
*
|
||||
* @adev: amdgpu device structure
|
||||
*
|
||||
* Reset VMID to force flush on next use
|
||||
*/
|
||||
void amdgpu_vm_reset_all_ids(struct amdgpu_device *adev)
|
||||
{
|
||||
unsigned i, j;
|
||||
|
||||
for (i = 0; i < AMDGPU_MAX_VMHUBS; ++i) {
|
||||
struct amdgpu_vm_id_manager *id_mgr =
|
||||
&adev->vm_manager.id_mgr[i];
|
||||
|
||||
for (j = 1; j < id_mgr->num_ids; ++j)
|
||||
amdgpu_vm_reset_id(adev, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_vm_bo_find - find the bo_va for a specific vm & bo
|
||||
*
|
||||
@ -2270,7 +2291,6 @@ void amdgpu_vm_manager_init(struct amdgpu_device *adev)
|
||||
for (i = 0; i < AMDGPU_MAX_RINGS; ++i)
|
||||
adev->vm_manager.seqno[i] = 0;
|
||||
|
||||
|
||||
atomic_set(&adev->vm_manager.vm_pte_next_ring, 0);
|
||||
atomic64_set(&adev->vm_manager.client_counter, 0);
|
||||
spin_lock_init(&adev->vm_manager.prt_lock);
|
||||
|
@ -204,6 +204,7 @@ int amdgpu_vm_grab_id(struct amdgpu_vm *vm, struct amdgpu_ring *ring,
|
||||
int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job);
|
||||
void amdgpu_vm_reset_id(struct amdgpu_device *adev, unsigned vmhub,
|
||||
unsigned vmid);
|
||||
void amdgpu_vm_reset_all_ids(struct amdgpu_device *adev);
|
||||
int amdgpu_vm_update_directories(struct amdgpu_device *adev,
|
||||
struct amdgpu_vm *vm);
|
||||
int amdgpu_vm_clear_freed(struct amdgpu_device *adev,
|
||||
|
@ -220,9 +220,9 @@ static void amdgpu_vram_mgr_debug(struct ttm_mem_type_manager *man,
|
||||
}
|
||||
|
||||
const struct ttm_mem_type_manager_func amdgpu_vram_mgr_func = {
|
||||
amdgpu_vram_mgr_init,
|
||||
amdgpu_vram_mgr_fini,
|
||||
amdgpu_vram_mgr_new,
|
||||
amdgpu_vram_mgr_del,
|
||||
amdgpu_vram_mgr_debug
|
||||
.init = amdgpu_vram_mgr_init,
|
||||
.takedown = amdgpu_vram_mgr_fini,
|
||||
.get_node = amdgpu_vram_mgr_new,
|
||||
.put_node = amdgpu_vram_mgr_del,
|
||||
.debug = amdgpu_vram_mgr_debug
|
||||
};
|
||||
|
@ -906,6 +906,12 @@ static bool ci_dpm_vblank_too_short(struct amdgpu_device *adev)
|
||||
u32 vblank_time = amdgpu_dpm_get_vblank_time(adev);
|
||||
u32 switch_limit = adev->mc.vram_type == AMDGPU_VRAM_TYPE_GDDR5 ? 450 : 300;
|
||||
|
||||
/* disable mclk switching if the refresh is >120Hz, even if the
|
||||
* blanking period would allow it
|
||||
*/
|
||||
if (amdgpu_dpm_get_vrefresh(adev) > 120)
|
||||
return true;
|
||||
|
||||
if (vblank_time < switch_limit)
|
||||
return true;
|
||||
else
|
||||
|
@ -950,10 +950,6 @@ static int gmc_v6_0_suspend(void *handle)
|
||||
{
|
||||
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
|
||||
|
||||
if (adev->vm_manager.enabled) {
|
||||
gmc_v6_0_vm_fini(adev);
|
||||
adev->vm_manager.enabled = false;
|
||||
}
|
||||
gmc_v6_0_hw_fini(adev);
|
||||
|
||||
return 0;
|
||||
@ -968,16 +964,9 @@ static int gmc_v6_0_resume(void *handle)
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (!adev->vm_manager.enabled) {
|
||||
r = gmc_v6_0_vm_init(adev);
|
||||
if (r) {
|
||||
dev_err(adev->dev, "vm manager initialization failed (%d).\n", r);
|
||||
return r;
|
||||
}
|
||||
adev->vm_manager.enabled = true;
|
||||
}
|
||||
amdgpu_vm_reset_all_ids(adev);
|
||||
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool gmc_v6_0_is_idle(void *handle)
|
||||
|
@ -1117,10 +1117,6 @@ static int gmc_v7_0_suspend(void *handle)
|
||||
{
|
||||
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
|
||||
|
||||
if (adev->vm_manager.enabled) {
|
||||
gmc_v7_0_vm_fini(adev);
|
||||
adev->vm_manager.enabled = false;
|
||||
}
|
||||
gmc_v7_0_hw_fini(adev);
|
||||
|
||||
return 0;
|
||||
@ -1135,16 +1131,9 @@ static int gmc_v7_0_resume(void *handle)
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (!adev->vm_manager.enabled) {
|
||||
r = gmc_v7_0_vm_init(adev);
|
||||
if (r) {
|
||||
dev_err(adev->dev, "vm manager initialization failed (%d).\n", r);
|
||||
return r;
|
||||
}
|
||||
adev->vm_manager.enabled = true;
|
||||
}
|
||||
amdgpu_vm_reset_all_ids(adev);
|
||||
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool gmc_v7_0_is_idle(void *handle)
|
||||
|
@ -1209,10 +1209,6 @@ static int gmc_v8_0_suspend(void *handle)
|
||||
{
|
||||
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
|
||||
|
||||
if (adev->vm_manager.enabled) {
|
||||
gmc_v8_0_vm_fini(adev);
|
||||
adev->vm_manager.enabled = false;
|
||||
}
|
||||
gmc_v8_0_hw_fini(adev);
|
||||
|
||||
return 0;
|
||||
@ -1227,16 +1223,9 @@ static int gmc_v8_0_resume(void *handle)
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (!adev->vm_manager.enabled) {
|
||||
r = gmc_v8_0_vm_init(adev);
|
||||
if (r) {
|
||||
dev_err(adev->dev, "vm manager initialization failed (%d).\n", r);
|
||||
return r;
|
||||
}
|
||||
adev->vm_manager.enabled = true;
|
||||
}
|
||||
amdgpu_vm_reset_all_ids(adev);
|
||||
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool gmc_v8_0_is_idle(void *handle)
|
||||
|
@ -791,10 +791,6 @@ static int gmc_v9_0_suspend(void *handle)
|
||||
{
|
||||
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
|
||||
|
||||
if (adev->vm_manager.enabled) {
|
||||
gmc_v9_0_vm_fini(adev);
|
||||
adev->vm_manager.enabled = false;
|
||||
}
|
||||
gmc_v9_0_hw_fini(adev);
|
||||
|
||||
return 0;
|
||||
@ -809,17 +805,9 @@ static int gmc_v9_0_resume(void *handle)
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (!adev->vm_manager.enabled) {
|
||||
r = gmc_v9_0_vm_init(adev);
|
||||
if (r) {
|
||||
dev_err(adev->dev,
|
||||
"vm manager initialization failed (%d).\n", r);
|
||||
return r;
|
||||
}
|
||||
adev->vm_manager.enabled = true;
|
||||
}
|
||||
amdgpu_vm_reset_all_ids(adev);
|
||||
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool gmc_v9_0_is_idle(void *handle)
|
||||
|
@ -77,13 +77,26 @@ static int vce_v3_0_set_clockgating_state(void *handle,
|
||||
static uint64_t vce_v3_0_ring_get_rptr(struct amdgpu_ring *ring)
|
||||
{
|
||||
struct amdgpu_device *adev = ring->adev;
|
||||
u32 v;
|
||||
|
||||
mutex_lock(&adev->grbm_idx_mutex);
|
||||
if (adev->vce.harvest_config == 0 ||
|
||||
adev->vce.harvest_config == AMDGPU_VCE_HARVEST_VCE1)
|
||||
WREG32(mmGRBM_GFX_INDEX, GET_VCE_INSTANCE(0));
|
||||
else if (adev->vce.harvest_config == AMDGPU_VCE_HARVEST_VCE0)
|
||||
WREG32(mmGRBM_GFX_INDEX, GET_VCE_INSTANCE(1));
|
||||
|
||||
if (ring == &adev->vce.ring[0])
|
||||
return RREG32(mmVCE_RB_RPTR);
|
||||
v = RREG32(mmVCE_RB_RPTR);
|
||||
else if (ring == &adev->vce.ring[1])
|
||||
return RREG32(mmVCE_RB_RPTR2);
|
||||
v = RREG32(mmVCE_RB_RPTR2);
|
||||
else
|
||||
return RREG32(mmVCE_RB_RPTR3);
|
||||
v = RREG32(mmVCE_RB_RPTR3);
|
||||
|
||||
WREG32(mmGRBM_GFX_INDEX, mmGRBM_GFX_INDEX_DEFAULT);
|
||||
mutex_unlock(&adev->grbm_idx_mutex);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,13 +109,26 @@ static uint64_t vce_v3_0_ring_get_rptr(struct amdgpu_ring *ring)
|
||||
static uint64_t vce_v3_0_ring_get_wptr(struct amdgpu_ring *ring)
|
||||
{
|
||||
struct amdgpu_device *adev = ring->adev;
|
||||
u32 v;
|
||||
|
||||
mutex_lock(&adev->grbm_idx_mutex);
|
||||
if (adev->vce.harvest_config == 0 ||
|
||||
adev->vce.harvest_config == AMDGPU_VCE_HARVEST_VCE1)
|
||||
WREG32(mmGRBM_GFX_INDEX, GET_VCE_INSTANCE(0));
|
||||
else if (adev->vce.harvest_config == AMDGPU_VCE_HARVEST_VCE0)
|
||||
WREG32(mmGRBM_GFX_INDEX, GET_VCE_INSTANCE(1));
|
||||
|
||||
if (ring == &adev->vce.ring[0])
|
||||
return RREG32(mmVCE_RB_WPTR);
|
||||
v = RREG32(mmVCE_RB_WPTR);
|
||||
else if (ring == &adev->vce.ring[1])
|
||||
return RREG32(mmVCE_RB_WPTR2);
|
||||
v = RREG32(mmVCE_RB_WPTR2);
|
||||
else
|
||||
return RREG32(mmVCE_RB_WPTR3);
|
||||
v = RREG32(mmVCE_RB_WPTR3);
|
||||
|
||||
WREG32(mmGRBM_GFX_INDEX, mmGRBM_GFX_INDEX_DEFAULT);
|
||||
mutex_unlock(&adev->grbm_idx_mutex);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -116,12 +142,22 @@ static void vce_v3_0_ring_set_wptr(struct amdgpu_ring *ring)
|
||||
{
|
||||
struct amdgpu_device *adev = ring->adev;
|
||||
|
||||
mutex_lock(&adev->grbm_idx_mutex);
|
||||
if (adev->vce.harvest_config == 0 ||
|
||||
adev->vce.harvest_config == AMDGPU_VCE_HARVEST_VCE1)
|
||||
WREG32(mmGRBM_GFX_INDEX, GET_VCE_INSTANCE(0));
|
||||
else if (adev->vce.harvest_config == AMDGPU_VCE_HARVEST_VCE0)
|
||||
WREG32(mmGRBM_GFX_INDEX, GET_VCE_INSTANCE(1));
|
||||
|
||||
if (ring == &adev->vce.ring[0])
|
||||
WREG32(mmVCE_RB_WPTR, lower_32_bits(ring->wptr));
|
||||
else if (ring == &adev->vce.ring[1])
|
||||
WREG32(mmVCE_RB_WPTR2, lower_32_bits(ring->wptr));
|
||||
else
|
||||
WREG32(mmVCE_RB_WPTR3, lower_32_bits(ring->wptr));
|
||||
|
||||
WREG32(mmGRBM_GFX_INDEX, mmGRBM_GFX_INDEX_DEFAULT);
|
||||
mutex_unlock(&adev->grbm_idx_mutex);
|
||||
}
|
||||
|
||||
static void vce_v3_0_override_vce_clock_gating(struct amdgpu_device *adev, bool override)
|
||||
@ -231,33 +267,38 @@ static int vce_v3_0_start(struct amdgpu_device *adev)
|
||||
struct amdgpu_ring *ring;
|
||||
int idx, r;
|
||||
|
||||
ring = &adev->vce.ring[0];
|
||||
WREG32(mmVCE_RB_RPTR, lower_32_bits(ring->wptr));
|
||||
WREG32(mmVCE_RB_WPTR, lower_32_bits(ring->wptr));
|
||||
WREG32(mmVCE_RB_BASE_LO, ring->gpu_addr);
|
||||
WREG32(mmVCE_RB_BASE_HI, upper_32_bits(ring->gpu_addr));
|
||||
WREG32(mmVCE_RB_SIZE, ring->ring_size / 4);
|
||||
|
||||
ring = &adev->vce.ring[1];
|
||||
WREG32(mmVCE_RB_RPTR2, lower_32_bits(ring->wptr));
|
||||
WREG32(mmVCE_RB_WPTR2, lower_32_bits(ring->wptr));
|
||||
WREG32(mmVCE_RB_BASE_LO2, ring->gpu_addr);
|
||||
WREG32(mmVCE_RB_BASE_HI2, upper_32_bits(ring->gpu_addr));
|
||||
WREG32(mmVCE_RB_SIZE2, ring->ring_size / 4);
|
||||
|
||||
ring = &adev->vce.ring[2];
|
||||
WREG32(mmVCE_RB_RPTR3, lower_32_bits(ring->wptr));
|
||||
WREG32(mmVCE_RB_WPTR3, lower_32_bits(ring->wptr));
|
||||
WREG32(mmVCE_RB_BASE_LO3, ring->gpu_addr);
|
||||
WREG32(mmVCE_RB_BASE_HI3, upper_32_bits(ring->gpu_addr));
|
||||
WREG32(mmVCE_RB_SIZE3, ring->ring_size / 4);
|
||||
|
||||
mutex_lock(&adev->grbm_idx_mutex);
|
||||
for (idx = 0; idx < 2; ++idx) {
|
||||
if (adev->vce.harvest_config & (1 << idx))
|
||||
continue;
|
||||
|
||||
WREG32(mmGRBM_GFX_INDEX, GET_VCE_INSTANCE(idx));
|
||||
|
||||
/* Program instance 0 reg space for two instances or instance 0 case
|
||||
program instance 1 reg space for only instance 1 available case */
|
||||
if (idx != 1 || adev->vce.harvest_config == AMDGPU_VCE_HARVEST_VCE0) {
|
||||
ring = &adev->vce.ring[0];
|
||||
WREG32(mmVCE_RB_RPTR, lower_32_bits(ring->wptr));
|
||||
WREG32(mmVCE_RB_WPTR, lower_32_bits(ring->wptr));
|
||||
WREG32(mmVCE_RB_BASE_LO, ring->gpu_addr);
|
||||
WREG32(mmVCE_RB_BASE_HI, upper_32_bits(ring->gpu_addr));
|
||||
WREG32(mmVCE_RB_SIZE, ring->ring_size / 4);
|
||||
|
||||
ring = &adev->vce.ring[1];
|
||||
WREG32(mmVCE_RB_RPTR2, lower_32_bits(ring->wptr));
|
||||
WREG32(mmVCE_RB_WPTR2, lower_32_bits(ring->wptr));
|
||||
WREG32(mmVCE_RB_BASE_LO2, ring->gpu_addr);
|
||||
WREG32(mmVCE_RB_BASE_HI2, upper_32_bits(ring->gpu_addr));
|
||||
WREG32(mmVCE_RB_SIZE2, ring->ring_size / 4);
|
||||
|
||||
ring = &adev->vce.ring[2];
|
||||
WREG32(mmVCE_RB_RPTR3, lower_32_bits(ring->wptr));
|
||||
WREG32(mmVCE_RB_WPTR3, lower_32_bits(ring->wptr));
|
||||
WREG32(mmVCE_RB_BASE_LO3, ring->gpu_addr);
|
||||
WREG32(mmVCE_RB_BASE_HI3, upper_32_bits(ring->gpu_addr));
|
||||
WREG32(mmVCE_RB_SIZE3, ring->ring_size / 4);
|
||||
}
|
||||
|
||||
vce_v3_0_mc_resume(adev, idx);
|
||||
WREG32_FIELD(VCE_STATUS, JOB_BUSY, 1);
|
||||
|
||||
|
@ -2655,6 +2655,28 @@ static int smu7_get_power_state_size(struct pp_hwmgr *hwmgr)
|
||||
return sizeof(struct smu7_power_state);
|
||||
}
|
||||
|
||||
static int smu7_vblank_too_short(struct pp_hwmgr *hwmgr,
|
||||
uint32_t vblank_time_us)
|
||||
{
|
||||
struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
|
||||
uint32_t switch_limit_us;
|
||||
|
||||
switch (hwmgr->chip_id) {
|
||||
case CHIP_POLARIS10:
|
||||
case CHIP_POLARIS11:
|
||||
case CHIP_POLARIS12:
|
||||
switch_limit_us = data->is_memory_gddr5 ? 190 : 150;
|
||||
break;
|
||||
default:
|
||||
switch_limit_us = data->is_memory_gddr5 ? 450 : 150;
|
||||
break;
|
||||
}
|
||||
|
||||
if (vblank_time_us < switch_limit_us)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static int smu7_apply_state_adjust_rules(struct pp_hwmgr *hwmgr,
|
||||
struct pp_power_state *request_ps,
|
||||
@ -2669,6 +2691,7 @@ static int smu7_apply_state_adjust_rules(struct pp_hwmgr *hwmgr,
|
||||
bool disable_mclk_switching;
|
||||
bool disable_mclk_switching_for_frame_lock;
|
||||
struct cgs_display_info info = {0};
|
||||
struct cgs_mode_info mode_info = {0};
|
||||
const struct phm_clock_and_voltage_limits *max_limits;
|
||||
uint32_t i;
|
||||
struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
|
||||
@ -2677,6 +2700,7 @@ static int smu7_apply_state_adjust_rules(struct pp_hwmgr *hwmgr,
|
||||
int32_t count;
|
||||
int32_t stable_pstate_sclk = 0, stable_pstate_mclk = 0;
|
||||
|
||||
info.mode_info = &mode_info;
|
||||
data->battery_state = (PP_StateUILabel_Battery ==
|
||||
request_ps->classification.ui_label);
|
||||
|
||||
@ -2703,8 +2727,6 @@ static int smu7_apply_state_adjust_rules(struct pp_hwmgr *hwmgr,
|
||||
|
||||
cgs_get_active_displays_info(hwmgr->device, &info);
|
||||
|
||||
/*TO DO result = PHM_CheckVBlankTime(hwmgr, &vblankTooShort);*/
|
||||
|
||||
minimum_clocks.engineClock = hwmgr->display_config.min_core_set_clock;
|
||||
minimum_clocks.memoryClock = hwmgr->display_config.min_mem_set_clock;
|
||||
|
||||
@ -2769,8 +2791,10 @@ static int smu7_apply_state_adjust_rules(struct pp_hwmgr *hwmgr,
|
||||
PHM_PlatformCaps_DisableMclkSwitchingForFrameLock);
|
||||
|
||||
|
||||
disable_mclk_switching = (1 < info.display_count) ||
|
||||
disable_mclk_switching_for_frame_lock;
|
||||
disable_mclk_switching = ((1 < info.display_count) ||
|
||||
disable_mclk_switching_for_frame_lock ||
|
||||
smu7_vblank_too_short(hwmgr, mode_info.vblank_time_us) ||
|
||||
(mode_info.refresh_rate > 120));
|
||||
|
||||
sclk = smu7_ps->performance_levels[0].engine_clock;
|
||||
mclk = smu7_ps->performance_levels[0].memory_clock;
|
||||
|
@ -4186,7 +4186,7 @@ static int vega10_force_clock_level(struct pp_hwmgr *hwmgr,
|
||||
enum pp_clock_type type, uint32_t mask)
|
||||
{
|
||||
struct vega10_hwmgr *data = (struct vega10_hwmgr *)(hwmgr->backend);
|
||||
uint32_t i;
|
||||
int i;
|
||||
|
||||
if (hwmgr->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL)
|
||||
return -EINVAL;
|
||||
|
@ -709,17 +709,17 @@ static int tf_vega10_thermal_disable_alert(struct pp_hwmgr *hwmgr,
|
||||
|
||||
static struct phm_master_table_item
|
||||
vega10_thermal_start_thermal_controller_master_list[] = {
|
||||
{NULL, tf_vega10_thermal_initialize},
|
||||
{NULL, tf_vega10_thermal_set_temperature_range},
|
||||
{NULL, tf_vega10_thermal_enable_alert},
|
||||
{ .tableFunction = tf_vega10_thermal_initialize },
|
||||
{ .tableFunction = tf_vega10_thermal_set_temperature_range },
|
||||
{ .tableFunction = tf_vega10_thermal_enable_alert },
|
||||
/* We should restrict performance levels to low before we halt the SMC.
|
||||
* On the other hand we are still in boot state when we do this
|
||||
* so it would be pointless.
|
||||
* If this assumption changes we have to revisit this table.
|
||||
*/
|
||||
{NULL, tf_vega10_thermal_setup_fan_table},
|
||||
{NULL, tf_vega10_thermal_start_smc_fan_control},
|
||||
{NULL, NULL}
|
||||
{ .tableFunction = tf_vega10_thermal_setup_fan_table },
|
||||
{ .tableFunction = tf_vega10_thermal_start_smc_fan_control },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct phm_master_table_header
|
||||
@ -731,10 +731,10 @@ vega10_thermal_start_thermal_controller_master = {
|
||||
|
||||
static struct phm_master_table_item
|
||||
vega10_thermal_set_temperature_range_master_list[] = {
|
||||
{NULL, tf_vega10_thermal_disable_alert},
|
||||
{NULL, tf_vega10_thermal_set_temperature_range},
|
||||
{NULL, tf_vega10_thermal_enable_alert},
|
||||
{NULL, NULL}
|
||||
{ .tableFunction = tf_vega10_thermal_disable_alert },
|
||||
{ .tableFunction = tf_vega10_thermal_set_temperature_range },
|
||||
{ .tableFunction = tf_vega10_thermal_enable_alert },
|
||||
{ }
|
||||
};
|
||||
|
||||
struct phm_master_table_header
|
||||
|
@ -1208,3 +1208,86 @@ int drm_dp_stop_crc(struct drm_dp_aux *aux)
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_stop_crc);
|
||||
|
||||
struct dpcd_quirk {
|
||||
u8 oui[3];
|
||||
bool is_branch;
|
||||
u32 quirks;
|
||||
};
|
||||
|
||||
#define OUI(first, second, third) { (first), (second), (third) }
|
||||
|
||||
static const struct dpcd_quirk dpcd_quirk_list[] = {
|
||||
/* Analogix 7737 needs reduced M and N at HBR2 link rates */
|
||||
{ OUI(0x00, 0x22, 0xb9), true, BIT(DP_DPCD_QUIRK_LIMITED_M_N) },
|
||||
};
|
||||
|
||||
#undef OUI
|
||||
|
||||
/*
|
||||
* Get a bit mask of DPCD quirks for the sink/branch device identified by
|
||||
* ident. The quirk data is shared but it's up to the drivers to act on the
|
||||
* data.
|
||||
*
|
||||
* For now, only the OUI (first three bytes) is used, but this may be extended
|
||||
* to device identification string and hardware/firmware revisions later.
|
||||
*/
|
||||
static u32
|
||||
drm_dp_get_quirks(const struct drm_dp_dpcd_ident *ident, bool is_branch)
|
||||
{
|
||||
const struct dpcd_quirk *quirk;
|
||||
u32 quirks = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dpcd_quirk_list); i++) {
|
||||
quirk = &dpcd_quirk_list[i];
|
||||
|
||||
if (quirk->is_branch != is_branch)
|
||||
continue;
|
||||
|
||||
if (memcmp(quirk->oui, ident->oui, sizeof(ident->oui)) != 0)
|
||||
continue;
|
||||
|
||||
quirks |= quirk->quirks;
|
||||
}
|
||||
|
||||
return quirks;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_dp_read_desc - read sink/branch descriptor from DPCD
|
||||
* @aux: DisplayPort AUX channel
|
||||
* @desc: Device decriptor to fill from DPCD
|
||||
* @is_branch: true for branch devices, false for sink devices
|
||||
*
|
||||
* Read DPCD 0x400 (sink) or 0x500 (branch) into @desc. Also debug log the
|
||||
* identification.
|
||||
*
|
||||
* Returns 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int drm_dp_read_desc(struct drm_dp_aux *aux, struct drm_dp_desc *desc,
|
||||
bool is_branch)
|
||||
{
|
||||
struct drm_dp_dpcd_ident *ident = &desc->ident;
|
||||
unsigned int offset = is_branch ? DP_BRANCH_OUI : DP_SINK_OUI;
|
||||
int ret, dev_id_len;
|
||||
|
||||
ret = drm_dp_dpcd_read(aux, offset, ident, sizeof(*ident));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
desc->quirks = drm_dp_get_quirks(ident, is_branch);
|
||||
|
||||
dev_id_len = strnlen(ident->device_id, sizeof(ident->device_id));
|
||||
|
||||
DRM_DEBUG_KMS("DP %s: OUI %*phD dev-ID %*pE HW-rev %d.%d SW-rev %d.%d quirks 0x%04x\n",
|
||||
is_branch ? "branch" : "sink",
|
||||
(int)sizeof(ident->oui), ident->oui,
|
||||
dev_id_len, ident->device_id,
|
||||
ident->hw_rev >> 4, ident->hw_rev & 0xf,
|
||||
ident->sw_major_rev, ident->sw_minor_rev,
|
||||
desc->quirks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_read_desc);
|
||||
|
@ -948,8 +948,6 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret && crtc->funcs->page_flip_target)
|
||||
drm_crtc_vblank_put(crtc);
|
||||
if (fb)
|
||||
drm_framebuffer_put(fb);
|
||||
if (crtc->primary->old_fb)
|
||||
@ -964,5 +962,8 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
|
||||
if (ret && crtc->funcs->page_flip_target)
|
||||
drm_crtc_vblank_put(crtc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -82,14 +82,9 @@ static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void exynos_drm_preclose(struct drm_device *dev,
|
||||
struct drm_file *file)
|
||||
{
|
||||
exynos_drm_subdrv_close(dev, file);
|
||||
}
|
||||
|
||||
static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
|
||||
{
|
||||
exynos_drm_subdrv_close(dev, file);
|
||||
kfree(file->driver_priv);
|
||||
file->driver_priv = NULL;
|
||||
}
|
||||
@ -145,7 +140,6 @@ static struct drm_driver exynos_drm_driver = {
|
||||
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME
|
||||
| DRIVER_ATOMIC | DRIVER_RENDER,
|
||||
.open = exynos_drm_open,
|
||||
.preclose = exynos_drm_preclose,
|
||||
.lastclose = exynos_drm_lastclose,
|
||||
.postclose = exynos_drm_postclose,
|
||||
.gem_free_object_unlocked = exynos_drm_gem_free_object,
|
||||
|
@ -160,12 +160,9 @@ struct exynos_drm_clk {
|
||||
* drm framework doesn't support multiple irq yet.
|
||||
* we can refer to the crtc to current hardware interrupt occurred through
|
||||
* this pipe value.
|
||||
* @enabled: if the crtc is enabled or not
|
||||
* @event: vblank event that is currently queued for flip
|
||||
* @wait_update: wait all pending planes updates to finish
|
||||
* @pending_update: number of pending plane updates in this crtc
|
||||
* @ops: pointer to callbacks for exynos drm specific functionality
|
||||
* @ctx: A pointer to the crtc's implementation specific context
|
||||
* @pipe_clk: A pointer to the crtc's pipeline clock.
|
||||
*/
|
||||
struct exynos_drm_crtc {
|
||||
struct drm_crtc base;
|
||||
|
@ -1633,7 +1633,6 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
|
||||
{
|
||||
struct device *dev = dsi->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct device_node *ep;
|
||||
int ret;
|
||||
|
||||
ret = exynos_dsi_of_read_u32(node, "samsung,pll-clock-frequency",
|
||||
@ -1641,32 +1640,21 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ep = of_graph_get_endpoint_by_regs(node, DSI_PORT_OUT, 0);
|
||||
if (!ep) {
|
||||
dev_err(dev, "no output port with endpoint specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = exynos_dsi_of_read_u32(ep, "samsung,burst-clock-frequency",
|
||||
ret = exynos_dsi_of_read_u32(node, "samsung,burst-clock-frequency",
|
||||
&dsi->burst_clk_rate);
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
return ret;
|
||||
|
||||
ret = exynos_dsi_of_read_u32(ep, "samsung,esc-clock-frequency",
|
||||
ret = exynos_dsi_of_read_u32(node, "samsung,esc-clock-frequency",
|
||||
&dsi->esc_clk_rate);
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
|
||||
of_node_put(ep);
|
||||
return ret;
|
||||
|
||||
dsi->bridge_node = of_graph_get_remote_node(node, DSI_PORT_OUT, 0);
|
||||
if (!dsi->bridge_node)
|
||||
return -EINVAL;
|
||||
|
||||
end:
|
||||
of_node_put(ep);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_dsi_bind(struct device *dev, struct device *master,
|
||||
@ -1817,6 +1805,10 @@ static int exynos_dsi_probe(struct platform_device *pdev)
|
||||
|
||||
static int exynos_dsi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct exynos_dsi *dsi = platform_get_drvdata(pdev);
|
||||
|
||||
of_node_put(dsi->bridge_node);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
component_del(&pdev->dev, &exynos_dsi_component_ops);
|
||||
|
@ -759,20 +759,23 @@ void psb_intel_lvds_init(struct drm_device *dev,
|
||||
if (scan->type & DRM_MODE_TYPE_PREFERRED) {
|
||||
mode_dev->panel_fixed_mode =
|
||||
drm_mode_duplicate(dev, scan);
|
||||
DRM_DEBUG_KMS("Using mode from DDC\n");
|
||||
goto out; /* FIXME: check for quirks */
|
||||
}
|
||||
}
|
||||
|
||||
/* Failed to get EDID, what about VBT? do we need this? */
|
||||
if (mode_dev->vbt_mode)
|
||||
if (dev_priv->lfp_lvds_vbt_mode) {
|
||||
mode_dev->panel_fixed_mode =
|
||||
drm_mode_duplicate(dev, mode_dev->vbt_mode);
|
||||
drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
|
||||
|
||||
if (!mode_dev->panel_fixed_mode)
|
||||
if (dev_priv->lfp_lvds_vbt_mode)
|
||||
mode_dev->panel_fixed_mode =
|
||||
drm_mode_duplicate(dev,
|
||||
dev_priv->lfp_lvds_vbt_mode);
|
||||
if (mode_dev->panel_fixed_mode) {
|
||||
mode_dev->panel_fixed_mode->type |=
|
||||
DRM_MODE_TYPE_PREFERRED;
|
||||
DRM_DEBUG_KMS("Using mode from VBT\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we didn't get EDID, try checking if the panel is already turned
|
||||
@ -789,6 +792,7 @@ void psb_intel_lvds_init(struct drm_device *dev,
|
||||
if (mode_dev->panel_fixed_mode) {
|
||||
mode_dev->panel_fixed_mode->type |=
|
||||
DRM_MODE_TYPE_PREFERRED;
|
||||
DRM_DEBUG_KMS("Using pre-programmed mode\n");
|
||||
goto out; /* FIXME: check for quirks */
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user