mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
ASoC: Add support for GPIOs driven amplifiers
Herve Codina <herve.codina@bootlin.com> says: On some embedded system boards, audio amplifiers are designed using discrete components such as op-amp, several resistors and switches to either adjust the gain (switching resistors) or fully switch the audio signal path (mute and/or bypass features). Those switches are usually driven by simple GPIOs. This kind of amplifiers are not handled in ASoC and the fallback is to let the user-space handle those GPIOs out of the ALSA world. In order to have those kind of amplifiers fully integrated in the audio stack, this series introduces the audio-gpio-amp to handle them. This new ASoC component allows to have the amplifiers seen as ASoC auxiliarty devices and so it allows to control them through audio mixer controls. In order to ease the review, I choose to split modifications related to the merge of the gpio-audio-amp part into the simple-amplfier driver in several commits. Link: https://patch.msgid.link/20260513081702.317117-1-herve.codina@bootlin.com
This commit is contained in:
commit
e76ccf19e2
1
.mailmap
1
.mailmap
|
|
@ -857,6 +857,7 @@ Tobias Klauser <tklauser@distanz.ch> <klto@zhaw.ch>
|
|||
Tobias Klauser <tklauser@distanz.ch> <tklauser@nuerscht.ch>
|
||||
Tobias Klauser <tklauser@distanz.ch> <tklauser@xenon.tklauser.home>
|
||||
Todor Tomov <todor.too@gmail.com> <todor.tomov@linaro.org>
|
||||
Tomasz Jeznach <tomasz.jeznach@linux.dev> <tjeznach@rivosinc.com>
|
||||
Tony Luck <tony.luck@intel.com>
|
||||
Trilok Soni <quic_tsoni@quicinc.com> <tsoni@codeaurora.org>
|
||||
TripleX Chung <xxx.phy@gmail.com> <triplex@zh-kernel.org>
|
||||
|
|
|
|||
|
|
@ -21,13 +21,13 @@ call at each patchable function entry, and patches it dynamically at runtime to
|
|||
enable or disable the redirection. In the case of RISC-V, 2 instructions,
|
||||
AUIPC + JALR, are required to compose a function call. However, it is impossible
|
||||
to patch 2 instructions and expect that a concurrent read-side executes them
|
||||
without a race condition. This series makes atmoic code patching possible in
|
||||
without a race condition. This series makes atomic code patching possible in
|
||||
RISC-V ftrace. Kernel preemption makes things even worse as it allows the old
|
||||
state to persist across the patching process with stop_machine().
|
||||
|
||||
In order to get rid of stop_machine() and run dynamic ftrace with full kernel
|
||||
preemption, we partially initialize each patchable function entry at boot-time,
|
||||
setting the first instruction to AUIPC, and the second to NOP. Now, atmoic
|
||||
setting the first instruction to AUIPC, and the second to NOP. Now, atomic
|
||||
patching is possible because the kernel only has to update one instruction.
|
||||
According to Ziccif, as long as an instruction is naturally aligned, the ISA
|
||||
guarantee an atomic update.
|
||||
|
|
@ -36,8 +36,8 @@ By fixing down the first instruction, AUIPC, the range of the ftrace trampoline
|
|||
is limited to +-2K from the predetermined target, ftrace_caller, due to the lack
|
||||
of immediate encoding space in RISC-V. To address the issue, we introduce
|
||||
CALL_OPS, where an 8B naturally align metadata is added in front of each
|
||||
pacthable function. The metadata is resolved at the first trampoline, then the
|
||||
execution can be derect to another custom trampoline.
|
||||
patchable function. The metadata is resolved at the first trampoline, then the
|
||||
execution can be directed to another custom trampoline.
|
||||
|
||||
CMODX in the User Space
|
||||
-----------------------
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ the program.
|
|||
|
||||
Per-task indirect branch tracking state can be monitored and
|
||||
controlled via the :c:macro:`PR_GET_CFI` and :c:macro:`PR_SET_CFI`
|
||||
``prctl()` arguments (respectively), by supplying
|
||||
``prctl()`` arguments (respectively), by supplying
|
||||
:c:macro:`PR_CFI_BRANCH_LANDING_PADS` as the second argument. These
|
||||
are architecture-agnostic, and will return -EINVAL if the underlying
|
||||
functionality is not supported.
|
||||
|
|
|
|||
270
Documentation/devicetree/bindings/sound/gpio-audio-amp.yaml
Normal file
270
Documentation/devicetree/bindings/sound/gpio-audio-amp.yaml
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/gpio-audio-amp.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Audio amplifier driven by GPIOs
|
||||
|
||||
maintainers:
|
||||
- Herve Codina <herve.codina@bootlin.com>
|
||||
|
||||
description: |
|
||||
Audio GPIO amplifiers are driven by GPIO in order to control the gain value
|
||||
of the amplifier, its mute function and/or its bypass function.
|
||||
|
||||
Those amplifiers are based on discrete components (analog switches, op-amps
|
||||
and more) where some of them, mostly analog switches, are controlled by GPIOs
|
||||
to adjust the gain value of the whole amplifier and/or to control
|
||||
the mute and/or bypass function.
|
||||
|
||||
For instance, the following piece of hardware is a GPIO amplifier
|
||||
|
||||
+5VA
|
||||
^
|
||||
|\ |
|
||||
| \
|
||||
Vin >---------------------------|+ \
|
||||
| +-------+-----> Vout
|
||||
.--\/\/\/--+------------|- / |
|
||||
| | | / |
|
||||
v | |/ | |
|
||||
GND o v |
|
||||
\ GND |
|
||||
gpio >-----------> \ |
|
||||
o o |
|
||||
| | |
|
||||
| '--\/\/\/--. |
|
||||
| +--\/\/\/--'
|
||||
'---------------'
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: gpio-audio-amp-mono
|
||||
description:
|
||||
A single channel amplifier. All features apply to this sole channel.
|
||||
|
||||
- const: gpio-audio-amp-stereo
|
||||
description:
|
||||
A dual channel amplifier (left and right). All features apply to both
|
||||
channels producing the same effect on both channels at the same time.
|
||||
|
||||
vdd-supply:
|
||||
description: Main power supply of the amplifier
|
||||
|
||||
vddio-supply:
|
||||
description: Power supply related to the control path
|
||||
|
||||
vdda1-supply:
|
||||
description: Analog power supply
|
||||
|
||||
vdda2-supply:
|
||||
description: Additional analog power supply
|
||||
|
||||
mute-gpios:
|
||||
description: GPIO to control the mute function
|
||||
maxItems: 1
|
||||
|
||||
bypass-gpios:
|
||||
description: GPIO to control the bypass function
|
||||
maxItems: 1
|
||||
|
||||
gain-gpios:
|
||||
description: |
|
||||
GPIOs to control the amplifier gain
|
||||
|
||||
The gain value is computed from GPIOs value from 0 to 2^N-1 with N the
|
||||
number of GPIO described. The first GPIO described is the lsb of the gain
|
||||
value.
|
||||
|
||||
For instance assuming 2 gpios
|
||||
gain-gpios = <&gpio1 GPIO_ACTIVE_HIGH> <&gpio2 GPIO_ACTIVE_HIGH>;
|
||||
The gain value will be the following:
|
||||
|
||||
gpio1 | gpio2 | gain
|
||||
------+-------+-----
|
||||
0 | 0 | 0b00 -> 0
|
||||
1 | 0 | 0b01 -> 1
|
||||
0 | 1 | 0b10 -> 2
|
||||
1 | 1 | 0b11 -> 3
|
||||
------+-------+-----
|
||||
|
||||
Note: The gain value, bits set to 1 or 0, indicate the state active (bit
|
||||
set) or the state inactive (bit unset) of the related GPIO. The
|
||||
physical voltage corresponding to this active/inactive state is
|
||||
given by the GPIO_ACTIVE_HIGH and GPIO_ACTIVE_LOW flags.
|
||||
|
||||
minItems: 1
|
||||
maxItems: 16
|
||||
|
||||
gain-ranges:
|
||||
$ref: /schemas/types.yaml#/definitions/int32-matrix
|
||||
description: |
|
||||
A list of one or more ranges of possible values. Each range is defined by
|
||||
the first and last point in the range. Each point is defined by the pair
|
||||
(GPIOs value, Gain in 0.01 dB unit).
|
||||
|
||||
Ranges can be contiguous or holes can be present between ranges if some
|
||||
gpios value should not be used. Also in a range the first point and the
|
||||
last point can be identical. In that case, the range contains only one
|
||||
item, the given point.
|
||||
|
||||
items:
|
||||
items:
|
||||
- description: GPIOs value of the first point in the range
|
||||
- description: Gain in 0.01 dB unit of the first point in the range
|
||||
- description: GPIOs value of the last point in the range
|
||||
- description: Gain in 0.01 dB unit of the last point in the range
|
||||
description: |
|
||||
A range defines a linear function (linear in dB) from the first point
|
||||
to the last point, both included. The number of items in the range is
|
||||
N = abs(first_point.gpio_value - last_point.gpio_value) + 1
|
||||
|
||||
It allows to define the gain range from the first_point.gain to
|
||||
the last_point.gain, both points included.
|
||||
|
||||
Gain (0.01 dB unit)
|
||||
^
|
||||
| last
|
||||
+- - - - - - - - - - + point
|
||||
| + .
|
||||
| + .
|
||||
| + .
|
||||
+- - - - + .
|
||||
| first . .
|
||||
| point . .
|
||||
| . .
|
||||
+--------+-----------+---> gpios
|
||||
value
|
||||
|
||||
Note: Even if first_point.gpio_value is lower than last_point.gpio_value
|
||||
and first_point.gain is lower than last_point.gain in the above
|
||||
graphic, all combination of values are supported leading to an
|
||||
increasing or a decreasing linear segment.
|
||||
|
||||
minItems: 1
|
||||
maxItems: 65536
|
||||
|
||||
gain-labels:
|
||||
$ref: /schemas/types.yaml#/definitions/string-array
|
||||
minItems: 2
|
||||
maxItems: 65536
|
||||
description: |
|
||||
List of the gain labels attached to the combination of GPIOs controlling
|
||||
the gain. The first label is related to the gain value 0, the second label
|
||||
is related to the gain value 1 and so on.
|
||||
|
||||
With 2 GPIOs controlling the gain, GPIOs value can be 0, 1, 2 and 3.
|
||||
Assuming that gain value set the hardware according to the following
|
||||
table:
|
||||
|
||||
GPIOs | Hardware
|
||||
value | amplification
|
||||
------+--------------
|
||||
0 | Low
|
||||
1 | Middle
|
||||
2 | High
|
||||
3 | Max
|
||||
------+--------------
|
||||
|
||||
The description using gain labels can be:
|
||||
gain-labels = "Low", "Middle", "High", "Max";
|
||||
|
||||
dependencies:
|
||||
gain-ranges: [ gain-gpios ]
|
||||
gain-labels: [ gain-gpios ]
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- vdd-supply
|
||||
|
||||
anyOf:
|
||||
- required:
|
||||
- gain-gpios
|
||||
- required:
|
||||
- mute-gpios
|
||||
- required:
|
||||
- bypass-gpios
|
||||
|
||||
allOf:
|
||||
- $ref: component-common.yaml#
|
||||
- if:
|
||||
required:
|
||||
- gain-ranges
|
||||
then:
|
||||
properties:
|
||||
gain-labels: false
|
||||
- if:
|
||||
required:
|
||||
- gain-labels
|
||||
then:
|
||||
properties:
|
||||
gain-ranges: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
/* Gain controlled by gpios */
|
||||
amplifier-0 {
|
||||
compatible = "gpio-audio-amp-mono";
|
||||
vdd-supply = <®ulator>;
|
||||
gain-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>, <&gpio 1 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
|
||||
/* Gain controlled by gpio using a simple range on a stereo amplifier */
|
||||
amplifier-1 {
|
||||
compatible = "gpio-audio-amp-stereo";
|
||||
vdd-supply = <®ulator>;
|
||||
gain-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>, <&gpio 1 GPIO_ACTIVE_HIGH>;
|
||||
gain-ranges = <0 (-300) 3 600>;
|
||||
};
|
||||
|
||||
/* Gain controlled by gpio with labels */
|
||||
amplifier-3 {
|
||||
compatible = "gpio-audio-amp-mono";
|
||||
vdd-supply = <®ulator>;
|
||||
gain-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>;
|
||||
gain-labels = "Low", "High";
|
||||
};
|
||||
|
||||
/* A mutable stereo amplifier without any gain control */
|
||||
amplifier-4 {
|
||||
compatible = "gpio-audio-amp-stereo";
|
||||
vdd-supply = <®ulator>;
|
||||
mute-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
|
||||
/*
|
||||
* Several supplies, gain controlled using more complex ranges, mute and
|
||||
* bypass.
|
||||
*
|
||||
* Assuming 3 gpios for controlling the gain with the following table
|
||||
* gpios value Gain
|
||||
* 0b000 Do not use (gpios value not allowed)
|
||||
* 0b001 - 3dB
|
||||
* 0b010 + 3dB
|
||||
* 0b011 + 10dB
|
||||
* 0b100 Do not use (gpios value not allowed)
|
||||
* 0b101 + 6dB
|
||||
* 0b110 + 7dB
|
||||
* 0b111 + 8dB
|
||||
*/
|
||||
amplifier-5 {
|
||||
compatible = "gpio-audio-amp-mono";
|
||||
vdd-supply = <®ulator>;
|
||||
vddio-supply = <®ulator1>;
|
||||
vdda1-supply = <®ulator2>;
|
||||
gain-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>,
|
||||
<&gpio 1 GPIO_ACTIVE_HIGH>,
|
||||
<&gpio 2 GPIO_ACTIVE_HIGH>;
|
||||
gain-ranges = <1 (-300) 2 300>,
|
||||
<3 1000 3 1000>,
|
||||
<5 600 7 800>;
|
||||
mute-gpios = <&gpio 3 GPIO_ACTIVE_HIGH>;
|
||||
bypass-gpios = <&gpio 4 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
...
|
||||
|
|
@ -22,5 +22,5 @@ The following sensors are supported
|
|||
sysfs-Interface
|
||||
---------------
|
||||
|
||||
temp0_input
|
||||
temp1_input
|
||||
- Temperature of external NTC (milli-degree C)
|
||||
|
|
|
|||
|
|
@ -69,6 +69,15 @@ properties:
|
|||
header:
|
||||
description: For C-compatible languages, header which already defines this value.
|
||||
type: string
|
||||
scope:
|
||||
description: |
|
||||
Visibility of this definition. "uapi" (default) renders into
|
||||
the uAPI header, "kernel" renders into the kernel-side
|
||||
generated header, "user" renders into the user-side
|
||||
generated header. When combined with `header:`, the
|
||||
definition is not rendered, and the named header is
|
||||
included only by code matching the scope.
|
||||
enum: [ uapi, kernel, user ]
|
||||
type:
|
||||
enum: [ const, enum, flags ]
|
||||
doc:
|
||||
|
|
|
|||
|
|
@ -83,6 +83,15 @@ properties:
|
|||
header:
|
||||
description: For C-compatible languages, header which already defines this value.
|
||||
type: string
|
||||
scope:
|
||||
description: |
|
||||
Visibility of this definition. "uapi" (default) renders into
|
||||
the uAPI header, "kernel" renders into the kernel-side
|
||||
generated header, "user" renders into the user-side
|
||||
generated header. When combined with `header:`, the
|
||||
definition is not rendered, and the named header is
|
||||
included only by code matching the scope.
|
||||
enum: [ uapi, kernel, user ]
|
||||
type:
|
||||
enum: [ const, enum, flags, struct ] # Trim
|
||||
doc:
|
||||
|
|
|
|||
|
|
@ -55,6 +55,15 @@ properties:
|
|||
header:
|
||||
description: For C-compatible languages, header which already defines this value.
|
||||
type: string
|
||||
scope:
|
||||
description: |
|
||||
Visibility of this definition. "uapi" (default) renders into
|
||||
the uAPI header, "kernel" renders into the kernel-side
|
||||
generated header, "user" renders into the user-side
|
||||
generated header. When combined with `header:`, the
|
||||
definition is not rendered, and the named header is
|
||||
included only by code matching the scope.
|
||||
enum: [ uapi, kernel, user ]
|
||||
type:
|
||||
enum: [ const, enum, flags ]
|
||||
doc:
|
||||
|
|
|
|||
|
|
@ -87,6 +87,15 @@ properties:
|
|||
header:
|
||||
description: For C-compatible languages, header which already defines this value.
|
||||
type: string
|
||||
scope:
|
||||
description: |
|
||||
Visibility of this definition. "uapi" (default) renders into
|
||||
the uAPI header, "kernel" renders into the kernel-side
|
||||
generated header, "user" renders into the user-side
|
||||
generated header. When combined with `header:`, the
|
||||
definition is not rendered, and the named header is
|
||||
included only by code matching the scope.
|
||||
enum: [ uapi, kernel, user ]
|
||||
type:
|
||||
enum: [ const, enum, flags, struct ] # Trim
|
||||
doc:
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@ doc: |
|
|||
@cap-get operation.
|
||||
|
||||
definitions:
|
||||
-
|
||||
type: const
|
||||
name: max-handle-id
|
||||
value: 0x3fffffe
|
||||
scope: kernel
|
||||
-
|
||||
type: enum
|
||||
name: scope
|
||||
|
|
@ -140,6 +145,8 @@ attribute-sets:
|
|||
-
|
||||
name: id
|
||||
type: u32
|
||||
checks:
|
||||
max: max-handle-id
|
||||
doc: |
|
||||
Numeric identifier of a shaper. The id semantic depends on
|
||||
the scope. For @queue scope it's the queue id and for @node
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ regressions and security problems.
|
|||
debugging/index
|
||||
handling-regressions
|
||||
security-bugs
|
||||
threat-model
|
||||
cve
|
||||
embargoed-hardware-issues
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,42 @@ In addition, the following information are highly desirable:
|
|||
the issue appear. It is useful to share them, as they can be helpful to
|
||||
keep end users protected during the time it takes them to apply the fix.
|
||||
|
||||
What qualifies as a security bug
|
||||
--------------------------------
|
||||
|
||||
It is important that most bugs are handled publicly so as to involve the widest
|
||||
possible audience and find the best solution. By nature, bugs that are handled
|
||||
in closed discussions between a small set of participants are less likely to
|
||||
produce the best possible fix (e.g., risk of missing valid use cases, limited
|
||||
testing abilities).
|
||||
|
||||
It turns out that the majority of the bugs reported via the security team are
|
||||
just regular bugs that have been improperly qualified as security bugs due to
|
||||
a lack of awareness of the Linux kernel's threat model, as described in
|
||||
Documentation/process/threat-model.rst, and ought to have been sent through
|
||||
the normal channels described in Documentation/admin-guide/reporting-issues.rst
|
||||
instead.
|
||||
|
||||
The security list exists for urgent bugs that grant an attacker a capability
|
||||
they are not supposed to have on a correctly configured production system, and
|
||||
can be easily exploited, representing an imminent threat to many users. Before
|
||||
reporting, consider whether the issue actually crosses a trust boundary on such
|
||||
a system.
|
||||
|
||||
**If you resorted to AI assistance to identify a bug, you must treat it as
|
||||
public**. While you may have valid reasons to believe it is not, the security
|
||||
team's experience shows that bugs discovered this way systematically surface
|
||||
simultaneously across multiple researchers, often on the same day. In this
|
||||
case, do not publicly share a reproducer, as this could cause unintended harm;
|
||||
just mention that one is available and maintainers might ask for it privately
|
||||
if they need it.
|
||||
|
||||
If you are unsure whether an issue qualifies, err on the side of reporting
|
||||
privately: the security team would rather triage a borderline report than miss
|
||||
a real vulnerability. Reporting ordinary bugs to the security list, however,
|
||||
does not make them move faster and consumes triage capacity that other reports
|
||||
need.
|
||||
|
||||
Identifying contacts
|
||||
--------------------
|
||||
|
||||
|
|
@ -74,7 +110,7 @@ affected subsystem's maintainers and Cc: the Linux kernel security team. Do
|
|||
not send it to a public list at this stage, unless you have good reasons to
|
||||
consider the issue as being public or trivial to discover (e.g. result of a
|
||||
widely available automated vulnerability scanning tool that can be repeated by
|
||||
anyone).
|
||||
anyone, or use of AI-based tools).
|
||||
|
||||
If you're sending a report for issues affecting multiple parts in the kernel,
|
||||
even if they're fairly similar issues, please send individual messages (think
|
||||
|
|
@ -131,6 +167,64 @@ the Linux kernel security team only. Your message will be triaged, and you
|
|||
will receive instructions about whom to contact, if needed. Your message may
|
||||
equally be forwarded as-is to the relevant maintainers.
|
||||
|
||||
Responsible use of AI to find bugs
|
||||
----------------------------------
|
||||
|
||||
A significant fraction of bug reports submitted to the security team are
|
||||
actually the result of code reviews assisted by AI tools. While this can be an
|
||||
efficient means to find bugs in rarely explored areas, it causes an overload on
|
||||
maintainers, who are sometimes forced to ignore such reports due to their poor
|
||||
quality or accuracy. As such, reporters must be particularly cautious about a
|
||||
number of points which tend to make these reports needlessly difficult to
|
||||
handle:
|
||||
|
||||
* **Length**: AI-generated reports tend to be excessively long, containing
|
||||
multiple sections and excessive detail. This makes it difficult to spot
|
||||
important information such as affected files, versions, and impact. Please
|
||||
ensure that a clear summary of the problem and all critical details are
|
||||
presented first. Do not require triage engineers to scan multiple pages of
|
||||
text. Configure your tools to produce concise, human-style reports.
|
||||
|
||||
* **Formatting**: Most AI-generated reports are littered with Markdown tags.
|
||||
These decorations complicate the search for important information and do
|
||||
not survive the quoting processes involved in forwarding or replying.
|
||||
Please **always convert your report to plain text** without any formatting
|
||||
decorations before sending it.
|
||||
|
||||
* **Impact Evaluation**: Many AI-generated reports lack an understanding
|
||||
of the kernel's threat model (see Documentation/process/threat-model.rst)
|
||||
and go to great lengths inventing theoretical consequences. This adds
|
||||
noise and complicates triage. Please stick to verifiable facts (e.g.,
|
||||
"this bug permits any user to gain CAP_NET_ADMIN") without enumerating
|
||||
speculative implications. Have your tool read this documentation as
|
||||
part of the evaluation process.
|
||||
|
||||
* **Reproducer**: AI-based tools are often capable of generating reproducers.
|
||||
Please always ensure your tool provides one and **test it thoroughly**. If
|
||||
the reproducer does not work, or if the tool cannot produce one, the
|
||||
validity of the report should be seriously questioned. Note that since the
|
||||
report will be posted to a public list, the reproducer should only be
|
||||
shared upon maintainers' request.
|
||||
|
||||
* **Propose a Fix**: Many AI tools are actually better at writing code than
|
||||
evaluating it. Please ask your tool to propose a fix and **test it** before
|
||||
reporting the problem. If the fix cannot be tested because it relies on
|
||||
rare hardware or almost extinct network protocols, the issue is likely not
|
||||
a security bug. In any case, if a fix is proposed, it must adhere to
|
||||
Documentation/process/submitting-patches.rst and include a 'Fixes:' tag
|
||||
designating the commit that introduced the bug.
|
||||
|
||||
Failure to consider these points exposes your report to the risk of being
|
||||
ignored.
|
||||
|
||||
Use common sense when evaluating the report. If the affected file has not been
|
||||
touched for more than one year and is maintained by a single individual, it is
|
||||
likely that usage has declined and exposed users are virtually non-existent
|
||||
(e.g., drivers for very old hardware, obsolete filesystems). In such cases,
|
||||
there is no need to consume a maintainer's time with an unimportant report. If
|
||||
the issue is clearly trivial and publicly discoverable, you should report it
|
||||
directly to the public mailing lists.
|
||||
|
||||
Sending the report
|
||||
------------------
|
||||
|
||||
|
|
@ -148,7 +242,15 @@ run additional tests. Reports where the reporter does not respond promptly
|
|||
or cannot effectively discuss their findings may be abandoned if the
|
||||
communication does not quickly improve.
|
||||
|
||||
The report must be sent to maintainers, with the security team in ``Cc:``.
|
||||
The report must be sent to maintainers. If there are two or fewer
|
||||
recipients in your message, you must also always Cc: the Linux kernel
|
||||
security team who will ensure the message is delivered to the proper
|
||||
people, and will be able to assist small maintainer teams with processes
|
||||
they may not be familiar with. For larger teams, Cc: the Linux kernel
|
||||
security team for your first few reports or when seeking specific help,
|
||||
such as when resending a message which got no response within a week.
|
||||
Once you have become comfortable with the process for a few reports, it is
|
||||
no longer necessary to Cc: the security list when sending to large teams.
|
||||
The Linux kernel security team can be contacted by email at
|
||||
<security@kernel.org>. This is a private list of security officers
|
||||
who will help verify the bug report and assist developers working on a fix.
|
||||
|
|
|
|||
235
Documentation/process/threat-model.rst
Normal file
235
Documentation/process/threat-model.rst
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
The Linux Kernel threat model
|
||||
=============================
|
||||
|
||||
There are a lot of assumptions regarding what the kernel does and does not
|
||||
protect against. These assumptions tend to cause confusion for bug reports
|
||||
(:doc:`security-related ones <security-bugs>` vs :doc:`non-security ones
|
||||
<../admin-guide/reporting-issues>`), and can complicate security enforcement
|
||||
when the responsibilities for some boundaries is not clear between the kernel,
|
||||
distros, administrators and users.
|
||||
|
||||
This document tries to clarify the responsibilities of the kernel in this
|
||||
domain.
|
||||
|
||||
The kernel's responsibilities
|
||||
-----------------------------
|
||||
|
||||
The kernel abstracts access to local hardware resources and to remote systems
|
||||
in a way that allows multiple local users to get a fair share of the available
|
||||
resources granted to them, and, when the underlying hardware permits, to assign
|
||||
a level of confidentiality to their communications and to the data they are
|
||||
processing or storing.
|
||||
|
||||
The kernel assumes that the underlying hardware behaves according to its
|
||||
specifications. This includes the integrity of the CPU's instruction set, the
|
||||
transparency of the branch prediction unit and the cache units, the consistency
|
||||
of the Memory Management Unit (MMU), the isolation of DMA-capable peripherals
|
||||
(e.g., via IOMMU), state transitions in controllers, ranges of values read from
|
||||
registers, the respect of documented hardware limitations, etc.
|
||||
|
||||
When hardware fails to maintain its specified isolation (e.g., CPU bugs,
|
||||
side-channels, hardware response to unexpected inputs), the kernel will usually
|
||||
attempt to implement reasonable mitigations. These are best-effort measures
|
||||
intended to reduce the attack surface or elevate the cost of an attack within
|
||||
the limits of the hardware's facilities; they do not constitute a
|
||||
kernel-provided safety guarantee.
|
||||
|
||||
Users always perform their activities under the authority of an administrator
|
||||
who is able to grant or deny various types of permissions that may affect how
|
||||
users benefit from available resources, or the level of confidentiality of
|
||||
their activities. Administrators may also delegate all or part of their own
|
||||
permissions to some users, particularly via capabilities but not only. All this
|
||||
is performed via configuration (sysctl, file-system permissions etc).
|
||||
|
||||
The Linux Kernel applies a certain collection of default settings that match
|
||||
its threat model. Distros have their own threat model and will come with their
|
||||
own configuration presets, that the administrator may have to adjust to better
|
||||
suit their expectations (relax or restrict).
|
||||
|
||||
By default, the Linux Kernel guarantees the following protections when running
|
||||
on common processors featuring privilege levels and memory management units:
|
||||
|
||||
* **User-based isolation**: an unprivileged user may restrict access to their
|
||||
own data from other unprivileged users running on the same system. This
|
||||
includes:
|
||||
|
||||
* stored data, via file system permissions
|
||||
* in-memory data (pages are not accessible by default to other users)
|
||||
* process activity (ptrace is not permitted to other users)
|
||||
* inter-process communication (other users may not observe data exchanged via
|
||||
UNIX domain sockets or other IPC mechanisms).
|
||||
* network communications within the same or with other systems
|
||||
|
||||
* **Capability-based protection**:
|
||||
|
||||
* users not having elevated capabilities (including but not limited to
|
||||
CAP_SYS_ADMIN) may not alter the
|
||||
kernel's configuration, memory nor state, change other users' view of the
|
||||
file system layout, grant any user capabilities they do not have, nor
|
||||
affect the system's availability (shutdown, reboot, panic, hang, or making
|
||||
the system unresponsive via unbounded resource exhaustion).
|
||||
* users not having the ``CAP_NET_ADMIN`` capability may not alter the network
|
||||
configuration, intercept nor spoof network communications from other users
|
||||
nor systems.
|
||||
* users not having ``CAP_SYS_PTRACE`` may not observe other users' processes
|
||||
activities.
|
||||
|
||||
When ``CONFIG_USER_NS`` is set, the kernel also permits unprivileged users to
|
||||
create their own user namespace in which they have all capabilities, but with a
|
||||
number of restrictions (they may not perform actions that have impacts on the
|
||||
initial user namespace, such as changing time, loading modules or mounting
|
||||
block devices). Please refer to ``user_namespaces(7)`` for more details, the
|
||||
possibilities of user namespaces are not covered in this document.
|
||||
|
||||
The kernel also offers a lot of troubleshooting and debugging facilities, which
|
||||
can constitute attack vectors when placed in wrong hands. While some of them
|
||||
are designed to be accessible to regular local users with a low risk (e.g.
|
||||
kernel logs via ``/proc/kmsg``), some would expose enough information to
|
||||
represent a risk in most places and the decision to expose them is under the
|
||||
administrator's responsibility (perf events, traces), and others are not
|
||||
designed to be accessed by non-privileged users (e.g. debugfs). Access to these
|
||||
facilities by a user who has been explicitly granted permission by an
|
||||
administrator does not constitute a security breach.
|
||||
|
||||
Bugs that permit to violate the principles above constitute security breaches.
|
||||
However, bugs that permit one violation only once another one was already
|
||||
achieved are only weaknesses. The kernel applies a number of self-protection
|
||||
measures whose purpose is to avoid crossing a security boundary when certain
|
||||
classes of bugs are found, but a failure of these extra protections do not
|
||||
constitute a vulnerability alone.
|
||||
|
||||
What does not constitute a security bug
|
||||
---------------------------------------
|
||||
|
||||
In the Linux kernel's threat model, the following classes of problems are
|
||||
**NOT** considered as Linux Kernel security bugs. However, when it is believed
|
||||
that the kernel could do better, they should be reported, so that they can be
|
||||
reviewed and fixed where reasonably possible, but they will be handled as any
|
||||
regular bug:
|
||||
|
||||
* **Configuration**:
|
||||
|
||||
* outdated kernels and particularly end-of-life branches are out of the scope
|
||||
of the kernel's threat model: administrators are responsible for keeping
|
||||
their system up to date. For a bug to qualify as a security bug, it must be
|
||||
demonstrated that it affects actively maintained versions.
|
||||
|
||||
* build-level: changes to the kernel configuration that are explicitly
|
||||
documented as lowering the security level (e.g. ``CONFIG_NOMMU``), or
|
||||
targeted at developers only.
|
||||
|
||||
* OS-level: changes to command line parameters, sysctls, filesystem
|
||||
permissions, user capabilities, exposure of privileged interfaces, that
|
||||
explicitly increase exposure by either offering non-default access to
|
||||
unprivileged users, or reduce the kernel's ability to enforce some
|
||||
protections or mitigations. Example: write access to procfs or debugfs.
|
||||
|
||||
* issues triggered only when using features intended for development or
|
||||
debugging (e.g., LOCKDEP, KASAN, FAULT_INJECTION): these features are known
|
||||
to introduce overhead and potential instability and are not intended for
|
||||
production use.
|
||||
|
||||
* issues affecting drivers exposed under CONFIG_STAGING, as well as features
|
||||
marked EXPERIMENTAL in the configuration.
|
||||
|
||||
* loading of explicitly insecure/broken/staging modules, and generally any
|
||||
using any subsystem marked as experimental or not intended for production
|
||||
use.
|
||||
|
||||
* running out-of-tree modules or unofficial kernel forks; these should be
|
||||
reported to the relevant vendor.
|
||||
|
||||
* **Excess of initial privileges**:
|
||||
|
||||
* actions performed by a user already possessing the privileges required to
|
||||
perform that action or modify that state (e.g. ``CAP_SYS_ADMIN``,
|
||||
``CAP_NET_ADMIN``, ``CAP_SYS_RAWIO``, ``CAP_SYS_MODULE`` with no further
|
||||
boundary being crossed).
|
||||
|
||||
* actions performed in user namespace that do not bypass the restrictions
|
||||
imposed to the initial user (e.g. ptrace usage, signal delivery, resource
|
||||
usage, access to FS/device/sysctl/memory, network binding, system/network
|
||||
configuration etc).
|
||||
|
||||
* anything performed by the root user in the initial namespace (e.g. kernel
|
||||
oops when writing to a privileged device).
|
||||
|
||||
* **Out of production use**:
|
||||
|
||||
This covers theoretical/probabilistic attacks that rely on laboratory
|
||||
conditions with zero system noise, or those requiring an unrealistic number
|
||||
of attempts (e.g., billions of trials) that would be detected by standard
|
||||
system monitoring long before success, such as:
|
||||
|
||||
* prediction of random numbers that only works in a totally silent
|
||||
environment (such as IP ID, TCP ports or sequence numbers that can only be
|
||||
guessed in a lab).
|
||||
|
||||
* activity observation and information leaks based on probabilistic
|
||||
approaches that are prone to measurement noise and not realistically
|
||||
reproducible on a production system.
|
||||
|
||||
* issues that can only be triggered by heavy attacks (e.g. brute force) whose
|
||||
impact on the system makes it unlikely or impossible to remain undetected
|
||||
before they succeed (e.g. consuming all memory before succeeding).
|
||||
|
||||
* problems seen only under development simulators, emulators, or combinations
|
||||
that do not exist on real systems at the time of reporting (issues
|
||||
involving tens of millions of threads, tens of thousands of CPUs,
|
||||
unrealistic CPU frequencies, RAM sizes or disk capacities, network speeds.
|
||||
|
||||
* issues whose reproduction requires hardware modification or emulation,
|
||||
including fake USB devices that pretend to be another one.
|
||||
|
||||
* as well as issues that can be triggered at a cost that is orders of
|
||||
magnitude higher than the expected benefits (e.g. fully functional keyboard
|
||||
emulator only to retrieve 7 uninitialized bytes in a structure, or
|
||||
brute-force method involving millions of connection attempts to guess a
|
||||
port number).
|
||||
|
||||
* **Hardening failures**:
|
||||
|
||||
* ability to bypass some of the kernel's hardening measures with no
|
||||
demonstrable exploit path (e.g. ASLR bypass, events timing or probing with
|
||||
no demonstrable consequence). These are just weaknesses, not
|
||||
vulnerabilities.
|
||||
|
||||
* missing argument checks and failure to report certain errors with no
|
||||
immediate consequence.
|
||||
|
||||
* **Random information leaks**:
|
||||
|
||||
This concerns information leaks of small data parts that happen to be there
|
||||
and that cannot be chosen by the attacker, or face access restrictions:
|
||||
|
||||
* structure padding reported by syscalls or other interfaces.
|
||||
|
||||
* identifiers, partial data, non-terminated strings reported in error
|
||||
messages.
|
||||
|
||||
* Leaks of kernel memory addresses/pointers do not constitute an immediately
|
||||
exploitable vector and are not security bugs, though they must be reported
|
||||
and fixed.
|
||||
|
||||
* **Crafted file system images**:
|
||||
|
||||
* bugs triggered by mounting a corrupted or maliciously crafted file system
|
||||
image are generally not security bugs, as the kernel assumes the underlying
|
||||
storage media is under the administrator's control, unless the filesystem
|
||||
driver is specifically documented as being hardened against untrusted media.
|
||||
|
||||
* issues that are resolved, mitigated, or detected by running a filesystem
|
||||
consistency check (fsck) on the image prior to mounting.
|
||||
|
||||
* **Physical access**:
|
||||
|
||||
Issues that require physical access to the machine, hardware modification, or
|
||||
the use of specialized hardware (e.g., logic analyzers, DMA-attack tools over
|
||||
PCI-E/Thunderbolt) are out of scope unless the system is explicitly
|
||||
configured with technologies meant to defend against such attacks
|
||||
(e.g. IOMMU).
|
||||
|
||||
* **Functional and performance regressions**:
|
||||
|
||||
Any issue that can be mitigated by setting proper permissions and limits
|
||||
doesn't qualify as a security bug.
|
||||
|
|
@ -40,7 +40,7 @@ There are two drivers in the kernel
|
|||
|
||||
*For systems using SoundWire*: sound/soc/codecs/cs35l56.c and associated files
|
||||
|
||||
*For systems using HDA*: sound/pci/hda/cs35l56_hda.c
|
||||
*For systems using HDA*: sound/hda/codecs/side-codecs/cs35l56_hda.c
|
||||
|
||||
Firmware
|
||||
========
|
||||
|
|
|
|||
|
|
@ -656,8 +656,8 @@ References
|
|||
See [white-paper]_, [api-spec]_, [amd-apm]_, [kvm-forum]_, and [snp-fw-abi]_
|
||||
for more info.
|
||||
|
||||
.. [white-paper] https://developer.amd.com/wordpress/media/2013/12/AMD_Memory_Encryption_Whitepaper_v7-Public.pdf
|
||||
.. [api-spec] https://support.amd.com/TechDocs/55766_SEV-KM_API_Specification.pdf
|
||||
.. [amd-apm] https://support.amd.com/TechDocs/24593.pdf (section 15.34)
|
||||
.. [white-paper] https://docs.amd.com/v/u/en-US/memory-encryption-white-paper
|
||||
.. [api-spec] https://docs.amd.com/v/u/en-US/55766_PUB_3.24_SEV_API
|
||||
.. [amd-apm] https://docs.amd.com/v/u/en-US/24593_3.44_APM_Vol2 (section 15.34)
|
||||
.. [kvm-forum] https://www.linux-kvm.org/images/7/74/02x08A-Thomas_Lendacky-AMDs_Virtualizatoin_Memory_Encryption_Technology.pdf
|
||||
.. [snp-fw-abi] https://www.amd.com/system/files/TechDocs/56860.pdf
|
||||
.. [snp-fw-abi] https://www.amd.com/content/dam/amd/en/documents/developer/56860.pdf
|
||||
|
|
|
|||
24
MAINTAINERS
24
MAINTAINERS
|
|
@ -68,6 +68,12 @@ Maintainers List
|
|||
first. When adding to this list, please keep the entries in
|
||||
alphabetical order.
|
||||
|
||||
3C509 NETWORK DRIVER
|
||||
M: "Maciej W. Rozycki" <macro@orcam.me.uk>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/net/ethernet/3com/3c509.c
|
||||
|
||||
3C59X NETWORK DRIVER
|
||||
M: Steffen Klassert <klassert@kernel.org>
|
||||
L: netdev@vger.kernel.org
|
||||
|
|
@ -2015,7 +2021,7 @@ F: Documentation/hwmon/aquacomputer_d5next.rst
|
|||
F: drivers/hwmon/aquacomputer_d5next.c
|
||||
|
||||
AQUANTIA ETHERNET DRIVER (atlantic)
|
||||
M: Igor Russkikh <irusskikh@marvell.com>
|
||||
M: Sukhdeep Singh <sukhdeeps@marvell.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
W: https://www.marvell.com/
|
||||
|
|
@ -2024,7 +2030,7 @@ F: Documentation/networking/device_drivers/ethernet/aquantia/atlantic.rst
|
|||
F: drivers/net/ethernet/aquantia/atlantic/
|
||||
|
||||
AQUANTIA ETHERNET DRIVER PTP SUBSYSTEM
|
||||
M: Egor Pomozov <epomozov@marvell.com>
|
||||
M: Sukhdeep Singh <sukhdeeps@marvell.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
W: http://www.aquantia.com
|
||||
|
|
@ -4181,8 +4187,8 @@ F: include/uapi/linux/sonet.h
|
|||
F: net/atm/
|
||||
|
||||
ATMEL MACB ETHERNET DRIVER
|
||||
M: Nicolas Ferre <nicolas.ferre@microchip.com>
|
||||
M: Claudiu Beznea <claudiu.beznea@tuxon.dev>
|
||||
M: Théo Lebrun <theo.lebrun@bootlin.com>
|
||||
R: Conor Dooley <conor.dooley@microchip.com>
|
||||
S: Maintained
|
||||
F: drivers/net/ethernet/cadence/
|
||||
|
||||
|
|
@ -14055,6 +14061,7 @@ KERNEL VIRTUAL MACHINE FOR ARM64 (KVM/arm64)
|
|||
M: Marc Zyngier <maz@kernel.org>
|
||||
M: Oliver Upton <oupton@kernel.org>
|
||||
R: Joey Gouly <joey.gouly@arm.com>
|
||||
R: Steffen Eiden <seiden@linux.ibm.com>
|
||||
R: Suzuki K Poulose <suzuki.poulose@arm.com>
|
||||
R: Zenghui Yu <yuzenghui@huawei.com>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
|
|
@ -22943,7 +22950,7 @@ N: riscv
|
|||
K: riscv
|
||||
|
||||
RISC-V IOMMU
|
||||
M: Tomasz Jeznach <tjeznach@rivosinc.com>
|
||||
M: Tomasz Jeznach <tomasz.jeznach@linux.dev>
|
||||
L: iommu@lists.linux.dev
|
||||
L: linux-riscv@lists.infradead.org
|
||||
S: Maintained
|
||||
|
|
@ -25024,6 +25031,13 @@ F: include/sound/dmaengine_pcm.h
|
|||
F: sound/core/pcm_dmaengine.c
|
||||
F: sound/soc/soc-generic-dmaengine-pcm.c
|
||||
|
||||
SOUND - SOC LAYER / GPIO AUDIO AMPLIFIER
|
||||
M: Herve Codina <herve.codina@bootlin.com>
|
||||
L: linux-sound@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/sound/gpio-audio-amp.yaml
|
||||
F: sound/soc/codecs/simple-amplifier.c
|
||||
|
||||
SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC)
|
||||
M: Liam Girdwood <lgirdwood@gmail.com>
|
||||
M: Mark Brown <broonie@kernel.org>
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -2,7 +2,7 @@
|
|||
VERSION = 7
|
||||
PATCHLEVEL = 1
|
||||
SUBLEVEL = 0
|
||||
EXTRAVERSION = -rc3
|
||||
EXTRAVERSION = -rc4
|
||||
NAME = Baby Opossum Posse
|
||||
|
||||
# *DOCUMENTATION*
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ static inline u64 tcr_el2_ps_to_tcr_el1_ips(u64 tcr_el2)
|
|||
static inline u64 translate_tcr_el2_to_tcr_el1(u64 tcr)
|
||||
{
|
||||
return TCR_EPD1_MASK | /* disable TTBR1_EL1 */
|
||||
((tcr & TCR_EL2_DS) ? TCR_DS : 0) |
|
||||
((tcr & TCR_EL2_TBI) ? TCR_TBI0 : 0) |
|
||||
tcr_el2_ps_to_tcr_el1_ips(tcr) |
|
||||
(tcr & TCR_EL2_TG0_MASK) |
|
||||
|
|
|
|||
|
|
@ -844,7 +844,7 @@
|
|||
#define INIT_SCTLR_EL2_MMU_ON \
|
||||
(SCTLR_ELx_M | SCTLR_ELx_C | SCTLR_ELx_SA | SCTLR_ELx_I | \
|
||||
SCTLR_ELx_IESB | SCTLR_ELx_WXN | ENDIAN_SET_EL2 | \
|
||||
SCTLR_ELx_ITFSB | SCTLR_EL2_RES1)
|
||||
SCTLR_ELx_ITFSB | SCTLR_ELx_EIS | SCTLR_ELx_EOS | SCTLR_EL2_RES1)
|
||||
|
||||
#define INIT_SCTLR_EL2_MMU_OFF \
|
||||
(SCTLR_EL2_RES1 | ENDIAN_SET_EL2)
|
||||
|
|
|
|||
|
|
@ -62,6 +62,13 @@ static void noinstr arm64_exit_to_kernel_mode(struct pt_regs *regs,
|
|||
irqentry_exit_to_kernel_mode_after_preempt(regs, state);
|
||||
}
|
||||
|
||||
static __always_inline void arm64_syscall_enter_from_user_mode(struct pt_regs *regs)
|
||||
{
|
||||
enter_from_user_mode(regs);
|
||||
mte_disable_tco_entry(current);
|
||||
sme_enter_from_user_mode();
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle IRQ/context state management when entering from user mode.
|
||||
* Before this function is called it is not safe to call regular kernel code,
|
||||
|
|
@ -70,20 +77,30 @@ static void noinstr arm64_exit_to_kernel_mode(struct pt_regs *regs,
|
|||
static __always_inline void arm64_enter_from_user_mode(struct pt_regs *regs)
|
||||
{
|
||||
enter_from_user_mode(regs);
|
||||
rseq_note_user_irq_entry();
|
||||
mte_disable_tco_entry(current);
|
||||
sme_enter_from_user_mode();
|
||||
}
|
||||
|
||||
static __always_inline void arm64_syscall_exit_to_user_mode(struct pt_regs *regs)
|
||||
{
|
||||
local_irq_disable();
|
||||
syscall_exit_to_user_mode_prepare(regs);
|
||||
local_daif_mask();
|
||||
sme_exit_to_user_mode();
|
||||
mte_check_tfsr_exit();
|
||||
exit_to_user_mode();
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle IRQ/context state management when exiting to user mode.
|
||||
* After this function returns it is not safe to call regular kernel code,
|
||||
* instrumentable code, or any code which may trigger an exception.
|
||||
*/
|
||||
|
||||
static __always_inline void arm64_exit_to_user_mode(struct pt_regs *regs)
|
||||
{
|
||||
local_irq_disable();
|
||||
exit_to_user_mode_prepare_legacy(regs);
|
||||
irqentry_exit_to_user_mode_prepare(regs);
|
||||
local_daif_mask();
|
||||
sme_exit_to_user_mode();
|
||||
mte_check_tfsr_exit();
|
||||
|
|
@ -92,7 +109,7 @@ static __always_inline void arm64_exit_to_user_mode(struct pt_regs *regs)
|
|||
|
||||
asmlinkage void noinstr asm_exit_to_user_mode(struct pt_regs *regs)
|
||||
{
|
||||
arm64_exit_to_user_mode(regs);
|
||||
arm64_syscall_exit_to_user_mode(regs);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -716,12 +733,12 @@ static void noinstr el0_brk64(struct pt_regs *regs, unsigned long esr)
|
|||
|
||||
static void noinstr el0_svc(struct pt_regs *regs)
|
||||
{
|
||||
arm64_enter_from_user_mode(regs);
|
||||
arm64_syscall_enter_from_user_mode(regs);
|
||||
cortex_a76_erratum_1463225_svc_handler();
|
||||
fpsimd_syscall_enter();
|
||||
local_daif_restore(DAIF_PROCCTX);
|
||||
do_el0_svc(regs);
|
||||
arm64_exit_to_user_mode(regs);
|
||||
arm64_syscall_exit_to_user_mode(regs);
|
||||
fpsimd_syscall_exit();
|
||||
}
|
||||
|
||||
|
|
@ -868,11 +885,11 @@ static void noinstr el0_cp15(struct pt_regs *regs, unsigned long esr)
|
|||
|
||||
static void noinstr el0_svc_compat(struct pt_regs *regs)
|
||||
{
|
||||
arm64_enter_from_user_mode(regs);
|
||||
arm64_syscall_enter_from_user_mode(regs);
|
||||
cortex_a76_erratum_1463225_svc_handler();
|
||||
local_daif_restore(DAIF_PROCCTX);
|
||||
do_el0_svc_compat(regs);
|
||||
arm64_exit_to_user_mode(regs);
|
||||
arm64_syscall_exit_to_user_mode(regs);
|
||||
}
|
||||
|
||||
static void noinstr el0_bkpt32(struct pt_regs *regs, unsigned long esr)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
* Author: Christoffer Dall <c.dall@virtualopensystems.com>
|
||||
*/
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/errno.h>
|
||||
|
|
@ -2638,6 +2639,22 @@ static int init_pkvm_host_sve_state(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pkvm_check_sme_dvmsync_fw_call(void)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
|
||||
if (!cpus_have_final_cap(ARM64_WORKAROUND_4193714))
|
||||
return 0;
|
||||
|
||||
arm_smccc_1_1_smc(ARM_SMCCC_CPU_WORKAROUND_4193714, &res);
|
||||
if (res.a0) {
|
||||
kvm_err("pKVM requires firmware support for C1-Pro erratum 4193714\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finalizes the initialization of hyp mode, once everything else is initialized
|
||||
* and the initialziation process cannot fail.
|
||||
|
|
@ -2838,6 +2855,10 @@ static int __init init_hyp_mode(void)
|
|||
if (err)
|
||||
goto out_err;
|
||||
|
||||
err = pkvm_check_sme_dvmsync_fw_call();
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
err = kvm_hyp_init_protection(hyp_va_bits);
|
||||
if (err) {
|
||||
kvm_err("Failed to init hyp memory protection\n");
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ static inline void __activate_traps_ich_hfgxtr(struct kvm_vcpu *vcpu)
|
|||
__activate_fgt(hctxt, vcpu, ICH_HFGITR_EL2);
|
||||
}
|
||||
|
||||
#define __deactivate_fgt(htcxt, vcpu, reg) \
|
||||
#define __deactivate_fgt(hctxt, vcpu, reg) \
|
||||
do { \
|
||||
write_sysreg_s(ctxt_sys_reg(hctxt, reg), \
|
||||
SYS_ ## reg); \
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@ void trace_clock_update(u32 mult, u32 shift, u64 epoch_ns, u64 epoch_cyc)
|
|||
struct clock_data *clock = &trace_clock_data;
|
||||
u64 bank = clock->cur ^ 1;
|
||||
|
||||
if (!mult || shift >= 64)
|
||||
return;
|
||||
|
||||
clock->data[bank].mult = mult;
|
||||
clock->data[bank].shift = shift;
|
||||
clock->data[bank].epoch_ns = epoch_ns;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/kvm_host.h>
|
||||
|
||||
#include <asm/kvm_emulate.h>
|
||||
#include <asm/kvm_hyp.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
|
|
@ -14,6 +15,7 @@
|
|||
|
||||
#include <hyp/fault.h>
|
||||
|
||||
#include <nvhe/arm-smccc.h>
|
||||
#include <nvhe/gfp.h>
|
||||
#include <nvhe/memory.h>
|
||||
#include <nvhe/mem_protect.h>
|
||||
|
|
@ -29,6 +31,19 @@ static struct hyp_pool host_s2_pool;
|
|||
static DEFINE_PER_CPU(struct pkvm_hyp_vm *, __current_vm);
|
||||
#define current_vm (*this_cpu_ptr(&__current_vm))
|
||||
|
||||
static void pkvm_sme_dvmsync_fw_call(void)
|
||||
{
|
||||
if (alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714)) {
|
||||
struct arm_smccc_res res;
|
||||
|
||||
/*
|
||||
* Ignore the return value. Probing for the workaround
|
||||
* availability took place in init_hyp_mode().
|
||||
*/
|
||||
hyp_smccc_1_1_smc(ARM_SMCCC_CPU_WORKAROUND_4193714, &res);
|
||||
}
|
||||
}
|
||||
|
||||
static void guest_lock_component(struct pkvm_hyp_vm *vm)
|
||||
{
|
||||
hyp_spin_lock(&vm->lock);
|
||||
|
|
@ -574,8 +589,14 @@ static int host_stage2_set_owner_metadata_locked(phys_addr_t addr, u64 size,
|
|||
ret = host_stage2_try(kvm_pgtable_stage2_annotate, &host_mmu.pgt,
|
||||
addr, size, &host_s2_pool,
|
||||
KVM_HOST_INVALID_PTE_TYPE_DONATION, annotation);
|
||||
if (!ret)
|
||||
if (!ret) {
|
||||
/*
|
||||
* After stage2 maintenance has happened, but before the page
|
||||
* owner has changed.
|
||||
*/
|
||||
pkvm_sme_dvmsync_fw_call();
|
||||
__host_update_page_state(addr, size, PKVM_NOPAGE);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1369,6 +1390,22 @@ int __pkvm_host_reclaim_page_guest(u64 gfn, struct pkvm_hyp_vm *vm)
|
|||
return ret && ret != -EHWPOISON ? ret : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* share/donate install at most one stage-2 leaf (PAGE_SIZE, or one
|
||||
* KVM_PGTABLE_LAST_LEVEL - 1 block for share). kvm_mmu_cache_min_pages()
|
||||
* bounds the worst-case allocation: exact for the PAGE_SIZE leaf,
|
||||
* conservative by one for the block.
|
||||
*/
|
||||
static int __guest_check_pgtable_memcache(struct pkvm_hyp_vcpu *vcpu)
|
||||
{
|
||||
struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu);
|
||||
|
||||
if (vcpu->vcpu.arch.pkvm_memcache.nr_pages < kvm_mmu_cache_min_pages(vm->pgt.mmu))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __pkvm_host_donate_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vcpu *vcpu)
|
||||
{
|
||||
struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu);
|
||||
|
|
@ -1388,6 +1425,10 @@ int __pkvm_host_donate_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vcpu *vcpu)
|
|||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = __guest_check_pgtable_memcache(vcpu);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
meta = host_stage2_encode_gfn_meta(vm, gfn);
|
||||
WARN_ON(host_stage2_set_owner_metadata_locked(phys, PAGE_SIZE,
|
||||
PKVM_ID_GUEST, meta));
|
||||
|
|
@ -1453,6 +1494,10 @@ int __pkvm_host_share_guest(u64 pfn, u64 gfn, u64 nr_pages, struct pkvm_hyp_vcpu
|
|||
}
|
||||
}
|
||||
|
||||
ret = __guest_check_pgtable_memcache(vcpu);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
for_each_hyp_page(page, phys, size) {
|
||||
set_host_state(page, PKVM_PAGE_SHARED_OWNED);
|
||||
page->host_share_guest_count++;
|
||||
|
|
|
|||
|
|
@ -752,16 +752,30 @@ static struct pkvm_hyp_vcpu selftest_vcpu = {
|
|||
struct pkvm_hyp_vcpu *init_selftest_vm(void *virt)
|
||||
{
|
||||
struct hyp_page *p = hyp_virt_to_page(virt);
|
||||
unsigned long min_pages, seeded = 0;
|
||||
int i;
|
||||
|
||||
selftest_vm.kvm.arch.mmu.vtcr = host_mmu.arch.mmu.vtcr;
|
||||
WARN_ON(kvm_guest_prepare_stage2(&selftest_vm, virt));
|
||||
|
||||
/*
|
||||
* Mirror pkvm_refill_memcache() for the share/donate pre-checks;
|
||||
* the selftest invokes those functions directly and would
|
||||
* otherwise see an empty memcache.
|
||||
*/
|
||||
min_pages = kvm_mmu_cache_min_pages(&selftest_vm.kvm.arch.mmu);
|
||||
|
||||
for (i = 0; i < pkvm_selftest_pages(); i++) {
|
||||
if (p[i].refcount)
|
||||
continue;
|
||||
p[i].refcount = 1;
|
||||
hyp_put_page(&selftest_vm.pool, hyp_page_to_virt(&p[i]));
|
||||
if (seeded < min_pages) {
|
||||
push_hyp_memcache(&selftest_vcpu.vcpu.arch.pkvm_memcache,
|
||||
hyp_page_to_virt(&p[i]), hyp_virt_to_phys);
|
||||
seeded++;
|
||||
} else {
|
||||
hyp_put_page(&selftest_vm.pool, hyp_page_to_virt(&p[i]));
|
||||
}
|
||||
}
|
||||
|
||||
selftest_vm.kvm.arch.pkvm.handle = __pkvm_reserve_vm();
|
||||
|
|
|
|||
|
|
@ -663,7 +663,8 @@ static void __noreturn __hyp_call_panic(u64 spsr, u64 elr, u64 par)
|
|||
host_ctxt = host_data_ptr(host_ctxt);
|
||||
vcpu = host_ctxt->__hyp_running_vcpu;
|
||||
|
||||
__deactivate_traps(vcpu);
|
||||
if (vcpu)
|
||||
__deactivate_traps(vcpu);
|
||||
sysreg_restore_host_state_vhe(host_ctxt);
|
||||
|
||||
panic("HYP panic:\nPS:%08llx PC:%016llx ESR:%08llx\nFAR:%016llx HPFAR:%016llx PAR:%016llx\nVCPU:%p\n",
|
||||
|
|
|
|||
|
|
@ -1576,21 +1576,24 @@ struct kvm_s2_fault_desc {
|
|||
static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
|
||||
{
|
||||
bool write_fault, exec_fault;
|
||||
bool perm_fault = kvm_vcpu_trap_is_permission_fault(s2fd->vcpu);
|
||||
enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_SHARED;
|
||||
enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R;
|
||||
struct kvm_pgtable *pgt = s2fd->vcpu->arch.hw_mmu->pgt;
|
||||
unsigned long mmu_seq;
|
||||
struct page *page;
|
||||
struct kvm *kvm = s2fd->vcpu->kvm;
|
||||
void *memcache;
|
||||
void *memcache = NULL;
|
||||
kvm_pfn_t pfn;
|
||||
gfn_t gfn;
|
||||
int ret;
|
||||
|
||||
memcache = get_mmu_memcache(s2fd->vcpu);
|
||||
ret = topup_mmu_memcache(s2fd->vcpu, memcache);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!perm_fault) {
|
||||
memcache = get_mmu_memcache(s2fd->vcpu);
|
||||
ret = topup_mmu_memcache(s2fd->vcpu, memcache);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (s2fd->nested)
|
||||
gfn = kvm_s2_trans_output(s2fd->nested) >> PAGE_SHIFT;
|
||||
|
|
@ -1631,9 +1634,19 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, s2fd->fault_ipa, PAGE_SIZE,
|
||||
__pfn_to_phys(pfn), prot,
|
||||
memcache, flags);
|
||||
if (perm_fault) {
|
||||
/*
|
||||
* Drop the SW bits in favour of those stored in the
|
||||
* PTE, which will be preserved.
|
||||
*/
|
||||
prot &= ~KVM_NV_GUEST_MAP_SZ;
|
||||
ret = KVM_PGT_FN(kvm_pgtable_stage2_relax_perms)(pgt, s2fd->fault_ipa,
|
||||
prot, flags);
|
||||
} else {
|
||||
ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, s2fd->fault_ipa, PAGE_SIZE,
|
||||
__pfn_to_phys(pfn), prot,
|
||||
memcache, flags);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
kvm_release_faultin_page(kvm, page, !!ret, prot & KVM_PGTABLE_PROT_W);
|
||||
|
|
|
|||
|
|
@ -83,11 +83,10 @@ config MSI_BITMAP_SELFTEST
|
|||
depends on DEBUG_KERNEL
|
||||
|
||||
config GUEST_STATE_BUFFER_TEST
|
||||
def_tristate n
|
||||
def_tristate KUNIT_ALL_TESTS
|
||||
prompt "Enable Guest State Buffer unit tests"
|
||||
depends on KUNIT
|
||||
depends on KVM_BOOK3S_HV_POSSIBLE
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
The Guest State Buffer is a data format specified in the PAPR.
|
||||
It is by hcalls to communicate the state of L2 guests between
|
||||
|
|
|
|||
|
|
@ -85,6 +85,8 @@ CONFIG_PMAC_SMU=y
|
|||
CONFIG_MAC_EMUMOUSEBTN=y
|
||||
CONFIG_WINDFARM=y
|
||||
CONFIG_WINDFARM_PM81=y
|
||||
CONFIG_WINDFARM_PM72=y
|
||||
CONFIG_WINDFARM_RM31=y
|
||||
CONFIG_WINDFARM_PM91=y
|
||||
CONFIG_WINDFARM_PM112=y
|
||||
CONFIG_WINDFARM_PM121=y
|
||||
|
|
|
|||
|
|
@ -79,10 +79,6 @@ extern int pmac_i2c_match_adapter(struct device_node *dev,
|
|||
struct i2c_adapter *adapter);
|
||||
|
||||
|
||||
/* (legacy) Locking functions exposed to i2c-keywest */
|
||||
extern int pmac_low_i2c_lock(struct device_node *np);
|
||||
extern int pmac_low_i2c_unlock(struct device_node *np);
|
||||
|
||||
/* Access functions for platform code */
|
||||
extern int pmac_i2c_open(struct pmac_i2c_bus *bus, int polled);
|
||||
extern void pmac_i2c_close(struct pmac_i2c_bus *bus);
|
||||
|
|
|
|||
|
|
@ -458,6 +458,10 @@ DEFINE_PER_CPU(u8, irq_work_pending);
|
|||
|
||||
#endif /* 32 vs 64 bit */
|
||||
|
||||
/*
|
||||
* Must be called with preemption disabled since it updates
|
||||
* per-CPU irq_work state and programs the local CPU decrementer.
|
||||
*/
|
||||
void arch_irq_work_raise(void)
|
||||
{
|
||||
/*
|
||||
|
|
@ -471,10 +475,8 @@ void arch_irq_work_raise(void)
|
|||
* which could get tangled up if we're messing with the same state
|
||||
* here.
|
||||
*/
|
||||
preempt_disable();
|
||||
set_irq_work_pending_flag();
|
||||
set_dec(1);
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
static void set_dec_or_work(u64 val)
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ static ssize_t processor_bus_topology_show(struct device *dev, struct device_att
|
|||
0, 0, buf, &n, arg);
|
||||
|
||||
if (!ret)
|
||||
return n;
|
||||
goto out_success;
|
||||
|
||||
if (ret != H_PARAMETER)
|
||||
goto out;
|
||||
|
|
@ -244,12 +244,14 @@ static ssize_t processor_bus_topology_show(struct device *dev, struct device_att
|
|||
starting_index, 0, buf, &n, arg);
|
||||
|
||||
if (!ret)
|
||||
return n;
|
||||
goto out_success;
|
||||
|
||||
if (ret != H_PARAMETER)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out_success:
|
||||
put_cpu_var(hv_gpci_reqb);
|
||||
return n;
|
||||
|
||||
out:
|
||||
|
|
@ -278,7 +280,7 @@ static ssize_t processor_config_show(struct device *dev, struct device_attribute
|
|||
0, 0, buf, &n, arg);
|
||||
|
||||
if (!ret)
|
||||
return n;
|
||||
goto out_success;
|
||||
|
||||
if (ret != H_PARAMETER)
|
||||
goto out;
|
||||
|
|
@ -312,12 +314,14 @@ static ssize_t processor_config_show(struct device *dev, struct device_attribute
|
|||
starting_index, 0, buf, &n, arg);
|
||||
|
||||
if (!ret)
|
||||
return n;
|
||||
goto out_success;
|
||||
|
||||
if (ret != H_PARAMETER)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out_success:
|
||||
put_cpu_var(hv_gpci_reqb);
|
||||
return n;
|
||||
|
||||
out:
|
||||
|
|
@ -346,7 +350,7 @@ static ssize_t affinity_domain_via_virtual_processor_show(struct device *dev,
|
|||
0, 0, buf, &n, arg);
|
||||
|
||||
if (!ret)
|
||||
return n;
|
||||
goto out_success;
|
||||
|
||||
if (ret != H_PARAMETER)
|
||||
goto out;
|
||||
|
|
@ -382,12 +386,14 @@ static ssize_t affinity_domain_via_virtual_processor_show(struct device *dev,
|
|||
starting_index, secondary_index, buf, &n, arg);
|
||||
|
||||
if (!ret)
|
||||
return n;
|
||||
goto out_success;
|
||||
|
||||
if (ret != H_PARAMETER)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out_success:
|
||||
put_cpu_var(hv_gpci_reqb);
|
||||
return n;
|
||||
|
||||
out:
|
||||
|
|
@ -416,7 +422,7 @@ static ssize_t affinity_domain_via_domain_show(struct device *dev, struct device
|
|||
0, 0, buf, &n, arg);
|
||||
|
||||
if (!ret)
|
||||
return n;
|
||||
goto out_success;
|
||||
|
||||
if (ret != H_PARAMETER)
|
||||
goto out;
|
||||
|
|
@ -448,12 +454,14 @@ static ssize_t affinity_domain_via_domain_show(struct device *dev, struct device
|
|||
starting_index, 0, buf, &n, arg);
|
||||
|
||||
if (!ret)
|
||||
return n;
|
||||
goto out_success;
|
||||
|
||||
if (ret != H_PARAMETER)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out_success:
|
||||
put_cpu_var(hv_gpci_reqb);
|
||||
return n;
|
||||
|
||||
out:
|
||||
|
|
|
|||
|
|
@ -293,6 +293,8 @@ static int pika_dtm_thread(void __iomem *fpga)
|
|||
schedule_timeout(HZ);
|
||||
}
|
||||
|
||||
put_device(&client->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@
|
|||
|
||||
static void __init km82xx_pic_init(void)
|
||||
{
|
||||
struct device_node *np __free(device_node);
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,pq2-pic");
|
||||
struct device_node *np __free(device_node) = of_find_compatible_node(NULL,
|
||||
NULL, "fsl,pq2-pic");
|
||||
|
||||
if (!np) {
|
||||
pr_err("PIC init: can not find cpm-pic node\n");
|
||||
|
|
|
|||
|
|
@ -1058,40 +1058,6 @@ int pmac_i2c_match_adapter(struct device_node *dev, struct i2c_adapter *adapter)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(pmac_i2c_match_adapter);
|
||||
|
||||
int pmac_low_i2c_lock(struct device_node *np)
|
||||
{
|
||||
struct pmac_i2c_bus *bus, *found = NULL;
|
||||
|
||||
list_for_each_entry(bus, &pmac_i2c_busses, link) {
|
||||
if (np == bus->controller) {
|
||||
found = bus;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
return -ENODEV;
|
||||
return pmac_i2c_open(bus, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmac_low_i2c_lock);
|
||||
|
||||
int pmac_low_i2c_unlock(struct device_node *np)
|
||||
{
|
||||
struct pmac_i2c_bus *bus, *found = NULL;
|
||||
|
||||
list_for_each_entry(bus, &pmac_i2c_busses, link) {
|
||||
if (np == bus->controller) {
|
||||
found = bus;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
return -ENODEV;
|
||||
pmac_i2c_close(bus);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmac_low_i2c_unlock);
|
||||
|
||||
|
||||
int pmac_i2c_open(struct pmac_i2c_bus *bus, int polled)
|
||||
{
|
||||
int rc;
|
||||
|
|
|
|||
|
|
@ -937,6 +937,28 @@ config RISCV_VECTOR_MISALIGNED
|
|||
help
|
||||
Enable detecting support for vector misaligned loads and stores.
|
||||
|
||||
config RISCV_SBI_FWFT_DELEGATE_MISALIGNED
|
||||
bool "Request firmware delegation of unaligned access exceptions"
|
||||
depends on RISCV_SBI
|
||||
depends on NONPORTABLE
|
||||
help
|
||||
Use SBI FWFT to request delegation of load address misaligned and
|
||||
store address misaligned exceptions, if possible, and prefer Linux
|
||||
kernel emulation of these accesses to firmware emulation.
|
||||
|
||||
Unfortunately, Linux's emulation is still incomplete. Namely, it
|
||||
currently does not handle vector instructions and KVM guest accesses.
|
||||
On platforms where these accesses would have been handled by firmware,
|
||||
enabling this causes unexpected kernel oopses, userspaces crashes and
|
||||
KVM guest crashes. If you are sure that these are not a problem for
|
||||
your platform, you can say Y here, which may improve performance.
|
||||
|
||||
Saying N here will not worsen emulation support for unaligned accesses
|
||||
even in the case where the firmware also has incomplete support. It
|
||||
simply keeps the firmware's emulation enabled.
|
||||
|
||||
If you don't know what to do here, say N.
|
||||
|
||||
choice
|
||||
prompt "Unaligned Accesses Support"
|
||||
default RISCV_PROBE_UNALIGNED_ACCESS
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ void mips_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
|
|||
}
|
||||
|
||||
tmp = (1U << alt->patch_id);
|
||||
if (cpu_req_errata && tmp) {
|
||||
if (cpu_req_errata & tmp) {
|
||||
mutex_lock(&text_mutex);
|
||||
patch_text_nosync(ALT_OLD_PTR(alt), ALT_ALT_PTR(alt),
|
||||
alt->alt_len);
|
||||
|
|
|
|||
|
|
@ -107,6 +107,8 @@ static long compat_restore_sigcontext(struct pt_regs *regs,
|
|||
|
||||
/* sc_regs is structured the same as the start of pt_regs */
|
||||
err = __copy_from_user(&cregs, &sc->sc_regs, sizeof(sc->sc_regs));
|
||||
if (unlikely(err))
|
||||
return err;
|
||||
|
||||
cregs_to_regs(&cregs, regs);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (C) 2023 Rivos Inc. */
|
||||
|
||||
#include <linux/cfi_types.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/asm.h>
|
||||
|
||||
|
|
@ -9,7 +10,7 @@
|
|||
/* void __riscv_copy_words_unaligned(void *, const void *, size_t) */
|
||||
/* Performs a memcpy without aligning buffers, using word loads and stores. */
|
||||
/* Note: The size is truncated to a multiple of 8 * SZREG */
|
||||
SYM_FUNC_START(__riscv_copy_words_unaligned)
|
||||
SYM_TYPED_FUNC_START(__riscv_copy_words_unaligned)
|
||||
andi a4, a2, ~((8*SZREG)-1)
|
||||
beqz a4, 2f
|
||||
add a3, a1, a4
|
||||
|
|
@ -41,7 +42,7 @@ SYM_FUNC_END(__riscv_copy_words_unaligned)
|
|||
/* void __riscv_copy_bytes_unaligned(void *, const void *, size_t) */
|
||||
/* Performs a memcpy without aligning buffers, using only byte accesses. */
|
||||
/* Note: The size is truncated to a multiple of 8 */
|
||||
SYM_FUNC_START(__riscv_copy_bytes_unaligned)
|
||||
SYM_TYPED_FUNC_START(__riscv_copy_bytes_unaligned)
|
||||
andi a4, a2, ~(8-1)
|
||||
beqz a4, 2f
|
||||
add a3, a1, a4
|
||||
|
|
|
|||
|
|
@ -896,10 +896,8 @@ static void __init riscv_fill_hwcap_from_isa_string(unsigned long *isa2hwcap)
|
|||
* CPU cores with the ratified spec will contain non-zero
|
||||
* marchid.
|
||||
*/
|
||||
if (acpi_disabled && boot_vendorid == THEAD_VENDOR_ID && boot_archid == 0x0) {
|
||||
this_hwcap &= ~isa2hwcap[RISCV_ISA_EXT_v];
|
||||
if (acpi_disabled && boot_vendorid == THEAD_VENDOR_ID && boot_archid == 0x0)
|
||||
clear_bit(RISCV_ISA_EXT_v, source_isa);
|
||||
}
|
||||
|
||||
riscv_resolve_isa(source_isa, isainfo->isa, &this_hwcap, isa2hwcap);
|
||||
|
||||
|
|
@ -1104,16 +1102,16 @@ early_param("riscv_isa_fallback", riscv_isa_fallback_setup);
|
|||
void __init riscv_fill_hwcap(void)
|
||||
{
|
||||
char print_str[NUM_ALPHA_EXTS + 1];
|
||||
unsigned long isa2hwcap[26] = {0};
|
||||
unsigned long isa2hwcap[RISCV_ISA_EXT_BASE] = {0};
|
||||
int i, j;
|
||||
|
||||
isa2hwcap['i' - 'a'] = COMPAT_HWCAP_ISA_I;
|
||||
isa2hwcap['m' - 'a'] = COMPAT_HWCAP_ISA_M;
|
||||
isa2hwcap['a' - 'a'] = COMPAT_HWCAP_ISA_A;
|
||||
isa2hwcap['f' - 'a'] = COMPAT_HWCAP_ISA_F;
|
||||
isa2hwcap['d' - 'a'] = COMPAT_HWCAP_ISA_D;
|
||||
isa2hwcap['c' - 'a'] = COMPAT_HWCAP_ISA_C;
|
||||
isa2hwcap['v' - 'a'] = COMPAT_HWCAP_ISA_V;
|
||||
isa2hwcap[RISCV_ISA_EXT_i] = COMPAT_HWCAP_ISA_I;
|
||||
isa2hwcap[RISCV_ISA_EXT_m] = COMPAT_HWCAP_ISA_M;
|
||||
isa2hwcap[RISCV_ISA_EXT_a] = COMPAT_HWCAP_ISA_A;
|
||||
isa2hwcap[RISCV_ISA_EXT_f] = COMPAT_HWCAP_ISA_F;
|
||||
isa2hwcap[RISCV_ISA_EXT_d] = COMPAT_HWCAP_ISA_D;
|
||||
isa2hwcap[RISCV_ISA_EXT_c] = COMPAT_HWCAP_ISA_C;
|
||||
isa2hwcap[RISCV_ISA_EXT_v] = COMPAT_HWCAP_ISA_V;
|
||||
|
||||
if (!acpi_disabled) {
|
||||
riscv_fill_hwcap_from_isa_string(isa2hwcap);
|
||||
|
|
|
|||
|
|
@ -577,8 +577,8 @@ static int compat_riscv_gpr_set(struct task_struct *target,
|
|||
struct compat_user_regs_struct cregs;
|
||||
|
||||
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &cregs, 0, -1);
|
||||
|
||||
cregs_to_regs(&cregs, task_pt_regs(target));
|
||||
if (!ret)
|
||||
cregs_to_regs(&cregs, task_pt_regs(target));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -584,7 +584,7 @@ static int cpu_online_check_unaligned_access_emulated(unsigned int cpu)
|
|||
|
||||
static bool misaligned_traps_delegated;
|
||||
|
||||
#ifdef CONFIG_RISCV_SBI
|
||||
#if defined(CONFIG_RISCV_SBI_FWFT_DELEGATE_MISALIGNED)
|
||||
|
||||
static int cpu_online_sbi_unaligned_setup(unsigned int cpu)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -109,15 +109,16 @@ void set_indir_lp_lock(struct task_struct *task, bool lock)
|
|||
task->thread_info.user_cfi_state.ufcfi_locked = lock;
|
||||
}
|
||||
/*
|
||||
* If size is 0, then to be compatible with regular stack we want it to be as big as
|
||||
* regular stack. Else PAGE_ALIGN it and return back
|
||||
* The shadow stack only stores the return address and not any variables
|
||||
* this should be more than sufficient for most applications.
|
||||
* Else PAGE_ALIGN it and return back
|
||||
*/
|
||||
static unsigned long calc_shstk_size(unsigned long size)
|
||||
{
|
||||
if (size)
|
||||
return PAGE_ALIGN(size);
|
||||
|
||||
return PAGE_ALIGN(min_t(unsigned long long, rlimit(RLIMIT_STACK), SZ_4G));
|
||||
return PAGE_ALIGN(min(rlimit(RLIMIT_STACK) / 2, SZ_2G));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
/* Copyright (C) 2024 Rivos Inc. */
|
||||
|
||||
#include <linux/args.h>
|
||||
#include <linux/cfi_types.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/asm.h>
|
||||
|
||||
|
|
@ -16,7 +17,7 @@
|
|||
/* void __riscv_copy_vec_words_unaligned(void *, const void *, size_t) */
|
||||
/* Performs a memcpy without aligning buffers, using word loads and stores. */
|
||||
/* Note: The size is truncated to a multiple of WORD_EEW */
|
||||
SYM_FUNC_START(__riscv_copy_vec_words_unaligned)
|
||||
SYM_TYPED_FUNC_START(__riscv_copy_vec_words_unaligned)
|
||||
andi a4, a2, ~(WORD_EEW-1)
|
||||
beqz a4, 2f
|
||||
add a3, a1, a4
|
||||
|
|
@ -38,7 +39,7 @@ SYM_FUNC_END(__riscv_copy_vec_words_unaligned)
|
|||
/* void __riscv_copy_vec_bytes_unaligned(void *, const void *, size_t) */
|
||||
/* Performs a memcpy without aligning buffers, using only byte accesses. */
|
||||
/* Note: The size is truncated to a multiple of 8 */
|
||||
SYM_FUNC_START(__riscv_copy_vec_bytes_unaligned)
|
||||
SYM_TYPED_FUNC_START(__riscv_copy_vec_bytes_unaligned)
|
||||
andi a4, a2, ~(8-1)
|
||||
beqz a4, 2f
|
||||
add a3, a1, a4
|
||||
|
|
|
|||
|
|
@ -792,6 +792,27 @@ static void __init set_mmap_rnd_bits_max(void)
|
|||
mmap_rnd_bits_max = MMAP_VA_BITS - PAGE_SHIFT - 3;
|
||||
}
|
||||
|
||||
static bool __init is_vaddr_valid(unsigned long va)
|
||||
{
|
||||
unsigned long up = 0;
|
||||
|
||||
switch (satp_mode) {
|
||||
case SATP_MODE_39:
|
||||
up = 1UL << 38;
|
||||
break;
|
||||
case SATP_MODE_48:
|
||||
up = 1UL << 47;
|
||||
break;
|
||||
case SATP_MODE_57:
|
||||
up = 1UL << 56;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return (va < up) || (va >= (ULONG_MAX - up + 1));
|
||||
}
|
||||
|
||||
/*
|
||||
* There is a simple way to determine if 4-level is supported by the
|
||||
* underlying hardware: establish 1:1 mapping in 4-level page table mode
|
||||
|
|
@ -833,6 +854,9 @@ static __init void set_satp_mode(uintptr_t dtb_pa)
|
|||
set_satp_mode_pmd + PMD_SIZE,
|
||||
PMD_SIZE, PAGE_KERNEL_EXEC);
|
||||
retry:
|
||||
if (!is_vaddr_valid(set_satp_mode_pmd))
|
||||
goto out;
|
||||
|
||||
create_pgd_mapping(early_pg_dir,
|
||||
set_satp_mode_pmd,
|
||||
pgtable_l5_enabled ?
|
||||
|
|
@ -855,6 +879,7 @@ static __init void set_satp_mode(uintptr_t dtb_pa)
|
|||
disable_pgtable_l4();
|
||||
}
|
||||
|
||||
out:
|
||||
memset(early_pg_dir, 0, PAGE_SIZE);
|
||||
memset(early_p4d, 0, PAGE_SIZE);
|
||||
memset(early_pud, 0, PAGE_SIZE);
|
||||
|
|
|
|||
|
|
@ -3310,8 +3310,7 @@ static void aen_host_forward(unsigned long si)
|
|||
struct zpci_gaite *gaite;
|
||||
struct kvm *kvm;
|
||||
|
||||
gaite = (struct zpci_gaite *)aift->gait +
|
||||
(si * sizeof(struct zpci_gaite));
|
||||
gaite = aift->gait + si;
|
||||
if (gaite->count == 0)
|
||||
return;
|
||||
if (gaite->aisb != 0)
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ static int kvm_zpci_set_airq(struct zpci_dev *zdev)
|
|||
fib.fmt0.noi = airq_iv_end(zdev->aibv);
|
||||
fib.fmt0.aibv = virt_to_phys(zdev->aibv->vector);
|
||||
fib.fmt0.aibvo = 0;
|
||||
fib.fmt0.aisb = virt_to_phys(aift->sbv->vector + (zdev->aisb / 64) * 8);
|
||||
fib.fmt0.aisb = virt_to_phys(aift->sbv->vector) + (zdev->aisb / 64) * 8;
|
||||
fib.fmt0.aisbo = zdev->aisb & 63;
|
||||
fib.gd = zdev->gisa;
|
||||
|
||||
|
|
@ -290,8 +290,7 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
|
|||
phys_to_virt(fib->fmt0.aibv));
|
||||
|
||||
spin_lock_irq(&aift->gait_lock);
|
||||
gaite = (struct zpci_gaite *)aift->gait + (zdev->aisb *
|
||||
sizeof(struct zpci_gaite));
|
||||
gaite = aift->gait + zdev->aisb;
|
||||
|
||||
/* If assist not requested, host will get all alerts */
|
||||
if (assist)
|
||||
|
|
@ -309,7 +308,7 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
|
|||
|
||||
/* Update guest FIB for re-issue */
|
||||
fib->fmt0.aisbo = zdev->aisb & 63;
|
||||
fib->fmt0.aisb = virt_to_phys(aift->sbv->vector + (zdev->aisb / 64) * 8);
|
||||
fib->fmt0.aisb = virt_to_phys(aift->sbv->vector) + (zdev->aisb / 64) * 8;
|
||||
fib->fmt0.isc = gisc;
|
||||
|
||||
/* Save some guest fib values in the host for later use */
|
||||
|
|
@ -357,8 +356,7 @@ static int kvm_s390_pci_aif_disable(struct zpci_dev *zdev, bool force)
|
|||
if (zdev->kzdev->fib.fmt0.aibv == 0)
|
||||
goto out;
|
||||
spin_lock_irq(&aift->gait_lock);
|
||||
gaite = (struct zpci_gaite *)aift->gait + (zdev->aisb *
|
||||
sizeof(struct zpci_gaite));
|
||||
gaite = aift->gait + zdev->aisb;
|
||||
isc = gaite->gisc;
|
||||
gaite->count--;
|
||||
if (gaite->count == 0) {
|
||||
|
|
|
|||
|
|
@ -803,9 +803,10 @@
|
|||
#define MSR_AMD64_LBR_SELECT 0xc000010e
|
||||
|
||||
/* Zen4 */
|
||||
#define MSR_ZEN4_BP_CFG 0xc001102e
|
||||
#define MSR_ZEN4_BP_CFG 0xc001102e
|
||||
#define MSR_ZEN4_BP_CFG_BP_SPEC_REDUCE_BIT 4
|
||||
#define MSR_ZEN4_BP_CFG_SHARED_BTB_FIX_BIT 5
|
||||
#define MSR_ZEN2_BP_CFG_BUG_FIX_BIT 33
|
||||
|
||||
/* Fam 19h MSRs */
|
||||
#define MSR_F19H_UMC_PERF_CTL 0xc0010800
|
||||
|
|
|
|||
|
|
@ -88,19 +88,19 @@ static void amd_set_max_freq_ratio(void)
|
|||
|
||||
rc = cppc_get_perf_caps(0, &perf_caps);
|
||||
if (rc) {
|
||||
pr_warn("Could not retrieve perf counters (%d)\n", rc);
|
||||
pr_debug("Could not retrieve perf counters (%d)\n", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = amd_get_boost_ratio_numerator(0, &numerator);
|
||||
if (rc) {
|
||||
pr_warn("Could not retrieve highest performance (%d)\n", rc);
|
||||
pr_debug("Could not retrieve highest performance (%d)\n", rc);
|
||||
return;
|
||||
}
|
||||
nominal_perf = perf_caps.nominal_perf;
|
||||
|
||||
if (!nominal_perf) {
|
||||
pr_warn("Could not retrieve nominal performance\n");
|
||||
pr_debug("Could not retrieve nominal performance\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -989,6 +989,9 @@ static void init_amd_zen2(struct cpuinfo_x86 *c)
|
|||
|
||||
/* Correct misconfigured CPUID on some clients. */
|
||||
clear_cpu_cap(c, X86_FEATURE_INVLPGB);
|
||||
|
||||
if (!cpu_has(c, X86_FEATURE_HYPERVISOR))
|
||||
msr_set_bit(MSR_ZEN4_BP_CFG, MSR_ZEN2_BP_CFG_BUG_FIX_BIT);
|
||||
}
|
||||
|
||||
static void init_amd_zen3(struct cpuinfo_x86 *c)
|
||||
|
|
|
|||
|
|
@ -90,7 +90,6 @@ struct mca_config mca_cfg __read_mostly = {
|
|||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct mce_hw_err, hw_errs_seen);
|
||||
static unsigned long mce_need_notify;
|
||||
|
||||
/*
|
||||
* MCA banks polled by the period polling timer for corrected events.
|
||||
|
|
@ -152,8 +151,10 @@ EXPORT_PER_CPU_SYMBOL_GPL(injectm);
|
|||
|
||||
void mce_log(struct mce_hw_err *err)
|
||||
{
|
||||
if (mce_gen_pool_add(err))
|
||||
if (mce_gen_pool_add(err)) {
|
||||
pr_info(HW_ERR "Machine check events logged\n");
|
||||
irq_work_queue(&mce_irq_work);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mce_log);
|
||||
|
||||
|
|
@ -585,28 +586,6 @@ bool mce_is_correctable(struct mce *m)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(mce_is_correctable);
|
||||
|
||||
/*
|
||||
* Notify the user(s) about new machine check events.
|
||||
* Can be called from interrupt context, but not from machine check/NMI
|
||||
* context.
|
||||
*/
|
||||
static bool mce_notify_irq(void)
|
||||
{
|
||||
/* Not more than two messages every minute */
|
||||
static DEFINE_RATELIMIT_STATE(ratelimit, 60*HZ, 2);
|
||||
|
||||
if (test_and_clear_bit(0, &mce_need_notify)) {
|
||||
mce_work_trigger();
|
||||
|
||||
if (__ratelimit(&ratelimit))
|
||||
pr_info(HW_ERR "Machine check events logged\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int mce_early_notifier(struct notifier_block *nb, unsigned long val,
|
||||
void *data)
|
||||
{
|
||||
|
|
@ -618,9 +597,7 @@ static int mce_early_notifier(struct notifier_block *nb, unsigned long val,
|
|||
/* Emit the trace record: */
|
||||
trace_mce_record(err);
|
||||
|
||||
set_bit(0, &mce_need_notify);
|
||||
|
||||
mce_notify_irq();
|
||||
mce_work_trigger();
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
|
@ -1804,7 +1781,7 @@ static void mce_timer_fn(struct timer_list *t)
|
|||
* Alert userspace if needed. If we logged an MCE, reduce the polling
|
||||
* interval, otherwise increase the polling interval.
|
||||
*/
|
||||
if (mce_notify_irq())
|
||||
if (!mce_gen_pool_empty())
|
||||
iv = max(iv / 2, (unsigned long) HZ/100);
|
||||
else
|
||||
iv = min(iv * 2, round_jiffies_relative(check_interval * HZ));
|
||||
|
|
|
|||
|
|
@ -136,6 +136,14 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
|
|||
* %r13 original CR4 when relocate_kernel() was invoked
|
||||
*/
|
||||
|
||||
/*
|
||||
* Set return address to 0 if not preserving context. The purgatory
|
||||
* shipped in kexec-tools will unconditionally look for the return
|
||||
* address on the stack and set a kexec_jump_back_entry= command
|
||||
* line option if it's non-zero. There's no other way that it can
|
||||
* tell a preserve-context (kjump) kexec from a normal one.
|
||||
*/
|
||||
pushq $0
|
||||
/* store the start address on the stack */
|
||||
pushq %rdx
|
||||
|
||||
|
|
|
|||
|
|
@ -4481,7 +4481,7 @@ static const struct opcode opcode_map_0f_38[256] = {
|
|||
X16(N), X16(N),
|
||||
/* 0x20 - 0x2f */
|
||||
X8(N),
|
||||
X2(N), GP(SrcReg | DstMem | ModRM | Mov | Aligned, &pfx_0f_e7_0f_38_2a), N, N, N, N, N,
|
||||
X2(N), GP(SrcMem | DstReg | ModRM | Mov | Aligned, &pfx_0f_e7_0f_38_2a), N, N, N, N, N,
|
||||
/* 0x30 - 0x7f */
|
||||
X16(N), X16(N), X16(N), X16(N), X16(N),
|
||||
/* 0x80 - 0xef */
|
||||
|
|
|
|||
|
|
@ -2526,6 +2526,23 @@ static void shadow_walk_next(struct kvm_shadow_walk_iterator *iterator)
|
|||
__shadow_walk_next(iterator, *iterator->sptep);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: while normally KVM uses a "bool flush" return value to let
|
||||
* the caller batch flushes, __link_shadow_page() flushes immediately
|
||||
* before populating the parent PTE with the new shadow page. The
|
||||
* typical callers, direct_map() and FNAME(fetch)(), are not going
|
||||
* to zap more than one huge SPTE anyway.
|
||||
*
|
||||
* The only exception, where @flush can be false, is when a huge SPTE
|
||||
* is replaced with a shadow page SPTE with a fully populated page table,
|
||||
* which can happen from shadow_mmu_split_huge_page(). In this case,
|
||||
* no memory is unmapped across the change to the page tables and no
|
||||
* immediate flush is needed for correctness.
|
||||
*
|
||||
* Even in that case, calls to kvm_mmu_commit_zap_page() are not
|
||||
* batched. Doing so would require adding an invalid_list argument
|
||||
* all the way down to __walk_slot_rmaps().
|
||||
*/
|
||||
static void __link_shadow_page(struct kvm *kvm,
|
||||
struct kvm_mmu_memory_cache *cache, u64 *sptep,
|
||||
struct kvm_mmu_page *sp, bool flush)
|
||||
|
|
@ -2541,8 +2558,10 @@ static void __link_shadow_page(struct kvm *kvm,
|
|||
parent_sp = sptep_to_sp(sptep);
|
||||
WARN_ON_ONCE(parent_sp->role.level == PG_LEVEL_4K);
|
||||
|
||||
mmu_page_zap_pte(kvm, parent_sp, sptep, &invalid_list);
|
||||
kvm_mmu_remote_flush_or_zap(kvm, &invalid_list, true);
|
||||
if (mmu_page_zap_pte(kvm, parent_sp, sptep, &invalid_list))
|
||||
kvm_mmu_commit_zap_page(kvm, &invalid_list);
|
||||
else if (flush)
|
||||
kvm_flush_remote_tlbs_sptep(kvm, sptep);
|
||||
}
|
||||
|
||||
spte = make_nonleaf_spte(sp->spt, sp_ad_disabled(sp));
|
||||
|
|
|
|||
|
|
@ -160,6 +160,16 @@ void nested_vmcb02_recalc_intercepts(struct vcpu_svm *svm)
|
|||
if (!intercept_smi)
|
||||
vmcb_clr_intercept(&vmcb02->control, INTERCEPT_SMI);
|
||||
|
||||
/*
|
||||
* Intercept PAUSE if and only if L1 wants to. KVM intercepts PAUSE so
|
||||
* that a vCPU that may be spinning waiting for a lock can be scheduled
|
||||
* out in favor of the vCPU that holds said lock. KVM doesn't support
|
||||
* yielding across L2 vCPUs, as KVM has limited visilibity into which
|
||||
* L2 vCPUs are in the same L2 VM, i.e. may be contending for locks.
|
||||
*/
|
||||
if (!vmcb12_is_intercept(&svm->nested.ctl, INTERCEPT_PAUSE))
|
||||
vmcb_clr_intercept(&vmcb02->control, INTERCEPT_PAUSE);
|
||||
|
||||
if (nested_vmcb_needs_vls_intercept(svm)) {
|
||||
/*
|
||||
* If the virtual VMLOAD/VMSAVE is not enabled for the L2,
|
||||
|
|
@ -819,7 +829,6 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm)
|
|||
struct vmcb *vmcb02 = svm->nested.vmcb02.ptr;
|
||||
struct vmcb *vmcb01 = svm->vmcb01.ptr;
|
||||
struct kvm_vcpu *vcpu = &svm->vcpu;
|
||||
u32 pause_count12, pause_thresh12;
|
||||
|
||||
nested_svm_transition_tlb_flush(vcpu);
|
||||
|
||||
|
|
@ -947,31 +956,13 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm)
|
|||
vmcb02->control.misc_ctl2 |= SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE;
|
||||
|
||||
if (guest_cpu_cap_has(vcpu, X86_FEATURE_PAUSEFILTER))
|
||||
pause_count12 = vmcb12_ctrl->pause_filter_count;
|
||||
vmcb02->control.pause_filter_count = vmcb12_ctrl->pause_filter_count;
|
||||
else
|
||||
pause_count12 = 0;
|
||||
vmcb02->control.pause_filter_count = 0;
|
||||
if (guest_cpu_cap_has(vcpu, X86_FEATURE_PFTHRESHOLD))
|
||||
pause_thresh12 = vmcb12_ctrl->pause_filter_thresh;
|
||||
vmcb02->control.pause_filter_thresh = vmcb12_ctrl->pause_filter_thresh;
|
||||
else
|
||||
pause_thresh12 = 0;
|
||||
if (kvm_pause_in_guest(svm->vcpu.kvm)) {
|
||||
/* use guest values since host doesn't intercept PAUSE */
|
||||
vmcb02->control.pause_filter_count = pause_count12;
|
||||
vmcb02->control.pause_filter_thresh = pause_thresh12;
|
||||
|
||||
} else {
|
||||
/* start from host values otherwise */
|
||||
vmcb02->control.pause_filter_count = vmcb01->control.pause_filter_count;
|
||||
vmcb02->control.pause_filter_thresh = vmcb01->control.pause_filter_thresh;
|
||||
|
||||
/* ... but ensure filtering is disabled if so requested. */
|
||||
if (vmcb12_is_intercept(vmcb12_ctrl, INTERCEPT_PAUSE)) {
|
||||
if (!pause_count12)
|
||||
vmcb02->control.pause_filter_count = 0;
|
||||
if (!pause_thresh12)
|
||||
vmcb02->control.pause_filter_thresh = 0;
|
||||
}
|
||||
}
|
||||
vmcb02->control.pause_filter_thresh = 0;
|
||||
|
||||
/*
|
||||
* Take ALLOW_LARGER_RAP from vmcb12 even though it should be safe to
|
||||
|
|
@ -1298,12 +1289,6 @@ void nested_svm_vmexit(struct vcpu_svm *svm)
|
|||
/* in case we halted in L2 */
|
||||
kvm_set_mp_state(vcpu, KVM_MP_STATE_RUNNABLE);
|
||||
|
||||
if (!kvm_pause_in_guest(vcpu->kvm)) {
|
||||
vmcb01->control.pause_filter_count = vmcb02->control.pause_filter_count;
|
||||
vmcb_mark_dirty(vmcb01, VMCB_INTERCEPTS);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Invalidate last_bus_lock_rip unless KVM is still waiting for the
|
||||
* guest to make forward progress before re-enabling bus lock detection.
|
||||
|
|
|
|||
|
|
@ -913,7 +913,15 @@ static void grow_ple_window(struct kvm_vcpu *vcpu)
|
|||
struct vmcb_control_area *control = &svm->vmcb->control;
|
||||
int old = control->pause_filter_count;
|
||||
|
||||
if (kvm_pause_in_guest(vcpu->kvm))
|
||||
/* Adjusting pause_filter_count makes no sense if PLE is disabled. */
|
||||
WARN_ON_ONCE(kvm_pause_in_guest(vcpu->kvm));
|
||||
|
||||
/*
|
||||
* While running L2, KVM should intercept PAUSE if and only if L1 wants
|
||||
* to intercept PAUSE, and L1's intercept should take priority, i.e.
|
||||
* KVM should never handle a PAUSE intercept from L2.
|
||||
*/
|
||||
if (WARN_ON_ONCE(is_guest_mode(vcpu)))
|
||||
return;
|
||||
|
||||
control->pause_filter_count = __grow_ple_window(old,
|
||||
|
|
@ -934,7 +942,10 @@ static void shrink_ple_window(struct kvm_vcpu *vcpu)
|
|||
struct vmcb_control_area *control = &svm->vmcb->control;
|
||||
int old = control->pause_filter_count;
|
||||
|
||||
if (kvm_pause_in_guest(vcpu->kvm))
|
||||
/* Adjusting pause_filter_count makes no sense if PLE is disabled. */
|
||||
WARN_ON_ONCE(kvm_pause_in_guest(vcpu->kvm));
|
||||
|
||||
if (is_guest_mode(vcpu))
|
||||
return;
|
||||
|
||||
control->pause_filter_count =
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ TRACE_EVENT(kvm_xen_hypercall,
|
|||
__entry->a2 = a2;
|
||||
__entry->a3 = a3;
|
||||
__entry->a4 = a4;
|
||||
__entry->a4 = a5;
|
||||
__entry->a5 = a5;
|
||||
),
|
||||
|
||||
TP_printk("cpl %d nr 0x%lx a0 0x%lx a1 0x%lx a2 0x%lx a3 0x%lx a4 0x%lx a5 %lx",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ extern bool __read_mostly flexpriority_enabled;
|
|||
extern bool __read_mostly enable_ept;
|
||||
extern bool __read_mostly enable_unrestricted_guest;
|
||||
extern bool __read_mostly enable_ept_ad_bits;
|
||||
extern bool __read_mostly enable_cet;
|
||||
extern bool __read_mostly enable_pml;
|
||||
extern int __read_mostly pt_mode;
|
||||
|
||||
|
|
|
|||
|
|
@ -108,6 +108,9 @@ module_param_named(unrestricted_guest,
|
|||
bool __read_mostly enable_ept_ad_bits = 1;
|
||||
module_param_named(eptad, enable_ept_ad_bits, bool, 0444);
|
||||
|
||||
bool __read_mostly enable_cet = 1;
|
||||
module_param_named(cet, enable_cet, bool, 0444);
|
||||
|
||||
static bool __read_mostly emulate_invalid_guest_state = true;
|
||||
module_param(emulate_invalid_guest_state, bool, 0444);
|
||||
|
||||
|
|
@ -4476,7 +4479,7 @@ void vmx_set_constant_host_state(struct vcpu_vmx *vmx)
|
|||
* SSP is reloaded from IA32_PL3_SSP. Check SDM Vol.2A/B Chapter
|
||||
* 3 and 4 for details.
|
||||
*/
|
||||
if (cpu_has_load_cet_ctrl()) {
|
||||
if (enable_cet) {
|
||||
vmcs_writel(HOST_S_CET, kvm_host.s_cet);
|
||||
vmcs_writel(HOST_SSP, 0);
|
||||
vmcs_writel(HOST_INTR_SSP_TABLE, 0);
|
||||
|
|
@ -4532,6 +4535,10 @@ static u32 vmx_get_initial_vmentry_ctrl(void)
|
|||
if (vmx_pt_mode_is_system())
|
||||
vmentry_ctrl &= ~(VM_ENTRY_PT_CONCEAL_PIP |
|
||||
VM_ENTRY_LOAD_IA32_RTIT_CTL);
|
||||
|
||||
if (!enable_cet)
|
||||
vmentry_ctrl &= ~VM_ENTRY_LOAD_CET_STATE;
|
||||
|
||||
/*
|
||||
* IA32e mode, and loading of EFER and PERF_GLOBAL_CTRL are toggled dynamically.
|
||||
*/
|
||||
|
|
@ -4546,6 +4553,9 @@ static u32 vmx_get_initial_vmexit_ctrl(void)
|
|||
{
|
||||
u32 vmexit_ctrl = vmcs_config.vmexit_ctrl;
|
||||
|
||||
if (!enable_cet)
|
||||
vmexit_ctrl &= ~VM_EXIT_LOAD_CET_STATE;
|
||||
|
||||
/*
|
||||
* Not used by KVM and never set in vmcs01 or vmcs02, but emulated for
|
||||
* nested virtualization and thus allowed to be set in vmcs12.
|
||||
|
|
@ -8155,7 +8165,7 @@ static __init void vmx_set_cpu_caps(void)
|
|||
* VMX_BASIC[bit56] == 0, inject #CP at VMX entry with error code
|
||||
* fails, so disable CET in this case too.
|
||||
*/
|
||||
if (!cpu_has_load_cet_ctrl() || !enable_unrestricted_guest ||
|
||||
if (!enable_cet || !enable_unrestricted_guest ||
|
||||
!cpu_has_vmx_basic_no_hw_errcode_cc()) {
|
||||
kvm_cpu_cap_clear(X86_FEATURE_SHSTK);
|
||||
kvm_cpu_cap_clear(X86_FEATURE_IBT);
|
||||
|
|
@ -8630,6 +8640,9 @@ __init int vmx_hardware_setup(void)
|
|||
!cpu_has_vmx_invept_global())
|
||||
enable_ept = 0;
|
||||
|
||||
if (!cpu_has_load_cet_ctrl())
|
||||
enable_cet = 0;
|
||||
|
||||
/* NX support is required for shadow paging. */
|
||||
if (!enable_ept && !boot_cpu_has(X86_FEATURE_NX)) {
|
||||
pr_err_ratelimited("NX (Execute Disable) not supported\n");
|
||||
|
|
|
|||
|
|
@ -2145,7 +2145,10 @@ static void xen_set_fixmap(unsigned idx, phys_addr_t phys, pgprot_t prot)
|
|||
|
||||
static void xen_enter_lazy_mmu(void)
|
||||
{
|
||||
enter_lazy(XEN_LAZY_MMU);
|
||||
preempt_disable();
|
||||
if (xen_get_lazy_mode() != XEN_LAZY_MMU)
|
||||
enter_lazy(XEN_LAZY_MMU);
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
static void xen_flush_lazy_mmu(void)
|
||||
|
|
@ -2182,7 +2185,8 @@ static void xen_leave_lazy_mmu(void)
|
|||
{
|
||||
preempt_disable();
|
||||
xen_mc_flush();
|
||||
leave_lazy(XEN_LAZY_MMU);
|
||||
if (xen_get_lazy_mode() != XEN_LAZY_NONE)
|
||||
leave_lazy(XEN_LAZY_MMU);
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -655,7 +655,7 @@ static void __init xen_e820_swap_entry_with_ram(struct e820_entry *swap_entry)
|
|||
/* Fill new entry (keep size and page offset). */
|
||||
entry->type = swap_entry->type;
|
||||
entry->addr = entry_end - swap_size +
|
||||
swap_addr - swap_entry->addr;
|
||||
swap_entry->addr - swap_addr;
|
||||
entry->size = swap_entry->size;
|
||||
|
||||
/* Convert old entry to RAM, align to pages. */
|
||||
|
|
|
|||
|
|
@ -308,7 +308,6 @@ static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec,
|
|||
}
|
||||
|
||||
bip->bip_flags |= BIP_COPY_USER;
|
||||
bip->bip_vcnt = nr_vecs;
|
||||
return 0;
|
||||
free_bip:
|
||||
bio_integrity_free(bio);
|
||||
|
|
@ -403,6 +402,24 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter)
|
|||
if (unlikely(ret < 0))
|
||||
goto free_bvec;
|
||||
|
||||
/*
|
||||
* Handle partial pinning. This can happen when pin_user_pages_fast()
|
||||
* returns fewer pages than requested.
|
||||
*/
|
||||
if (user_backed_iter(iter) && unlikely(ret != bytes)) {
|
||||
if (ret > 0) {
|
||||
int npinned = DIV_ROUND_UP(offset + ret, PAGE_SIZE);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < npinned; i++)
|
||||
unpin_user_page(pages[i]);
|
||||
}
|
||||
if (pages != stack_pages)
|
||||
kvfree(pages);
|
||||
ret = -EFAULT;
|
||||
goto free_bvec;
|
||||
}
|
||||
|
||||
nr_bvecs = bvec_from_pages(bvec, pages, nr_vecs, bytes, offset,
|
||||
&is_p2p);
|
||||
if (pages != stack_pages)
|
||||
|
|
|
|||
27
block/bio.c
27
block/bio.c
|
|
@ -1279,11 +1279,12 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,
|
|||
return bio_iov_iter_align_down(bio, iter, len_align_mask);
|
||||
}
|
||||
|
||||
static struct folio *folio_alloc_greedy(gfp_t gfp, size_t *size)
|
||||
static struct folio *folio_alloc_greedy(gfp_t gfp, size_t *size,
|
||||
size_t minsize)
|
||||
{
|
||||
struct folio *folio;
|
||||
|
||||
while (*size > PAGE_SIZE) {
|
||||
while (*size > minsize) {
|
||||
folio = folio_alloc(gfp | __GFP_NORETRY, get_order(*size));
|
||||
if (folio)
|
||||
return folio;
|
||||
|
|
@ -1307,7 +1308,7 @@ static void bio_free_folios(struct bio *bio)
|
|||
}
|
||||
|
||||
static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter,
|
||||
size_t maxlen)
|
||||
size_t maxlen, size_t minsize)
|
||||
{
|
||||
size_t total_len = min(maxlen, iov_iter_count(iter));
|
||||
|
||||
|
|
@ -1322,13 +1323,13 @@ static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter,
|
|||
size_t this_len = min(total_len, SZ_1M);
|
||||
struct folio *folio;
|
||||
|
||||
if (this_len > PAGE_SIZE * 2)
|
||||
if (this_len > minsize * 2)
|
||||
this_len = rounddown_pow_of_two(this_len);
|
||||
|
||||
if (bio->bi_iter.bi_size > BIO_MAX_SIZE - this_len)
|
||||
break;
|
||||
|
||||
folio = folio_alloc_greedy(GFP_KERNEL, &this_len);
|
||||
folio = folio_alloc_greedy(GFP_KERNEL, &this_len, minsize);
|
||||
if (!folio)
|
||||
break;
|
||||
bio_add_folio_nofail(bio, folio, this_len, 0);
|
||||
|
|
@ -1344,16 +1345,16 @@ static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter,
|
|||
|
||||
if (!bio->bi_iter.bi_size)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
return bio_iov_iter_align_down(bio, iter, minsize - 1);
|
||||
}
|
||||
|
||||
static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter,
|
||||
size_t maxlen)
|
||||
size_t maxlen, size_t minsize)
|
||||
{
|
||||
size_t len = min3(iov_iter_count(iter), maxlen, SZ_1M);
|
||||
struct folio *folio;
|
||||
|
||||
folio = folio_alloc_greedy(GFP_KERNEL, &len);
|
||||
folio = folio_alloc_greedy(GFP_KERNEL, &len, minsize);
|
||||
if (!folio)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -1382,7 +1383,7 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter,
|
|||
bvec_set_folio(&bio->bi_io_vec[0], folio, bio->bi_iter.bi_size, 0);
|
||||
if (iov_iter_extract_will_pin(iter))
|
||||
bio_set_flag(bio, BIO_PAGE_PINNED);
|
||||
return 0;
|
||||
return bio_iov_iter_align_down(bio, iter, minsize - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1390,6 +1391,7 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter,
|
|||
* @bio: bio to send
|
||||
* @iter: iter to read from / write into
|
||||
* @maxlen: maximum size to bounce
|
||||
* @minsize: minimum folio allocation size
|
||||
*
|
||||
* Helper for direct I/O implementations that need to bounce buffer because
|
||||
* we need to checksum the data or perform other operations that require
|
||||
|
|
@ -1397,11 +1399,12 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter,
|
|||
* copies the data into it. Needs to be paired with bio_iov_iter_unbounce()
|
||||
* called on completion.
|
||||
*/
|
||||
int bio_iov_iter_bounce(struct bio *bio, struct iov_iter *iter, size_t maxlen)
|
||||
int bio_iov_iter_bounce(struct bio *bio, struct iov_iter *iter, size_t maxlen,
|
||||
size_t minsize)
|
||||
{
|
||||
if (op_is_write(bio_op(bio)))
|
||||
return bio_iov_iter_bounce_write(bio, iter, maxlen);
|
||||
return bio_iov_iter_bounce_read(bio, iter, maxlen);
|
||||
return bio_iov_iter_bounce_write(bio, iter, maxlen, minsize);
|
||||
return bio_iov_iter_bounce_read(bio, iter, maxlen, minsize);
|
||||
}
|
||||
|
||||
static void bvec_unpin(struct bio_vec *bv, bool mark_dirty)
|
||||
|
|
|
|||
|
|
@ -3307,6 +3307,25 @@ blk_status_t blk_insert_cloned_request(struct request *rq)
|
|||
return BLK_STS_IOERR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Integrity segment counting depends on the same queue limits
|
||||
* (virt_boundary_mask, seg_boundary_mask, max_segment_size) that
|
||||
* vary across stacked queues, so recompute against the bottom
|
||||
* queue just like nr_phys_segments above.
|
||||
*/
|
||||
if (blk_integrity_rq(rq) && rq->bio) {
|
||||
unsigned short max_int_segs = queue_max_integrity_segments(q);
|
||||
|
||||
rq->nr_integrity_segments =
|
||||
blk_rq_count_integrity_sg(rq->q, rq->bio);
|
||||
if (rq->nr_integrity_segments > max_int_segs) {
|
||||
printk(KERN_ERR "%s: over max integrity segments limit. (%u > %u)\n",
|
||||
__func__, rq->nr_integrity_segments,
|
||||
max_int_segs);
|
||||
return BLK_STS_IOERR;
|
||||
}
|
||||
}
|
||||
|
||||
if (q->disk && should_fail_request(q->disk->part0, blk_rq_bytes(rq)))
|
||||
return BLK_STS_IOERR;
|
||||
|
||||
|
|
|
|||
|
|
@ -623,6 +623,28 @@ static void disk_mark_zone_wplug_dead(struct blk_zone_wplug *zwplug)
|
|||
}
|
||||
}
|
||||
|
||||
static inline bool disk_check_zone_wplug_dead(struct blk_zone_wplug *zwplug)
|
||||
{
|
||||
if (!(zwplug->flags & BLK_ZONE_WPLUG_DEAD))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* If a new write is received right after a zone reset completes and
|
||||
* while the disk_zone_wplugs_worker() thread has not yet released the
|
||||
* reference on the zone write plug after processing the last write to
|
||||
* the zone, then the new write BIO will see the zone write plug marked
|
||||
* as dead. This case is however a false positive and a perfectly valid
|
||||
* pattern. In such case, restore the zone write plug to a live one.
|
||||
*/
|
||||
if (!zwplug->wp_offset && bio_list_empty(&zwplug->bio_list)) {
|
||||
zwplug->flags &= ~BLK_ZONE_WPLUG_DEAD;
|
||||
refcount_inc(&zwplug->ref);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool disk_zone_wplug_submit_bio(struct gendisk *disk,
|
||||
struct blk_zone_wplug *zwplug);
|
||||
|
||||
|
|
@ -1444,12 +1466,12 @@ static bool blk_zone_wplug_handle_write(struct bio *bio, unsigned int nr_segs)
|
|||
spin_lock_irqsave(&zwplug->lock, flags);
|
||||
|
||||
/*
|
||||
* If we got a zone write plug marked as dead, then the user is issuing
|
||||
* writes to a full zone, or without synchronizing with zone reset or
|
||||
* zone finish operations. In such case, fail the BIO to signal this
|
||||
* invalid usage.
|
||||
* Check if we got a zone write plug marked as dead. If yes, then the
|
||||
* user is likely issuing writes to a full zone, or without
|
||||
* synchronizing with zone reset or zone finish operations. In such
|
||||
* case, fail the BIO to signal this invalid usage.
|
||||
*/
|
||||
if (zwplug->flags & BLK_ZONE_WPLUG_DEAD) {
|
||||
if (disk_check_zone_wplug_dead(zwplug)) {
|
||||
spin_unlock_irqrestore(&zwplug->lock, flags);
|
||||
disk_put_zone_wplug(zwplug);
|
||||
bio_io_error(bio);
|
||||
|
|
|
|||
|
|
@ -606,8 +606,11 @@ static const struct vm_operations_struct drm_vm_ops = {
|
|||
static int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
|
||||
{
|
||||
struct qaic_bo *bo = to_qaic_bo(obj);
|
||||
unsigned long remap_start;
|
||||
unsigned long offset = 0;
|
||||
unsigned long remap_end;
|
||||
struct scatterlist *sg;
|
||||
unsigned long length;
|
||||
int ret = 0;
|
||||
|
||||
if (drm_gem_is_imported(obj))
|
||||
|
|
@ -615,11 +618,27 @@ static int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struc
|
|||
|
||||
for (sg = bo->sgt->sgl; sg; sg = sg_next(sg)) {
|
||||
if (sg_page(sg)) {
|
||||
/* if sg is too large for the VMA, so truncate it to fit */
|
||||
if (check_add_overflow(vma->vm_start, offset, &remap_start))
|
||||
return -EINVAL;
|
||||
if (check_add_overflow(remap_start, sg->length, &remap_end))
|
||||
return -EINVAL;
|
||||
|
||||
if (remap_end > vma->vm_end) {
|
||||
if (check_sub_overflow(vma->vm_end, remap_start, &length))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
length = sg->length;
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
goto out;
|
||||
|
||||
ret = remap_pfn_range(vma, vma->vm_start + offset, page_to_pfn(sg_page(sg)),
|
||||
sg->length, vma->vm_page_prot);
|
||||
length, vma->vm_page_prot);
|
||||
if (ret)
|
||||
goto out;
|
||||
offset += sg->length;
|
||||
offset += length;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -145,6 +145,8 @@ int rocket_ioctl_prep_bo(struct drm_device *dev, void *data, struct drm_file *fi
|
|||
ret = dma_resv_wait_timeout(gem_obj->resv, DMA_RESV_USAGE_WRITE, true, timeout);
|
||||
if (!ret)
|
||||
ret = timeout ? -ETIMEDOUT : -EBUSY;
|
||||
else if (ret > 0)
|
||||
ret = 0;
|
||||
|
||||
shmem_obj = &to_rocket_bo(gem_obj)->base;
|
||||
|
||||
|
|
|
|||
|
|
@ -192,11 +192,15 @@ static const struct dmi_system_id ac_dmi_table[] __initconst = {
|
|||
|
||||
static int acpi_ac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct acpi_device *adev;
|
||||
struct acpi_ac *ac;
|
||||
int result;
|
||||
|
||||
adev = ACPI_COMPANION(&pdev->dev);
|
||||
if (!adev)
|
||||
return -ENODEV;
|
||||
|
||||
ac = kzalloc_obj(struct acpi_ac);
|
||||
if (!ac)
|
||||
return -ENOMEM;
|
||||
|
|
|
|||
|
|
@ -423,7 +423,11 @@ static void acpi_pad_notify(acpi_handle handle, u32 event, void *data)
|
|||
|
||||
static int acpi_pad_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
|
||||
struct acpi_device *adev;
|
||||
|
||||
adev = ACPI_COMPANION(&pdev->dev);
|
||||
if (!adev)
|
||||
return -ENODEV;
|
||||
|
||||
return acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY,
|
||||
acpi_pad_notify, adev);
|
||||
|
|
|
|||
|
|
@ -815,12 +815,16 @@ static void acpi_tad_remove(void *data)
|
|||
static int acpi_tad_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
acpi_handle handle = ACPI_HANDLE(dev);
|
||||
struct acpi_tad_driver_data *dd;
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
unsigned long long caps;
|
||||
int ret;
|
||||
|
||||
handle = ACPI_HANDLE(dev);
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Initialization failure messages are mostly about firmware issues, so
|
||||
* print them at the "info" level.
|
||||
|
|
|
|||
|
|
@ -1214,10 +1214,14 @@ static void sysfs_battery_cleanup(struct acpi_battery *battery)
|
|||
|
||||
static int acpi_battery_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
|
||||
struct acpi_battery *battery;
|
||||
struct acpi_device *device;
|
||||
int result;
|
||||
|
||||
device = ACPI_COMPANION(&pdev->dev);
|
||||
if (!device)
|
||||
return -ENODEV;
|
||||
|
||||
if (device->dep_unmet)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
|
|
|
|||
|
|
@ -531,15 +531,20 @@ static int acpi_lid_input_open(struct input_dev *input)
|
|||
|
||||
static int acpi_button_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
|
||||
acpi_notify_handler handler;
|
||||
struct acpi_device *device;
|
||||
struct acpi_button *button;
|
||||
struct input_dev *input;
|
||||
const char *hid = acpi_device_hid(device);
|
||||
acpi_status status;
|
||||
char *name, *class;
|
||||
const char *hid;
|
||||
int error = 0;
|
||||
|
||||
device = ACPI_COMPANION(&pdev->dev);
|
||||
if (!device)
|
||||
return -ENODEV;
|
||||
|
||||
hid = acpi_device_hid(device);
|
||||
if (!strcmp(hid, ACPI_BUTTON_HID_LID) &&
|
||||
lid_init_state == ACPI_BUTTON_LID_INIT_DISABLED)
|
||||
return -ENODEV;
|
||||
|
|
|
|||
|
|
@ -1676,10 +1676,14 @@ static int acpi_ec_setup(struct acpi_ec *ec, struct acpi_device *device, bool ca
|
|||
|
||||
static int acpi_ec_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
|
||||
struct acpi_device *device;
|
||||
struct acpi_ec *ec;
|
||||
int ret;
|
||||
|
||||
device = ACPI_COMPANION(&pdev->dev);
|
||||
if (!device)
|
||||
return -ENODEV;
|
||||
|
||||
if (boot_ec && (boot_ec->handle == device->handle ||
|
||||
!strcmp(acpi_device_hid(device), ACPI_ECDT_HID))) {
|
||||
/* Fast path: this device corresponds to the boot EC. */
|
||||
|
|
|
|||
|
|
@ -50,9 +50,13 @@ static void acpi_hed_notify(acpi_handle handle, u32 event, void *data)
|
|||
|
||||
static int acpi_hed_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
|
||||
struct acpi_device *device;
|
||||
int err;
|
||||
|
||||
device = ACPI_COMPANION(&pdev->dev);
|
||||
if (!device)
|
||||
return -ENODEV;
|
||||
|
||||
/* Only one hardware error device */
|
||||
if (hed_handle)
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -3341,12 +3341,16 @@ static int acpi_nfit_probe(struct platform_device *pdev)
|
|||
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
struct acpi_nfit_desc *acpi_desc;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct acpi_device *adev = ACPI_COMPANION(dev);
|
||||
struct acpi_table_header *tbl;
|
||||
struct acpi_device *adev;
|
||||
acpi_status status = AE_OK;
|
||||
acpi_size sz;
|
||||
int rc = 0;
|
||||
|
||||
adev = ACPI_COMPANION(&pdev->dev);
|
||||
if (!adev)
|
||||
return -ENODEV;
|
||||
|
||||
rc = acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY,
|
||||
acpi_nfit_notify, dev);
|
||||
if (rc)
|
||||
|
|
|
|||
|
|
@ -360,10 +360,14 @@ static void pfrt_log_put_idx(void *data)
|
|||
|
||||
static int acpi_pfrt_log_probe(struct platform_device *pdev)
|
||||
{
|
||||
acpi_handle handle = ACPI_HANDLE(&pdev->dev);
|
||||
struct pfrt_log_device *pfrt_log_dev;
|
||||
acpi_handle handle;
|
||||
int ret;
|
||||
|
||||
handle = ACPI_HANDLE(&pdev->dev);
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
if (!acpi_has_method(handle, "_DSM")) {
|
||||
dev_dbg(&pdev->dev, "Missing _DSM\n");
|
||||
return -ENODEV;
|
||||
|
|
|
|||
|
|
@ -538,10 +538,14 @@ static void pfru_put_idx(void *data)
|
|||
|
||||
static int acpi_pfru_probe(struct platform_device *pdev)
|
||||
{
|
||||
acpi_handle handle = ACPI_HANDLE(&pdev->dev);
|
||||
struct pfru_device *pfru_dev;
|
||||
acpi_handle handle;
|
||||
int ret;
|
||||
|
||||
handle = ACPI_HANDLE(&pdev->dev);
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
if (!acpi_has_method(handle, "_DSM")) {
|
||||
dev_dbg(&pdev->dev, "Missing _DSM\n");
|
||||
return -ENODEV;
|
||||
|
|
|
|||
|
|
@ -629,11 +629,15 @@ static void acpi_sbs_callback(void *context)
|
|||
|
||||
static int acpi_sbs_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
|
||||
struct acpi_device *device;
|
||||
struct acpi_sbs *sbs;
|
||||
int result = 0;
|
||||
int id;
|
||||
|
||||
device = ACPI_COMPANION(&pdev->dev);
|
||||
if (!device)
|
||||
return -ENODEV;
|
||||
|
||||
sbs = kzalloc_obj(struct acpi_sbs);
|
||||
if (!sbs) {
|
||||
result = -ENOMEM;
|
||||
|
|
|
|||
|
|
@ -237,11 +237,15 @@ static int smbus_alarm(void *context)
|
|||
|
||||
static int acpi_smbus_hc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
|
||||
struct acpi_device *device;
|
||||
int status;
|
||||
unsigned long long val;
|
||||
struct acpi_smb_hc *hc;
|
||||
|
||||
device = ACPI_COMPANION(&pdev->dev);
|
||||
if (!device)
|
||||
return -ENODEV;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "_EC", NULL, &val);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_err("error obtaining _EC.\n");
|
||||
|
|
|
|||
|
|
@ -789,7 +789,7 @@ static int acpi_thermal_probe(struct platform_device *pdev)
|
|||
int i;
|
||||
|
||||
if (!device)
|
||||
return -EINVAL;
|
||||
return -ENODEV;
|
||||
|
||||
tz = kzalloc_obj(struct acpi_thermal);
|
||||
if (!tz)
|
||||
|
|
|
|||
|
|
@ -38,9 +38,13 @@ static u32 acpi_tiny_power_button_event(void *not_used)
|
|||
|
||||
static int acpi_tiny_power_button_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
|
||||
struct acpi_device *device;
|
||||
acpi_status status;
|
||||
|
||||
device = ACPI_COMPANION(&pdev->dev);
|
||||
if (!device)
|
||||
return -ENODEV;
|
||||
|
||||
if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) {
|
||||
status = acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
|
||||
acpi_tiny_power_button_event,
|
||||
|
|
|
|||
|
|
@ -920,6 +920,9 @@ static int ublk_validate_params(const struct ublk_device *ub)
|
|||
if (p->max_sectors > (ub->dev_info.max_io_buf_bytes >> 9))
|
||||
return -EINVAL;
|
||||
|
||||
if (p->max_sectors < PAGE_SECTORS)
|
||||
return -EINVAL;
|
||||
|
||||
if (ublk_dev_is_zoned(ub) && !p->chunk_sectors)
|
||||
return -EINVAL;
|
||||
} else
|
||||
|
|
|
|||
|
|
@ -552,8 +552,9 @@ static ssize_t amdgpu_debugfs_ring_read(struct file *f, char __user *buf,
|
|||
size_t size, loff_t *pos)
|
||||
{
|
||||
struct amdgpu_ring *ring = file_inode(f)->i_private;
|
||||
uint32_t value, result, early[3];
|
||||
u32 value, result, early[3] = { 0 };
|
||||
uint64_t p;
|
||||
u32 avail_dw, start_dw, read_dw;
|
||||
loff_t i;
|
||||
int r;
|
||||
|
||||
|
|
@ -565,10 +566,10 @@ static ssize_t amdgpu_debugfs_ring_read(struct file *f, char __user *buf,
|
|||
|
||||
result = 0;
|
||||
|
||||
if (*pos < 12) {
|
||||
if (ring->funcs->type == AMDGPU_RING_TYPE_CPER)
|
||||
mutex_lock(&ring->adev->cper.ring_lock);
|
||||
if (ring->funcs->type == AMDGPU_RING_TYPE_CPER)
|
||||
mutex_lock(&ring->adev->cper.ring_lock);
|
||||
|
||||
if (*pos < 12) {
|
||||
early[0] = amdgpu_ring_get_rptr(ring) & ring->buf_mask;
|
||||
early[1] = amdgpu_ring_get_wptr(ring) & ring->buf_mask;
|
||||
early[2] = ring->wptr & ring->buf_mask;
|
||||
|
|
@ -600,13 +601,24 @@ static ssize_t amdgpu_debugfs_ring_read(struct file *f, char __user *buf,
|
|||
*pos += 4;
|
||||
}
|
||||
} else {
|
||||
early[0] = amdgpu_ring_get_rptr(ring) & ring->buf_mask;
|
||||
early[1] = amdgpu_ring_get_wptr(ring) & ring->buf_mask;
|
||||
|
||||
p = early[0];
|
||||
if (early[0] <= early[1])
|
||||
size = (early[1] - early[0]);
|
||||
avail_dw = early[1] - early[0];
|
||||
else
|
||||
size = ring->ring_size - (early[0] - early[1]);
|
||||
avail_dw = ring->buf_mask + 1 - (early[0] - early[1]);
|
||||
|
||||
while (size) {
|
||||
start_dw = (*pos > 12) ? ((*pos - 12) >> 2) : 0;
|
||||
if (start_dw >= avail_dw)
|
||||
goto out;
|
||||
|
||||
p = (p + start_dw) & ring->ptr_mask;
|
||||
avail_dw -= start_dw;
|
||||
read_dw = min_t(u32, avail_dw, size >> 2);
|
||||
|
||||
while (read_dw) {
|
||||
if (p == early[1])
|
||||
goto out;
|
||||
|
||||
|
|
@ -619,9 +631,10 @@ static ssize_t amdgpu_debugfs_ring_read(struct file *f, char __user *buf,
|
|||
|
||||
buf += 4;
|
||||
result += 4;
|
||||
size--;
|
||||
read_dw--;
|
||||
p++;
|
||||
p &= ring->ptr_mask;
|
||||
*pos += 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -106,9 +106,6 @@ amdgpu_userq_detect_and_reset_queues(struct amdgpu_userq_mgr *uq_mgr)
|
|||
int r = 0;
|
||||
int i;
|
||||
|
||||
/* Warning if current process mutex is not held */
|
||||
WARN_ON(!mutex_is_locked(&uq_mgr->userq_mutex));
|
||||
|
||||
if (unlikely(adev->debug_disable_gpu_ring_reset)) {
|
||||
dev_err(adev->dev, "userq reset disabled by debug mask\n");
|
||||
return 0;
|
||||
|
|
@ -127,9 +124,11 @@ amdgpu_userq_detect_and_reset_queues(struct amdgpu_userq_mgr *uq_mgr)
|
|||
*/
|
||||
for (i = 0; i < num_queue_types; i++) {
|
||||
int ring_type = queue_types[i];
|
||||
const struct amdgpu_userq_funcs *funcs = adev->userq_funcs[ring_type];
|
||||
const struct amdgpu_userq_funcs *funcs =
|
||||
adev->userq_funcs[ring_type];
|
||||
|
||||
if (!amdgpu_userq_is_reset_type_supported(adev, ring_type, AMDGPU_RESET_TYPE_PER_QUEUE))
|
||||
if (!amdgpu_userq_is_reset_type_supported(adev, ring_type,
|
||||
AMDGPU_RESET_TYPE_PER_QUEUE))
|
||||
continue;
|
||||
|
||||
if (atomic_read(&uq_mgr->userq_count[ring_type]) > 0 &&
|
||||
|
|
@ -150,38 +149,22 @@ amdgpu_userq_detect_and_reset_queues(struct amdgpu_userq_mgr *uq_mgr)
|
|||
|
||||
static void amdgpu_userq_hang_detect_work(struct work_struct *work)
|
||||
{
|
||||
struct amdgpu_usermode_queue *queue = container_of(work,
|
||||
struct amdgpu_usermode_queue,
|
||||
hang_detect_work.work);
|
||||
struct dma_fence *fence;
|
||||
struct amdgpu_userq_mgr *uq_mgr;
|
||||
struct amdgpu_usermode_queue *queue =
|
||||
container_of(work, struct amdgpu_usermode_queue,
|
||||
hang_detect_work.work);
|
||||
|
||||
if (!queue->userq_mgr)
|
||||
return;
|
||||
|
||||
uq_mgr = queue->userq_mgr;
|
||||
fence = READ_ONCE(queue->hang_detect_fence);
|
||||
/* Fence already signaled – no action needed */
|
||||
if (!fence || dma_fence_is_signaled(fence))
|
||||
return;
|
||||
|
||||
mutex_lock(&uq_mgr->userq_mutex);
|
||||
amdgpu_userq_detect_and_reset_queues(uq_mgr);
|
||||
mutex_unlock(&uq_mgr->userq_mutex);
|
||||
amdgpu_userq_detect_and_reset_queues(queue->userq_mgr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start hang detection for a user queue fence. A delayed work will be scheduled
|
||||
* to check if the fence is still pending after the timeout period.
|
||||
*/
|
||||
* to reset the queues when the fence doesn't signal in time.
|
||||
*/
|
||||
void amdgpu_userq_start_hang_detect_work(struct amdgpu_usermode_queue *queue)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
unsigned long timeout_ms;
|
||||
|
||||
if (!queue || !queue->userq_mgr || !queue->userq_mgr->adev)
|
||||
return;
|
||||
|
||||
adev = queue->userq_mgr->adev;
|
||||
/* Determine timeout based on queue type */
|
||||
switch (queue->queue_type) {
|
||||
|
|
@ -199,8 +182,6 @@ void amdgpu_userq_start_hang_detect_work(struct amdgpu_usermode_queue *queue)
|
|||
break;
|
||||
}
|
||||
|
||||
/* Store the fence to monitor and schedule hang detection */
|
||||
WRITE_ONCE(queue->hang_detect_fence, queue->last_fence);
|
||||
schedule_delayed_work(&queue->hang_detect_work,
|
||||
msecs_to_jiffies(timeout_ms));
|
||||
}
|
||||
|
|
@ -210,18 +191,24 @@ void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell)
|
|||
struct xarray *xa = &adev->userq_doorbell_xa;
|
||||
struct amdgpu_usermode_queue *queue;
|
||||
unsigned long flags;
|
||||
int r;
|
||||
|
||||
xa_lock_irqsave(xa, flags);
|
||||
queue = xa_load(xa, doorbell);
|
||||
if (queue)
|
||||
amdgpu_userq_fence_driver_process(queue->fence_drv);
|
||||
xa_unlock_irqrestore(xa, flags);
|
||||
}
|
||||
if (queue) {
|
||||
r = amdgpu_userq_fence_driver_process(queue->fence_drv);
|
||||
/*
|
||||
* We are in interrupt context here, this *can't* wait for
|
||||
* reset work to finish.
|
||||
*/
|
||||
if (r >= 0)
|
||||
cancel_delayed_work(&queue->hang_detect_work);
|
||||
|
||||
static void amdgpu_userq_init_hang_detect_work(struct amdgpu_usermode_queue *queue)
|
||||
{
|
||||
INIT_DELAYED_WORK(&queue->hang_detect_work, amdgpu_userq_hang_detect_work);
|
||||
queue->hang_detect_fence = NULL;
|
||||
/* Restart the timer when there are still fences pending */
|
||||
if (r == 1)
|
||||
amdgpu_userq_start_hang_detect_work(queue);
|
||||
}
|
||||
xa_unlock_irqrestore(xa, flags);
|
||||
}
|
||||
|
||||
static int amdgpu_userq_buffer_va_list_add(struct amdgpu_usermode_queue *queue,
|
||||
|
|
@ -345,23 +332,18 @@ static int amdgpu_userq_preempt_helper(struct amdgpu_usermode_queue *queue)
|
|||
struct amdgpu_device *adev = uq_mgr->adev;
|
||||
const struct amdgpu_userq_funcs *userq_funcs =
|
||||
adev->userq_funcs[queue->queue_type];
|
||||
bool found_hung_queue = false;
|
||||
int r = 0;
|
||||
int r;
|
||||
|
||||
if (queue->state == AMDGPU_USERQ_STATE_MAPPED) {
|
||||
r = userq_funcs->preempt(queue);
|
||||
if (r) {
|
||||
queue->state = AMDGPU_USERQ_STATE_HUNG;
|
||||
found_hung_queue = true;
|
||||
return r;
|
||||
} else {
|
||||
queue->state = AMDGPU_USERQ_STATE_PREEMPTED;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_hung_queue)
|
||||
amdgpu_userq_detect_and_reset_queues(uq_mgr);
|
||||
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amdgpu_userq_restore_helper(struct amdgpu_usermode_queue *queue)
|
||||
|
|
@ -390,24 +372,21 @@ static int amdgpu_userq_unmap_helper(struct amdgpu_usermode_queue *queue)
|
|||
struct amdgpu_device *adev = uq_mgr->adev;
|
||||
const struct amdgpu_userq_funcs *userq_funcs =
|
||||
adev->userq_funcs[queue->queue_type];
|
||||
bool found_hung_queue = false;
|
||||
int r = 0;
|
||||
int r;
|
||||
|
||||
if ((queue->state == AMDGPU_USERQ_STATE_MAPPED) ||
|
||||
(queue->state == AMDGPU_USERQ_STATE_PREEMPTED)) {
|
||||
(queue->state == AMDGPU_USERQ_STATE_PREEMPTED)) {
|
||||
|
||||
r = userq_funcs->unmap(queue);
|
||||
if (r) {
|
||||
queue->state = AMDGPU_USERQ_STATE_HUNG;
|
||||
found_hung_queue = true;
|
||||
return r;
|
||||
} else {
|
||||
queue->state = AMDGPU_USERQ_STATE_UNMAPPED;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_hung_queue)
|
||||
amdgpu_userq_detect_and_reset_queues(uq_mgr);
|
||||
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amdgpu_userq_map_helper(struct amdgpu_usermode_queue *queue)
|
||||
|
|
@ -416,19 +395,19 @@ static int amdgpu_userq_map_helper(struct amdgpu_usermode_queue *queue)
|
|||
struct amdgpu_device *adev = uq_mgr->adev;
|
||||
const struct amdgpu_userq_funcs *userq_funcs =
|
||||
adev->userq_funcs[queue->queue_type];
|
||||
int r = 0;
|
||||
int r;
|
||||
|
||||
if (queue->state == AMDGPU_USERQ_STATE_UNMAPPED) {
|
||||
r = userq_funcs->map(queue);
|
||||
if (r) {
|
||||
queue->state = AMDGPU_USERQ_STATE_HUNG;
|
||||
amdgpu_userq_detect_and_reset_queues(uq_mgr);
|
||||
return r;
|
||||
} else {
|
||||
queue->state = AMDGPU_USERQ_STATE_MAPPED;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void amdgpu_userq_wait_for_last_fence(struct amdgpu_usermode_queue *queue)
|
||||
|
|
@ -648,13 +627,11 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que
|
|||
amdgpu_bo_unreserve(vm->root.bo);
|
||||
|
||||
mutex_lock(&uq_mgr->userq_mutex);
|
||||
queue->hang_detect_fence = NULL;
|
||||
amdgpu_userq_wait_for_last_fence(queue);
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
debugfs_remove_recursive(queue->debugfs_queue);
|
||||
#endif
|
||||
amdgpu_userq_detect_and_reset_queues(uq_mgr);
|
||||
r = amdgpu_userq_unmap_helper(queue);
|
||||
atomic_dec(&uq_mgr->userq_count[queue->queue_type]);
|
||||
amdgpu_userq_cleanup(queue);
|
||||
|
|
@ -800,6 +777,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
|
|||
}
|
||||
|
||||
queue->doorbell_index = index;
|
||||
mutex_init(&queue->fence_drv_lock);
|
||||
xa_init_flags(&queue->fence_drv_xa, XA_FLAGS_ALLOC);
|
||||
r = amdgpu_userq_fence_driver_alloc(adev, &queue->fence_drv);
|
||||
if (r) {
|
||||
|
|
@ -855,7 +833,8 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
|
|||
up_read(&adev->reset_domain->sem);
|
||||
|
||||
amdgpu_debugfs_userq_init(filp, queue, qid);
|
||||
amdgpu_userq_init_hang_detect_work(queue);
|
||||
INIT_DELAYED_WORK(&queue->hang_detect_work,
|
||||
amdgpu_userq_hang_detect_work);
|
||||
|
||||
args->out.queue_id = qid;
|
||||
atomic_inc(&uq_mgr->userq_count[queue->queue_type]);
|
||||
|
|
@ -873,6 +852,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
|
|||
amdgpu_bo_reserve(fpriv->vm.root.bo, true);
|
||||
amdgpu_userq_buffer_vas_list_cleanup(adev, queue);
|
||||
amdgpu_bo_unreserve(fpriv->vm.root.bo);
|
||||
mutex_destroy(&queue->fence_drv_lock);
|
||||
free_queue:
|
||||
kfree(queue);
|
||||
err_pm_runtime:
|
||||
|
|
@ -1262,7 +1242,6 @@ amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr)
|
|||
unsigned long queue_id;
|
||||
int ret = 0, r;
|
||||
|
||||
amdgpu_userq_detect_and_reset_queues(uq_mgr);
|
||||
/* Try to unmap all the queues in this process ctx */
|
||||
xa_for_each(&uq_mgr->userq_xa, queue_id, queue) {
|
||||
r = amdgpu_userq_preempt_helper(queue);
|
||||
|
|
@ -1270,9 +1249,11 @@ amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr)
|
|||
ret = r;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
if (ret) {
|
||||
drm_file_err(uq_mgr->file,
|
||||
"Couldn't unmap all the queues, eviction failed ret=%d\n", ret);
|
||||
amdgpu_userq_detect_and_reset_queues(uq_mgr);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1372,7 +1353,6 @@ int amdgpu_userq_suspend(struct amdgpu_device *adev)
|
|||
uqm = queue->userq_mgr;
|
||||
cancel_delayed_work_sync(&uqm->resume_work);
|
||||
guard(mutex)(&uqm->userq_mutex);
|
||||
amdgpu_userq_detect_and_reset_queues(uqm);
|
||||
if (adev->in_s0ix)
|
||||
r = amdgpu_userq_preempt_helper(queue);
|
||||
else
|
||||
|
|
@ -1431,7 +1411,6 @@ int amdgpu_userq_stop_sched_for_enforce_isolation(struct amdgpu_device *adev,
|
|||
if (((queue->queue_type == AMDGPU_HW_IP_GFX) ||
|
||||
(queue->queue_type == AMDGPU_HW_IP_COMPUTE)) &&
|
||||
(queue->xcp_id == idx)) {
|
||||
amdgpu_userq_detect_and_reset_queues(uqm);
|
||||
r = amdgpu_userq_preempt_helper(queue);
|
||||
if (r)
|
||||
ret = r;
|
||||
|
|
@ -1504,23 +1483,21 @@ void amdgpu_userq_pre_reset(struct amdgpu_device *adev)
|
|||
{
|
||||
const struct amdgpu_userq_funcs *userq_funcs;
|
||||
struct amdgpu_usermode_queue *queue;
|
||||
struct amdgpu_userq_mgr *uqm;
|
||||
unsigned long queue_id;
|
||||
|
||||
/* TODO: We probably need a new lock for the queue state */
|
||||
xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
|
||||
uqm = queue->userq_mgr;
|
||||
cancel_delayed_work_sync(&uqm->resume_work);
|
||||
if (queue->state == AMDGPU_USERQ_STATE_MAPPED) {
|
||||
amdgpu_userq_wait_for_last_fence(queue);
|
||||
userq_funcs = adev->userq_funcs[queue->queue_type];
|
||||
userq_funcs->unmap(queue);
|
||||
/* just mark all queues as hung at this point.
|
||||
* if unmap succeeds, we could map again
|
||||
* in amdgpu_userq_post_reset() if vram is not lost
|
||||
*/
|
||||
queue->state = AMDGPU_USERQ_STATE_HUNG;
|
||||
amdgpu_userq_fence_driver_force_completion(queue);
|
||||
}
|
||||
if (queue->state != AMDGPU_USERQ_STATE_MAPPED)
|
||||
continue;
|
||||
|
||||
userq_funcs = adev->userq_funcs[queue->queue_type];
|
||||
userq_funcs->unmap(queue);
|
||||
/* just mark all queues as hung at this point.
|
||||
* if unmap succeeds, we could map again
|
||||
* in amdgpu_userq_post_reset() if vram is not lost
|
||||
*/
|
||||
queue->state = AMDGPU_USERQ_STATE_HUNG;
|
||||
amdgpu_userq_fence_driver_force_completion(queue);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,18 @@ struct amdgpu_usermode_queue {
|
|||
struct amdgpu_userq_obj db_obj;
|
||||
struct amdgpu_userq_obj fw_obj;
|
||||
struct amdgpu_userq_obj wptr_obj;
|
||||
|
||||
/**
|
||||
* @fence_drv_lock: Protecting @fence_drv_xa.
|
||||
*/
|
||||
struct mutex fence_drv_lock;
|
||||
|
||||
/**
|
||||
* @fence_drv_xa:
|
||||
*
|
||||
* References to the external fence drivers returned by wait_ioctl.
|
||||
* Dropped on the next signaled dma_fence or queue destruction.
|
||||
*/
|
||||
struct xarray fence_drv_xa;
|
||||
struct amdgpu_userq_fence_driver *fence_drv;
|
||||
struct dma_fence *last_fence;
|
||||
|
|
@ -73,7 +85,6 @@ struct amdgpu_usermode_queue {
|
|||
int priority;
|
||||
struct dentry *debugfs_queue;
|
||||
struct delayed_work hang_detect_work;
|
||||
struct dma_fence *hang_detect_fence;
|
||||
struct kref refcount;
|
||||
|
||||
struct list_head userq_va_list;
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ amdgpu_userq_fence_driver_free(struct amdgpu_usermode_queue *userq)
|
|||
userq->last_fence = NULL;
|
||||
amdgpu_userq_walk_and_drop_fence_drv(&userq->fence_drv_xa);
|
||||
xa_destroy(&userq->fence_drv_xa);
|
||||
mutex_destroy(&userq->fence_drv_lock);
|
||||
/* Drop the queue's ownership reference to fence_drv explicitly */
|
||||
amdgpu_userq_fence_driver_put(userq->fence_drv);
|
||||
}
|
||||
|
|
@ -134,7 +135,14 @@ amdgpu_userq_fence_put_fence_drv_array(struct amdgpu_userq_fence *userq_fence)
|
|||
userq_fence->fence_drv_array_count = 0;
|
||||
}
|
||||
|
||||
void amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_drv)
|
||||
/*
|
||||
* Returns:
|
||||
* -ENOENT when no fences were processes
|
||||
* 1 when more fences are pending
|
||||
* 0 when no fences are pending any more
|
||||
*/
|
||||
int
|
||||
amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_drv)
|
||||
{
|
||||
struct amdgpu_userq_fence *userq_fence, *tmp;
|
||||
LIST_HEAD(to_be_signaled);
|
||||
|
|
@ -142,9 +150,6 @@ void amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_d
|
|||
unsigned long flags;
|
||||
u64 rptr;
|
||||
|
||||
if (!fence_drv)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&fence_drv->fence_list_lock, flags);
|
||||
rptr = amdgpu_userq_fence_read(fence_drv);
|
||||
|
||||
|
|
@ -157,6 +162,9 @@ void amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_d
|
|||
&userq_fence->link);
|
||||
spin_unlock_irqrestore(&fence_drv->fence_list_lock, flags);
|
||||
|
||||
if (list_empty(&to_be_signaled))
|
||||
return -ENOENT;
|
||||
|
||||
list_for_each_entry_safe(userq_fence, tmp, &to_be_signaled, link) {
|
||||
fence = &userq_fence->base;
|
||||
list_del_init(&userq_fence->link);
|
||||
|
|
@ -168,6 +176,8 @@ void amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_d
|
|||
dma_fence_put(fence);
|
||||
}
|
||||
|
||||
/* That doesn't need to be accurate so no locking */
|
||||
return list_empty(&fence_drv->fences) ? 0 : 1;
|
||||
}
|
||||
|
||||
void amdgpu_userq_fence_driver_destroy(struct kref *ref)
|
||||
|
|
@ -209,80 +219,84 @@ void amdgpu_userq_fence_driver_put(struct amdgpu_userq_fence_driver *fence_drv)
|
|||
kref_put(&fence_drv->refcount, amdgpu_userq_fence_driver_destroy);
|
||||
}
|
||||
|
||||
static int amdgpu_userq_fence_alloc(struct amdgpu_userq_fence **userq_fence)
|
||||
static int amdgpu_userq_fence_alloc(struct amdgpu_usermode_queue *userq,
|
||||
struct amdgpu_userq_fence **pfence)
|
||||
{
|
||||
*userq_fence = kmalloc(sizeof(**userq_fence), GFP_KERNEL);
|
||||
return *userq_fence ? 0 : -ENOMEM;
|
||||
struct amdgpu_userq_fence_driver *fence_drv = userq->fence_drv;
|
||||
struct amdgpu_userq_fence *userq_fence;
|
||||
void *entry;
|
||||
|
||||
userq_fence = kmalloc(sizeof(*userq_fence), GFP_KERNEL);
|
||||
if (!userq_fence)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Get the next unused entry, since we fill from the start this can be
|
||||
* used as size to allocate the array.
|
||||
*/
|
||||
mutex_lock(&userq->fence_drv_lock);
|
||||
XA_STATE(xas, &userq->fence_drv_xa, 0);
|
||||
|
||||
rcu_read_lock();
|
||||
do {
|
||||
entry = xas_find_marked(&xas, ULONG_MAX, XA_FREE_MARK);
|
||||
} while (xas_retry(&xas, entry));
|
||||
rcu_read_unlock();
|
||||
|
||||
userq_fence->fence_drv_array = kvmalloc_array(xas.xa_index,
|
||||
sizeof(fence_drv),
|
||||
GFP_KERNEL);
|
||||
if (!userq_fence->fence_drv_array) {
|
||||
mutex_unlock(&userq->fence_drv_lock);
|
||||
kfree(userq_fence);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
userq_fence->fence_drv_array_count = xas.xa_index;
|
||||
xa_extract(&userq->fence_drv_xa, (void **)userq_fence->fence_drv_array,
|
||||
0, ULONG_MAX, xas.xa_index, XA_PRESENT);
|
||||
xa_destroy(&userq->fence_drv_xa);
|
||||
|
||||
mutex_unlock(&userq->fence_drv_lock);
|
||||
|
||||
amdgpu_userq_fence_driver_get(fence_drv);
|
||||
userq_fence->fence_drv = fence_drv;
|
||||
|
||||
*pfence = userq_fence;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amdgpu_userq_fence_create(struct amdgpu_usermode_queue *userq,
|
||||
struct amdgpu_userq_fence *userq_fence,
|
||||
u64 seq, struct dma_fence **f)
|
||||
static void amdgpu_userq_fence_init(struct amdgpu_usermode_queue *userq,
|
||||
struct amdgpu_userq_fence *fence,
|
||||
u64 seq)
|
||||
{
|
||||
struct amdgpu_userq_fence_driver *fence_drv;
|
||||
struct dma_fence *fence;
|
||||
struct amdgpu_userq_fence_driver *fence_drv = userq->fence_drv;
|
||||
unsigned long flags;
|
||||
bool signaled = false;
|
||||
|
||||
fence_drv = userq->fence_drv;
|
||||
if (!fence_drv)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_init(&userq_fence->lock);
|
||||
INIT_LIST_HEAD(&userq_fence->link);
|
||||
fence = &userq_fence->base;
|
||||
userq_fence->fence_drv = fence_drv;
|
||||
|
||||
dma_fence_init64(fence, &amdgpu_userq_fence_ops, &userq_fence->lock,
|
||||
spin_lock_init(&fence->lock);
|
||||
dma_fence_init64(&fence->base, &amdgpu_userq_fence_ops, &fence->lock,
|
||||
fence_drv->context, seq);
|
||||
|
||||
amdgpu_userq_fence_driver_get(fence_drv);
|
||||
dma_fence_get(fence);
|
||||
/* Make sure the fence is visible to the hang detect worker */
|
||||
dma_fence_put(userq->last_fence);
|
||||
userq->last_fence = dma_fence_get(&fence->base);
|
||||
|
||||
if (!xa_empty(&userq->fence_drv_xa)) {
|
||||
struct amdgpu_userq_fence_driver *stored_fence_drv;
|
||||
unsigned long index, count = 0;
|
||||
int i = 0;
|
||||
|
||||
xa_lock(&userq->fence_drv_xa);
|
||||
xa_for_each(&userq->fence_drv_xa, index, stored_fence_drv)
|
||||
count++;
|
||||
|
||||
userq_fence->fence_drv_array =
|
||||
kvmalloc_objs(struct amdgpu_userq_fence_driver *, count,
|
||||
GFP_ATOMIC);
|
||||
|
||||
if (userq_fence->fence_drv_array) {
|
||||
xa_for_each(&userq->fence_drv_xa, index, stored_fence_drv) {
|
||||
userq_fence->fence_drv_array[i] = stored_fence_drv;
|
||||
__xa_erase(&userq->fence_drv_xa, index);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
userq_fence->fence_drv_array_count = i;
|
||||
xa_unlock(&userq->fence_drv_xa);
|
||||
} else {
|
||||
userq_fence->fence_drv_array = NULL;
|
||||
userq_fence->fence_drv_array_count = 0;
|
||||
}
|
||||
|
||||
/* Check if hardware has already processed the job */
|
||||
/* Check if hardware has already processed the fence */
|
||||
spin_lock_irqsave(&fence_drv->fence_list_lock, flags);
|
||||
if (!dma_fence_is_signaled(fence)) {
|
||||
list_add_tail(&userq_fence->link, &fence_drv->fences);
|
||||
if (!dma_fence_is_signaled(&fence->base)) {
|
||||
dma_fence_get(&fence->base);
|
||||
list_add_tail(&fence->link, &fence_drv->fences);
|
||||
} else {
|
||||
INIT_LIST_HEAD(&fence->link);
|
||||
signaled = true;
|
||||
dma_fence_put(fence);
|
||||
}
|
||||
spin_unlock_irqrestore(&fence_drv->fence_list_lock, flags);
|
||||
|
||||
if (signaled)
|
||||
amdgpu_userq_fence_put_fence_drv_array(userq_fence);
|
||||
|
||||
*f = fence;
|
||||
|
||||
return 0;
|
||||
amdgpu_userq_fence_put_fence_drv_array(fence);
|
||||
else
|
||||
amdgpu_userq_start_hang_detect_work(userq);
|
||||
}
|
||||
|
||||
static const char *amdgpu_userq_fence_get_driver_name(struct dma_fence *f)
|
||||
|
|
@ -403,11 +417,6 @@ static int amdgpu_userq_fence_read_wptr(struct amdgpu_device *adev,
|
|||
return r;
|
||||
}
|
||||
|
||||
static void amdgpu_userq_fence_cleanup(struct dma_fence *fence)
|
||||
{
|
||||
dma_fence_put(fence);
|
||||
}
|
||||
|
||||
static void
|
||||
amdgpu_userq_fence_driver_set_error(struct amdgpu_userq_fence *fence,
|
||||
int error)
|
||||
|
|
@ -451,13 +460,14 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data,
|
|||
const unsigned int num_read_bo_handles = args->num_bo_read_handles;
|
||||
struct amdgpu_fpriv *fpriv = filp->driver_priv;
|
||||
struct amdgpu_userq_mgr *userq_mgr = &fpriv->userq_mgr;
|
||||
|
||||
struct drm_gem_object **gobj_write, **gobj_read;
|
||||
u32 *syncobj_handles, num_syncobj_handles;
|
||||
struct amdgpu_userq_fence *userq_fence;
|
||||
struct amdgpu_usermode_queue *queue = NULL;
|
||||
struct drm_syncobj **syncobj = NULL;
|
||||
struct dma_fence *fence;
|
||||
struct amdgpu_usermode_queue *queue;
|
||||
struct amdgpu_userq_fence *fence;
|
||||
struct drm_syncobj **syncobj;
|
||||
struct drm_exec exec;
|
||||
void __user *ptr;
|
||||
int r, i, entry;
|
||||
u64 wptr;
|
||||
|
||||
|
|
@ -469,13 +479,14 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data,
|
|||
return -EINVAL;
|
||||
|
||||
num_syncobj_handles = args->num_syncobj_handles;
|
||||
syncobj_handles = memdup_array_user(u64_to_user_ptr(args->syncobj_handles),
|
||||
num_syncobj_handles, sizeof(u32));
|
||||
ptr = u64_to_user_ptr(args->syncobj_handles);
|
||||
syncobj_handles = memdup_array_user(ptr, num_syncobj_handles,
|
||||
sizeof(u32));
|
||||
if (IS_ERR(syncobj_handles))
|
||||
return PTR_ERR(syncobj_handles);
|
||||
|
||||
/* Array of pointers to the looked up syncobjs */
|
||||
syncobj = kmalloc_array(num_syncobj_handles, sizeof(*syncobj), GFP_KERNEL);
|
||||
syncobj = kmalloc_array(num_syncobj_handles, sizeof(*syncobj),
|
||||
GFP_KERNEL);
|
||||
if (!syncobj) {
|
||||
r = -ENOMEM;
|
||||
goto free_syncobj_handles;
|
||||
|
|
@ -489,21 +500,17 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data,
|
|||
}
|
||||
}
|
||||
|
||||
r = drm_gem_objects_lookup(filp,
|
||||
u64_to_user_ptr(args->bo_read_handles),
|
||||
num_read_bo_handles,
|
||||
&gobj_read);
|
||||
ptr = u64_to_user_ptr(args->bo_read_handles);
|
||||
r = drm_gem_objects_lookup(filp, ptr, num_read_bo_handles, &gobj_read);
|
||||
if (r)
|
||||
goto free_syncobj;
|
||||
|
||||
r = drm_gem_objects_lookup(filp,
|
||||
u64_to_user_ptr(args->bo_write_handles),
|
||||
num_write_bo_handles,
|
||||
ptr = u64_to_user_ptr(args->bo_write_handles);
|
||||
r = drm_gem_objects_lookup(filp, ptr, num_write_bo_handles,
|
||||
&gobj_write);
|
||||
if (r)
|
||||
goto put_gobj_read;
|
||||
|
||||
/* Retrieve the user queue */
|
||||
queue = amdgpu_userq_get(userq_mgr, args->queue_id);
|
||||
if (!queue) {
|
||||
r = -ENOENT;
|
||||
|
|
@ -512,73 +519,61 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data,
|
|||
|
||||
r = amdgpu_userq_fence_read_wptr(adev, queue, &wptr);
|
||||
if (r)
|
||||
goto put_gobj_write;
|
||||
goto put_queue;
|
||||
|
||||
r = amdgpu_userq_fence_alloc(&userq_fence);
|
||||
r = amdgpu_userq_fence_alloc(queue, &fence);
|
||||
if (r)
|
||||
goto put_gobj_write;
|
||||
goto put_queue;
|
||||
|
||||
/* We are here means UQ is active, make sure the eviction fence is valid */
|
||||
amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr);
|
||||
|
||||
/* Create a new fence */
|
||||
r = amdgpu_userq_fence_create(queue, userq_fence, wptr, &fence);
|
||||
if (r) {
|
||||
mutex_unlock(&userq_mgr->userq_mutex);
|
||||
kfree(userq_fence);
|
||||
goto put_gobj_write;
|
||||
}
|
||||
/* Create the new fence */
|
||||
amdgpu_userq_fence_init(queue, fence, wptr);
|
||||
|
||||
dma_fence_put(queue->last_fence);
|
||||
queue->last_fence = dma_fence_get(fence);
|
||||
amdgpu_userq_start_hang_detect_work(queue);
|
||||
mutex_unlock(&userq_mgr->userq_mutex);
|
||||
|
||||
/*
|
||||
* This needs to come after the fence is created since
|
||||
* amdgpu_userq_ensure_ev_fence() can't be called while holding the resv
|
||||
* locks.
|
||||
*/
|
||||
drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT,
|
||||
(num_read_bo_handles + num_write_bo_handles));
|
||||
|
||||
/* Lock all BOs with retry handling */
|
||||
drm_exec_until_all_locked(&exec) {
|
||||
r = drm_exec_prepare_array(&exec, gobj_read, num_read_bo_handles, 1);
|
||||
r = drm_exec_prepare_array(&exec, gobj_read,
|
||||
num_read_bo_handles, 1);
|
||||
drm_exec_retry_on_contention(&exec);
|
||||
if (r) {
|
||||
amdgpu_userq_fence_cleanup(fence);
|
||||
if (r)
|
||||
goto exec_fini;
|
||||
}
|
||||
|
||||
r = drm_exec_prepare_array(&exec, gobj_write, num_write_bo_handles, 1);
|
||||
r = drm_exec_prepare_array(&exec, gobj_write,
|
||||
num_write_bo_handles, 1);
|
||||
drm_exec_retry_on_contention(&exec);
|
||||
if (r) {
|
||||
amdgpu_userq_fence_cleanup(fence);
|
||||
if (r)
|
||||
goto exec_fini;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < num_read_bo_handles; i++) {
|
||||
if (!gobj_read || !gobj_read[i]->resv)
|
||||
continue;
|
||||
|
||||
dma_resv_add_fence(gobj_read[i]->resv, fence,
|
||||
/* And publish the new fence in the BOs and syncobj */
|
||||
for (i = 0; i < num_read_bo_handles; i++)
|
||||
dma_resv_add_fence(gobj_read[i]->resv, &fence->base,
|
||||
DMA_RESV_USAGE_READ);
|
||||
}
|
||||
|
||||
for (i = 0; i < num_write_bo_handles; i++) {
|
||||
if (!gobj_write || !gobj_write[i]->resv)
|
||||
continue;
|
||||
|
||||
dma_resv_add_fence(gobj_write[i]->resv, fence,
|
||||
for (i = 0; i < num_write_bo_handles; i++)
|
||||
dma_resv_add_fence(gobj_write[i]->resv, &fence->base,
|
||||
DMA_RESV_USAGE_WRITE);
|
||||
}
|
||||
|
||||
/* Add the created fence to syncobj/BO's */
|
||||
for (i = 0; i < num_syncobj_handles; i++)
|
||||
drm_syncobj_replace_fence(syncobj[i], fence);
|
||||
|
||||
/* drop the reference acquired in fence creation function */
|
||||
dma_fence_put(fence);
|
||||
drm_syncobj_replace_fence(syncobj[i], &fence->base);
|
||||
|
||||
exec_fini:
|
||||
/* drop the reference acquired in fence creation function */
|
||||
dma_fence_put(&fence->base);
|
||||
|
||||
drm_exec_fini(&exec);
|
||||
put_queue:
|
||||
amdgpu_userq_put(queue);
|
||||
put_gobj_write:
|
||||
for (i = 0; i < num_write_bo_handles; i++)
|
||||
drm_gem_object_put(gobj_write[i]);
|
||||
|
|
@ -589,15 +584,11 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data,
|
|||
kvfree(gobj_read);
|
||||
free_syncobj:
|
||||
while (entry-- > 0)
|
||||
if (syncobj[entry])
|
||||
drm_syncobj_put(syncobj[entry]);
|
||||
drm_syncobj_put(syncobj[entry]);
|
||||
kfree(syncobj);
|
||||
free_syncobj_handles:
|
||||
kfree(syncobj_handles);
|
||||
|
||||
if (queue)
|
||||
amdgpu_userq_put(queue);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
@ -872,8 +863,10 @@ amdgpu_userq_wait_return_fence_info(struct drm_file *filp,
|
|||
* Otherwise, we would gather those references until we don't
|
||||
* have any more space left and crash.
|
||||
*/
|
||||
mutex_lock(&waitq->fence_drv_lock);
|
||||
r = xa_alloc(&waitq->fence_drv_xa, &index, fence_drv,
|
||||
xa_limit_32b, GFP_KERNEL);
|
||||
mutex_unlock(&waitq->fence_drv_lock);
|
||||
if (r)
|
||||
goto put_waitq;
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ void amdgpu_userq_fence_driver_put(struct amdgpu_userq_fence_driver *fence_drv);
|
|||
int amdgpu_userq_fence_driver_alloc(struct amdgpu_device *adev,
|
||||
struct amdgpu_userq_fence_driver **fence_drv_req);
|
||||
void amdgpu_userq_fence_driver_free(struct amdgpu_usermode_queue *userq);
|
||||
void amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_drv);
|
||||
int amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_drv);
|
||||
void amdgpu_userq_fence_driver_force_completion(struct amdgpu_usermode_queue *userq);
|
||||
void amdgpu_userq_fence_driver_destroy(struct kref *ref);
|
||||
int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data,
|
||||
|
|
|
|||
|
|
@ -602,6 +602,13 @@ static int gfx_v12_0_init_microcode(struct amdgpu_device *adev)
|
|||
"amdgpu/%s_pfp.bin", ucode_prefix);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
adev->gfx.rs64_enable = amdgpu_ucode_hdr_version(
|
||||
(union amdgpu_firmware_header *)
|
||||
adev->gfx.pfp_fw->data, 2, 0);
|
||||
if (adev->gfx.rs64_enable)
|
||||
dev_dbg(adev->dev, "CP RS64 enable\n");
|
||||
|
||||
amdgpu_gfx_cp_init_microcode(adev, AMDGPU_UCODE_ID_CP_RS64_PFP);
|
||||
amdgpu_gfx_cp_init_microcode(adev, AMDGPU_UCODE_ID_CP_RS64_PFP_P0_STACK);
|
||||
|
||||
|
|
|
|||
|
|
@ -92,9 +92,14 @@
|
|||
#include "dml/dcn32/dcn32_fpu.h"
|
||||
|
||||
#include "dc_state_priv.h"
|
||||
#include "dc_fpu.h"
|
||||
|
||||
#include "dml2_0/dml2_wrapper.h"
|
||||
|
||||
#if !defined(DC_RUN_WITH_PREEMPTION_ENABLED)
|
||||
#define DC_RUN_WITH_PREEMPTION_ENABLED(code) code
|
||||
#endif
|
||||
|
||||
#define DC_LOGGER_INIT(logger)
|
||||
|
||||
enum dcn32_clk_src_array_id {
|
||||
|
|
@ -1684,7 +1689,8 @@ static void dcn32_enable_phantom_plane(struct dc *dc,
|
|||
if (curr_pipe->top_pipe && curr_pipe->top_pipe->plane_state == curr_pipe->plane_state)
|
||||
phantom_plane = prev_phantom_plane;
|
||||
else
|
||||
phantom_plane = dc_state_create_phantom_plane(dc, context, curr_pipe->plane_state);
|
||||
DC_RUN_WITH_PREEMPTION_ENABLED(phantom_plane =
|
||||
dc_state_create_phantom_plane(dc, context, curr_pipe->plane_state));
|
||||
|
||||
if (!phantom_plane)
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -222,52 +222,58 @@ static const struct drm_bridge_funcs imx8qxp_pxl2dpi_bridge_funcs = {
|
|||
imx8qxp_pxl2dpi_bridge_atomic_get_output_bus_fmts,
|
||||
};
|
||||
|
||||
static struct device_node *
|
||||
static int
|
||||
imx8qxp_pxl2dpi_get_available_ep_from_port(struct imx8qxp_pxl2dpi *p2d,
|
||||
u32 port_id)
|
||||
u32 port_id,
|
||||
struct device_node **ep)
|
||||
{
|
||||
struct device_node *port, *ep;
|
||||
struct device_node *port;
|
||||
int ret = 0;
|
||||
int ep_cnt;
|
||||
|
||||
*ep = NULL;
|
||||
|
||||
port = of_graph_get_port_by_id(p2d->dev->of_node, port_id);
|
||||
if (!port) {
|
||||
DRM_DEV_ERROR(p2d->dev, "failed to get port@%u\n", port_id);
|
||||
return ERR_PTR(-ENODEV);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ep_cnt = of_get_available_child_count(port);
|
||||
if (ep_cnt == 0) {
|
||||
DRM_DEV_ERROR(p2d->dev, "no available endpoints of port@%u\n",
|
||||
port_id);
|
||||
ep = ERR_PTR(-ENODEV);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
} else if (ep_cnt > 1) {
|
||||
DRM_DEV_ERROR(p2d->dev,
|
||||
"invalid available endpoints of port@%u\n",
|
||||
port_id);
|
||||
ep = ERR_PTR(-EINVAL);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ep = of_get_next_available_child(port, NULL);
|
||||
if (!ep) {
|
||||
*ep = of_get_next_available_child(port, NULL);
|
||||
if (!*ep) {
|
||||
DRM_DEV_ERROR(p2d->dev,
|
||||
"failed to get available endpoint of port@%u\n",
|
||||
port_id);
|
||||
ep = ERR_PTR(-ENODEV);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
of_node_put(port);
|
||||
return ep;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx8qxp_pxl2dpi_find_next_bridge(struct imx8qxp_pxl2dpi *p2d)
|
||||
{
|
||||
struct device_node *ep __free(device_node) =
|
||||
imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 1);
|
||||
if (IS_ERR(ep))
|
||||
return PTR_ERR(ep);
|
||||
struct device_node *ep __free(device_node) = NULL;
|
||||
int ret;
|
||||
|
||||
ret = imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 1, &ep);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
struct device_node *remote __free(device_node) = of_graph_get_remote_port_parent(ep);
|
||||
if (!remote || !of_device_is_available(remote)) {
|
||||
|
|
@ -291,9 +297,9 @@ static int imx8qxp_pxl2dpi_set_pixel_link_sel(struct imx8qxp_pxl2dpi *p2d)
|
|||
struct of_endpoint endpoint;
|
||||
int ret;
|
||||
|
||||
ep = imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 0);
|
||||
if (IS_ERR(ep))
|
||||
return PTR_ERR(ep);
|
||||
ret = imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 0, &ep);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_graph_parse_endpoint(ep, &endpoint);
|
||||
if (ret) {
|
||||
|
|
|
|||
|
|
@ -1067,17 +1067,12 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data,
|
|||
|
||||
spin_unlock(&file_priv->table_lock);
|
||||
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
|
||||
if (obj->dma_buf) {
|
||||
ret = drm_prime_add_buf_handle(&file_priv->prime, obj->dma_buf,
|
||||
handle);
|
||||
if (ret < 0) {
|
||||
spin_lock(&file_priv->table_lock);
|
||||
idr_remove(&file_priv->object_idr, handle);
|
||||
idrobj = idr_replace(&file_priv->object_idr, obj, handle);
|
||||
WARN_ON(idrobj != NULL);
|
||||
spin_unlock(&file_priv->table_lock);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
|
@ -1089,7 +1084,9 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data,
|
|||
|
||||
spin_lock(&file_priv->table_lock);
|
||||
idr_remove(&file_priv->object_idr, args->handle);
|
||||
idrobj = idr_replace(&file_priv->object_idr, obj, handle);
|
||||
spin_unlock(&file_priv->table_lock);
|
||||
WARN_ON(idrobj != NULL);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&file_priv->prime.lock);
|
||||
|
|
|
|||
|
|
@ -580,6 +580,7 @@ static int oaktrail_hdmi_get_modes(struct drm_connector *connector)
|
|||
} else {
|
||||
edid = (struct edid *)raw_edid;
|
||||
/* FIXME ? edid = drm_get_edid(connector, i2c_adap); */
|
||||
i2c_put_adapter(i2c_adap);
|
||||
}
|
||||
|
||||
if (edid) {
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ void oaktrail_lvds_init(struct drm_device *dev,
|
|||
{
|
||||
struct gma_encoder *gma_encoder;
|
||||
struct gma_connector *gma_connector;
|
||||
struct gma_i2c_chan *ddc_bus;
|
||||
struct gma_i2c_chan *ddc_bus = NULL;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
|
||||
|
|
@ -367,6 +367,8 @@ void oaktrail_lvds_init(struct drm_device *dev,
|
|||
if (edid == NULL && dev_priv->lpc_gpio_base) {
|
||||
ddc_bus = oaktrail_lvds_i2c_init(dev);
|
||||
if (!IS_ERR(ddc_bus)) {
|
||||
if (i2c_adap)
|
||||
i2c_put_adapter(i2c_adap);
|
||||
i2c_adap = &ddc_bus->base;
|
||||
edid = drm_get_edid(connector, i2c_adap);
|
||||
}
|
||||
|
|
@ -421,7 +423,10 @@ void oaktrail_lvds_init(struct drm_device *dev,
|
|||
|
||||
err_unlock:
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
gma_i2c_destroy(to_gma_i2c_chan(connector->ddc));
|
||||
if (!IS_ERR_OR_NULL(ddc_bus))
|
||||
gma_i2c_destroy(ddc_bus);
|
||||
else if (i2c_adap)
|
||||
i2c_put_adapter(i2c_adap);
|
||||
drm_encoder_cleanup(encoder);
|
||||
err_connector_cleanup:
|
||||
drm_connector_cleanup(connector);
|
||||
|
|
|
|||
|
|
@ -3119,8 +3119,13 @@ static void intel_dp_compute_vsc_colorimetry(const struct intel_crtc_state *crtc
|
|||
drm_WARN_ON(display->drm,
|
||||
vsc->bpc == 6 && vsc->pixelformat != DP_PIXELFORMAT_RGB);
|
||||
|
||||
/* all YCbCr are always limited range */
|
||||
vsc->dynamic_range = DP_DYNAMIC_RANGE_CTA;
|
||||
/* All YCbCr formats are always limited range. */
|
||||
if (vsc->pixelformat == DP_PIXELFORMAT_RGB)
|
||||
vsc->dynamic_range = crtc_state->limited_color_range ?
|
||||
DP_DYNAMIC_RANGE_CTA : DP_DYNAMIC_RANGE_VESA;
|
||||
else
|
||||
vsc->dynamic_range = DP_DYNAMIC_RANGE_CTA;
|
||||
|
||||
vsc->content_type = DP_CONTENT_TYPE_NOT_DEFINED;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -132,7 +132,8 @@ void __i915_request_reset(struct i915_request *rq, bool guilty)
|
|||
rcu_read_lock(); /* protect the GEM context */
|
||||
if (guilty) {
|
||||
i915_request_set_error_once(rq, -EIO);
|
||||
__i915_request_skip(rq);
|
||||
if (!i915_request_signaled(rq))
|
||||
__i915_request_skip(rq);
|
||||
banned = mark_guilty(rq);
|
||||
} else {
|
||||
i915_request_set_error_once(rq, -EAGAIN);
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ static int lsdc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
|
||||
vga_client_register(pdev, lsdc_vga_set_decode);
|
||||
|
||||
drm_kms_helper_poll_init(ddev);
|
||||
drmm_kms_helper_poll_init(ddev);
|
||||
|
||||
if (loongson_vblank) {
|
||||
ret = drm_vblank_init(ddev, descp->num_of_crtc);
|
||||
|
|
|
|||
|
|
@ -390,6 +390,8 @@ panfrost_ioctl_wait_bo(struct drm_device *dev, void *data,
|
|||
true, timeout);
|
||||
if (!ret)
|
||||
ret = timeout ? -ETIMEDOUT : -EBUSY;
|
||||
else if (ret > 0)
|
||||
ret = 0;
|
||||
|
||||
drm_gem_object_put(gem_obj);
|
||||
|
||||
|
|
|
|||
|
|
@ -739,7 +739,7 @@ static int ttm_bo_alloc_resource(struct ttm_buffer_object *bo,
|
|||
may_evict = (force_space && place->mem_type != TTM_PL_SYSTEM);
|
||||
ret = ttm_resource_alloc(bo, place, res, force_space ? &limit_pool : NULL);
|
||||
if (ret) {
|
||||
if (ret != -ENOSPC && ret != -EAGAIN) {
|
||||
if (ret != -ENOSPC) {
|
||||
dmem_cgroup_pool_state_put(limit_pool);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1177,17 +1177,13 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo)
|
|||
bdev->funcs->swap_notify(bo);
|
||||
|
||||
if (ttm_tt_is_populated(tt)) {
|
||||
spin_lock(&bdev->lru_lock);
|
||||
ttm_resource_del_bulk_move(bo->resource, bo);
|
||||
spin_unlock(&bdev->lru_lock);
|
||||
|
||||
ret = ttm_tt_swapout(bdev, tt, swapout_walk->gfp_flags);
|
||||
|
||||
spin_lock(&bdev->lru_lock);
|
||||
if (ret)
|
||||
ttm_resource_add_bulk_move(bo->resource, bo);
|
||||
ttm_resource_move_to_lru_tail(bo->resource);
|
||||
spin_unlock(&bdev->lru_lock);
|
||||
if (!ret) {
|
||||
spin_lock(&bdev->lru_lock);
|
||||
ttm_resource_del_bulk_move_unevictable(bo->resource, bo);
|
||||
ttm_resource_move_to_lru_tail(bo->resource);
|
||||
spin_unlock(&bdev->lru_lock);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
|
|
|
|||
|
|
@ -1112,19 +1112,14 @@ long ttm_bo_shrink(struct ttm_operation_ctx *ctx, struct ttm_buffer_object *bo,
|
|||
if (lret < 0)
|
||||
return lret;
|
||||
|
||||
if (bo->bulk_move) {
|
||||
spin_lock(&bdev->lru_lock);
|
||||
ttm_resource_del_bulk_move(bo->resource, bo);
|
||||
spin_unlock(&bdev->lru_lock);
|
||||
}
|
||||
|
||||
lret = ttm_tt_backup(bdev, bo->ttm, (struct ttm_backup_flags)
|
||||
{.purge = flags.purge,
|
||||
.writeback = flags.writeback});
|
||||
|
||||
if (lret <= 0 && bo->bulk_move) {
|
||||
if (lret > 0) {
|
||||
spin_lock(&bdev->lru_lock);
|
||||
ttm_resource_add_bulk_move(bo->resource, bo);
|
||||
ttm_resource_del_bulk_move_unevictable(bo->resource, bo);
|
||||
ttm_resource_move_to_lru_tail(bo->resource);
|
||||
spin_unlock(&bdev->lru_lock);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -292,6 +292,19 @@ void ttm_resource_del_bulk_move(struct ttm_resource *res,
|
|||
ttm_lru_bulk_move_del(bo->bulk_move, res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a resource from its bulk_move, bypassing the unevictable check.
|
||||
* Use only when the resource is known to still be tracked in the range despite
|
||||
* the BO having just become unevictable; asserts that this is the case.
|
||||
*/
|
||||
void ttm_resource_del_bulk_move_unevictable(struct ttm_resource *res,
|
||||
struct ttm_buffer_object *bo)
|
||||
{
|
||||
WARN_ON_ONCE(!ttm_resource_unevictable(res, bo));
|
||||
if (bo->bulk_move)
|
||||
ttm_lru_bulk_move_del(bo->bulk_move, res);
|
||||
}
|
||||
|
||||
/* Move a resource to the LRU or bulk tail */
|
||||
void ttm_resource_move_to_lru_tail(struct ttm_resource *res)
|
||||
{
|
||||
|
|
@ -385,8 +398,11 @@ int ttm_resource_alloc(struct ttm_buffer_object *bo,
|
|||
|
||||
if (man->cg) {
|
||||
ret = dmem_cgroup_try_charge(man->cg, bo->base.size, &pool, ret_limit_pool);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
if (ret == -EAGAIN)
|
||||
ret = -ENOSPC;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = man->func->alloc(man, bo, place, res_ptr);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user