mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-18 22:14:16 +00:00
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:
commit
8514d8f80e
674 changed files with 28177 additions and 4296 deletions
3
.mailmap
3
.mailmap
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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``
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -82,7 +82,7 @@ examples:
|
|||
|
||||
uimage@100000 {
|
||||
reg = <0x0100000 0x200000>;
|
||||
compress = "lzma";
|
||||
compression = "lzma";
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
|
||||
...
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
|
||||
...
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
|
|
|
@ -25,6 +25,7 @@ properties:
|
|||
- fsl,imx8mm-micfil
|
||||
- fsl,imx8mp-micfil
|
||||
- fsl,imx93-micfil
|
||||
- fsl,imx943-micfil
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
|
|
|
@ -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";
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
156
Documentation/devicetree/bindings/sound/realtek,rt5682.yaml
Normal file
156
Documentation/devicetree/bindings/sound/realtek,rt5682.yaml
Normal 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>;
|
||||
};
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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>;
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
|
@ -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/CoW’d 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`.
|
||||
|
|
|
@ -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 ]
|
||||
|
|
20
MAINTAINERS
20
MAINTAINERS
|
@ -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
|
||||
|
|
2
Makefile
2
Makefile
|
@ -2,7 +2,7 @@
|
|||
VERSION = 6
|
||||
PATCHLEVEL = 13
|
||||
SUBLEVEL = 0
|
||||
EXTRAVERSION = -rc3
|
||||
EXTRAVERSION = -rc6
|
||||
NAME = Baby Opossum Posse
|
||||
|
||||
# *DOCUMENTATION*
|
||||
|
|
|
@ -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
|
||||
|
|
8
arch/arc/include/asm/cachetype.h
Normal file
8
arch/arc/include/asm/cachetype.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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; \
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
/*
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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++)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
Loading…
Add table
Reference in a new issue