ASoC: Updates for v6.14

This was quite a quiet release for what I imagine are holiday related
 reasons, the diffstat is dominated by some Cirrus Logic Kunit tests.
 There's the usual mix of small improvements and fixes, plus a few new
 drivers and features.  The diffstat includes some DRM changes due to
 work on HDMI audio.
 
  - Allow clocking on each DAI in an audio graph card to be configured
    separately.
  - Improved power management for Renesas RZ-SSI.
  - KUnit testing for the Cirrus DSP framework.
  - Memory to meory operation support for Freescale/NXP platforms.
  - Support for pause operations in SOF.
  - Support for Allwinner suinv F1C100s, Awinc AW88083, Realtek
    ALC5682I-VE
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmeOX4sACgkQJNaLcl1U
 h9BFQAf/Q4OkXtVsoNMxz0gqOE9nEJXII2pbogssE19cVMF38JgONC1qqIHag+Gj
 FR/HBw+WRmdQQHk+tgIYI6AXnD5N47EyJfm6+Ya0y6YGCrzv8AMQkAl1qO8KCZnY
 ywVq8uNtximupX3l13KAwUymrRflBJMRticIDBi0M8+NYB7M+qQzjVW4+XF+U5iz
 VNMzNQP9JV0dbTuNedHIj8vkvOeS7AMv11VyZ6leZr2Y4yEhIsoTnOfhHkz59/lo
 SdBj5ZouoYRjlZs24Snu9rcpz3eLy5/MiSmlD9yJkMUR8aySzmdzAJdreOmakk+k
 xk0XHWOa80aG7Gt6I0hugpQJuI5Diw==
 =64A0
 -----END PGP SIGNATURE-----

Merge tag 'asoc-v6.14' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus

ASoC: Updates for v6.14

This was quite a quiet release for what I imagine are holiday related
reasons, the diffstat is dominated by some Cirrus Logic Kunit tests.
There's the usual mix of small improvements and fixes, plus a few new
drivers and features.  The diffstat includes some DRM changes due to
work on HDMI audio.

 - Allow clocking on each DAI in an audio graph card to be configured
   separately.
 - Improved power management for Renesas RZ-SSI.
 - KUnit testing for the Cirrus DSP framework.
 - Memory to meory operation support for Freescale/NXP platforms.
 - Support for pause operations in SOF.
 - Support for Allwinner suinv F1C100s, Awinc AW88083, Realtek
   ALC5682I-VE
This commit is contained in:
Takashi Iwai 2025-01-20 16:15:07 +01:00
commit 8514d8f80e
674 changed files with 28177 additions and 4296 deletions

View file

@ -435,7 +435,7 @@ Martin Kepplinger <martink@posteo.de> <martin.kepplinger@ginzinger.com>
Martin Kepplinger <martink@posteo.de> <martin.kepplinger@puri.sm>
Martin Kepplinger <martink@posteo.de> <martin.kepplinger@theobroma-systems.com>
Martyna Szapar-Mudlaw <martyna.szapar-mudlaw@linux.intel.com> <martyna.szapar-mudlaw@intel.com>
Mathieu Othacehe <m.othacehe@gmail.com> <othacehe@gnu.org>
Mathieu Othacehe <othacehe@gnu.org> <m.othacehe@gmail.com>
Mat Martineau <martineau@kernel.org> <mathew.j.martineau@linux.intel.com>
Mat Martineau <martineau@kernel.org> <mathewm@codeaurora.org>
Matthew Wilcox <willy@infradead.org> <matthew.r.wilcox@intel.com>
@ -735,6 +735,7 @@ Wolfram Sang <wsa@kernel.org> <w.sang@pengutronix.de>
Wolfram Sang <wsa@kernel.org> <wsa@the-dreams.de>
Yakir Yang <kuankuan.y@gmail.com> <ykk@rock-chips.com>
Yanteng Si <si.yanteng@linux.dev> <siyanteng@loongson.cn>
Ying Huang <huang.ying.caritas@gmail.com> <ying.huang@intel.com>
Yusuke Goda <goda.yusuke@renesas.com>
Zack Rusin <zack.rusin@broadcom.com> <zackr@vmware.com>
Zhu Yanjun <zyjzyj2000@gmail.com> <yanjunz@nvidia.com>

View file

@ -445,8 +445,10 @@ event code Key Notes
0x1008 0x07 FN+F8 IBM: toggle screen expand
Lenovo: configure UltraNav,
or toggle screen expand.
On newer platforms (2024+)
replaced by 0x131f (see below)
On 2024 platforms replaced by
0x131f (see below) and on newer
platforms (2025 +) keycode is
replaced by 0x1401 (see below).
0x1009 0x08 FN+F9 -
@ -506,9 +508,11 @@ event code Key Notes
0x1019 0x18 unknown
0x131f ... FN+F8 Platform Mode change.
0x131f ... FN+F8 Platform Mode change (2024 systems).
Implemented in driver.
0x1401 ... FN+F8 Platform Mode change (2025 + systems).
Implemented in driver.
... ... ...
0x1020 0x1F unknown

View file

@ -436,7 +436,7 @@ AnonHugePmdMapped).
The number of file transparent huge pages mapped to userspace is available
by reading ShmemPmdMapped and ShmemHugePages fields in ``/proc/meminfo``.
To identify what applications are mapping file transparent huge pages, it
is necessary to read ``/proc/PID/smaps`` and count the FileHugeMapped fields
is necessary to read ``/proc/PID/smaps`` and count the FilePmdMapped fields
for each mapping.
Note that reading the smaps file is expensive and reading it

View file

@ -251,9 +251,7 @@ performance supported in `AMD CPPC Performance Capability <perf_cap_>`_).
In some ASICs, the highest CPPC performance is not the one in the ``_CPC``
table, so we need to expose it to sysfs. If boost is not active, but
still supported, this maximum frequency will be larger than the one in
``cpuinfo``. On systems that support preferred core, the driver will have
different values for some cores than others and this will reflect the values
advertised by the platform at bootup.
``cpuinfo``.
This attribute is read-only.
``amd_pstate_lowest_nonlinear_freq``

View file

@ -114,8 +114,9 @@ patternProperties:
table that specifies the PPID to LIODN mapping. Needed if the PAMU is
used. Value is a 12 bit value where value is a LIODN ID for this JR.
This property is normally set by boot firmware.
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 0xfff
$ref: /schemas/types.yaml#/definitions/uint32-array
items:
- maximum: 0xfff
'^rtic@[0-9a-f]+$':
type: object
@ -186,8 +187,9 @@ patternProperties:
Needed if the PAMU is used. Value is a 12 bit value where value
is a LIODN ID for this JR. This property is normally set by boot
firmware.
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 0xfff
$ref: /schemas/types.yaml#/definitions/uint32-array
items:
- maximum: 0xfff
fsl,rtic-region:
description:

View file

@ -90,7 +90,7 @@ properties:
adi,dsi-lanes:
description: Number of DSI data lanes connected to the DSI host.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [ 1, 2, 3, 4 ]
enum: [ 2, 3, 4 ]
"#sound-dai-cells":
const: 0

View file

@ -82,7 +82,7 @@ examples:
uimage@100000 {
reg = <0x0100000 0x200000>;
compress = "lzma";
compression = "lzma";
};
};

View file

@ -35,6 +35,7 @@ properties:
fsl,liodn:
$ref: /schemas/types.yaml#/definitions/uint32-array
maxItems: 2
description: See pamu.txt. Two LIODN(s). DQRR LIODN (DLIODN) and Frame LIODN
(FLIODN)
@ -69,6 +70,7 @@ patternProperties:
type: object
properties:
fsl,liodn:
$ref: /schemas/types.yaml#/definitions/uint32-array
description: See pamu.txt, PAMU property used for static LIODN assignment
fsl,iommu-parent:

View file

@ -36,12 +36,14 @@ unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@34 {
compatible = "adi,ssm2518";
reg = <0x34>;
gpios = <&gpio 5 0>;
};
};
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@34 {
compatible = "adi,ssm2518";
reg = <0x34>;
gpios = <&gpio 5 GPIO_ACTIVE_HIGH>;
};
};

View file

@ -23,6 +23,7 @@ properties:
- allwinner,sun8i-h3-codec
- allwinner,sun8i-v3s-codec
- allwinner,sun50i-h616-codec
- allwinner,suniv-f1c100s-codec
reg:
maxItems: 1
@ -77,6 +78,7 @@ properties:
- MIC1
- MIC2
- MIC3
- MIC
# Microphone Biases from the SoC
- HBIAS
@ -87,6 +89,8 @@ properties:
- Headset Mic
- Line In
- Line Out
- Right FM In
- Left FM In
- Mic
- Speaker
@ -270,6 +274,33 @@ allOf:
- const: rx
- const: tx
- if:
properties:
compatible:
enum:
- allwinner,suniv-f1c100s-codec
then:
properties:
allwinner,audio-routing:
items:
enum:
- HP
- HPCOM
- LINEIN
- LINEOUT
- MIC
- HBIAS
- MBIAS
- Headphone
- Headset Mic
- Line In
- Line Out
- Right FM In
- Left FM In
- Mic
- Speaker
unevaluatedProperties: false
examples:

View file

@ -18,6 +18,7 @@ properties:
compatible:
enum:
- awinic,aw88081
- awinic,aw88083
- awinic,aw88261
- awinic,aw88395
- awinic,aw88399
@ -58,6 +59,7 @@ allOf:
contains:
enum:
- awinic,aw88081
- awinic,aw88083
- awinic,aw88261
then:
properties:

View file

@ -53,10 +53,10 @@ unevaluatedProperties: false
examples:
- |
codec {
compatible = "everest,es7134";
#sound-dai-cells = <0>;
VDD-supply = <&vdd_supply>;
};
codec {
compatible = "everest,es7134";
#sound-dai-cells = <0>;
VDD-supply = <&vdd_supply>;
};
...

View file

@ -54,14 +54,15 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
codec {
compatible = "everest,es7241";
#sound-dai-cells = <0>;
reset-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
VDDP-supply = <&vddp_supply>;
VDDA-supply = <&vdda_supply>;
VDDD-supply = <&vddd_supply>;
};
#include <dt-bindings/gpio/gpio.h>
codec {
compatible = "everest,es7241";
#sound-dai-cells = <0>;
reset-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
VDDP-supply = <&vddp_supply>;
VDDA-supply = <&vdda_supply>;
VDDD-supply = <&vddd_supply>;
};
...

View file

@ -87,20 +87,20 @@ examples:
#include <dt-bindings/clock/imx8mn-clock.h>
easrc: easrc@300c0000 {
compatible = "fsl,imx8mn-easrc";
reg = <0x300c0000 0x10000>;
interrupts = <0x0 122 0x4>;
clocks = <&clk IMX8MN_CLK_ASRC_ROOT>;
clock-names = "mem";
dmas = <&sdma2 16 23 0> , <&sdma2 17 23 0>,
<&sdma2 18 23 0> , <&sdma2 19 23 0>,
<&sdma2 20 23 0> , <&sdma2 21 23 0>,
<&sdma2 22 23 0> , <&sdma2 23 23 0>;
dma-names = "ctx0_rx", "ctx0_tx",
"ctx1_rx", "ctx1_tx",
"ctx2_rx", "ctx2_tx",
"ctx3_rx", "ctx3_tx";
firmware-name = "imx/easrc/easrc-imx8mn.bin";
fsl,asrc-rate = <8000>;
fsl,asrc-format = <2>;
compatible = "fsl,imx8mn-easrc";
reg = <0x300c0000 0x10000>;
interrupts = <0x0 122 0x4>;
clocks = <&clk IMX8MN_CLK_ASRC_ROOT>;
clock-names = "mem";
dmas = <&sdma2 16 23 0> , <&sdma2 17 23 0>,
<&sdma2 18 23 0> , <&sdma2 19 23 0>,
<&sdma2 20 23 0> , <&sdma2 21 23 0>,
<&sdma2 22 23 0> , <&sdma2 23 23 0>;
dma-names = "ctx0_rx", "ctx0_tx",
"ctx1_rx", "ctx1_tx",
"ctx2_rx", "ctx2_tx",
"ctx3_rx", "ctx3_tx";
firmware-name = "imx/easrc/easrc-imx8mn.bin";
fsl,asrc-rate = <8000>;
fsl,asrc-format = <2>;
};

View file

@ -25,6 +25,7 @@ properties:
- fsl,imx8mm-micfil
- fsl,imx8mp-micfil
- fsl,imx93-micfil
- fsl,imx943-micfil
reg:
maxItems: 1

View file

@ -23,6 +23,8 @@ properties:
- fsl,imx8qm-mqs
- fsl,imx8qxp-mqs
- fsl,imx93-mqs
- fsl,imx943-aonmix-mqs
- fsl,imx943-wakeupmix-mqs
- fsl,imx95-aonmix-mqs
- fsl,imx95-netcmix-mqs

View file

@ -140,21 +140,21 @@ examples:
#include <dt-bindings/reset/imx8mp-reset.h>
xcvr: xcvr@30cc0000 {
compatible = "fsl,imx8mp-xcvr";
reg = <0x30cc0000 0x800>,
<0x30cc0800 0x400>,
<0x30cc0c00 0x080>,
<0x30cc0e00 0x080>;
reg-names = "ram", "regs", "rxfifo", "txfifo";
interrupts = <GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&audiomix_clk IMX8MP_CLK_AUDIOMIX_EARC_IPG>,
<&audiomix_clk IMX8MP_CLK_AUDIOMIX_EARC_PHY>,
<&audiomix_clk IMX8MP_CLK_AUDIOMIX_SPBA2_ROOT>,
<&audiomix_clk IMX8MP_CLK_AUDIOMIX_AUDPLL_ROOT>;
clock-names = "ipg", "phy", "spba", "pll_ipg";
dmas = <&sdma2 30 2 0>, <&sdma2 31 2 0>;
dma-names = "rx", "tx";
resets = <&audiomix_reset 0>;
compatible = "fsl,imx8mp-xcvr";
reg = <0x30cc0000 0x800>,
<0x30cc0800 0x400>,
<0x30cc0c00 0x080>,
<0x30cc0e00 0x080>;
reg-names = "ram", "regs", "rxfifo", "txfifo";
interrupts = <GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&audiomix_clk IMX8MP_CLK_AUDIOMIX_EARC_IPG>,
<&audiomix_clk IMX8MP_CLK_AUDIOMIX_EARC_PHY>,
<&audiomix_clk IMX8MP_CLK_AUDIOMIX_SPBA2_ROOT>,
<&audiomix_clk IMX8MP_CLK_AUDIOMIX_AUDPLL_ROOT>;
clock-names = "ipg", "phy", "spba", "pll_ipg";
dmas = <&sdma2 30 2 0>, <&sdma2 31 2 0>;
dma-names = "rx", "tx";
resets = <&audiomix_reset 0>;
};

View file

@ -72,19 +72,19 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
#define KEEM_BAY_PSS_AUX_I2S3
#define KEEM_BAY_PSS_I2S3
i2s3: i2s@20140000 {
compatible = "intel,keembay-i2s";
#sound-dai-cells = <0>;
reg = <0x20140000 0x200>, /* I2S registers */
<0x202a00a4 0x4>; /* I2S gen configuration */
reg-names = "i2s-regs", "i2s_gen_cfg";
interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
clock-names = "osc", "apb_clk";
clocks = <&scmi_clk KEEM_BAY_PSS_AUX_I2S3>, <&scmi_clk KEEM_BAY_PSS_I2S3>;
dmas = <&axi_dma0 29>, <&axi_dma0 33>;
dma-names = "tx", "rx";
};
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
#define KEEM_BAY_PSS_AUX_I2S3
#define KEEM_BAY_PSS_I2S3
i2s@20140000 {
compatible = "intel,keembay-i2s";
#sound-dai-cells = <0>;
reg = <0x20140000 0x200>, /* I2S registers */
<0x202a00a4 0x4>; /* I2S gen configuration */
reg-names = "i2s-regs", "i2s_gen_cfg";
interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
clock-names = "osc", "apb_clk";
clocks = <&scmi_clk KEEM_BAY_PSS_AUX_I2S3>, <&scmi_clk KEEM_BAY_PSS_I2S3>;
dmas = <&axi_dma0 29>, <&axi_dma0 33>;
dma-names = "tx", "rx";
};

View file

@ -14,11 +14,15 @@ allOf:
properties:
compatible:
enum:
- mediatek,mt8188-es8326
- mediatek,mt8188-mt6359-evb
- mediatek,mt8188-nau8825
- mediatek,mt8188-rt5682s
oneOf:
- enum:
- mediatek,mt8188-es8326
- mediatek,mt8188-mt6359-evb
- mediatek,mt8188-nau8825
- mediatek,mt8188-rt5682s
- items:
- const: mediatek,mt8390-mt6359-evk
- const: mediatek,mt8188-mt6359-evb
audio-routing:
description:
@ -56,6 +60,8 @@ patternProperties:
- ETDM2_OUT_BE
- ETDM3_OUT_BE
- PCM1_BE
- DL_SRC_BE
- UL_SRC_BE
codec:
description: Holds subnode which indicates codec dai.

View file

@ -55,16 +55,18 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
audio-codec@2a {
compatible = "neofidelity,ntp8918";
#sound-dai-cells = <0>;
reg = <0x2a>;
clocks = <&clkc 150>, <&clkc 151>, <&clkc 152>;
clock-names = "wck", "scl", "bck";
reset-gpios = <&gpio 5 GPIO_ACTIVE_LOW>;
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
audio-codec@2a {
compatible = "neofidelity,ntp8918";
#sound-dai-cells = <0>;
reg = <0x2a>;
clocks = <&clkc 150>, <&clkc 151>, <&clkc 152>;
clock-names = "wck", "scl", "bck";
reset-gpios = <&gpio 5 GPIO_ACTIVE_LOW>;
};
};
};

View file

@ -0,0 +1,156 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/realtek,rt5682.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Realtek rt5682 and rt5682i codecs
maintainers:
- Bard Liao <bardliao@realtek.com>
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
enum:
- realtek,rt5682
- realtek,rt5682i
reg:
maxItems: 1
description: I2C address of the device.
interrupts:
maxItems: 1
description: The CODEC's interrupt output.
realtek,dmic1-data-pin:
$ref: /schemas/types.yaml#/definitions/uint32
enum:
- 0 # dmic1 data is not used
- 1 # using GPIO2 pin as dmic1 data pin
- 2 # using GPIO5 pin as dmic1 data pin
description:
Specify which GPIO pin be used as DMIC1 data pin.
realtek,dmic1-clk-pin:
$ref: /schemas/types.yaml#/definitions/uint32
enum:
- 0 # using GPIO1 pin as dmic1 clock pin
- 1 # using GPIO3 pin as dmic1 clock pin
description:
Specify which GPIO pin be used as DMIC1 clk pin.
realtek,jd-src:
$ref: /schemas/types.yaml#/definitions/uint32
enum:
- 0 # No JD is used
- 1 # using JD1 as JD source
description:
Specify which JD source be used.
realtek,ldo1-en-gpios:
description:
The GPIO that controls the CODEC's LDO1_EN pin.
realtek,btndet-delay:
$ref: /schemas/types.yaml#/definitions/uint32
description:
The debounce delay for push button.
The delay time is realtek,btndet-delay value multiple of 8.192 ms.
If absent, the default is 16.
realtek,dmic-clk-rate-hz:
description:
Set the clock rate (hz) for the requirement of the particular DMIC.
realtek,dmic-delay-ms:
description:
Set the delay time (ms) for the requirement of the particular DMIC.
realtek,dmic-clk-driving-high:
type: boolean
description:
Set the high driving of the DMIC clock out.
clocks:
items:
- description: phandle and clock specifier for codec MCLK.
clock-names:
items:
- const: mclk
"#clock-cells":
const: 1
clock-output-names:
minItems: 2
maxItems: 2
description: Name given for DAI word clock and bit clock outputs.
"#sound-dai-cells":
const: 1
AVDD-supply:
description: Regulator supplying analog power through the AVDD pin.
MICVDD-supply:
description: Regulator supplying power for the microphone bias through
the MICVDD pin.
VBAT-supply:
description: Regulator supplying battery power through the VBAT pin.
DBVDD-supply:
description: Regulator supplying I/O power through the DBVDD pin.
LDO1-IN-supply:
description: Regulator supplying power to the digital core and charge
pump through the LDO1_IN pin.
required:
- compatible
- reg
- AVDD-supply
- VBAT-supply
- MICVDD-supply
- DBVDD-supply
- LDO1-IN-supply
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@1a {
compatible = "realtek,rt5682";
reg = <0x1a>;
interrupts = <6 IRQ_TYPE_LEVEL_HIGH>;
realtek,ldo1-en-gpios =
<&gpio 2 GPIO_ACTIVE_HIGH>;
realtek,dmic1-data-pin = <1>;
realtek,dmic1-clk-pin = <1>;
realtek,jd-src = <1>;
#clock-cells = <1>;
clock-output-names = "rt5682-dai-wclk", "rt5682-dai-bclk";
clocks = <&osc>;
clock-names = "mclk";
AVDD-supply = <&avdd_reg>;
VBAT-supply = <&vbat_reg>;
MICVDD-supply = <&micvdd_reg>;
DBVDD-supply = <&dbvdd_reg>;
LDO1-IN-supply = <&ldo1_in_reg>;
};
};

View file

@ -112,12 +112,6 @@ properties:
description: List of necessary clock names.
# details are defined below
post-init-providers:
description: At least if rsnd is using DPCM connection on Audio-Graph-Card2,
fw_devlink might doesn't have enough information to break the cycle. rsnd
driver will not be probed in such case. Same problem might occur with
Multi-CPU/Codec or Codec2Codec.
# ports is below
port:
$ref: audio-graph-port.yaml#/definitions/port-base

View file

@ -19,6 +19,7 @@ properties:
- renesas,r9a07g043-ssi # RZ/G2UL and RZ/Five
- renesas,r9a07g044-ssi # RZ/G2{L,LC}
- renesas,r9a07g054-ssi # RZ/V2L
- renesas,r9a08g045-ssi # RZ/G3S
- const: renesas,rz-ssi
reg:
@ -57,24 +58,6 @@ properties:
dmas:
minItems: 1
maxItems: 2
description:
The first cell represents a phandle to dmac.
The second cell specifies the encoded MID/RID values of the SSI port
connected to the DMA client and the slave channel configuration
parameters.
bits[0:9] - Specifies MID/RID value of a SSI channel as below
MID/RID value of SSI rx0 = 0x256
MID/RID value of SSI tx0 = 0x255
MID/RID value of SSI rx1 = 0x25a
MID/RID value of SSI tx1 = 0x259
MID/RID value of SSI rt2 = 0x25f
MID/RID value of SSI rx3 = 0x262
MID/RID value of SSI tx3 = 0x261
bit[10] - HIEN = 1, Detects a request in response to the rising edge
of the signal
bit[11] - LVL = 0, Detects based on the edge
bits[12:14] - AM = 2, Bus cycle mode
bit[15] - TM = 0, Single transfer mode
dma-names:
oneOf:

View file

@ -1,98 +0,0 @@
RT5682 audio CODEC
This device supports I2C only.
Required properties:
- compatible : "realtek,rt5682" or "realtek,rt5682i"
- reg : The I2C address of the device.
- AVDD-supply: phandle to the regulator supplying analog power through the
AVDD pin
- MICVDD-supply: phandle to the regulator supplying power for the microphone
bias through the MICVDD pin. Either MICVDD or VBAT should be present.
- VBAT-supply: phandle to the regulator supplying battery power through the
VBAT pin. Either MICVDD or VBAT should be present.
- DBVDD-supply: phandle to the regulator supplying I/O power through the DBVDD
pin.
- LDO1-IN-supply: phandle to the regulator supplying power to the digital core
and charge pump through the LDO1_IN pin.
Optional properties:
- interrupts : The CODEC's interrupt output.
- realtek,dmic1-data-pin
0: dmic1 is not used
1: using GPIO2 pin as dmic1 data pin
2: using GPIO5 pin as dmic1 data pin
- realtek,dmic1-clk-pin
0: using GPIO1 pin as dmic1 clock pin
1: using GPIO3 pin as dmic1 clock pin
- realtek,jd-src
0: No JD is used
1: using JD1 as JD source
- realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin.
- realtek,btndet-delay
The debounce delay for push button.
The delay time is realtek,btndet-delay value multiple of 8.192 ms.
If absent, the default is 16.
- #clock-cells : Should be set to '<1>', wclk and bclk sources provided.
- clock-output-names : Name given for DAI clocks output.
- clocks : phandle and clock specifier for codec MCLK.
- clock-names : Clock name string for 'clocks' attribute, should be "mclk".
- realtek,dmic-clk-rate-hz : Set the clock rate (hz) for the requirement of
the particular DMIC.
- realtek,dmic-delay-ms : Set the delay time (ms) for the requirement of
the particular DMIC.
- realtek,dmic-clk-driving-high : Set the high driving of the DMIC clock out.
- #sound-dai-cells: Should be set to '<1>'.
Pins on the device (for linking into audio routes) for RT5682:
* DMIC L1
* DMIC R1
* IN1P
* HPOL
* HPOR
Example:
rt5682 {
compatible = "realtek,rt5682i";
reg = <0x1a>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(U, 6) IRQ_TYPE_LEVEL_HIGH>;
realtek,ldo1-en-gpios =
<&gpio TEGRA_GPIO(R, 2) GPIO_ACTIVE_HIGH>;
realtek,dmic1-data-pin = <1>;
realtek,dmic1-clk-pin = <1>;
realtek,jd-src = <1>;
realtek,btndet-delay = <16>;
#clock-cells = <1>;
clock-output-names = "rt5682-dai-wclk", "rt5682-dai-bclk";
clocks = <&osc>;
clock-names = "mclk";
AVDD-supply = <&avdd_reg>;
MICVDD-supply = <&micvdd_reg>;
DBVDD-supply = <&dbvdd_reg>;
LDO1-IN-supply = <&ldo1_in_reg>;
};

View file

@ -159,19 +159,21 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
/* example for two devices with interrupt support */
#address-cells = <1>;
#size-cells = <0>;
pcm6240: audio-codec@48 {
compatible = "ti,pcm6240";
reg = <0x48>, /* primary-device */
<0x4b>; /* secondary-device */
#sound-dai-cells = <0>;
reset-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
interrupt-parent = <&gpio1>;
interrupts = <15>;
};
};
#include <dt-bindings/gpio/gpio.h>
i2c {
/* example for two devices with interrupt support */
#address-cells = <1>;
#size-cells = <0>;
audio-codec@48 {
compatible = "ti,pcm6240";
reg = <0x48>, /* primary-device */
<0x4b>; /* secondary-device */
#sound-dai-cells = <0>;
reset-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
interrupt-parent = <&gpio1>;
interrupts = <15>;
};
};
...

View file

@ -65,17 +65,19 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec: codec@4c {
compatible = "ti,tas2562";
reg = <0x4c>;
#sound-dai-cells = <0>;
interrupt-parent = <&gpio1>;
interrupts = <14>;
shutdown-gpios = <&gpio1 15 0>;
ti,imon-slot-no = <0>;
};
};
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@4c {
compatible = "ti,tas2562";
reg = <0x4c>;
#sound-dai-cells = <0>;
interrupt-parent = <&gpio1>;
interrupts = <14>;
shutdown-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
ti,imon-slot-no = <0>;
};
};

View file

@ -69,19 +69,21 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec: codec@41 {
compatible = "ti,tas2770";
reg = <0x41>;
#sound-dai-cells = <0>;
interrupt-parent = <&gpio1>;
interrupts = <14>;
reset-gpio = <&gpio1 15 0>;
shutdown-gpios = <&gpio1 14 0>;
ti,imon-slot-no = <0>;
ti,vmon-slot-no = <2>;
};
};
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@41 {
compatible = "ti,tas2770";
reg = <0x41>;
#sound-dai-cells = <0>;
interrupt-parent = <&gpio1>;
interrupts = <14>;
reset-gpio = <&gpio1 15 GPIO_ACTIVE_HIGH>;
shutdown-gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>;
ti,imon-slot-no = <0>;
ti,vmon-slot-no = <2>;
};
};

View file

@ -101,22 +101,24 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
/* example with quad tas2781s, such as tablet or pad device */
#address-cells = <1>;
#size-cells = <0>;
quad_tas2781: tas2781@38 {
compatible = "ti,tas2781";
reg = <0x38>, /* Audio slot 0 */
<0x3a>, /* Audio slot 1 */
<0x39>, /* Audio slot 2 */
<0x3b>; /* Audio slot 3 */
#include <dt-bindings/gpio/gpio.h>
#sound-dai-cells = <0>;
reset-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
interrupt-parent = <&gpio1>;
interrupts = <15>;
};
};
i2c {
/* example with quad tas2781s, such as tablet or pad device */
#address-cells = <1>;
#size-cells = <0>;
audio-codec@38 {
compatible = "ti,tas2781";
reg = <0x38>, /* Audio slot 0 */
<0x3a>, /* Audio slot 1 */
<0x39>, /* Audio slot 2 */
<0x3b>; /* Audio slot 3 */
#sound-dai-cells = <0>;
reset-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
interrupt-parent = <&gpio1>;
interrupts = <15>;
};
};
...

View file

@ -62,21 +62,23 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec: codec@38 {
compatible = "ti,tas2764";
reg = <0x38>;
#sound-dai-cells = <0>;
interrupt-parent = <&gpio1>;
interrupts = <14>;
reset-gpios = <&gpio1 15 0>;
shutdown-gpios = <&gpio1 15 0>;
ti,imon-slot-no = <0>;
ti,vmon-slot-no = <2>;
};
};
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@38 {
compatible = "ti,tas2764";
reg = <0x38>;
#sound-dai-cells = <0>;
interrupt-parent = <&gpio1>;
interrupts = <14>;
reset-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
shutdown-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
ti,imon-slot-no = <0>;
ti,vmon-slot-no = <2>;
};
};
...

View file

@ -112,22 +112,24 @@ unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
#include <dt-bindings/gpio/gpio.h>
codec@2a {
compatible = "ti,tas5717";
reg = <0x2a>;
#sound-dai-cells = <0>;
reset-gpios = <&gpio1 15 0>;
pdn-gpios = <&gpio1 15 0>;
AVDD-supply = <&avdd_supply>;
DVDD-supply = <&dvdd_supply>;
HPVDD-supply = <&hpvdd_supply>;
PVDD_AB-supply = <&pvdd_ab_supply>;
PVDD_CD-supply = <&pvdd_cd_supply>;
};
};
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@2a {
compatible = "ti,tas5717";
reg = <0x2a>;
#sound-dai-cells = <0>;
reset-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
pdn-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
AVDD-supply = <&avdd_supply>;
DVDD-supply = <&dvdd_supply>;
HPVDD-supply = <&hpvdd_supply>;
PVDD_AB-supply = <&pvdd_ab_supply>;
PVDD_CD-supply = <&pvdd_cd_supply>;
};
};
...

View file

@ -3,3 +3,853 @@
=================
Process Addresses
=================
.. toctree::
:maxdepth: 3
Userland memory ranges are tracked by the kernel via Virtual Memory Areas or
'VMA's of type :c:struct:`!struct vm_area_struct`.
Each VMA describes a virtually contiguous memory range with identical
attributes, each described by a :c:struct:`!struct vm_area_struct`
object. Userland access outside of VMAs is invalid except in the case where an
adjacent stack VMA could be extended to contain the accessed address.
All VMAs are contained within one and only one virtual address space, described
by a :c:struct:`!struct mm_struct` object which is referenced by all tasks (that is,
threads) which share the virtual address space. We refer to this as the
:c:struct:`!mm`.
Each mm object contains a maple tree data structure which describes all VMAs
within the virtual address space.
.. note:: An exception to this is the 'gate' VMA which is provided by
architectures which use :c:struct:`!vsyscall` and is a global static
object which does not belong to any specific mm.
-------
Locking
-------
The kernel is designed to be highly scalable against concurrent read operations
on VMA **metadata** so a complicated set of locks are required to ensure memory
corruption does not occur.
.. note:: Locking VMAs for their metadata does not have any impact on the memory
they describe nor the page tables that map them.
Terminology
-----------
* **mmap locks** - Each MM has a read/write semaphore :c:member:`!mmap_lock`
which locks at a process address space granularity which can be acquired via
:c:func:`!mmap_read_lock`, :c:func:`!mmap_write_lock` and variants.
* **VMA locks** - The VMA lock is at VMA granularity (of course) which behaves
as a read/write semaphore in practice. A VMA read lock is obtained via
:c:func:`!lock_vma_under_rcu` (and unlocked via :c:func:`!vma_end_read`) and a
write lock via :c:func:`!vma_start_write` (all VMA write locks are unlocked
automatically when the mmap write lock is released). To take a VMA write lock
you **must** have already acquired an :c:func:`!mmap_write_lock`.
* **rmap locks** - When trying to access VMAs through the reverse mapping via a
:c:struct:`!struct address_space` or :c:struct:`!struct anon_vma` object
(reachable from a folio via :c:member:`!folio->mapping`). VMAs must be stabilised via
:c:func:`!anon_vma_[try]lock_read` or :c:func:`!anon_vma_[try]lock_write` for
anonymous memory and :c:func:`!i_mmap_[try]lock_read` or
:c:func:`!i_mmap_[try]lock_write` for file-backed memory. We refer to these
locks as the reverse mapping locks, or 'rmap locks' for brevity.
We discuss page table locks separately in the dedicated section below.
The first thing **any** of these locks achieve is to **stabilise** the VMA
within the MM tree. That is, guaranteeing that the VMA object will not be
deleted from under you nor modified (except for some specific fields
described below).
Stabilising a VMA also keeps the address space described by it around.
Lock usage
----------
If you want to **read** VMA metadata fields or just keep the VMA stable, you
must do one of the following:
* Obtain an mmap read lock at the MM granularity via :c:func:`!mmap_read_lock` (or a
suitable variant), unlocking it with a matching :c:func:`!mmap_read_unlock` when
you're done with the VMA, *or*
* Try to obtain a VMA read lock via :c:func:`!lock_vma_under_rcu`. This tries to
acquire the lock atomically so might fail, in which case fall-back logic is
required to instead obtain an mmap read lock if this returns :c:macro:`!NULL`,
*or*
* Acquire an rmap lock before traversing the locked interval tree (whether
anonymous or file-backed) to obtain the required VMA.
If you want to **write** VMA metadata fields, then things vary depending on the
field (we explore each VMA field in detail below). For the majority you must:
* Obtain an mmap write lock at the MM granularity via :c:func:`!mmap_write_lock` (or a
suitable variant), unlocking it with a matching :c:func:`!mmap_write_unlock` when
you're done with the VMA, *and*
* Obtain a VMA write lock via :c:func:`!vma_start_write` for each VMA you wish to
modify, which will be released automatically when :c:func:`!mmap_write_unlock` is
called.
* If you want to be able to write to **any** field, you must also hide the VMA
from the reverse mapping by obtaining an **rmap write lock**.
VMA locks are special in that you must obtain an mmap **write** lock **first**
in order to obtain a VMA **write** lock. A VMA **read** lock however can be
obtained without any other lock (:c:func:`!lock_vma_under_rcu` will acquire then
release an RCU lock to lookup the VMA for you).
This constrains the impact of writers on readers, as a writer can interact with
one VMA while a reader interacts with another simultaneously.
.. note:: The primary users of VMA read locks are page fault handlers, which
means that without a VMA write lock, page faults will run concurrent with
whatever you are doing.
Examining all valid lock states:
.. table::
========= ======== ========= ======= ===== =========== ==========
mmap lock VMA lock rmap lock Stable? Read? Write most? Write all?
========= ======== ========= ======= ===== =========== ==========
\- \- \- N N N N
\- R \- Y Y N N
\- \- R/W Y Y N N
R/W \-/R \-/R/W Y Y N N
W W \-/R Y Y Y N
W W W Y Y Y Y
========= ======== ========= ======= ===== =========== ==========
.. warning:: While it's possible to obtain a VMA lock while holding an mmap read lock,
attempting to do the reverse is invalid as it can result in deadlock - if
another task already holds an mmap write lock and attempts to acquire a VMA
write lock that will deadlock on the VMA read lock.
All of these locks behave as read/write semaphores in practice, so you can
obtain either a read or a write lock for each of these.
.. note:: Generally speaking, a read/write semaphore is a class of lock which
permits concurrent readers. However a write lock can only be obtained
once all readers have left the critical region (and pending readers
made to wait).
This renders read locks on a read/write semaphore concurrent with other
readers and write locks exclusive against all others holding the semaphore.
VMA fields
^^^^^^^^^^
We can subdivide :c:struct:`!struct vm_area_struct` fields by their purpose, which makes it
easier to explore their locking characteristics:
.. note:: We exclude VMA lock-specific fields here to avoid confusion, as these
are in effect an internal implementation detail.
.. table:: Virtual layout fields
===================== ======================================== ===========
Field Description Write lock
===================== ======================================== ===========
:c:member:`!vm_start` Inclusive start virtual address of range mmap write,
VMA describes. VMA write,
rmap write.
:c:member:`!vm_end` Exclusive end virtual address of range mmap write,
VMA describes. VMA write,
rmap write.
:c:member:`!vm_pgoff` Describes the page offset into the file, mmap write,
the original page offset within the VMA write,
virtual address space (prior to any rmap write.
:c:func:`!mremap`), or PFN if a PFN map
and the architecture does not support
:c:macro:`!CONFIG_ARCH_HAS_PTE_SPECIAL`.
===================== ======================================== ===========
These fields describes the size, start and end of the VMA, and as such cannot be
modified without first being hidden from the reverse mapping since these fields
are used to locate VMAs within the reverse mapping interval trees.
.. table:: Core fields
============================ ======================================== =========================
Field Description Write lock
============================ ======================================== =========================
:c:member:`!vm_mm` Containing mm_struct. None - written once on
initial map.
:c:member:`!vm_page_prot` Architecture-specific page table mmap write, VMA write.
protection bits determined from VMA
flags.
:c:member:`!vm_flags` Read-only access to VMA flags describing N/A
attributes of the VMA, in union with
private writable
:c:member:`!__vm_flags`.
:c:member:`!__vm_flags` Private, writable access to VMA flags mmap write, VMA write.
field, updated by
:c:func:`!vm_flags_*` functions.
:c:member:`!vm_file` If the VMA is file-backed, points to a None - written once on
struct file object describing the initial map.
underlying file, if anonymous then
:c:macro:`!NULL`.
:c:member:`!vm_ops` If the VMA is file-backed, then either None - Written once on
the driver or file-system provides a initial map by
:c:struct:`!struct vm_operations_struct` :c:func:`!f_ops->mmap()`.
object describing callbacks to be
invoked on VMA lifetime events.
:c:member:`!vm_private_data` A :c:member:`!void *` field for Handled by driver.
driver-specific metadata.
============================ ======================================== =========================
These are the core fields which describe the MM the VMA belongs to and its attributes.
.. table:: Config-specific fields
================================= ===================== ======================================== ===============
Field Configuration option Description Write lock
================================= ===================== ======================================== ===============
:c:member:`!anon_name` CONFIG_ANON_VMA_NAME A field for storing a mmap write,
:c:struct:`!struct anon_vma_name` VMA write.
object providing a name for anonymous
mappings, or :c:macro:`!NULL` if none
is set or the VMA is file-backed. The
underlying object is reference counted
and can be shared across multiple VMAs
for scalability.
:c:member:`!swap_readahead_info` CONFIG_SWAP Metadata used by the swap mechanism mmap read,
to perform readahead. This field is swap-specific
accessed atomically. lock.
:c:member:`!vm_policy` CONFIG_NUMA :c:type:`!mempolicy` object which mmap write,
describes the NUMA behaviour of the VMA write.
VMA. The underlying object is reference
counted.
:c:member:`!numab_state` CONFIG_NUMA_BALANCING :c:type:`!vma_numab_state` object which mmap read,
describes the current state of numab-specific
NUMA balancing in relation to this VMA. lock.
Updated under mmap read lock by
:c:func:`!task_numa_work`.
:c:member:`!vm_userfaultfd_ctx` CONFIG_USERFAULTFD Userfaultfd context wrapper object of mmap write,
type :c:type:`!vm_userfaultfd_ctx`, VMA write.
either of zero size if userfaultfd is
disabled, or containing a pointer
to an underlying
:c:type:`!userfaultfd_ctx` object which
describes userfaultfd metadata.
================================= ===================== ======================================== ===============
These fields are present or not depending on whether the relevant kernel
configuration option is set.
.. table:: Reverse mapping fields
=================================== ========================================= ============================
Field Description Write lock
=================================== ========================================= ============================
:c:member:`!shared.rb` A red/black tree node used, if the mmap write, VMA write,
mapping is file-backed, to place the VMA i_mmap write.
in the
:c:member:`!struct address_space->i_mmap`
red/black interval tree.
:c:member:`!shared.rb_subtree_last` Metadata used for management of the mmap write, VMA write,
interval tree if the VMA is file-backed. i_mmap write.
:c:member:`!anon_vma_chain` List of pointers to both forked/CoWd mmap read, anon_vma write.
:c:type:`!anon_vma` objects and
:c:member:`!vma->anon_vma` if it is
non-:c:macro:`!NULL`.
:c:member:`!anon_vma` :c:type:`!anon_vma` object used by When :c:macro:`NULL` and
anonymous folios mapped exclusively to setting non-:c:macro:`NULL`:
this VMA. Initially set by mmap read, page_table_lock.
:c:func:`!anon_vma_prepare` serialised
by the :c:macro:`!page_table_lock`. This When non-:c:macro:`NULL` and
is set as soon as any page is faulted in. setting :c:macro:`NULL`:
mmap write, VMA write,
anon_vma write.
=================================== ========================================= ============================
These fields are used to both place the VMA within the reverse mapping, and for
anonymous mappings, to be able to access both related :c:struct:`!struct anon_vma` objects
and the :c:struct:`!struct anon_vma` in which folios mapped exclusively to this VMA should
reside.
.. note:: If a file-backed mapping is mapped with :c:macro:`!MAP_PRIVATE` set
then it can be in both the :c:type:`!anon_vma` and :c:type:`!i_mmap`
trees at the same time, so all of these fields might be utilised at
once.
Page tables
-----------
We won't speak exhaustively on the subject but broadly speaking, page tables map
virtual addresses to physical ones through a series of page tables, each of
which contain entries with physical addresses for the next page table level
(along with flags), and at the leaf level the physical addresses of the
underlying physical data pages or a special entry such as a swap entry,
migration entry or other special marker. Offsets into these pages are provided
by the virtual address itself.
In Linux these are divided into five levels - PGD, P4D, PUD, PMD and PTE. Huge
pages might eliminate one or two of these levels, but when this is the case we
typically refer to the leaf level as the PTE level regardless.
.. note:: In instances where the architecture supports fewer page tables than
five the kernel cleverly 'folds' page table levels, that is stubbing
out functions related to the skipped levels. This allows us to
conceptually act as if there were always five levels, even if the
compiler might, in practice, eliminate any code relating to missing
ones.
There are four key operations typically performed on page tables:
1. **Traversing** page tables - Simply reading page tables in order to traverse
them. This only requires that the VMA is kept stable, so a lock which
establishes this suffices for traversal (there are also lockless variants
which eliminate even this requirement, such as :c:func:`!gup_fast`).
2. **Installing** page table mappings - Whether creating a new mapping or
modifying an existing one in such a way as to change its identity. This
requires that the VMA is kept stable via an mmap or VMA lock (explicitly not
rmap locks).
3. **Zapping/unmapping** page table entries - This is what the kernel calls
clearing page table mappings at the leaf level only, whilst leaving all page
tables in place. This is a very common operation in the kernel performed on
file truncation, the :c:macro:`!MADV_DONTNEED` operation via
:c:func:`!madvise`, and others. This is performed by a number of functions
including :c:func:`!unmap_mapping_range` and :c:func:`!unmap_mapping_pages`.
The VMA need only be kept stable for this operation.
4. **Freeing** page tables - When finally the kernel removes page tables from a
userland process (typically via :c:func:`!free_pgtables`) extreme care must
be taken to ensure this is done safely, as this logic finally frees all page
tables in the specified range, ignoring existing leaf entries (it assumes the
caller has both zapped the range and prevented any further faults or
modifications within it).
.. note:: Modifying mappings for reclaim or migration is performed under rmap
lock as it, like zapping, does not fundamentally modify the identity
of what is being mapped.
**Traversing** and **zapping** ranges can be performed holding any one of the
locks described in the terminology section above - that is the mmap lock, the
VMA lock or either of the reverse mapping locks.
That is - as long as you keep the relevant VMA **stable** - you are good to go
ahead and perform these operations on page tables (though internally, kernel
operations that perform writes also acquire internal page table locks to
serialise - see the page table implementation detail section for more details).
When **installing** page table entries, the mmap or VMA lock must be held to
keep the VMA stable. We explore why this is in the page table locking details
section below.
.. warning:: Page tables are normally only traversed in regions covered by VMAs.
If you want to traverse page tables in areas that might not be
covered by VMAs, heavier locking is required.
See :c:func:`!walk_page_range_novma` for details.
**Freeing** page tables is an entirely internal memory management operation and
has special requirements (see the page freeing section below for more details).
.. warning:: When **freeing** page tables, it must not be possible for VMAs
containing the ranges those page tables map to be accessible via
the reverse mapping.
The :c:func:`!free_pgtables` function removes the relevant VMAs
from the reverse mappings, but no other VMAs can be permitted to be
accessible and span the specified range.
Lock ordering
-------------
As we have multiple locks across the kernel which may or may not be taken at the
same time as explicit mm or VMA locks, we have to be wary of lock inversion, and
the **order** in which locks are acquired and released becomes very important.
.. note:: Lock inversion occurs when two threads need to acquire multiple locks,
but in doing so inadvertently cause a mutual deadlock.
For example, consider thread 1 which holds lock A and tries to acquire lock B,
while thread 2 holds lock B and tries to acquire lock A.
Both threads are now deadlocked on each other. However, had they attempted to
acquire locks in the same order, one would have waited for the other to
complete its work and no deadlock would have occurred.
The opening comment in :c:macro:`!mm/rmap.c` describes in detail the required
ordering of locks within memory management code:
.. code-block::
inode->i_rwsem (while writing or truncating, not reading or faulting)
mm->mmap_lock
mapping->invalidate_lock (in filemap_fault)
folio_lock
hugetlbfs_i_mmap_rwsem_key (in huge_pmd_share, see hugetlbfs below)
vma_start_write
mapping->i_mmap_rwsem
anon_vma->rwsem
mm->page_table_lock or pte_lock
swap_lock (in swap_duplicate, swap_info_get)
mmlist_lock (in mmput, drain_mmlist and others)
mapping->private_lock (in block_dirty_folio)
i_pages lock (widely used)
lruvec->lru_lock (in folio_lruvec_lock_irq)
inode->i_lock (in set_page_dirty's __mark_inode_dirty)
bdi.wb->list_lock (in set_page_dirty's __mark_inode_dirty)
sb_lock (within inode_lock in fs/fs-writeback.c)
i_pages lock (widely used, in set_page_dirty,
in arch-dependent flush_dcache_mmap_lock,
within bdi.wb->list_lock in __sync_single_inode)
There is also a file-system specific lock ordering comment located at the top of
:c:macro:`!mm/filemap.c`:
.. code-block::
->i_mmap_rwsem (truncate_pagecache)
->private_lock (__free_pte->block_dirty_folio)
->swap_lock (exclusive_swap_page, others)
->i_pages lock
->i_rwsem
->invalidate_lock (acquired by fs in truncate path)
->i_mmap_rwsem (truncate->unmap_mapping_range)
->mmap_lock
->i_mmap_rwsem
->page_table_lock or pte_lock (various, mainly in memory.c)
->i_pages lock (arch-dependent flush_dcache_mmap_lock)
->mmap_lock
->invalidate_lock (filemap_fault)
->lock_page (filemap_fault, access_process_vm)
->i_rwsem (generic_perform_write)
->mmap_lock (fault_in_readable->do_page_fault)
bdi->wb.list_lock
sb_lock (fs/fs-writeback.c)
->i_pages lock (__sync_single_inode)
->i_mmap_rwsem
->anon_vma.lock (vma_merge)
->anon_vma.lock
->page_table_lock or pte_lock (anon_vma_prepare and various)
->page_table_lock or pte_lock
->swap_lock (try_to_unmap_one)
->private_lock (try_to_unmap_one)
->i_pages lock (try_to_unmap_one)
->lruvec->lru_lock (follow_page_mask->mark_page_accessed)
->lruvec->lru_lock (check_pte_range->folio_isolate_lru)
->private_lock (folio_remove_rmap_pte->set_page_dirty)
->i_pages lock (folio_remove_rmap_pte->set_page_dirty)
bdi.wb->list_lock (folio_remove_rmap_pte->set_page_dirty)
->inode->i_lock (folio_remove_rmap_pte->set_page_dirty)
bdi.wb->list_lock (zap_pte_range->set_page_dirty)
->inode->i_lock (zap_pte_range->set_page_dirty)
->private_lock (zap_pte_range->block_dirty_folio)
Please check the current state of these comments which may have changed since
the time of writing of this document.
------------------------------
Locking Implementation Details
------------------------------
.. warning:: Locking rules for PTE-level page tables are very different from
locking rules for page tables at other levels.
Page table locking details
--------------------------
In addition to the locks described in the terminology section above, we have
additional locks dedicated to page tables:
* **Higher level page table locks** - Higher level page tables, that is PGD, P4D
and PUD each make use of the process address space granularity
:c:member:`!mm->page_table_lock` lock when modified.
* **Fine-grained page table locks** - PMDs and PTEs each have fine-grained locks
either kept within the folios describing the page tables or allocated
separated and pointed at by the folios if :c:macro:`!ALLOC_SPLIT_PTLOCKS` is
set. The PMD spin lock is obtained via :c:func:`!pmd_lock`, however PTEs are
mapped into higher memory (if a 32-bit system) and carefully locked via
:c:func:`!pte_offset_map_lock`.
These locks represent the minimum required to interact with each page table
level, but there are further requirements.
Importantly, note that on a **traversal** of page tables, sometimes no such
locks are taken. However, at the PTE level, at least concurrent page table
deletion must be prevented (using RCU) and the page table must be mapped into
high memory, see below.
Whether care is taken on reading the page table entries depends on the
architecture, see the section on atomicity below.
Locking rules
^^^^^^^^^^^^^
We establish basic locking rules when interacting with page tables:
* When changing a page table entry the page table lock for that page table
**must** be held, except if you can safely assume nobody can access the page
tables concurrently (such as on invocation of :c:func:`!free_pgtables`).
* Reads from and writes to page table entries must be *appropriately*
atomic. See the section on atomicity below for details.
* Populating previously empty entries requires that the mmap or VMA locks are
held (read or write), doing so with only rmap locks would be dangerous (see
the warning below).
* As mentioned previously, zapping can be performed while simply keeping the VMA
stable, that is holding any one of the mmap, VMA or rmap locks.
.. warning:: Populating previously empty entries is dangerous as, when unmapping
VMAs, :c:func:`!vms_clear_ptes` has a window of time between
zapping (via :c:func:`!unmap_vmas`) and freeing page tables (via
:c:func:`!free_pgtables`), where the VMA is still visible in the
rmap tree. :c:func:`!free_pgtables` assumes that the zap has
already been performed and removes PTEs unconditionally (along with
all other page tables in the freed range), so installing new PTE
entries could leak memory and also cause other unexpected and
dangerous behaviour.
There are additional rules applicable when moving page tables, which we discuss
in the section on this topic below.
PTE-level page tables are different from page tables at other levels, and there
are extra requirements for accessing them:
* On 32-bit architectures, they may be in high memory (meaning they need to be
mapped into kernel memory to be accessible).
* When empty, they can be unlinked and RCU-freed while holding an mmap lock or
rmap lock for reading in combination with the PTE and PMD page table locks.
In particular, this happens in :c:func:`!retract_page_tables` when handling
:c:macro:`!MADV_COLLAPSE`.
So accessing PTE-level page tables requires at least holding an RCU read lock;
but that only suffices for readers that can tolerate racing with concurrent
page table updates such that an empty PTE is observed (in a page table that
has actually already been detached and marked for RCU freeing) while another
new page table has been installed in the same location and filled with
entries. Writers normally need to take the PTE lock and revalidate that the
PMD entry still refers to the same PTE-level page table.
To access PTE-level page tables, a helper like :c:func:`!pte_offset_map_lock` or
:c:func:`!pte_offset_map` can be used depending on stability requirements.
These map the page table into kernel memory if required, take the RCU lock, and
depending on variant, may also look up or acquire the PTE lock.
See the comment on :c:func:`!__pte_offset_map_lock`.
Atomicity
^^^^^^^^^
Regardless of page table locks, the MMU hardware concurrently updates accessed
and dirty bits (perhaps more, depending on architecture). Additionally, page
table traversal operations in parallel (though holding the VMA stable) and
functionality like GUP-fast locklessly traverses (that is reads) page tables,
without even keeping the VMA stable at all.
When performing a page table traversal and keeping the VMA stable, whether a
read must be performed once and only once or not depends on the architecture
(for instance x86-64 does not require any special precautions).
If a write is being performed, or if a read informs whether a write takes place
(on an installation of a page table entry say, for instance in
:c:func:`!__pud_install`), special care must always be taken. In these cases we
can never assume that page table locks give us entirely exclusive access, and
must retrieve page table entries once and only once.
If we are reading page table entries, then we need only ensure that the compiler
does not rearrange our loads. This is achieved via :c:func:`!pXXp_get`
functions - :c:func:`!pgdp_get`, :c:func:`!p4dp_get`, :c:func:`!pudp_get`,
:c:func:`!pmdp_get`, and :c:func:`!ptep_get`.
Each of these uses :c:func:`!READ_ONCE` to guarantee that the compiler reads
the page table entry only once.
However, if we wish to manipulate an existing page table entry and care about
the previously stored data, we must go further and use an hardware atomic
operation as, for example, in :c:func:`!ptep_get_and_clear`.
Equally, operations that do not rely on the VMA being held stable, such as
GUP-fast (see :c:func:`!gup_fast` and its various page table level handlers like
:c:func:`!gup_fast_pte_range`), must very carefully interact with page table
entries, using functions such as :c:func:`!ptep_get_lockless` and equivalent for
higher level page table levels.
Writes to page table entries must also be appropriately atomic, as established
by :c:func:`!set_pXX` functions - :c:func:`!set_pgd`, :c:func:`!set_p4d`,
:c:func:`!set_pud`, :c:func:`!set_pmd`, and :c:func:`!set_pte`.
Equally functions which clear page table entries must be appropriately atomic,
as in :c:func:`!pXX_clear` functions - :c:func:`!pgd_clear`,
:c:func:`!p4d_clear`, :c:func:`!pud_clear`, :c:func:`!pmd_clear`, and
:c:func:`!pte_clear`.
Page table installation
^^^^^^^^^^^^^^^^^^^^^^^
Page table installation is performed with the VMA held stable explicitly by an
mmap or VMA lock in read or write mode (see the warning in the locking rules
section for details as to why).
When allocating a P4D, PUD or PMD and setting the relevant entry in the above
PGD, P4D or PUD, the :c:member:`!mm->page_table_lock` must be held. This is
acquired in :c:func:`!__p4d_alloc`, :c:func:`!__pud_alloc` and
:c:func:`!__pmd_alloc` respectively.
.. note:: :c:func:`!__pmd_alloc` actually invokes :c:func:`!pud_lock` and
:c:func:`!pud_lockptr` in turn, however at the time of writing it ultimately
references the :c:member:`!mm->page_table_lock`.
Allocating a PTE will either use the :c:member:`!mm->page_table_lock` or, if
:c:macro:`!USE_SPLIT_PMD_PTLOCKS` is defined, a lock embedded in the PMD
physical page metadata in the form of a :c:struct:`!struct ptdesc`, acquired by
:c:func:`!pmd_ptdesc` called from :c:func:`!pmd_lock` and ultimately
:c:func:`!__pte_alloc`.
Finally, modifying the contents of the PTE requires special treatment, as the
PTE page table lock must be acquired whenever we want stable and exclusive
access to entries contained within a PTE, especially when we wish to modify
them.
This is performed via :c:func:`!pte_offset_map_lock` which carefully checks to
ensure that the PTE hasn't changed from under us, ultimately invoking
:c:func:`!pte_lockptr` to obtain a spin lock at PTE granularity contained within
the :c:struct:`!struct ptdesc` associated with the physical PTE page. The lock
must be released via :c:func:`!pte_unmap_unlock`.
.. note:: There are some variants on this, such as
:c:func:`!pte_offset_map_rw_nolock` when we know we hold the PTE stable but
for brevity we do not explore this. See the comment for
:c:func:`!__pte_offset_map_lock` for more details.
When modifying data in ranges we typically only wish to allocate higher page
tables as necessary, using these locks to avoid races or overwriting anything,
and set/clear data at the PTE level as required (for instance when page faulting
or zapping).
A typical pattern taken when traversing page table entries to install a new
mapping is to optimistically determine whether the page table entry in the table
above is empty, if so, only then acquiring the page table lock and checking
again to see if it was allocated underneath us.
This allows for a traversal with page table locks only being taken when
required. An example of this is :c:func:`!__pud_alloc`.
At the leaf page table, that is the PTE, we can't entirely rely on this pattern
as we have separate PMD and PTE locks and a THP collapse for instance might have
eliminated the PMD entry as well as the PTE from under us.
This is why :c:func:`!__pte_offset_map_lock` locklessly retrieves the PMD entry
for the PTE, carefully checking it is as expected, before acquiring the
PTE-specific lock, and then *again* checking that the PMD entry is as expected.
If a THP collapse (or similar) were to occur then the lock on both pages would
be acquired, so we can ensure this is prevented while the PTE lock is held.
Installing entries this way ensures mutual exclusion on write.
Page table freeing
^^^^^^^^^^^^^^^^^^
Tearing down page tables themselves is something that requires significant
care. There must be no way that page tables designated for removal can be
traversed or referenced by concurrent tasks.
It is insufficient to simply hold an mmap write lock and VMA lock (which will
prevent racing faults, and rmap operations), as a file-backed mapping can be
truncated under the :c:struct:`!struct address_space->i_mmap_rwsem` alone.
As a result, no VMA which can be accessed via the reverse mapping (either
through the :c:struct:`!struct anon_vma->rb_root` or the :c:member:`!struct
address_space->i_mmap` interval trees) can have its page tables torn down.
The operation is typically performed via :c:func:`!free_pgtables`, which assumes
either the mmap write lock has been taken (as specified by its
:c:member:`!mm_wr_locked` parameter), or that the VMA is already unreachable.
It carefully removes the VMA from all reverse mappings, however it's important
that no new ones overlap these or any route remain to permit access to addresses
within the range whose page tables are being torn down.
Additionally, it assumes that a zap has already been performed and steps have
been taken to ensure that no further page table entries can be installed between
the zap and the invocation of :c:func:`!free_pgtables`.
Since it is assumed that all such steps have been taken, page table entries are
cleared without page table locks (in the :c:func:`!pgd_clear`, :c:func:`!p4d_clear`,
:c:func:`!pud_clear`, and :c:func:`!pmd_clear` functions.
.. note:: It is possible for leaf page tables to be torn down independent of
the page tables above it as is done by
:c:func:`!retract_page_tables`, which is performed under the i_mmap
read lock, PMD, and PTE page table locks, without this level of care.
Page table moving
^^^^^^^^^^^^^^^^^
Some functions manipulate page table levels above PMD (that is PUD, P4D and PGD
page tables). Most notable of these is :c:func:`!mremap`, which is capable of
moving higher level page tables.
In these instances, it is required that **all** locks are taken, that is
the mmap lock, the VMA lock and the relevant rmap locks.
You can observe this in the :c:func:`!mremap` implementation in the functions
:c:func:`!take_rmap_locks` and :c:func:`!drop_rmap_locks` which perform the rmap
side of lock acquisition, invoked ultimately by :c:func:`!move_page_tables`.
VMA lock internals
------------------
Overview
^^^^^^^^
VMA read locking is entirely optimistic - if the lock is contended or a competing
write has started, then we do not obtain a read lock.
A VMA **read** lock is obtained by :c:func:`!lock_vma_under_rcu`, which first
calls :c:func:`!rcu_read_lock` to ensure that the VMA is looked up in an RCU
critical section, then attempts to VMA lock it via :c:func:`!vma_start_read`,
before releasing the RCU lock via :c:func:`!rcu_read_unlock`.
VMA read locks hold the read lock on the :c:member:`!vma->vm_lock` semaphore for
their duration and the caller of :c:func:`!lock_vma_under_rcu` must release it
via :c:func:`!vma_end_read`.
VMA **write** locks are acquired via :c:func:`!vma_start_write` in instances where a
VMA is about to be modified, unlike :c:func:`!vma_start_read` the lock is always
acquired. An mmap write lock **must** be held for the duration of the VMA write
lock, releasing or downgrading the mmap write lock also releases the VMA write
lock so there is no :c:func:`!vma_end_write` function.
Note that a semaphore write lock is not held across a VMA lock. Rather, a
sequence number is used for serialisation, and the write semaphore is only
acquired at the point of write lock to update this.
This ensures the semantics we require - VMA write locks provide exclusive write
access to the VMA.
Implementation details
^^^^^^^^^^^^^^^^^^^^^^
The VMA lock mechanism is designed to be a lightweight means of avoiding the use
of the heavily contended mmap lock. It is implemented using a combination of a
read/write semaphore and sequence numbers belonging to the containing
:c:struct:`!struct mm_struct` and the VMA.
Read locks are acquired via :c:func:`!vma_start_read`, which is an optimistic
operation, i.e. it tries to acquire a read lock but returns false if it is
unable to do so. At the end of the read operation, :c:func:`!vma_end_read` is
called to release the VMA read lock.
Invoking :c:func:`!vma_start_read` requires that :c:func:`!rcu_read_lock` has
been called first, establishing that we are in an RCU critical section upon VMA
read lock acquisition. Once acquired, the RCU lock can be released as it is only
required for lookup. This is abstracted by :c:func:`!lock_vma_under_rcu` which
is the interface a user should use.
Writing requires the mmap to be write-locked and the VMA lock to be acquired via
:c:func:`!vma_start_write`, however the write lock is released by the termination or
downgrade of the mmap write lock so no :c:func:`!vma_end_write` is required.
All this is achieved by the use of per-mm and per-VMA sequence counts, which are
used in order to reduce complexity, especially for operations which write-lock
multiple VMAs at once.
If the mm sequence count, :c:member:`!mm->mm_lock_seq` is equal to the VMA
sequence count :c:member:`!vma->vm_lock_seq` then the VMA is write-locked. If
they differ, then it is not.
Each time the mmap write lock is released in :c:func:`!mmap_write_unlock` or
:c:func:`!mmap_write_downgrade`, :c:func:`!vma_end_write_all` is invoked which
also increments :c:member:`!mm->mm_lock_seq` via
:c:func:`!mm_lock_seqcount_end`.
This way, we ensure that, regardless of the VMA's sequence number, a write lock
is never incorrectly indicated and that when we release an mmap write lock we
efficiently release **all** VMA write locks contained within the mmap at the
same time.
Since the mmap write lock is exclusive against others who hold it, the automatic
release of any VMA locks on its release makes sense, as you would never want to
keep VMAs locked across entirely separate write operations. It also maintains
correct lock ordering.
Each time a VMA read lock is acquired, we acquire a read lock on the
:c:member:`!vma->vm_lock` read/write semaphore and hold it, while checking that
the sequence count of the VMA does not match that of the mm.
If it does, the read lock fails. If it does not, we hold the lock, excluding
writers, but permitting other readers, who will also obtain this lock under RCU.
Importantly, maple tree operations performed in :c:func:`!lock_vma_under_rcu`
are also RCU safe, so the whole read lock operation is guaranteed to function
correctly.
On the write side, we acquire a write lock on the :c:member:`!vma->vm_lock`
read/write semaphore, before setting the VMA's sequence number under this lock,
also simultaneously holding the mmap write lock.
This way, if any read locks are in effect, :c:func:`!vma_start_write` will sleep
until these are finished and mutual exclusion is achieved.
After setting the VMA's sequence number, the lock is released, avoiding
complexity with a long-term held write lock.
This clever combination of a read/write semaphore and sequence count allows for
fast RCU-based per-VMA lock acquisition (especially on page fault, though
utilised elsewhere) with minimal complexity around lock ordering.
mmap write lock downgrading
---------------------------
When an mmap write lock is held one has exclusive access to resources within the
mmap (with the usual caveats about requiring VMA write locks to avoid races with
tasks holding VMA read locks).
It is then possible to **downgrade** from a write lock to a read lock via
:c:func:`!mmap_write_downgrade` which, similar to :c:func:`!mmap_write_unlock`,
implicitly terminates all VMA write locks via :c:func:`!vma_end_write_all`, but
importantly does not relinquish the mmap lock while downgrading, therefore
keeping the locked virtual address space stable.
An interesting consequence of this is that downgraded locks are exclusive
against any other task possessing a downgraded lock (since a racing task would
have to acquire a write lock first to downgrade it, and the downgraded lock
prevents a new write lock from being obtained until the original lock is
released).
For clarity, we map read (R)/downgraded write (D)/write (W) locks against one
another showing which locks exclude the others:
.. list-table:: Lock exclusivity
:widths: 5 5 5 5
:header-rows: 1
:stub-columns: 1
* -
- R
- D
- W
* - R
- N
- N
- Y
* - D
- N
- Y
- Y
* - W
- Y
- Y
- Y
Here a Y indicates the locks in the matching row/column are mutually exclusive,
and N indicates that they are not.
Stack expansion
---------------
Stack expansion throws up additional complexities in that we cannot permit there
to be racing page faults, as a result we invoke :c:func:`!vma_start_write` to
prevent this in :c:func:`!expand_downwards` or :c:func:`!expand_upwards`.

View file

@ -22,65 +22,67 @@ definitions:
doc: unused event
-
name: created
doc:
token, family, saddr4 | saddr6, daddr4 | daddr6, sport, dport
doc: >-
A new MPTCP connection has been created. It is the good time to
allocate memory and send ADD_ADDR if needed. Depending on the
traffic-patterns it can take a long time until the
MPTCP_EVENT_ESTABLISHED is sent.
Attributes: token, family, saddr4 | saddr6, daddr4 | daddr6, sport,
dport, server-side.
-
name: established
doc:
token, family, saddr4 | saddr6, daddr4 | daddr6, sport, dport
doc: >-
A MPTCP connection is established (can start new subflows).
Attributes: token, family, saddr4 | saddr6, daddr4 | daddr6, sport,
dport, server-side.
-
name: closed
doc:
token
doc: >-
A MPTCP connection has stopped.
Attribute: token.
-
name: announced
value: 6
doc:
token, rem_id, family, daddr4 | daddr6 [, dport]
doc: >-
A new address has been announced by the peer.
Attributes: token, rem_id, family, daddr4 | daddr6 [, dport].
-
name: removed
doc:
token, rem_id
doc: >-
An address has been lost by the peer.
Attributes: token, rem_id.
-
name: sub-established
value: 10
doc:
token, family, loc_id, rem_id, saddr4 | saddr6, daddr4 | daddr6, sport,
dport, backup, if_idx [, error]
doc: >-
A new subflow has been established. 'error' should not be set.
Attributes: token, family, loc_id, rem_id, saddr4 | saddr6, daddr4 |
daddr6, sport, dport, backup, if_idx [, error].
-
name: sub-closed
doc:
token, family, loc_id, rem_id, saddr4 | saddr6, daddr4 | daddr6, sport,
dport, backup, if_idx [, error]
doc: >-
A subflow has been closed. An error (copy of sk_err) could be set if an
error has been detected for this subflow.
Attributes: token, family, loc_id, rem_id, saddr4 | saddr6, daddr4 |
daddr6, sport, dport, backup, if_idx [, error].
-
name: sub-priority
value: 13
doc:
token, family, loc_id, rem_id, saddr4 | saddr6, daddr4 | daddr6, sport,
dport, backup, if_idx [, error]
doc: >-
The priority of a subflow has changed. 'error' should not be set.
Attributes: token, family, loc_id, rem_id, saddr4 | saddr6, daddr4 |
daddr6, sport, dport, backup, if_idx [, error].
-
name: listener-created
value: 15
doc:
family, sport, saddr4 | saddr6
doc: >-
A new PM listener is created.
Attributes: family, sport, saddr4 | saddr6.
-
name: listener-closed
doc:
family, sport, saddr4 | saddr6
doc: >-
A PM listener is closed.
Attributes: family, sport, saddr4 | saddr6.
attribute-sets:
-
@ -306,8 +308,8 @@ operations:
attributes:
- addr
-
name: flush-addrs
doc: flush addresses
name: flush-addrs
doc: Flush addresses
attribute-set: endpoint
dont-validate: [ strict ]
flags: [ uns-admin-perm ]
@ -351,7 +353,7 @@ operations:
- addr-remote
-
name: announce
doc: announce new sf
doc: Announce new address
attribute-set: attr
dont-validate: [ strict ]
flags: [ uns-admin-perm ]
@ -362,7 +364,7 @@ operations:
- token
-
name: remove
doc: announce removal
doc: Announce removal
attribute-set: attr
dont-validate: [ strict ]
flags: [ uns-admin-perm ]
@ -373,7 +375,7 @@ operations:
- loc-id
-
name: subflow-create
doc: todo
doc: Create subflow
attribute-set: attr
dont-validate: [ strict ]
flags: [ uns-admin-perm ]
@ -385,7 +387,7 @@ operations:
- addr-remote
-
name: subflow-destroy
doc: todo
doc: Destroy subflow
attribute-set: attr
dont-validate: [ strict ]
flags: [ uns-admin-perm ]

View file

@ -1797,7 +1797,6 @@ F: include/uapi/linux/if_arcnet.h
ARM AND ARM64 SoC SUB-ARCHITECTURES (COMMON PARTS)
M: Arnd Bergmann <arnd@arndb.de>
M: Olof Johansson <olof@lixom.net>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: soc@lists.linux.dev
S: Maintained
@ -3608,6 +3607,7 @@ F: drivers/phy/qualcomm/phy-ath79-usb.c
ATHEROS ATH GENERIC UTILITIES
M: Kalle Valo <kvalo@kernel.org>
M: Jeff Johnson <jjohnson@kernel.org>
L: linux-wireless@vger.kernel.org
S: Supported
F: drivers/net/wireless/ath/*
@ -5502,8 +5502,8 @@ L: patches@opensource.cirrus.com
S: Supported
W: https://github.com/CirrusLogic/linux-drivers/wiki
T: git https://github.com/CirrusLogic/linux-drivers.git
F: drivers/firmware/cirrus/*
F: include/linux/firmware/cirrus/*
F: drivers/firmware/cirrus/
F: include/linux/firmware/cirrus/
CIRRUS LOGIC EP93XX ETHERNET DRIVER
M: Hartley Sweeten <hsweeten@visionengravers.com>
@ -7348,7 +7348,7 @@ F: drivers/gpu/drm/panel/panel-novatek-nt36672a.c
DRM DRIVER FOR NVIDIA GEFORCE/QUADRO GPUS
M: Karol Herbst <kherbst@redhat.com>
M: Lyude Paul <lyude@redhat.com>
M: Danilo Krummrich <dakr@redhat.com>
M: Danilo Krummrich <dakr@kernel.org>
L: dri-devel@lists.freedesktop.org
L: nouveau@lists.freedesktop.org
S: Supported
@ -8454,7 +8454,7 @@ F: include/video/s1d13xxxfb.h
EROFS FILE SYSTEM
M: Gao Xiang <xiang@kernel.org>
M: Chao Yu <chao@kernel.org>
R: Yue Hu <huyue2@coolpad.com>
R: Yue Hu <zbestahu@gmail.com>
R: Jeffle Xu <jefflexu@linux.alibaba.com>
R: Sandeep Dhavale <dhavale@google.com>
L: linux-erofs@lists.ozlabs.org
@ -8925,7 +8925,7 @@ F: include/linux/arm_ffa.h
FIRMWARE LOADER (request_firmware)
M: Luis Chamberlain <mcgrof@kernel.org>
M: Russ Weight <russ.weight@linux.dev>
M: Danilo Krummrich <dakr@redhat.com>
M: Danilo Krummrich <dakr@kernel.org>
L: linux-kernel@vger.kernel.org
S: Maintained
F: Documentation/firmware_class/
@ -14759,7 +14759,7 @@ F: drivers/memory/mtk-smi.c
F: include/soc/mediatek/smi.h
MEDIATEK SWITCH DRIVER
M: Arınç ÜNAL <arinc.unal@arinc9.com>
M: Chester A. Unal <chester.a.unal@arinc9.com>
M: Daniel Golle <daniel@makrotopia.org>
M: DENG Qingfang <dqfext@gmail.com>
M: Sean Wang <sean.wang@mediatek.com>
@ -18463,7 +18463,7 @@ F: Documentation/devicetree/bindings/pinctrl/mediatek,mt8183-pinctrl.yaml
F: drivers/pinctrl/mediatek/
PIN CONTROLLER - MEDIATEK MIPS
M: Arınç ÜNAL <arinc.unal@arinc9.com>
M: Chester A. Unal <chester.a.unal@arinc9.com>
M: Sergio Paracuellos <sergio.paracuellos@gmail.com>
L: linux-mediatek@lists.infradead.org (moderated for non-subscribers)
L: linux-mips@vger.kernel.org
@ -19507,7 +19507,7 @@ S: Maintained
F: arch/mips/ralink
RALINK MT7621 MIPS ARCHITECTURE
M: Arınç ÜNAL <arinc.unal@arinc9.com>
M: Chester A. Unal <chester.a.unal@arinc9.com>
M: Sergio Paracuellos <sergio.paracuellos@gmail.com>
L: linux-mips@vger.kernel.org
S: Maintained
@ -20910,6 +20910,8 @@ F: kernel/sched/
SCHEDULER - SCHED_EXT
R: Tejun Heo <tj@kernel.org>
R: David Vernet <void@manifault.com>
R: Andrea Righi <arighi@nvidia.com>
R: Changwoo Min <changwoo@igalia.com>
L: linux-kernel@vger.kernel.org
S: Maintained
W: https://github.com/sched-ext/scx

View file

@ -2,7 +2,7 @@
VERSION = 6
PATCHLEVEL = 13
SUBLEVEL = 0
EXTRAVERSION = -rc3
EXTRAVERSION = -rc6
NAME = Baby Opossum Posse
# *DOCUMENTATION*

View file

@ -6,6 +6,7 @@
config ARC
def_bool y
select ARC_TIMERS
select ARCH_HAS_CPU_CACHE_ALIASING
select ARCH_HAS_CACHE_LINE_SIZE
select ARCH_HAS_DEBUG_VM_PGTABLE
select ARCH_HAS_DMA_PREP_COHERENT

View file

@ -0,0 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_ARC_CACHETYPE_H
#define __ASM_ARC_CACHETYPE_H
#define cpu_dcache_is_aliasing() false
#define cpu_icache_is_aliasing() true
#endif

View file

@ -6,6 +6,7 @@ menuconfig ARCH_MXC
select CLKSRC_IMX_GPT
select GENERIC_IRQ_CHIP
select GPIOLIB
select PINCTRL
select PM_OPP if PM
select SOC_BUS
select SRAM

View file

@ -233,7 +233,7 @@
#interrupt-cells = <0x1>;
compatible = "pci-host-ecam-generic";
device_type = "pci";
bus-range = <0x0 0x1>;
bus-range = <0x0 0xff>;
reg = <0x0 0x40000000 0x0 0x10000000>;
ranges = <0x2000000 0x0 0x50000000 0x0 0x50000000 0x0 0x10000000>;
interrupt-map = <0 0 0 1 &gic 0 0 GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>,

View file

@ -67,7 +67,7 @@
l2_cache_l0: l2-cache-l0 {
compatible = "cache";
cache-size = <0x80000>;
cache-line-size = <128>;
cache-line-size = <64>;
cache-sets = <1024>; //512KiB(size)/64(line-size)=8192ways/8-way set
cache-level = <2>;
cache-unified;
@ -91,7 +91,7 @@
l2_cache_l1: l2-cache-l1 {
compatible = "cache";
cache-size = <0x80000>;
cache-line-size = <128>;
cache-line-size = <64>;
cache-sets = <1024>; //512KiB(size)/64(line-size)=8192ways/8-way set
cache-level = <2>;
cache-unified;
@ -115,7 +115,7 @@
l2_cache_l2: l2-cache-l2 {
compatible = "cache";
cache-size = <0x80000>;
cache-line-size = <128>;
cache-line-size = <64>;
cache-sets = <1024>; //512KiB(size)/64(line-size)=8192ways/8-way set
cache-level = <2>;
cache-unified;
@ -139,7 +139,7 @@
l2_cache_l3: l2-cache-l3 {
compatible = "cache";
cache-size = <0x80000>;
cache-line-size = <128>;
cache-line-size = <64>;
cache-sets = <1024>; //512KiB(size)/64(line-size)=8192ways/8-way set
cache-level = <2>;
cache-unified;

View file

@ -36,15 +36,8 @@
#include <asm/traps.h>
#include <asm/vdso.h>
#ifdef CONFIG_ARM64_GCS
#define GCS_SIGNAL_CAP(addr) (((unsigned long)addr) & GCS_CAP_ADDR_MASK)
static bool gcs_signal_cap_valid(u64 addr, u64 val)
{
return val == GCS_SIGNAL_CAP(addr);
}
#endif
/*
* Do a signal return; undo the signal stack. These are aligned to 128-bit.
*/
@ -1062,8 +1055,7 @@ static int restore_sigframe(struct pt_regs *regs,
#ifdef CONFIG_ARM64_GCS
static int gcs_restore_signal(void)
{
unsigned long __user *gcspr_el0;
u64 cap;
u64 gcspr_el0, cap;
int ret;
if (!system_supports_gcs())
@ -1072,7 +1064,7 @@ static int gcs_restore_signal(void)
if (!(current->thread.gcs_el0_mode & PR_SHADOW_STACK_ENABLE))
return 0;
gcspr_el0 = (unsigned long __user *)read_sysreg_s(SYS_GCSPR_EL0);
gcspr_el0 = read_sysreg_s(SYS_GCSPR_EL0);
/*
* Ensure that any changes to the GCS done via GCS operations
@ -1087,22 +1079,23 @@ static int gcs_restore_signal(void)
* then faults will be generated on GCS operations - the main
* concern is to protect GCS pages.
*/
ret = copy_from_user(&cap, gcspr_el0, sizeof(cap));
ret = copy_from_user(&cap, (unsigned long __user *)gcspr_el0,
sizeof(cap));
if (ret)
return -EFAULT;
/*
* Check that the cap is the actual GCS before replacing it.
*/
if (!gcs_signal_cap_valid((u64)gcspr_el0, cap))
if (cap != GCS_SIGNAL_CAP(gcspr_el0))
return -EINVAL;
/* Invalidate the token to prevent reuse */
put_user_gcs(0, (__user void*)gcspr_el0, &ret);
put_user_gcs(0, (unsigned long __user *)gcspr_el0, &ret);
if (ret != 0)
return -EFAULT;
write_sysreg_s(gcspr_el0 + 1, SYS_GCSPR_EL0);
write_sysreg_s(gcspr_el0 + 8, SYS_GCSPR_EL0);
return 0;
}
@ -1421,7 +1414,7 @@ static int get_sigframe(struct rt_sigframe_user_layout *user,
static int gcs_signal_entry(__sigrestore_t sigtramp, struct ksignal *ksig)
{
unsigned long __user *gcspr_el0;
u64 gcspr_el0;
int ret = 0;
if (!system_supports_gcs())
@ -1434,18 +1427,20 @@ static int gcs_signal_entry(__sigrestore_t sigtramp, struct ksignal *ksig)
* We are entering a signal handler, current register state is
* active.
*/
gcspr_el0 = (unsigned long __user *)read_sysreg_s(SYS_GCSPR_EL0);
gcspr_el0 = read_sysreg_s(SYS_GCSPR_EL0);
/*
* Push a cap and the GCS entry for the trampoline onto the GCS.
*/
put_user_gcs((unsigned long)sigtramp, gcspr_el0 - 2, &ret);
put_user_gcs(GCS_SIGNAL_CAP(gcspr_el0 - 1), gcspr_el0 - 1, &ret);
put_user_gcs((unsigned long)sigtramp,
(unsigned long __user *)(gcspr_el0 - 16), &ret);
put_user_gcs(GCS_SIGNAL_CAP(gcspr_el0 - 8),
(unsigned long __user *)(gcspr_el0 - 8), &ret);
if (ret != 0)
return ret;
gcspr_el0 -= 2;
write_sysreg_s((unsigned long)gcspr_el0, SYS_GCSPR_EL0);
gcspr_el0 -= 16;
write_sysreg_s(gcspr_el0, SYS_GCSPR_EL0);
return 0;
}

View file

@ -32,3 +32,9 @@ KBUILD_LDFLAGS += $(ldflags-y)
TIR_NAME := r19
KBUILD_CFLAGS += -ffixed-$(TIR_NAME) -DTHREADINFO_REG=$(TIR_NAME) -D__linux__
KBUILD_AFLAGS += -DTHREADINFO_REG=$(TIR_NAME)
# Disable HexagonConstExtenders pass for LLVM versions prior to 19.1.0
# https://github.com/llvm/llvm-project/issues/99714
ifneq ($(call clang-min-version, 190100),y)
KBUILD_CFLAGS += -mllvm -hexagon-cext=false
endif

View file

@ -143,11 +143,11 @@ static int show_cpuinfo(struct seq_file *m, void *v)
" DIV:\t\t%s\n"
" BMX:\t\t%s\n"
" CDX:\t\t%s\n",
cpuinfo.has_mul ? "yes" : "no",
cpuinfo.has_mulx ? "yes" : "no",
cpuinfo.has_div ? "yes" : "no",
cpuinfo.has_bmx ? "yes" : "no",
cpuinfo.has_cdx ? "yes" : "no");
str_yes_no(cpuinfo.has_mul),
str_yes_no(cpuinfo.has_mulx),
str_yes_no(cpuinfo.has_div),
str_yes_no(cpuinfo.has_bmx),
str_yes_no(cpuinfo.has_cdx));
seq_printf(m,
"Icache:\t\t%ukB, line length: %u\n",

View file

@ -208,6 +208,7 @@ CONFIG_FB_ATY=y
CONFIG_FB_ATY_CT=y
CONFIG_FB_ATY_GX=y
CONFIG_FB_3DFX=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
# CONFIG_VGA_CONSOLE is not set
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_LOGO=y

View file

@ -716,6 +716,7 @@ CONFIG_FB_TRIDENT=m
CONFIG_FB_SM501=m
CONFIG_FB_IBM_GXT4500=y
CONFIG_LCD_PLATFORM=m
CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
CONFIG_LOGO=y

View file

@ -464,7 +464,43 @@ static vm_fault_t vas_mmap_fault(struct vm_fault *vmf)
return VM_FAULT_SIGBUS;
}
/*
* During mmap() paste address, mapping VMA is saved in VAS window
* struct which is used to unmap during migration if the window is
* still open. But the user space can remove this mapping with
* munmap() before closing the window and the VMA address will
* be invalid. Set VAS window VMA to NULL in this function which
* is called before VMA free.
*/
static void vas_mmap_close(struct vm_area_struct *vma)
{
struct file *fp = vma->vm_file;
struct coproc_instance *cp_inst = fp->private_data;
struct vas_window *txwin;
/* Should not happen */
if (!cp_inst || !cp_inst->txwin) {
pr_err("No attached VAS window for the paste address mmap\n");
return;
}
txwin = cp_inst->txwin;
/*
* task_ref.vma is set in coproc_mmap() during mmap paste
* address. So it has to be the same VMA that is getting freed.
*/
if (WARN_ON(txwin->task_ref.vma != vma)) {
pr_err("Invalid paste address mmaping\n");
return;
}
mutex_lock(&txwin->task_ref.mmap_mutex);
txwin->task_ref.vma = NULL;
mutex_unlock(&txwin->task_ref.mmap_mutex);
}
static const struct vm_operations_struct vas_vm_ops = {
.close = vas_mmap_close,
.fault = vas_mmap_fault,
};

View file

@ -234,6 +234,8 @@ static unsigned long get_vmem_size(unsigned long identity_size,
vsize = round_up(SZ_2G + max_mappable, rte_size) +
round_up(vmemmap_size, rte_size) +
FIXMAP_SIZE + MODULES_LEN + KASLR_LEN;
if (IS_ENABLED(CONFIG_KMSAN))
vsize += MODULES_LEN * 2;
return size_add(vsize, vmalloc_size);
}

View file

@ -306,7 +306,7 @@ static void pgtable_pte_populate(pmd_t *pmd, unsigned long addr, unsigned long e
pages++;
}
}
if (mode == POPULATE_DIRECT)
if (mode == POPULATE_IDENTITY)
update_page_count(PG_DIRECT_MAP_4K, pages);
}
@ -339,7 +339,7 @@ static void pgtable_pmd_populate(pud_t *pud, unsigned long addr, unsigned long e
}
pgtable_pte_populate(pmd, addr, next, mode);
}
if (mode == POPULATE_DIRECT)
if (mode == POPULATE_IDENTITY)
update_page_count(PG_DIRECT_MAP_1M, pages);
}
@ -372,7 +372,7 @@ static void pgtable_pud_populate(p4d_t *p4d, unsigned long addr, unsigned long e
}
pgtable_pmd_populate(pud, addr, next, mode);
}
if (mode == POPULATE_DIRECT)
if (mode == POPULATE_IDENTITY)
update_page_count(PG_DIRECT_MAP_2G, pages);
}

View file

@ -270,7 +270,7 @@ static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \
if (len >= sizeof(_value)) \
return -E2BIG; \
len = strscpy(_value, buf, sizeof(_value)); \
if (len < 0) \
if ((ssize_t)len < 0) \
return len; \
strim(_value); \
return len; \

View file

@ -429,6 +429,16 @@ static struct event_constraint intel_lnc_event_constraints[] = {
EVENT_CONSTRAINT_END
};
static struct extra_reg intel_lnc_extra_regs[] __read_mostly = {
INTEL_UEVENT_EXTRA_REG(0x012a, MSR_OFFCORE_RSP_0, 0xfffffffffffull, RSP_0),
INTEL_UEVENT_EXTRA_REG(0x012b, MSR_OFFCORE_RSP_1, 0xfffffffffffull, RSP_1),
INTEL_UEVENT_PEBS_LDLAT_EXTRA_REG(0x01cd),
INTEL_UEVENT_EXTRA_REG(0x02c6, MSR_PEBS_FRONTEND, 0x9, FE),
INTEL_UEVENT_EXTRA_REG(0x03c6, MSR_PEBS_FRONTEND, 0x7fff1f, FE),
INTEL_UEVENT_EXTRA_REG(0x40ad, MSR_PEBS_FRONTEND, 0xf, FE),
INTEL_UEVENT_EXTRA_REG(0x04c2, MSR_PEBS_FRONTEND, 0x8, FE),
EVENT_EXTRA_END
};
EVENT_ATTR_STR(mem-loads, mem_ld_nhm, "event=0x0b,umask=0x10,ldlat=3");
EVENT_ATTR_STR(mem-loads, mem_ld_snb, "event=0xcd,umask=0x1,ldlat=3");
@ -6422,7 +6432,7 @@ static __always_inline void intel_pmu_init_lnc(struct pmu *pmu)
intel_pmu_init_glc(pmu);
hybrid(pmu, event_constraints) = intel_lnc_event_constraints;
hybrid(pmu, pebs_constraints) = intel_lnc_pebs_event_constraints;
hybrid(pmu, extra_regs) = intel_rwc_extra_regs;
hybrid(pmu, extra_regs) = intel_lnc_extra_regs;
}
static __always_inline void intel_pmu_init_skt(struct pmu *pmu)

View file

@ -2517,6 +2517,7 @@ void __init intel_ds_init(void)
x86_pmu.large_pebs_flags |= PERF_SAMPLE_TIME;
break;
case 6:
case 5:
x86_pmu.pebs_ept = 1;
fallthrough;

View file

@ -1910,6 +1910,7 @@ static const struct x86_cpu_id intel_uncore_match[] __initconst = {
X86_MATCH_VFM(INTEL_ATOM_GRACEMONT, &adl_uncore_init),
X86_MATCH_VFM(INTEL_ATOM_CRESTMONT_X, &gnr_uncore_init),
X86_MATCH_VFM(INTEL_ATOM_CRESTMONT, &gnr_uncore_init),
X86_MATCH_VFM(INTEL_ATOM_DARKMONT_X, &gnr_uncore_init),
{},
};
MODULE_DEVICE_TABLE(x86cpu, intel_uncore_match);

View file

@ -452,6 +452,7 @@
#define X86_FEATURE_SME_COHERENT (19*32+10) /* AMD hardware-enforced cache coherency */
#define X86_FEATURE_DEBUG_SWAP (19*32+14) /* "debug_swap" AMD SEV-ES full debug state swap support */
#define X86_FEATURE_SVSM (19*32+28) /* "svsm" SVSM present */
#define X86_FEATURE_HV_INUSE_WR_ALLOWED (19*32+30) /* Allow Write to in-use hypervisor-owned pages */
/* AMD-defined Extended Feature 2 EAX, CPUID level 0x80000021 (EAX), word 20 */
#define X86_FEATURE_NO_NESTED_DATA_BP (20*32+ 0) /* No Nested Data Breakpoints */

View file

@ -230,6 +230,8 @@ static inline unsigned long long l1tf_pfn_limit(void)
return BIT_ULL(boot_cpu_data.x86_cache_bits - 1 - PAGE_SHIFT);
}
void init_cpu_devs(void);
void get_cpu_vendor(struct cpuinfo_x86 *c);
extern void early_cpu_init(void);
extern void identify_secondary_cpu(struct cpuinfo_x86 *);
extern void print_cpu_info(struct cpuinfo_x86 *);

View file

@ -65,4 +65,19 @@
extern bool __static_call_fixup(void *tramp, u8 op, void *dest);
extern void __static_call_update_early(void *tramp, void *func);
#define static_call_update_early(name, _func) \
({ \
typeof(&STATIC_CALL_TRAMP(name)) __F = (_func); \
if (static_call_initialized) { \
__static_call_update(&STATIC_CALL_KEY(name), \
STATIC_CALL_TRAMP_ADDR(name), __F);\
} else { \
WRITE_ONCE(STATIC_CALL_KEY(name).func, _func); \
__static_call_update_early(STATIC_CALL_TRAMP_ADDR(name),\
__F); \
} \
})
#endif /* _ASM_STATIC_CALL_H */

View file

@ -8,7 +8,7 @@
#include <asm/special_insns.h>
#ifdef CONFIG_X86_32
static inline void iret_to_self(void)
static __always_inline void iret_to_self(void)
{
asm volatile (
"pushfl\n\t"
@ -19,7 +19,7 @@ static inline void iret_to_self(void)
: ASM_CALL_CONSTRAINT : : "memory");
}
#else
static inline void iret_to_self(void)
static __always_inline void iret_to_self(void)
{
unsigned int tmp;
@ -55,7 +55,7 @@ static inline void iret_to_self(void)
* Like all of Linux's memory ordering operations, this is a
* compiler barrier as well.
*/
static inline void sync_core(void)
static __always_inline void sync_core(void)
{
/*
* The SERIALIZE instruction is the most straightforward way to

View file

@ -39,9 +39,11 @@
#include <linux/string.h>
#include <linux/types.h>
#include <linux/pgtable.h>
#include <linux/instrumentation.h>
#include <trace/events/xen.h>
#include <asm/alternative.h>
#include <asm/page.h>
#include <asm/smap.h>
#include <asm/nospec-branch.h>
@ -86,11 +88,20 @@ struct xen_dm_op_buf;
* there aren't more than 5 arguments...)
*/
extern struct { char _entry[32]; } hypercall_page[];
void xen_hypercall_func(void);
DECLARE_STATIC_CALL(xen_hypercall, xen_hypercall_func);
#define __HYPERCALL "call hypercall_page+%c[offset]"
#define __HYPERCALL_ENTRY(x) \
[offset] "i" (__HYPERVISOR_##x * sizeof(hypercall_page[0]))
#ifdef MODULE
#define __ADDRESSABLE_xen_hypercall
#else
#define __ADDRESSABLE_xen_hypercall __ADDRESSABLE_ASM_STR(__SCK__xen_hypercall)
#endif
#define __HYPERCALL \
__ADDRESSABLE_xen_hypercall \
"call __SCT__xen_hypercall"
#define __HYPERCALL_ENTRY(x) "a" (x)
#ifdef CONFIG_X86_32
#define __HYPERCALL_RETREG "eax"
@ -148,7 +159,7 @@ extern struct { char _entry[32]; } hypercall_page[];
__HYPERCALL_0ARG(); \
asm volatile (__HYPERCALL \
: __HYPERCALL_0PARAM \
: __HYPERCALL_ENTRY(name) \
: __HYPERCALL_ENTRY(__HYPERVISOR_ ## name) \
: __HYPERCALL_CLOBBER0); \
(type)__res; \
})
@ -159,7 +170,7 @@ extern struct { char _entry[32]; } hypercall_page[];
__HYPERCALL_1ARG(a1); \
asm volatile (__HYPERCALL \
: __HYPERCALL_1PARAM \
: __HYPERCALL_ENTRY(name) \
: __HYPERCALL_ENTRY(__HYPERVISOR_ ## name) \
: __HYPERCALL_CLOBBER1); \
(type)__res; \
})
@ -170,7 +181,7 @@ extern struct { char _entry[32]; } hypercall_page[];
__HYPERCALL_2ARG(a1, a2); \
asm volatile (__HYPERCALL \
: __HYPERCALL_2PARAM \
: __HYPERCALL_ENTRY(name) \
: __HYPERCALL_ENTRY(__HYPERVISOR_ ## name) \
: __HYPERCALL_CLOBBER2); \
(type)__res; \
})
@ -181,7 +192,7 @@ extern struct { char _entry[32]; } hypercall_page[];
__HYPERCALL_3ARG(a1, a2, a3); \
asm volatile (__HYPERCALL \
: __HYPERCALL_3PARAM \
: __HYPERCALL_ENTRY(name) \
: __HYPERCALL_ENTRY(__HYPERVISOR_ ## name) \
: __HYPERCALL_CLOBBER3); \
(type)__res; \
})
@ -192,7 +203,7 @@ extern struct { char _entry[32]; } hypercall_page[];
__HYPERCALL_4ARG(a1, a2, a3, a4); \
asm volatile (__HYPERCALL \
: __HYPERCALL_4PARAM \
: __HYPERCALL_ENTRY(name) \
: __HYPERCALL_ENTRY(__HYPERVISOR_ ## name) \
: __HYPERCALL_CLOBBER4); \
(type)__res; \
})
@ -206,12 +217,9 @@ xen_single_call(unsigned int call,
__HYPERCALL_DECLS;
__HYPERCALL_5ARG(a1, a2, a3, a4, a5);
if (call >= PAGE_SIZE / sizeof(hypercall_page[0]))
return -EINVAL;
asm volatile(CALL_NOSPEC
asm volatile(__HYPERCALL
: __HYPERCALL_5PARAM
: [thunk_target] "a" (&hypercall_page[call])
: __HYPERCALL_ENTRY(call)
: __HYPERCALL_CLOBBER5);
return (long)__res;

View file

@ -142,11 +142,6 @@ static bool skip_addr(void *dest)
if (dest >= (void *)relocate_kernel &&
dest < (void*)relocate_kernel + KEXEC_CONTROL_CODE_MAX_SIZE)
return true;
#endif
#ifdef CONFIG_XEN
if (dest >= (void *)hypercall_page &&
dest < (void*)hypercall_page + PAGE_SIZE)
return true;
#endif
return false;
}

View file

@ -81,6 +81,34 @@ static void do_user_cp_fault(struct pt_regs *regs, unsigned long error_code)
static __ro_after_init bool ibt_fatal = true;
/*
* By definition, all missing-ENDBRANCH #CPs are a result of WFE && !ENDBR.
*
* For the kernel IBT no ENDBR selftest where #CPs are deliberately triggered,
* the WFE state of the interrupted context needs to be cleared to let execution
* continue. Otherwise when the CPU resumes from the instruction that just
* caused the previous #CP, another missing-ENDBRANCH #CP is raised and the CPU
* enters a dead loop.
*
* This is not a problem with IDT because it doesn't preserve WFE and IRET doesn't
* set WFE. But FRED provides space on the entry stack (in an expanded CS area)
* to save and restore the WFE state, thus the WFE state is no longer clobbered,
* so software must clear it.
*/
static void ibt_clear_fred_wfe(struct pt_regs *regs)
{
/*
* No need to do any FRED checks.
*
* For IDT event delivery, the high-order 48 bits of CS are pushed
* as 0s into the stack, and later IRET ignores these bits.
*
* For FRED, a test to check if fred_cs.wfe is set would be dropped
* by compilers.
*/
regs->fred_cs.wfe = 0;
}
static void do_kernel_cp_fault(struct pt_regs *regs, unsigned long error_code)
{
if ((error_code & CP_EC) != CP_ENDBR) {
@ -90,6 +118,7 @@ static void do_kernel_cp_fault(struct pt_regs *regs, unsigned long error_code)
if (unlikely(regs->ip == (unsigned long)&ibt_selftest_noendbr)) {
regs->ax = 0;
ibt_clear_fred_wfe(regs);
return;
}
@ -97,6 +126,7 @@ static void do_kernel_cp_fault(struct pt_regs *regs, unsigned long error_code)
if (!ibt_fatal) {
printk(KERN_DEFAULT CUT_HERE);
__warn(__FILE__, __LINE__, (void *)regs->ip, TAINT_WARN, regs, NULL);
ibt_clear_fred_wfe(regs);
return;
}
BUG();

View file

@ -867,7 +867,7 @@ static void cpu_detect_tlb(struct cpuinfo_x86 *c)
tlb_lld_4m[ENTRIES], tlb_lld_1g[ENTRIES]);
}
static void get_cpu_vendor(struct cpuinfo_x86 *c)
void get_cpu_vendor(struct cpuinfo_x86 *c)
{
char *v = c->x86_vendor_id;
int i;
@ -1649,15 +1649,11 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
detect_nopl();
}
void __init early_cpu_init(void)
void __init init_cpu_devs(void)
{
const struct cpu_dev *const *cdev;
int count = 0;
#ifdef CONFIG_PROCESSOR_SELECT
pr_info("KERNEL supported cpus:\n");
#endif
for (cdev = __x86_cpu_dev_start; cdev < __x86_cpu_dev_end; cdev++) {
const struct cpu_dev *cpudev = *cdev;
@ -1665,20 +1661,30 @@ void __init early_cpu_init(void)
break;
cpu_devs[count] = cpudev;
count++;
}
}
void __init early_cpu_init(void)
{
#ifdef CONFIG_PROCESSOR_SELECT
unsigned int i, j;
pr_info("KERNEL supported cpus:\n");
#endif
init_cpu_devs();
#ifdef CONFIG_PROCESSOR_SELECT
{
unsigned int j;
for (j = 0; j < 2; j++) {
if (!cpudev->c_ident[j])
continue;
pr_info(" %s %s\n", cpudev->c_vendor,
cpudev->c_ident[j]);
}
for (i = 0; i < X86_VENDOR_NUM && cpu_devs[i]; i++) {
for (j = 0; j < 2; j++) {
if (!cpu_devs[i]->c_ident[j])
continue;
pr_info(" %s %s\n", cpu_devs[i]->c_vendor,
cpu_devs[i]->c_ident[j]);
}
#endif
}
#endif
early_identify_cpu(&boot_cpu_data);
}

View file

@ -223,6 +223,63 @@ static void hv_machine_crash_shutdown(struct pt_regs *regs)
hyperv_cleanup();
}
#endif /* CONFIG_CRASH_DUMP */
static u64 hv_ref_counter_at_suspend;
static void (*old_save_sched_clock_state)(void);
static void (*old_restore_sched_clock_state)(void);
/*
* Hyper-V clock counter resets during hibernation. Save and restore clock
* offset during suspend/resume, while also considering the time passed
* before suspend. This is to make sure that sched_clock using hv tsc page
* based clocksource, proceeds from where it left off during suspend and
* it shows correct time for the timestamps of kernel messages after resume.
*/
static void save_hv_clock_tsc_state(void)
{
hv_ref_counter_at_suspend = hv_read_reference_counter();
}
static void restore_hv_clock_tsc_state(void)
{
/*
* Adjust the offsets used by hv tsc clocksource to
* account for the time spent before hibernation.
* adjusted value = reference counter (time) at suspend
* - reference counter (time) now.
*/
hv_adj_sched_clock_offset(hv_ref_counter_at_suspend - hv_read_reference_counter());
}
/*
* Functions to override save_sched_clock_state and restore_sched_clock_state
* functions of x86_platform. The Hyper-V clock counter is reset during
* suspend-resume and the offset used to measure time needs to be
* corrected, post resume.
*/
static void hv_save_sched_clock_state(void)
{
old_save_sched_clock_state();
save_hv_clock_tsc_state();
}
static void hv_restore_sched_clock_state(void)
{
restore_hv_clock_tsc_state();
old_restore_sched_clock_state();
}
static void __init x86_setup_ops_for_tsc_pg_clock(void)
{
if (!(ms_hyperv.features & HV_MSR_REFERENCE_TSC_AVAILABLE))
return;
old_save_sched_clock_state = x86_platform.save_sched_clock_state;
x86_platform.save_sched_clock_state = hv_save_sched_clock_state;
old_restore_sched_clock_state = x86_platform.restore_sched_clock_state;
x86_platform.restore_sched_clock_state = hv_restore_sched_clock_state;
}
#endif /* CONFIG_HYPERV */
static uint32_t __init ms_hyperv_platform(void)
@ -579,6 +636,7 @@ static void __init ms_hyperv_init_platform(void)
/* Register Hyper-V specific clocksource */
hv_init_clocksource();
x86_setup_ops_for_tsc_pg_clock();
hv_vtl_init_platform();
#endif
/*

View file

@ -172,6 +172,15 @@ void arch_static_call_transform(void *site, void *tramp, void *func, bool tail)
}
EXPORT_SYMBOL_GPL(arch_static_call_transform);
noinstr void __static_call_update_early(void *tramp, void *func)
{
BUG_ON(system_state != SYSTEM_BOOTING);
BUG_ON(!early_boot_irqs_disabled);
BUG_ON(static_call_initialized);
__text_gen_insn(tramp, JMP32_INSN_OPCODE, tramp, func, JMP32_INSN_SIZE);
sync_core();
}
#ifdef CONFIG_MITIGATION_RETHUNK
/*
* This is called by apply_returns() to fix up static call trampolines,

View file

@ -519,14 +519,10 @@ INIT_PER_CPU(irq_stack_backing_store);
* linker will never mark as relocatable. (Using just ABSOLUTE() is not
* sufficient for that).
*/
#ifdef CONFIG_XEN
#ifdef CONFIG_XEN_PV
xen_elfnote_entry_value =
ABSOLUTE(xen_elfnote_entry) + ABSOLUTE(startup_xen);
#endif
xen_elfnote_hypercall_page_value =
ABSOLUTE(xen_elfnote_hypercall_page) + ABSOLUTE(hypercall_page);
#endif
#ifdef CONFIG_PVH
xen_elfnote_phys32_entry_value =
ABSOLUTE(xen_elfnote_phys32_entry) + ABSOLUTE(pvh_start_xen - LOAD_OFFSET);

View file

@ -3364,18 +3364,6 @@ static bool fast_pf_fix_direct_spte(struct kvm_vcpu *vcpu,
return true;
}
static bool is_access_allowed(struct kvm_page_fault *fault, u64 spte)
{
if (fault->exec)
return is_executable_pte(spte);
if (fault->write)
return is_writable_pte(spte);
/* Fault was on Read access */
return spte & PT_PRESENT_MASK;
}
/*
* Returns the last level spte pointer of the shadow page walk for the given
* gpa, and sets *spte to the spte value. This spte may be non-preset. If no

View file

@ -461,6 +461,23 @@ static inline bool is_mmu_writable_spte(u64 spte)
return spte & shadow_mmu_writable_mask;
}
/*
* Returns true if the access indicated by @fault is allowed by the existing
* SPTE protections. Note, the caller is responsible for checking that the
* SPTE is a shadow-present, leaf SPTE (either before or after).
*/
static inline bool is_access_allowed(struct kvm_page_fault *fault, u64 spte)
{
if (fault->exec)
return is_executable_pte(spte);
if (fault->write)
return is_writable_pte(spte);
/* Fault was on Read access */
return spte & PT_PRESENT_MASK;
}
/*
* If the MMU-writable flag is cleared, i.e. the SPTE is write-protected for
* write-tracking, remote TLBs must be flushed, even if the SPTE was read-only,

View file

@ -985,6 +985,11 @@ static int tdp_mmu_map_handle_target_level(struct kvm_vcpu *vcpu,
if (fault->prefetch && is_shadow_present_pte(iter->old_spte))
return RET_PF_SPURIOUS;
if (is_shadow_present_pte(iter->old_spte) &&
is_access_allowed(fault, iter->old_spte) &&
is_last_spte(iter->old_spte, iter->level))
return RET_PF_SPURIOUS;
if (unlikely(!fault->slot))
new_spte = make_mmio_spte(vcpu, iter->gfn, ACC_ALL);
else

View file

@ -1199,6 +1199,12 @@ bool avic_hardware_setup(void)
return false;
}
if (cc_platform_has(CC_ATTR_HOST_SEV_SNP) &&
!boot_cpu_has(X86_FEATURE_HV_INUSE_WR_ALLOWED)) {
pr_warn("AVIC disabled: missing HvInUseWrAllowed on SNP-enabled system\n");
return false;
}
if (boot_cpu_has(X86_FEATURE_AVIC)) {
pr_info("AVIC enabled\n");
} else if (force_avic) {

View file

@ -3201,15 +3201,6 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
if (data & ~supported_de_cfg)
return 1;
/*
* Don't let the guest change the host-programmed value. The
* MSR is very model specific, i.e. contains multiple bits that
* are completely unknown to KVM, and the one bit known to KVM
* is simply a reflection of hardware capabilities.
*/
if (!msr->host_initiated && data != svm->msr_decfg)
return 1;
svm->msr_decfg = data;
break;
}

View file

@ -2,7 +2,7 @@
#ifndef __KVM_X86_VMX_POSTED_INTR_H
#define __KVM_X86_VMX_POSTED_INTR_H
#include <linux/find.h>
#include <linux/bitmap.h>
#include <asm/posted_intr.h>
void vmx_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu);

View file

@ -9976,7 +9976,7 @@ static int complete_hypercall_exit(struct kvm_vcpu *vcpu)
{
u64 ret = vcpu->run->hypercall.ret;
if (!is_64_bit_mode(vcpu))
if (!is_64_bit_hypercall(vcpu))
ret = (u32)ret;
kvm_rax_write(vcpu, ret);
++vcpu->stat.hypercalls;
@ -12724,6 +12724,13 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
kvm_hv_init_vm(kvm);
kvm_xen_init_vm(kvm);
if (ignore_msrs && !report_ignored_msrs) {
pr_warn_once("Running KVM with ignore_msrs=1 and report_ignored_msrs=0 is not a\n"
"a supported configuration. Lying to the guest about the existence of MSRs\n"
"may cause the guest operating system to hang or produce errors. If a guest\n"
"does not run without ignore_msrs=1, please report it to kvm@vger.kernel.org.\n");
}
return 0;
out_uninit_mmu:

View file

@ -2,6 +2,7 @@
#include <linux/console.h>
#include <linux/cpu.h>
#include <linux/instrumentation.h>
#include <linux/kexec.h>
#include <linux/memblock.h>
#include <linux/slab.h>
@ -21,7 +22,8 @@
#include "xen-ops.h"
EXPORT_SYMBOL_GPL(hypercall_page);
DEFINE_STATIC_CALL(xen_hypercall, xen_hypercall_hvm);
EXPORT_STATIC_CALL_TRAMP(xen_hypercall);
/*
* Pointer to the xen_vcpu_info structure or
@ -68,6 +70,67 @@ EXPORT_SYMBOL(xen_start_flags);
*/
struct shared_info *HYPERVISOR_shared_info = &xen_dummy_shared_info;
static __ref void xen_get_vendor(void)
{
init_cpu_devs();
cpu_detect(&boot_cpu_data);
get_cpu_vendor(&boot_cpu_data);
}
void xen_hypercall_setfunc(void)
{
if (static_call_query(xen_hypercall) != xen_hypercall_hvm)
return;
if ((boot_cpu_data.x86_vendor == X86_VENDOR_AMD ||
boot_cpu_data.x86_vendor == X86_VENDOR_HYGON))
static_call_update(xen_hypercall, xen_hypercall_amd);
else
static_call_update(xen_hypercall, xen_hypercall_intel);
}
/*
* Evaluate processor vendor in order to select the correct hypercall
* function for HVM/PVH guests.
* Might be called very early in boot before vendor has been set by
* early_cpu_init().
*/
noinstr void *__xen_hypercall_setfunc(void)
{
void (*func)(void);
/*
* Xen is supported only on CPUs with CPUID, so testing for
* X86_FEATURE_CPUID is a test for early_cpu_init() having been
* run.
*
* Note that __xen_hypercall_setfunc() is noinstr only due to a nasty
* dependency chain: it is being called via the xen_hypercall static
* call when running as a PVH or HVM guest. Hypercalls need to be
* noinstr due to PV guests using hypercalls in noinstr code. So we
* can safely tag the function body as "instrumentation ok", since
* the PV guest requirement is not of interest here (xen_get_vendor()
* calls noinstr functions, and static_call_update_early() might do
* so, too).
*/
instrumentation_begin();
if (!boot_cpu_has(X86_FEATURE_CPUID))
xen_get_vendor();
if ((boot_cpu_data.x86_vendor == X86_VENDOR_AMD ||
boot_cpu_data.x86_vendor == X86_VENDOR_HYGON))
func = xen_hypercall_amd;
else
func = xen_hypercall_intel;
static_call_update_early(xen_hypercall, func);
instrumentation_end();
return func;
}
static int xen_cpu_up_online(unsigned int cpu)
{
xen_init_lock_cpu(cpu);

View file

@ -106,15 +106,8 @@ static void __init init_hvm_pv_info(void)
/* PVH set up hypercall page in xen_prepare_pvh(). */
if (xen_pvh_domain())
pv_info.name = "Xen PVH";
else {
u64 pfn;
uint32_t msr;
else
pv_info.name = "Xen HVM";
msr = cpuid_ebx(base + 2);
pfn = __pa(hypercall_page);
wrmsr_safe(msr, (u32)pfn, (u32)(pfn >> 32));
}
xen_setup_features();
@ -300,6 +293,10 @@ static uint32_t __init xen_platform_hvm(void)
if (xen_pv_domain())
return 0;
/* Set correct hypercall function. */
if (xen_domain)
xen_hypercall_setfunc();
if (xen_pvh_domain() && nopv) {
/* Guest booting via the Xen-PVH boot entry goes here */
pr_info("\"nopv\" parameter is ignored in PVH guest\n");

View file

@ -1341,6 +1341,9 @@ asmlinkage __visible void __init xen_start_kernel(struct start_info *si)
xen_domain_type = XEN_PV_DOMAIN;
xen_start_flags = xen_start_info->flags;
/* Interrupts are guaranteed to be off initially. */
early_boot_irqs_disabled = true;
static_call_update_early(xen_hypercall, xen_hypercall_pv);
xen_setup_features();
@ -1431,7 +1434,6 @@ asmlinkage __visible void __init xen_start_kernel(struct start_info *si)
WARN_ON(xen_cpuhp_setup(xen_cpu_up_prepare_pv, xen_cpu_dead_pv));
local_irq_disable();
early_boot_irqs_disabled = true;
xen_raw_console_write("mapping kernel into physical memory\n");
xen_setup_kernel_pagetable((pgd_t *)xen_start_info->pt_base,

View file

@ -129,17 +129,10 @@ static void __init pvh_arch_setup(void)
void __init xen_pvh_init(struct boot_params *boot_params)
{
u32 msr;
u64 pfn;
xen_pvh = 1;
xen_domain_type = XEN_HVM_DOMAIN;
xen_start_flags = pvh_start_info.flags;
msr = cpuid_ebx(xen_cpuid_base() + 2);
pfn = __pa(hypercall_page);
wrmsr_safe(msr, (u32)pfn, (u32)(pfn >> 32));
x86_init.oem.arch_setup = pvh_arch_setup;
x86_init.oem.banner = xen_banner;

View file

@ -20,9 +20,32 @@
#include <linux/init.h>
#include <linux/linkage.h>
#include <linux/objtool.h>
#include <../entry/calling.h>
.pushsection .noinstr.text, "ax"
/*
* PV hypercall interface to the hypervisor.
*
* Called via inline asm(), so better preserve %rcx and %r11.
*
* Input:
* %eax: hypercall number
* %rdi, %rsi, %rdx, %r10, %r8: args 1..5 for the hypercall
* Output: %rax
*/
SYM_FUNC_START(xen_hypercall_pv)
ANNOTATE_NOENDBR
push %rcx
push %r11
UNWIND_HINT_SAVE
syscall
UNWIND_HINT_RESTORE
pop %r11
pop %rcx
RET
SYM_FUNC_END(xen_hypercall_pv)
/*
* Disabling events is simply a matter of making the event mask
* non-zero.
@ -176,7 +199,6 @@ SYM_CODE_START(xen_early_idt_handler_array)
SYM_CODE_END(xen_early_idt_handler_array)
__FINIT
hypercall_iret = hypercall_page + __HYPERVISOR_iret * 32
/*
* Xen64 iret frame:
*
@ -186,17 +208,28 @@ hypercall_iret = hypercall_page + __HYPERVISOR_iret * 32
* cs
* rip <-- standard iret frame
*
* flags
* flags <-- xen_iret must push from here on
*
* rcx }
* r11 }<-- pushed by hypercall page
* rsp->rax }
* rcx
* r11
* rsp->rax
*/
.macro xen_hypercall_iret
pushq $0 /* Flags */
push %rcx
push %r11
push %rax
mov $__HYPERVISOR_iret, %eax
syscall /* Do the IRET. */
#ifdef CONFIG_MITIGATION_SLS
int3
#endif
.endm
SYM_CODE_START(xen_iret)
UNWIND_HINT_UNDEFINED
ANNOTATE_NOENDBR
pushq $0
jmp hypercall_iret
xen_hypercall_iret
SYM_CODE_END(xen_iret)
/*
@ -301,8 +334,7 @@ SYM_CODE_START(xen_entry_SYSENTER_compat)
ENDBR
lea 16(%rsp), %rsp /* strip %rcx, %r11 */
mov $-ENOSYS, %rax
pushq $0
jmp hypercall_iret
xen_hypercall_iret
SYM_CODE_END(xen_entry_SYSENTER_compat)
SYM_CODE_END(xen_entry_SYSCALL_compat)

View file

@ -6,9 +6,11 @@
#include <linux/elfnote.h>
#include <linux/init.h>
#include <linux/instrumentation.h>
#include <asm/boot.h>
#include <asm/asm.h>
#include <asm/frame.h>
#include <asm/msr.h>
#include <asm/page_types.h>
#include <asm/percpu.h>
@ -20,28 +22,6 @@
#include <xen/interface/xen-mca.h>
#include <asm/xen/interface.h>
.pushsection .noinstr.text, "ax"
.balign PAGE_SIZE
SYM_CODE_START(hypercall_page)
.rept (PAGE_SIZE / 32)
UNWIND_HINT_FUNC
ANNOTATE_NOENDBR
ANNOTATE_UNRET_SAFE
ret
/*
* Xen will write the hypercall page, and sort out ENDBR.
*/
.skip 31, 0xcc
.endr
#define HYPERCALL(n) \
.equ xen_hypercall_##n, hypercall_page + __HYPERVISOR_##n * 32; \
.type xen_hypercall_##n, @function; .size xen_hypercall_##n, 32
#include <asm/xen-hypercalls.h>
#undef HYPERCALL
SYM_CODE_END(hypercall_page)
.popsection
#ifdef CONFIG_XEN_PV
__INIT
SYM_CODE_START(startup_xen)
@ -87,6 +67,87 @@ SYM_CODE_END(xen_cpu_bringup_again)
#endif
#endif
.pushsection .noinstr.text, "ax"
/*
* Xen hypercall interface to the hypervisor.
*
* Input:
* %eax: hypercall number
* 32-bit:
* %ebx, %ecx, %edx, %esi, %edi: args 1..5 for the hypercall
* 64-bit:
* %rdi, %rsi, %rdx, %r10, %r8: args 1..5 for the hypercall
* Output: %[er]ax
*/
SYM_FUNC_START(xen_hypercall_hvm)
ENDBR
FRAME_BEGIN
/* Save all relevant registers (caller save and arguments). */
#ifdef CONFIG_X86_32
push %eax
push %ebx
push %ecx
push %edx
push %esi
push %edi
#else
push %rax
push %rcx
push %rdx
push %rdi
push %rsi
push %r11
push %r10
push %r9
push %r8
#ifdef CONFIG_FRAME_POINTER
pushq $0 /* Dummy push for stack alignment. */
#endif
#endif
/* Set the vendor specific function. */
call __xen_hypercall_setfunc
/* Set ZF = 1 if AMD, Restore saved registers. */
#ifdef CONFIG_X86_32
lea xen_hypercall_amd, %ebx
cmp %eax, %ebx
pop %edi
pop %esi
pop %edx
pop %ecx
pop %ebx
pop %eax
#else
lea xen_hypercall_amd(%rip), %rbx
cmp %rax, %rbx
#ifdef CONFIG_FRAME_POINTER
pop %rax /* Dummy pop. */
#endif
pop %r8
pop %r9
pop %r10
pop %r11
pop %rsi
pop %rdi
pop %rdx
pop %rcx
pop %rax
#endif
/* Use correct hypercall function. */
jz xen_hypercall_amd
jmp xen_hypercall_intel
SYM_FUNC_END(xen_hypercall_hvm)
SYM_FUNC_START(xen_hypercall_amd)
vmmcall
RET
SYM_FUNC_END(xen_hypercall_amd)
SYM_FUNC_START(xen_hypercall_intel)
vmcall
RET
SYM_FUNC_END(xen_hypercall_intel)
.popsection
ELFNOTE(Xen, XEN_ELFNOTE_GUEST_OS, .asciz "linux")
ELFNOTE(Xen, XEN_ELFNOTE_GUEST_VERSION, .asciz "2.6")
ELFNOTE(Xen, XEN_ELFNOTE_XEN_VERSION, .asciz "xen-3.0")
@ -116,8 +177,6 @@ SYM_CODE_END(xen_cpu_bringup_again)
#else
# define FEATURES_DOM0 0
#endif
ELFNOTE(Xen, XEN_ELFNOTE_HYPERCALL_PAGE, .globl xen_elfnote_hypercall_page;
xen_elfnote_hypercall_page: _ASM_PTR xen_elfnote_hypercall_page_value - .)
ELFNOTE(Xen, XEN_ELFNOTE_SUPPORTED_FEATURES,
.long FEATURES_PV | FEATURES_PVH | FEATURES_DOM0)
ELFNOTE(Xen, XEN_ELFNOTE_LOADER, .asciz "generic")

View file

@ -326,4 +326,13 @@ static inline void xen_smp_intr_free_pv(unsigned int cpu) {}
static inline void xen_smp_count_cpus(void) { }
#endif /* CONFIG_SMP */
#ifdef CONFIG_XEN_PV
void xen_hypercall_pv(void);
#endif
void xen_hypercall_hvm(void);
void xen_hypercall_amd(void);
void xen_hypercall_intel(void);
void xen_hypercall_setfunc(void);
void *__xen_hypercall_setfunc(void);
#endif /* XEN_OPS_H */

View file

@ -155,8 +155,7 @@ int set_blocksize(struct file *file, int size)
struct inode *inode = file->f_mapping->host;
struct block_device *bdev = I_BDEV(inode);
/* Size must be a power of two, and between 512 and PAGE_SIZE */
if (size > PAGE_SIZE || size < 512 || !is_power_of_2(size))
if (blk_validate_block_size(size))
return -EINVAL;
/* Size cannot be smaller than the size supported by the device */

View file

@ -275,13 +275,15 @@ void blk_mq_sysfs_unregister_hctxs(struct request_queue *q)
struct blk_mq_hw_ctx *hctx;
unsigned long i;
lockdep_assert_held(&q->sysfs_dir_lock);
mutex_lock(&q->sysfs_dir_lock);
if (!q->mq_sysfs_init_done)
return;
goto unlock;
queue_for_each_hw_ctx(q, hctx, i)
blk_mq_unregister_hctx(hctx);
unlock:
mutex_unlock(&q->sysfs_dir_lock);
}
int blk_mq_sysfs_register_hctxs(struct request_queue *q)
@ -290,10 +292,9 @@ int blk_mq_sysfs_register_hctxs(struct request_queue *q)
unsigned long i;
int ret = 0;
lockdep_assert_held(&q->sysfs_dir_lock);
mutex_lock(&q->sysfs_dir_lock);
if (!q->mq_sysfs_init_done)
return ret;
goto unlock;
queue_for_each_hw_ctx(q, hctx, i) {
ret = blk_mq_register_hctx(hctx);
@ -301,5 +302,8 @@ int blk_mq_sysfs_register_hctxs(struct request_queue *q)
break;
}
unlock:
mutex_unlock(&q->sysfs_dir_lock);
return ret;
}

View file

@ -4412,6 +4412,15 @@ struct gendisk *blk_mq_alloc_disk_for_queue(struct request_queue *q,
}
EXPORT_SYMBOL(blk_mq_alloc_disk_for_queue);
/*
* Only hctx removed from cpuhp list can be reused
*/
static bool blk_mq_hctx_is_reusable(struct blk_mq_hw_ctx *hctx)
{
return hlist_unhashed(&hctx->cpuhp_online) &&
hlist_unhashed(&hctx->cpuhp_dead);
}
static struct blk_mq_hw_ctx *blk_mq_alloc_and_init_hctx(
struct blk_mq_tag_set *set, struct request_queue *q,
int hctx_idx, int node)
@ -4421,7 +4430,7 @@ static struct blk_mq_hw_ctx *blk_mq_alloc_and_init_hctx(
/* reuse dead hctx first */
spin_lock(&q->unused_hctx_lock);
list_for_each_entry(tmp, &q->unused_hctx_list, hctx_list) {
if (tmp->numa_node == node) {
if (tmp->numa_node == node && blk_mq_hctx_is_reusable(tmp)) {
hctx = tmp;
break;
}
@ -4453,8 +4462,7 @@ static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set,
unsigned long i, j;
/* protect against switching io scheduler */
lockdep_assert_held(&q->sysfs_lock);
mutex_lock(&q->sysfs_lock);
for (i = 0; i < set->nr_hw_queues; i++) {
int old_node;
int node = blk_mq_get_hctx_node(set, i);
@ -4487,6 +4495,7 @@ static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set,
xa_for_each_start(&q->hctx_table, j, hctx, j)
blk_mq_exit_hctx(q, set, hctx, j);
mutex_unlock(&q->sysfs_lock);
/* unregister cpuhp callbacks for exited hctxs */
blk_mq_remove_hw_queues_cpuhp(q);
@ -4518,14 +4527,10 @@ int blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,
xa_init(&q->hctx_table);
mutex_lock(&q->sysfs_lock);
blk_mq_realloc_hw_ctxs(set, q);
if (!q->nr_hw_queues)
goto err_hctxs;
mutex_unlock(&q->sysfs_lock);
INIT_WORK(&q->timeout_work, blk_mq_timeout_work);
blk_queue_rq_timeout(q, set->timeout ? set->timeout : 30 * HZ);
@ -4544,7 +4549,6 @@ int blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,
return 0;
err_hctxs:
mutex_unlock(&q->sysfs_lock);
blk_mq_release(q);
err_exit:
q->mq_ops = NULL;
@ -4925,12 +4929,12 @@ static bool blk_mq_elv_switch_none(struct list_head *head,
return false;
/* q->elevator needs protection from ->sysfs_lock */
lockdep_assert_held(&q->sysfs_lock);
mutex_lock(&q->sysfs_lock);
/* the check has to be done with holding sysfs_lock */
if (!q->elevator) {
kfree(qe);
goto out;
goto unlock;
}
INIT_LIST_HEAD(&qe->node);
@ -4940,7 +4944,9 @@ static bool blk_mq_elv_switch_none(struct list_head *head,
__elevator_get(qe->type);
list_add(&qe->node, head);
elevator_disable(q);
out:
unlock:
mutex_unlock(&q->sysfs_lock);
return true;
}
@ -4969,9 +4975,11 @@ static void blk_mq_elv_switch_back(struct list_head *head,
list_del(&qe->node);
kfree(qe);
mutex_lock(&q->sysfs_lock);
elevator_switch(q, t);
/* drop the reference acquired in blk_mq_elv_switch_none */
elevator_put(t);
mutex_unlock(&q->sysfs_lock);
}
static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set,
@ -4991,11 +4999,8 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set,
if (set->nr_maps == 1 && nr_hw_queues == set->nr_hw_queues)
return;
list_for_each_entry(q, &set->tag_list, tag_set_list) {
mutex_lock(&q->sysfs_dir_lock);
mutex_lock(&q->sysfs_lock);
list_for_each_entry(q, &set->tag_list, tag_set_list)
blk_mq_freeze_queue(q);
}
/*
* Switch IO scheduler to 'none', cleaning up the data associated
* with the previous scheduler. We will switch back once we are done
@ -5051,11 +5056,8 @@ switch_back:
list_for_each_entry(q, &set->tag_list, tag_set_list)
blk_mq_elv_switch_back(&head, q);
list_for_each_entry(q, &set->tag_list, tag_set_list) {
list_for_each_entry(q, &set->tag_list, tag_set_list)
blk_mq_unfreeze_queue(q);
mutex_unlock(&q->sysfs_lock);
mutex_unlock(&q->sysfs_dir_lock);
}
/* Free the excess tags when nr_hw_queues shrink. */
for (i = set->nr_hw_queues; i < prev_nr_hw_queues; i++)

View file

@ -706,11 +706,11 @@ queue_attr_store(struct kobject *kobj, struct attribute *attr,
if (entry->load_module)
entry->load_module(disk, page, length);
mutex_lock(&q->sysfs_lock);
blk_mq_freeze_queue(q);
mutex_lock(&q->sysfs_lock);
res = entry->store(disk, page, length);
blk_mq_unfreeze_queue(q);
mutex_unlock(&q->sysfs_lock);
blk_mq_unfreeze_queue(q);
return res;
}

View file

@ -409,7 +409,7 @@ static void ivpu_bo_print_info(struct ivpu_bo *bo, struct drm_printer *p)
mutex_lock(&bo->lock);
drm_printf(p, "%-9p %-3u 0x%-12llx %-10lu 0x%-8x %-4u",
bo, bo->ctx->id, bo->vpu_addr, bo->base.base.size,
bo, bo->ctx ? bo->ctx->id : 0, bo->vpu_addr, bo->base.base.size,
bo->flags, kref_read(&bo->base.base.refcount));
if (bo->base.pages)

View file

@ -612,18 +612,22 @@ int ivpu_mmu_reserved_context_init(struct ivpu_device *vdev)
if (!ivpu_mmu_ensure_pgd(vdev, &vdev->rctx.pgtable)) {
ivpu_err(vdev, "Failed to allocate root page table for reserved context\n");
ret = -ENOMEM;
goto unlock;
goto err_ctx_fini;
}
ret = ivpu_mmu_cd_set(vdev, vdev->rctx.id, &vdev->rctx.pgtable);
if (ret) {
ivpu_err(vdev, "Failed to set context descriptor for reserved context\n");
goto unlock;
goto err_ctx_fini;
}
unlock:
mutex_unlock(&vdev->rctx.lock);
return ret;
err_ctx_fini:
mutex_unlock(&vdev->rctx.lock);
ivpu_mmu_context_fini(vdev, &vdev->rctx);
return ret;
}
void ivpu_mmu_reserved_context_fini(struct ivpu_device *vdev)

View file

@ -378,6 +378,7 @@ void ivpu_pm_init(struct ivpu_device *vdev)
pm_runtime_use_autosuspend(dev);
pm_runtime_set_autosuspend_delay(dev, delay);
pm_runtime_set_active(dev);
ivpu_dbg(vdev, PM, "Autosuspend delay = %d\n", delay);
}
@ -392,7 +393,6 @@ void ivpu_pm_enable(struct ivpu_device *vdev)
{
struct device *dev = vdev->drm.dev;
pm_runtime_set_active(dev);
pm_runtime_allow(dev);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);

View file

@ -135,10 +135,10 @@ config ACPI_REV_OVERRIDE_POSSIBLE
config ACPI_EC
bool "Embedded Controller"
depends on HAS_IOPORT
default X86
default X86 || LOONGARCH
help
This driver handles communication with the microcontroller
on many x86 laptops and other machines.
on many x86/LoongArch laptops and other machines.
config ACPI_EC_DEBUGFS
tristate "EC read/write access through /sys/kernel/debug/ec"

View file

@ -489,7 +489,7 @@ config IMG_ASCII_LCD
config HT16K33
tristate "Holtek Ht16K33 LED controller with keyscan"
depends on FB && I2C && INPUT
depends on FB && I2C && INPUT && BACKLIGHT_CLASS_DEVICE
select FB_SYSMEM_HELPERS
select INPUT_MATRIXKMAP
select FB_BACKLIGHT

View file

@ -1618,6 +1618,21 @@ static void ublk_unquiesce_dev(struct ublk_device *ub)
blk_mq_kick_requeue_list(ub->ub_disk->queue);
}
static struct gendisk *ublk_detach_disk(struct ublk_device *ub)
{
struct gendisk *disk;
/* Sync with ublk_abort_queue() by holding the lock */
spin_lock(&ub->lock);
disk = ub->ub_disk;
ub->dev_info.state = UBLK_S_DEV_DEAD;
ub->dev_info.ublksrv_pid = -1;
ub->ub_disk = NULL;
spin_unlock(&ub->lock);
return disk;
}
static void ublk_stop_dev(struct ublk_device *ub)
{
struct gendisk *disk;
@ -1631,14 +1646,7 @@ static void ublk_stop_dev(struct ublk_device *ub)
ublk_unquiesce_dev(ub);
}
del_gendisk(ub->ub_disk);
/* Sync with ublk_abort_queue() by holding the lock */
spin_lock(&ub->lock);
disk = ub->ub_disk;
ub->dev_info.state = UBLK_S_DEV_DEAD;
ub->dev_info.ublksrv_pid = -1;
ub->ub_disk = NULL;
spin_unlock(&ub->lock);
disk = ublk_detach_disk(ub);
put_disk(disk);
unlock:
mutex_unlock(&ub->mutex);
@ -2336,7 +2344,7 @@ static int ublk_ctrl_start_dev(struct ublk_device *ub, struct io_uring_cmd *cmd)
out_put_cdev:
if (ret) {
ub->dev_info.state = UBLK_S_DEV_DEAD;
ublk_detach_disk(ub);
ublk_put_device(ub);
}
if (ret)

View file

@ -614,6 +614,12 @@ static ssize_t backing_dev_store(struct device *dev,
}
nr_pages = i_size_read(inode) >> PAGE_SHIFT;
/* Refuse to use zero sized device (also prevents self reference) */
if (!nr_pages) {
err = -EINVAL;
goto out;
}
bitmap_sz = BITS_TO_LONGS(nr_pages) * sizeof(long);
bitmap = kvzalloc(bitmap_sz, GFP_KERNEL);
if (!bitmap) {
@ -1438,12 +1444,16 @@ static void zram_meta_free(struct zram *zram, u64 disksize)
size_t num_pages = disksize >> PAGE_SHIFT;
size_t index;
if (!zram->table)
return;
/* Free all pages that are still in this zram device */
for (index = 0; index < num_pages; index++)
zram_free_page(zram, index);
zs_destroy_pool(zram->mem_pool);
vfree(zram->table);
zram->table = NULL;
}
static bool zram_meta_alloc(struct zram *zram, u64 disksize)
@ -2320,11 +2330,6 @@ static void zram_reset_device(struct zram *zram)
zram->limit_pages = 0;
if (!init_done(zram)) {
up_write(&zram->init_lock);
return;
}
set_capacity_and_notify(zram->disk, 0);
part_stat_set_all(zram->disk->part0, 0);

View file

@ -1106,7 +1106,7 @@ int open_for_data(struct cdrom_device_info *cdi)
}
}
cd_dbg(CD_OPEN, "all seems well, opening the devicen");
cd_dbg(CD_OPEN, "all seems well, opening the device\n");
/* all seems well, we can open the device */
ret = cdo->open(cdi, 0); /* open for data */

View file

@ -278,7 +278,8 @@ static int clk_imx8mp_audiomix_reset_controller_register(struct device *dev,
#else /* !CONFIG_RESET_CONTROLLER */
static int clk_imx8mp_audiomix_reset_controller_register(struct clk_imx8mp_audiomix_priv *priv)
static int clk_imx8mp_audiomix_reset_controller_register(struct device *dev,
struct clk_imx8mp_audiomix_priv *priv)
{
return 0;
}

View file

@ -779,6 +779,13 @@ static struct ccu_div dpu1_clk = {
},
};
static CLK_FIXED_FACTOR_HW(emmc_sdio_ref_clk, "emmc-sdio-ref",
&video_pll_clk.common.hw, 4, 1, 0);
static const struct clk_parent_data emmc_sdio_ref_clk_pd[] = {
{ .hw = &emmc_sdio_ref_clk.hw },
};
static CCU_GATE(CLK_BROM, brom_clk, "brom", ahb2_cpusys_hclk_pd, 0x100, BIT(4), 0);
static CCU_GATE(CLK_BMU, bmu_clk, "bmu", axi4_cpusys2_aclk_pd, 0x100, BIT(5), 0);
static CCU_GATE(CLK_AON2CPU_A2X, aon2cpu_a2x_clk, "aon2cpu-a2x", axi4_cpusys2_aclk_pd,
@ -798,7 +805,7 @@ static CCU_GATE(CLK_PERISYS_APB4_HCLK, perisys_apb4_hclk, "perisys-apb4-hclk", p
0x150, BIT(12), 0);
static CCU_GATE(CLK_NPU_AXI, npu_axi_clk, "npu-axi", axi_aclk_pd, 0x1c8, BIT(5), 0);
static CCU_GATE(CLK_CPU2VP, cpu2vp_clk, "cpu2vp", axi_aclk_pd, 0x1e0, BIT(13), 0);
static CCU_GATE(CLK_EMMC_SDIO, emmc_sdio_clk, "emmc-sdio", video_pll_clk_pd, 0x204, BIT(30), 0);
static CCU_GATE(CLK_EMMC_SDIO, emmc_sdio_clk, "emmc-sdio", emmc_sdio_ref_clk_pd, 0x204, BIT(30), 0);
static CCU_GATE(CLK_GMAC1, gmac1_clk, "gmac1", gmac_pll_clk_pd, 0x204, BIT(26), 0);
static CCU_GATE(CLK_PADCTRL1, padctrl1_clk, "padctrl1", perisys_apb_pclk_pd, 0x204, BIT(24), 0);
static CCU_GATE(CLK_DSMART, dsmart_clk, "dsmart", perisys_apb_pclk_pd, 0x204, BIT(23), 0);
@ -1059,6 +1066,10 @@ static int th1520_clk_probe(struct platform_device *pdev)
return ret;
priv->hws[CLK_PLL_GMAC_100M] = &gmac_pll_clk_100m.hw;
ret = devm_clk_hw_register(dev, &emmc_sdio_ref_clk.hw);
if (ret)
return ret;
ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, priv);
if (ret)
return ret;

View file

@ -27,7 +27,8 @@
#include <asm/mshyperv.h>
static struct clock_event_device __percpu *hv_clock_event;
static u64 hv_sched_clock_offset __ro_after_init;
/* Note: offset can hold negative values after hibernation. */
static u64 hv_sched_clock_offset __read_mostly;
/*
* If false, we're using the old mechanism for stimer0 interrupts
@ -470,6 +471,17 @@ static void resume_hv_clock_tsc(struct clocksource *arg)
hv_set_msr(HV_MSR_REFERENCE_TSC, tsc_msr.as_uint64);
}
/*
* Called during resume from hibernation, from overridden
* x86_platform.restore_sched_clock_state routine. This is to adjust offsets
* used to calculate time for hv tsc page based sched_clock, to account for
* time spent before hibernation.
*/
void hv_adj_sched_clock_offset(u64 offset)
{
hv_sched_clock_offset -= offset;
}
#ifdef HAVE_VDSO_CLOCKMODE_HVCLOCK
static int hv_cs_enable(struct clocksource *cs)
{

View file

@ -374,15 +374,19 @@ static inline int amd_pstate_cppc_enable(bool enable)
static int msr_init_perf(struct amd_cpudata *cpudata)
{
u64 cap1;
u64 cap1, numerator;
int ret = rdmsrl_safe_on_cpu(cpudata->cpu, MSR_AMD_CPPC_CAP1,
&cap1);
if (ret)
return ret;
WRITE_ONCE(cpudata->highest_perf, AMD_CPPC_HIGHEST_PERF(cap1));
WRITE_ONCE(cpudata->max_limit_perf, AMD_CPPC_HIGHEST_PERF(cap1));
ret = amd_get_boost_ratio_numerator(cpudata->cpu, &numerator);
if (ret)
return ret;
WRITE_ONCE(cpudata->highest_perf, numerator);
WRITE_ONCE(cpudata->max_limit_perf, numerator);
WRITE_ONCE(cpudata->nominal_perf, AMD_CPPC_NOMINAL_PERF(cap1));
WRITE_ONCE(cpudata->lowest_nonlinear_perf, AMD_CPPC_LOWNONLIN_PERF(cap1));
WRITE_ONCE(cpudata->lowest_perf, AMD_CPPC_LOWEST_PERF(cap1));
@ -394,13 +398,18 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
static int shmem_init_perf(struct amd_cpudata *cpudata)
{
struct cppc_perf_caps cppc_perf;
u64 numerator;
int ret = cppc_get_perf_caps(cpudata->cpu, &cppc_perf);
if (ret)
return ret;
WRITE_ONCE(cpudata->highest_perf, cppc_perf.highest_perf);
WRITE_ONCE(cpudata->max_limit_perf, cppc_perf.highest_perf);
ret = amd_get_boost_ratio_numerator(cpudata->cpu, &numerator);
if (ret)
return ret;
WRITE_ONCE(cpudata->highest_perf, numerator);
WRITE_ONCE(cpudata->max_limit_perf, numerator);
WRITE_ONCE(cpudata->nominal_perf, cppc_perf.nominal_perf);
WRITE_ONCE(cpudata->lowest_nonlinear_perf,
cppc_perf.lowest_nonlinear_perf);
@ -561,16 +570,13 @@ static int amd_pstate_verify(struct cpufreq_policy_data *policy_data)
static int amd_pstate_update_min_max_limit(struct cpufreq_policy *policy)
{
u32 max_limit_perf, min_limit_perf, lowest_perf, max_perf;
u32 max_limit_perf, min_limit_perf, lowest_perf, max_perf, max_freq;
struct amd_cpudata *cpudata = policy->driver_data;
if (cpudata->boost_supported && !policy->boost_enabled)
max_perf = READ_ONCE(cpudata->nominal_perf);
else
max_perf = READ_ONCE(cpudata->highest_perf);
max_limit_perf = div_u64(policy->max * max_perf, policy->cpuinfo.max_freq);
min_limit_perf = div_u64(policy->min * max_perf, policy->cpuinfo.max_freq);
max_perf = READ_ONCE(cpudata->highest_perf);
max_freq = READ_ONCE(cpudata->max_freq);
max_limit_perf = div_u64(policy->max * max_perf, max_freq);
min_limit_perf = div_u64(policy->min * max_perf, max_freq);
lowest_perf = READ_ONCE(cpudata->lowest_perf);
if (min_limit_perf < lowest_perf)
@ -889,7 +895,6 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
{
int ret;
u32 min_freq, max_freq;
u64 numerator;
u32 nominal_perf, nominal_freq;
u32 lowest_nonlinear_perf, lowest_nonlinear_freq;
u32 boost_ratio, lowest_nonlinear_ratio;
@ -911,10 +916,7 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
nominal_perf = READ_ONCE(cpudata->nominal_perf);
ret = amd_get_boost_ratio_numerator(cpudata->cpu, &numerator);
if (ret)
return ret;
boost_ratio = div_u64(numerator << SCHED_CAPACITY_SHIFT, nominal_perf);
boost_ratio = div_u64(cpudata->highest_perf << SCHED_CAPACITY_SHIFT, nominal_perf);
max_freq = (nominal_freq * boost_ratio >> SCHED_CAPACITY_SHIFT) * 1000;
lowest_nonlinear_perf = READ_ONCE(cpudata->lowest_nonlinear_perf);
@ -1869,18 +1871,18 @@ static int __init amd_pstate_init(void)
static_call_update(amd_pstate_update_perf, shmem_update_perf);
}
ret = amd_pstate_register_driver(cppc_state);
if (ret) {
pr_err("failed to register with return %d\n", ret);
return ret;
}
if (amd_pstate_prefcore) {
ret = amd_detect_prefcore(&amd_pstate_prefcore);
if (ret)
return ret;
}
ret = amd_pstate_register_driver(cppc_state);
if (ret) {
pr_err("failed to register with return %d\n", ret);
return ret;
}
dev_root = bus_get_dev_root(&cpu_subsys);
if (dev_root) {
ret = sysfs_create_group(&dev_root->kobj, &amd_pstate_global_attr_group);

View file

@ -1295,6 +1295,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
struct cxl_region_params *p = &cxlr->params;
struct cxl_decoder *cxld = cxl_rr->decoder;
struct cxl_switch_decoder *cxlsd;
struct cxl_port *iter = port;
u16 eig, peig;
u8 eiw, peiw;
@ -1311,16 +1312,26 @@ static int cxl_port_setup_targets(struct cxl_port *port,
cxlsd = to_cxl_switch_decoder(&cxld->dev);
if (cxl_rr->nr_targets_set) {
int i, distance;
int i, distance = 1;
struct cxl_region_ref *cxl_rr_iter;
/*
* Passthrough decoders impose no distance requirements between
* peers
* The "distance" between peer downstream ports represents which
* endpoint positions in the region interleave a given port can
* host.
*
* For example, at the root of a hierarchy the distance is
* always 1 as every index targets a different host-bridge. At
* each subsequent switch level those ports map every Nth region
* position where N is the width of the switch == distance.
*/
if (cxl_rr->nr_targets == 1)
distance = 0;
else
distance = p->nr_targets / cxl_rr->nr_targets;
do {
cxl_rr_iter = cxl_rr_load(iter, cxlr);
distance *= cxl_rr_iter->nr_targets;
iter = to_cxl_port(iter->dev.parent);
} while (!is_cxl_root(iter));
distance *= cxlrd->cxlsd.cxld.interleave_ways;
for (i = 0; i < cxl_rr->nr_targets_set; i++)
if (ep->dport == cxlsd->target[i]) {
rc = check_last_peer(cxled, ep, cxl_rr,

View file

@ -836,6 +836,9 @@ static ssize_t rcd_pcie_cap_emit(struct device *dev, u16 offset, char *buf, size
if (!root_dev)
return -ENXIO;
if (!dport->regs.rcd_pcie_cap)
return -ENXIO;
guard(device)(root_dev);
if (!root_dev->driver)
return -ENXIO;
@ -1032,8 +1035,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (rc)
return rc;
rc = cxl_pci_ras_unmask(pdev);
if (rc)
if (cxl_pci_ras_unmask(pdev))
dev_dbg(&pdev->dev, "No RAS reporting unmasked\n");
pci_save_state(pdev);

View file

@ -60,7 +60,7 @@ static void __dma_buf_debugfs_list_add(struct dma_buf *dmabuf)
{
}
static void __dma_buf_debugfs_list_del(struct file *file)
static void __dma_buf_debugfs_list_del(struct dma_buf *dmabuf)
{
}
#endif

View file

@ -297,7 +297,7 @@ static const struct dma_buf_ops udmabuf_ops = {
};
#define SEALS_WANTED (F_SEAL_SHRINK)
#define SEALS_DENIED (F_SEAL_WRITE)
#define SEALS_DENIED (F_SEAL_WRITE|F_SEAL_FUTURE_WRITE)
static int check_memfd_seals(struct file *memfd)
{
@ -317,12 +317,10 @@ static int check_memfd_seals(struct file *memfd)
return 0;
}
static int export_udmabuf(struct udmabuf *ubuf,
struct miscdevice *device,
u32 flags)
static struct dma_buf *export_udmabuf(struct udmabuf *ubuf,
struct miscdevice *device)
{
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
struct dma_buf *buf;
ubuf->device = device;
exp_info.ops = &udmabuf_ops;
@ -330,11 +328,7 @@ static int export_udmabuf(struct udmabuf *ubuf,
exp_info.priv = ubuf;
exp_info.flags = O_RDWR;
buf = dma_buf_export(&exp_info);
if (IS_ERR(buf))
return PTR_ERR(buf);
return dma_buf_fd(buf, flags);
return dma_buf_export(&exp_info);
}
static long udmabuf_pin_folios(struct udmabuf *ubuf, struct file *memfd,
@ -391,6 +385,7 @@ static long udmabuf_create(struct miscdevice *device,
struct folio **folios = NULL;
pgoff_t pgcnt = 0, pglimit;
struct udmabuf *ubuf;
struct dma_buf *dmabuf;
long ret = -EINVAL;
u32 i, flags;
@ -436,23 +431,39 @@ static long udmabuf_create(struct miscdevice *device,
goto err;
}
/*
* Take the inode lock to protect against concurrent
* memfd_add_seals(), which takes this lock in write mode.
*/
inode_lock_shared(file_inode(memfd));
ret = check_memfd_seals(memfd);
if (ret < 0) {
fput(memfd);
goto err;
}
if (ret)
goto out_unlock;
ret = udmabuf_pin_folios(ubuf, memfd, list[i].offset,
list[i].size, folios);
out_unlock:
inode_unlock_shared(file_inode(memfd));
fput(memfd);
if (ret)
goto err;
}
flags = head->flags & UDMABUF_FLAGS_CLOEXEC ? O_CLOEXEC : 0;
ret = export_udmabuf(ubuf, device, flags);
if (ret < 0)
dmabuf = export_udmabuf(ubuf, device);
if (IS_ERR(dmabuf)) {
ret = PTR_ERR(dmabuf);
goto err;
}
/*
* Ownership of ubuf is held by the dmabuf from here.
* If the following dma_buf_fd() fails, dma_buf_put() cleans up both the
* dmabuf and the ubuf (through udmabuf_ops.release).
*/
ret = dma_buf_fd(dmabuf, flags);
if (ret < 0)
dma_buf_put(dmabuf);
kvfree(folios);
return ret;

View file

@ -7,9 +7,9 @@
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/dma-map-ops.h>
#include <linux/platform_device.h>
#include <linux/platform_data/amd_qdma.h>
#include <linux/regmap.h>
@ -492,18 +492,9 @@ static int qdma_device_verify(struct qdma_device *qdev)
static int qdma_device_setup(struct qdma_device *qdev)
{
struct device *dev = &qdev->pdev->dev;
u32 ring_sz = QDMA_DEFAULT_RING_SIZE;
int ret = 0;
while (dev && get_dma_ops(dev))
dev = dev->parent;
if (!dev) {
qdma_err(qdev, "dma device not found");
return -EINVAL;
}
set_dma_ops(&qdev->pdev->dev, get_dma_ops(dev));
ret = qdma_setup_fmap_context(qdev);
if (ret) {
qdma_err(qdev, "Failed setup fmap context");
@ -548,11 +539,12 @@ static void qdma_free_queue_resources(struct dma_chan *chan)
{
struct qdma_queue *queue = to_qdma_queue(chan);
struct qdma_device *qdev = queue->qdev;
struct device *dev = qdev->dma_dev.dev;
struct qdma_platdata *pdata;
qdma_clear_queue_context(queue);
vchan_free_chan_resources(&queue->vchan);
dma_free_coherent(dev, queue->ring_size * QDMA_MM_DESC_SIZE,
pdata = dev_get_platdata(&qdev->pdev->dev);
dma_free_coherent(pdata->dma_dev, queue->ring_size * QDMA_MM_DESC_SIZE,
queue->desc_base, queue->dma_desc_base);
}
@ -565,6 +557,7 @@ static int qdma_alloc_queue_resources(struct dma_chan *chan)
struct qdma_queue *queue = to_qdma_queue(chan);
struct qdma_device *qdev = queue->qdev;
struct qdma_ctxt_sw_desc desc;
struct qdma_platdata *pdata;
size_t size;
int ret;
@ -572,8 +565,9 @@ static int qdma_alloc_queue_resources(struct dma_chan *chan)
if (ret)
return ret;
pdata = dev_get_platdata(&qdev->pdev->dev);
size = queue->ring_size * QDMA_MM_DESC_SIZE;
queue->desc_base = dma_alloc_coherent(qdev->dma_dev.dev, size,
queue->desc_base = dma_alloc_coherent(pdata->dma_dev, size,
&queue->dma_desc_base,
GFP_KERNEL);
if (!queue->desc_base) {
@ -588,7 +582,7 @@ static int qdma_alloc_queue_resources(struct dma_chan *chan)
if (ret) {
qdma_err(qdev, "Failed to setup SW desc ctxt for %s",
chan->name);
dma_free_coherent(qdev->dma_dev.dev, size, queue->desc_base,
dma_free_coherent(pdata->dma_dev, size, queue->desc_base,
queue->dma_desc_base);
return ret;
}
@ -948,8 +942,9 @@ static int qdma_init_error_irq(struct qdma_device *qdev)
static int qdmam_alloc_qintr_rings(struct qdma_device *qdev)
{
u32 ctxt[QDMA_CTXT_REGMAP_LEN];
struct qdma_platdata *pdata = dev_get_platdata(&qdev->pdev->dev);
struct device *dev = &qdev->pdev->dev;
u32 ctxt[QDMA_CTXT_REGMAP_LEN];
struct qdma_intr_ring *ring;
struct qdma_ctxt_intr intr_ctxt;
u32 vector;
@ -969,7 +964,8 @@ static int qdmam_alloc_qintr_rings(struct qdma_device *qdev)
ring->msix_id = qdev->err_irq_idx + i + 1;
ring->ridx = i;
ring->color = 1;
ring->base = dmam_alloc_coherent(dev, QDMA_INTR_RING_SIZE,
ring->base = dmam_alloc_coherent(pdata->dma_dev,
QDMA_INTR_RING_SIZE,
&ring->dev_base, GFP_KERNEL);
if (!ring->base) {
qdma_err(qdev, "Failed to alloc intr ring %d", i);

View file

@ -153,6 +153,8 @@ static int admac_alloc_sram_carveout(struct admac_data *ad,
{
struct admac_sram *sram;
int i, ret = 0, nblocks;
ad->txcache.size = readl_relaxed(ad->base + REG_TX_SRAM_SIZE);
ad->rxcache.size = readl_relaxed(ad->base + REG_RX_SRAM_SIZE);
if (dir == DMA_MEM_TO_DEV)
sram = &ad->txcache;
@ -912,12 +914,7 @@ static int admac_probe(struct platform_device *pdev)
goto free_irq;
}
ad->txcache.size = readl_relaxed(ad->base + REG_TX_SRAM_SIZE);
ad->rxcache.size = readl_relaxed(ad->base + REG_RX_SRAM_SIZE);
dev_info(&pdev->dev, "Audio DMA Controller\n");
dev_info(&pdev->dev, "imprint %x TX cache %u RX cache %u\n",
readl_relaxed(ad->base + REG_IMPRINT), ad->txcache.size, ad->rxcache.size);
return 0;

View file

@ -1363,6 +1363,8 @@ at_xdmac_prep_dma_memset(struct dma_chan *chan, dma_addr_t dest, int value,
return NULL;
desc = at_xdmac_memset_create_desc(chan, atchan, dest, len, value);
if (!desc)
return NULL;
list_add_tail(&desc->desc_node, &desc->descs_list);
desc->tx_dma_desc.cookie = -EBUSY;

View file

@ -8,13 +8,15 @@
static bool dw_dma_acpi_filter(struct dma_chan *chan, void *param)
{
struct dw_dma *dw = to_dw_dma(chan->device);
struct dw_dma_chip_pdata *data = dev_get_drvdata(dw->dma.dev);
struct acpi_dma_spec *dma_spec = param;
struct dw_dma_slave slave = {
.dma_dev = dma_spec->dev,
.src_id = dma_spec->slave_id,
.dst_id = dma_spec->slave_id,
.m_master = 0,
.p_master = 1,
.m_master = data->m_master,
.p_master = data->p_master,
};
return dw_dma_filter(chan, &slave);

Some files were not shown because too many files have changed in this diff Show more