diff options
363 files changed, 7932 insertions, 5244 deletions
diff --git a/.gitignore b/.gitignore index c59dc60ba62e..7902adf4f7f1 100644 --- a/.gitignore +++ b/.gitignore @@ -93,6 +93,12 @@ modules.order /tar-install/ # +# pacman files (make pacman-pkg) +# +/PKGBUILD +/pacman/ + +# # We don't want to ignore the following even if they are dot-files # !.clang-format diff --git a/Documentation/ABI/testing/sysfs-kernel-livepatch b/Documentation/ABI/testing/sysfs-kernel-livepatch index a5df9b4910dc..3735d868013d 100644 --- a/Documentation/ABI/testing/sysfs-kernel-livepatch +++ b/Documentation/ABI/testing/sysfs-kernel-livepatch @@ -47,6 +47,14 @@ Description: disabled when the feature is used. See Documentation/livepatch/livepatch.rst for more information. +What: /sys/kernel/livepatch/<patch>/replace +Date: Jun 2024 +KernelVersion: 6.11.0 +Contact: live-patching@vger.kernel.org +Description: + An attribute which indicates whether the patch supports + atomic-replace. + What: /sys/kernel/livepatch/<patch>/<object> Date: Nov 2014 KernelVersion: 3.19.0 diff --git a/Documentation/core-api/genericirq.rst b/Documentation/core-api/genericirq.rst index 582bde9bf5a9..25f94dfd66fa 100644 --- a/Documentation/core-api/genericirq.rst +++ b/Documentation/core-api/genericirq.rst @@ -410,6 +410,8 @@ which are used in the generic IRQ layer. .. kernel-doc:: include/linux/interrupt.h :internal: +.. kernel-doc:: include/linux/irqdomain.h + Public Functions Provided ========================= diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-gpio.yaml b/Documentation/devicetree/bindings/i2c/i2c-mux-gpio.yaml index f34cc7ad5a00..4a93d1f78f93 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-mux-gpio.yaml +++ b/Documentation/devicetree/bindings/i2c/i2c-mux-gpio.yaml @@ -57,6 +57,9 @@ properties: last value used. $ref: /schemas/types.yaml#/definitions/uint32 + settle-time-us: + description: Delay to wait before doing any transfer when a new bus gets selected. + allOf: - $ref: i2c-mux.yaml diff --git a/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml b/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml index daf4e71b8e7f..c33ae7b63b84 100644 --- a/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml +++ b/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml @@ -31,6 +31,8 @@ properties: - qcom,sm6350-cci - qcom,sm8250-cci - qcom,sm8450-cci + - qcom,sm8550-cci + - qcom,sm8650-cci - const: qcom,msm8996-cci # CCI v2 "#address-cells": @@ -195,6 +197,24 @@ allOf: - const: cpas_ahb - const: cci + - if: + properties: + compatible: + contains: + enum: + - qcom,sm8550-cci + - qcom,sm8650-cci + then: + properties: + clocks: + minItems: 3 + maxItems: 3 + clock-names: + items: + - const: camnoc_axi + - const: cpas_ahb + - const: cci + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/interrupt-controller/microchip,lan966x-oic.yaml b/Documentation/devicetree/bindings/interrupt-controller/microchip,lan966x-oic.yaml new file mode 100644 index 000000000000..b2adc7174177 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/microchip,lan966x-oic.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/microchip,lan966x-oic.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip LAN966x outband interrupt controller + +maintainers: + - Herve Codina <herve.codina@bootlin.com> + +allOf: + - $ref: /schemas/interrupt-controller.yaml# + +description: | + The Microchip LAN966x outband interrupt controller (OIC) maps the internal + interrupt sources of the LAN966x device to an external interrupt. + When the LAN966x device is used as a PCI device, the external interrupt is + routed to the PCI interrupt. + +properties: + compatible: + const: microchip,lan966x-oic + + '#interrupt-cells': + const: 2 + + interrupt-controller: true + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - '#interrupt-cells' + - interrupt-controller + - interrupts + - reg + +additionalProperties: false + +examples: + - | + interrupt-controller@e00c0120 { + compatible = "microchip,lan966x-oic"; + reg = <0xe00c0120 0x190>; + #interrupt-cells = <2>; + interrupt-controller; + interrupts = <0>; + interrupt-parent = <&intc>; + }; +... diff --git a/Documentation/devicetree/bindings/interrupt-controller/renesas,rzg2l-irqc.yaml b/Documentation/devicetree/bindings/interrupt-controller/renesas,rzg2l-irqc.yaml index daef4ee06f4e..44b6ae5fc802 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/renesas,rzg2l-irqc.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/renesas,rzg2l-irqc.yaml @@ -21,13 +21,16 @@ description: | properties: compatible: - items: - - enum: - - renesas,r9a07g043u-irqc # RZ/G2UL - - renesas,r9a07g044-irqc # RZ/G2{L,LC} - - renesas,r9a07g054-irqc # RZ/V2L - - renesas,r9a08g045-irqc # RZ/G3S - - const: renesas,rzg2l-irqc + oneOf: + - items: + - enum: + - renesas,r9a07g043u-irqc # RZ/G2UL + - renesas,r9a07g044-irqc # RZ/G2{L,LC} + - renesas,r9a07g054-irqc # RZ/V2L + - renesas,r9a08g045-irqc # RZ/G3S + - const: renesas,rzg2l-irqc + + - const: renesas,r9a07g043f-irqc # RZ/Five '#interrupt-cells': description: The first cell should contain a macro RZG2L_{NMI,IRQX} included in the diff --git a/Documentation/devicetree/bindings/mailbox/mediatek,gce-props.yaml b/Documentation/devicetree/bindings/mailbox/mediatek,gce-props.yaml new file mode 100644 index 000000000000..c25eed4606fe --- /dev/null +++ b/Documentation/devicetree/bindings/mailbox/mediatek,gce-props.yaml @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mailbox/mediatek,gce-props.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek Global Command Engine Common Properties + +maintainers: + - Houlong Wei <houlong.wei@mediatek.com> + +description: + The Global Command Engine (GCE) is an instruction based, multi-threaded, + single-core command dispatcher for MediaTek hardware. The Command Queue + (CMDQ) mailbox driver is a driver for GCE, implemented using the Linux + mailbox framework. It is used to receive messages from mailbox consumers + and configure GCE to execute the specified instruction set in the message. + We use mediatek,gce-mailbox.yaml to define the properties for CMDQ mailbox + driver. A device driver that uses the CMDQ driver to configure its hardware + registers is a mailbox consumer. The mailbox consumer can request a mailbox + channel corresponding to a GCE hardware thread to send a message, specifying + that the GCE thread to configure its hardware. The mailbox provider can also + reserve a mailbox channel to configure GCE hardware register by the specific + GCE thread. This binding defines the common GCE properties for both mailbox + provider and consumers. + +properties: + mediatek,gce-events: + description: + GCE has an event table in SRAM, consisting of 1024 event IDs (0~1023). + Each event ID has a boolean event value with the default value 0. + The property mediatek,gce-events is used to obtain the event IDs. + Some gce-events are hardware-bound and cannot be changed by software. + For instance, in MT8195, when VDO0_MUTEX is stream done, VDO_MUTEX will + send an event signal to GCE, setting the value of event ID 597 to 1. + Similarly, in MT8188, the value of event ID 574 will be set to 1 when + VOD0_MUTEX is stream done. + On the other hand, some gce-events are not hardware-bound and can be + changed by software. For example, in MT8188, we can set the value of + event ID 855, which is not bound to any hardware, to 1 when the driver + in the secure world completes a task. However, in MT8195, event ID 855 + is already bound to VDEC_LAT1, so we need to select another event ID to + achieve the same purpose. This event ID can be any ID that is not bound + to any hardware and is not yet used in any software driver. + To determine if the event ID is bound to the hardware or used by a + software driver, refer to the GCE header + include/dt-bindings/gce/<chip>-gce.h of each chip. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 32 + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml b/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml new file mode 100644 index 000000000000..f7342d04beec --- /dev/null +++ b/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mailbox/qcom,cpucp-mbox.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies, Inc. CPUCP Mailbox Controller + +maintainers: + - Sibi Sankar <quic_sibis@quicinc.com> + +description: + The CPUSS Control Processor (CPUCP) mailbox controller enables communication + between AP and CPUCP by acting as a doorbell between them. + +properties: + compatible: + items: + - const: qcom,x1e80100-cpucp-mbox + + reg: + items: + - description: CPUCP rx register region + - description: CPUCP tx register region + + interrupts: + maxItems: 1 + + "#mbox-cells": + const: 1 + +required: + - compatible + - reg + - interrupts + - "#mbox-cells" + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + + mailbox@17430000 { + compatible = "qcom,x1e80100-cpucp-mbox"; + reg = <0x17430000 0x10000>, <0x18830000 0x10000>; + interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>; + #mbox-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max17201.yaml b/Documentation/devicetree/bindings/power/supply/maxim,max17201.yaml new file mode 100644 index 000000000000..fe3dd9bd5585 --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/maxim,max17201.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/supply/maxim,max17201.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim MAX17201 fuel gauge + +maintainers: + - Dimitri Fedrau <dima.fedrau@gmail.com> + +allOf: + - $ref: power-supply.yaml# + +properties: + compatible: + oneOf: + - const: maxim,max17201 + - items: + - enum: + - maxim,max17205 + - const: maxim,max17201 + + reg: + items: + - description: ModelGauge m5 registers + - description: Nonvolatile registers + + reg-names: + items: + - const: m5 + - const: nvmem + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - reg-names + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + + fuel-gauge@36 { + compatible = "maxim,max17201"; + reg = <0x36>, <0xb>; + reg-names = "m5", "nvmem"; + interrupt-parent = <&gpio0>; + interrupts = <31 IRQ_TYPE_LEVEL_LOW>; + }; + }; diff --git a/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml index df36e29d974c..57d75acb0b5e 100644 --- a/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml +++ b/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml @@ -59,6 +59,7 @@ properties: maxItems: 32 power-domains: + minItems: 2 maxItems: 8 fsl,auto-boot: @@ -99,6 +100,20 @@ allOf: properties: fsl,iomuxc-gpr: false + - if: + properties: + compatible: + contains: + enum: + - fsl,imx8qxp-cm4 + - fsl,imx8qm-cm4 + then: + required: + - power-domains + else: + properties: + power-domains: false + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sa8775p-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sa8775p-pas.yaml new file mode 100644 index 000000000000..7fe401a06805 --- /dev/null +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sa8775p-pas.yaml @@ -0,0 +1,160 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/remoteproc/qcom,sa8775p-pas.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SA8775p Peripheral Authentication Service + +maintainers: + - Bartosz Golaszewski <bartosz.golaszewski@linaro.org> + +description: + Qualcomm SA8775p SoC Peripheral Authentication Service loads and boots firmware + on the Qualcomm DSP Hexagon cores. + +properties: + compatible: + enum: + - qcom,sa8775p-adsp-pas + - qcom,sa8775p-cdsp0-pas + - qcom,sa8775p-cdsp1-pas + - qcom,sa8775p-gpdsp0-pas + - qcom,sa8775p-gpdsp1-pas + + reg: + maxItems: 1 + + clocks: + items: + - description: XO clock + + clock-names: + items: + - const: xo + + qcom,qmp: + $ref: /schemas/types.yaml#/definitions/phandle + description: Reference to the AOSS side-channel message RAM. + + firmware-name: + $ref: /schemas/types.yaml#/definitions/string-array + items: + - description: Firmware name of the Hexagon core + + memory-region: + items: + - description: Memory region for main Firmware authentication + + interrupts: + maxItems: 5 + + interrupt-names: + maxItems: 5 + +required: + - compatible + - reg + - memory-region + +allOf: + - $ref: /schemas/remoteproc/qcom,pas-common.yaml# + + - if: + properties: + compatible: + enum: + - qcom,sa8775p-adsp-pas + then: + properties: + power-domains: + items: + - description: LCX power domain + - description: LMX power domain + power-domain-names: + items: + - const: lcx + - const: lmx + + - if: + properties: + compatible: + enum: + - qcom,sa8775p-cdsp0-pas + - qcom,sa8775p-cdsp1-pas + then: + properties: + power-domains: + items: + - description: CX power domain + - description: MXC power domain + - description: NSP0 power domain + power-domain-names: + items: + - const: cx + - const: mxc + - const: nsp + + - if: + properties: + compatible: + enum: + - qcom,sa8775p-gpdsp0-pas + - qcom,sa8775p-gpdsp1-pas + then: + properties: + power-domains: + items: + - description: CX power domain + - description: MXC power domain + power-domain-names: + items: + - const: cx + - const: mxc + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,rpmh.h> + #include <dt-bindings/interconnect/qcom,sa8775p-rpmh.h> + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/mailbox/qcom-ipcc.h> + #include <dt-bindings/power/qcom,rpmhpd.h> + + remoteproc@30000000 { + compatible = "qcom,sa8775p-adsp-pas"; + reg = <0x30000000 0x100>; + + interrupts-extended = <&pdc 6 IRQ_TYPE_EDGE_RISING>, + <&smp2p_adsp_in 0 IRQ_TYPE_EDGE_RISING>, + <&smp2p_adsp_in 2 IRQ_TYPE_EDGE_RISING>, + <&smp2p_adsp_in 1 IRQ_TYPE_EDGE_RISING>, + <&smp2p_adsp_in 3 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "wdog", "fatal", "ready", "handover", "stop-ack"; + + clocks = <&rpmhcc RPMH_CXO_CLK>; + clock-names = "xo"; + + power-domains = <&rpmhpd RPMHPD_LCX>, <&rpmhpd RPMHPD_LMX>; + power-domain-names = "lcx", "lmx"; + + interconnects = <&lpass_ag_noc MASTER_LPASS_PROC 0 &mc_virt SLAVE_EBI1 0>; + + memory-region = <&pil_adsp_mem>; + + qcom,qmp = <&aoss_qmp>; + + qcom,smem-states = <&smp2p_adsp_out 0>; + qcom,smem-state-names = "stop"; + + glink-edge { + interrupts-extended = <&ipcc IPCC_CLIENT_LPASS + IPCC_MPROC_SIGNAL_GLINK_QMP + IRQ_TYPE_EDGE_RISING>; + mboxes = <&ipcc IPCC_CLIENT_LPASS IPCC_MPROC_SIGNAL_GLINK_QMP>; + + label = "lpass"; + qcom,remote-pid = <2>; + }; + }; diff --git a/Documentation/devicetree/bindings/remoteproc/ti,k3-dsp-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/ti,k3-dsp-rproc.yaml index 9768db8663eb..b51bb863d759 100644 --- a/Documentation/devicetree/bindings/remoteproc/ti,k3-dsp-rproc.yaml +++ b/Documentation/devicetree/bindings/remoteproc/ti,k3-dsp-rproc.yaml @@ -25,9 +25,6 @@ description: | host processor (Arm CorePac) to perform the device management of the remote processor and to communicate with the remote processor. -allOf: - - $ref: /schemas/arm/keystone/ti,k3-sci-common.yaml# - properties: compatible: enum: @@ -89,41 +86,57 @@ properties: should be defined as per the generic bindings in, Documentation/devicetree/bindings/sram/sram.yaml -if: - properties: - compatible: - enum: - - ti,j721e-c66-dsp -then: - properties: - reg: - items: - - description: Address and Size of the L2 SRAM internal memory region - - description: Address and Size of the L1 PRAM internal memory region - - description: Address and Size of the L1 DRAM internal memory region - reg-names: - items: - - const: l2sram - - const: l1pram - - const: l1dram -else: - if: - properties: - compatible: - enum: - - ti,am62a-c7xv-dsp - - ti,j721e-c71-dsp - - ti,j721s2-c71-dsp - then: - properties: - reg: - items: - - description: Address and Size of the L2 SRAM internal memory region - - description: Address and Size of the L1 DRAM internal memory region - reg-names: - items: - - const: l2sram - - const: l1dram +allOf: + - if: + properties: + compatible: + enum: + - ti,j721e-c66-dsp + then: + properties: + reg: + items: + - description: Address and Size of the L2 SRAM internal memory region + - description: Address and Size of the L1 PRAM internal memory region + - description: Address and Size of the L1 DRAM internal memory region + reg-names: + items: + - const: l2sram + - const: l1pram + - const: l1dram + + - if: + properties: + compatible: + enum: + - ti,j721e-c71-dsp + - ti,j721s2-c71-dsp + then: + properties: + reg: + items: + - description: Address and Size of the L2 SRAM internal memory region + - description: Address and Size of the L1 DRAM internal memory region + reg-names: + items: + - const: l2sram + - const: l1dram + + - if: + properties: + compatible: + enum: + - ti,am62a-c7xv-dsp + then: + properties: + reg: + items: + - description: Address and Size of the L2 SRAM internal memory region + reg-names: + items: + - const: l2sram + + - $ref: /schemas/arm/keystone/ti,k3-sci-common.yaml# required: - compatible diff --git a/Documentation/features/debug/debug-vm-pgtable/arch-support.txt b/Documentation/features/debug/debug-vm-pgtable/arch-support.txt index bbf029f095cb..156687a7436d 100644 --- a/Documentation/features/debug/debug-vm-pgtable/arch-support.txt +++ b/Documentation/features/debug/debug-vm-pgtable/arch-support.txt @@ -12,7 +12,7 @@ | arm64: | ok | | csky: | TODO | | hexagon: | TODO | - | loongarch: | TODO | + | loongarch: | ok | | m68k: | TODO | | microblaze: | TODO | | mips: | TODO | diff --git a/Documentation/kbuild/kconfig-language.rst b/Documentation/kbuild/kconfig-language.rst index 1fb3f5e6193c..71b38a7670f3 100644 --- a/Documentation/kbuild/kconfig-language.rst +++ b/Documentation/kbuild/kconfig-language.rst @@ -409,16 +409,9 @@ choices:: "endchoice" This defines a choice group and accepts any of the above attributes as -options. A choice can only be of type bool or tristate. If no type is -specified for a choice, its type will be determined by the type of -the first choice element in the group or remain unknown if none of the -choice elements have a type specified, as well. - -While a boolean choice only allows a single config entry to be -selected, a tristate choice also allows any number of config entries -to be set to 'm'. This can be used if multiple drivers for a single -hardware exists and only a single driver can be compiled/loaded into -the kernel, but all drivers can be compiled as modules. +options. + +A choice only allows a single config entry to be selected. comment:: diff --git a/Documentation/kbuild/makefiles.rst b/Documentation/kbuild/makefiles.rst index 991ce6081e35..be43990f1e7f 100644 --- a/Documentation/kbuild/makefiles.rst +++ b/Documentation/kbuild/makefiles.rst @@ -578,7 +578,7 @@ cc-option Note: cc-option uses KBUILD_CFLAGS for $(CC) options cc-option-yn - cc-option-yn is used to check if gcc supports a given option + cc-option-yn is used to check if $(CC) supports a given option and return "y" if supported, otherwise "n". Example:: @@ -596,7 +596,7 @@ cc-option-yn Note: cc-option-yn uses KBUILD_CFLAGS for $(CC) options cc-disable-warning - cc-disable-warning checks if gcc supports a given warning and returns + cc-disable-warning checks if $(CC) supports a given warning and returns the commandline switch to disable it. This special function is needed, because gcc 4.4 and later accept any unknown -Wno-* option and only warn about it if there is another warning in the source file. @@ -606,7 +606,7 @@ cc-disable-warning KBUILD_CFLAGS += $(call cc-disable-warning, unused-but-set-variable) In the above example, -Wno-unused-but-set-variable will be added to - KBUILD_CFLAGS only if gcc really accepts it. + KBUILD_CFLAGS only if $(CC) really accepts it. gcc-min-version gcc-min-version tests if the value of $(CONFIG_GCC_VERSION) is greater than diff --git a/Documentation/locking/hwspinlock.rst b/Documentation/locking/hwspinlock.rst index 6f03713b7003..2ffaa3cbd63f 100644 --- a/Documentation/locking/hwspinlock.rst +++ b/Documentation/locking/hwspinlock.rst @@ -87,6 +87,17 @@ Should be called from a process context (might sleep). :: + int hwspin_lock_bust(struct hwspinlock *hwlock, unsigned int id); + +After verifying the owner of the hwspinlock, release a previously acquired +hwspinlock; returns 0 on success, or an appropriate error code on failure +(e.g. -EOPNOTSUPP if the bust operation is not defined for the specific +hwspinlock). + +Should be called from a process context (might sleep). + +:: + int hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int timeout); Lock a previously-assigned hwspinlock with a timeout limit (specified in diff --git a/Documentation/process/changes.rst b/Documentation/process/changes.rst index 8d225a9f65a2..1497e80f030e 100644 --- a/Documentation/process/changes.rst +++ b/Documentation/process/changes.rst @@ -33,7 +33,7 @@ GNU C 5.1 gcc --version Clang/LLVM (optional) 13.0.1 clang --version Rust (optional) 1.78.0 rustc --version bindgen (optional) 0.65.1 bindgen --version -GNU make 3.82 make --version +GNU make 4.0 make --version bash 4.2 bash --version binutils 2.25 ld -v flex 2.5.35 flex --version @@ -112,7 +112,7 @@ It depends on ``libclang``. Make ---- -You will need GNU make 3.82 or later to build the kernel. +You will need GNU make 4.0 or later to build the kernel. Bash ---- diff --git a/MAINTAINERS b/MAINTAINERS index a0baccca11de..07f753f039d2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8354,7 +8354,8 @@ F: Documentation/userspace-api/ELF.rst F: fs/*binfmt_*.c F: fs/Kconfig.binfmt F: fs/exec.c -F: fs/exec_test.c +F: fs/tests/binfmt_*_kunit.c +F: fs/tests/exec_kunit.c F: include/linux/binfmts.h F: include/linux/elf.h F: include/uapi/linux/binfmts.h @@ -12117,6 +12118,7 @@ F: scripts/Makefile* F: scripts/basic/ F: scripts/clang-tools/ F: scripts/dummy-tools/ +F: scripts/include/ F: scripts/mk* F: scripts/mod/ F: scripts/package/ @@ -12172,6 +12174,13 @@ F: include/uapi/linux/nfsd/ F: include/uapi/linux/sunrpc/ F: net/sunrpc/ +KERNEL PACMAN PACKAGING (in addition to generic KERNEL BUILD) +M: Thomas Weißschuh <linux@weissschuh.net> +R: Christian Heusel <christian@heusel.eu> +R: Nathan Chancellor <nathan@kernel.org> +S: Maintained +F: scripts/package/PKGBUILD + KERNEL REGRESSIONS M: Thorsten Leemhuis <linux@leemhuis.info> L: regressions@lists.linux.dev @@ -14942,6 +14951,12 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/microchip/lan966x/* +MICROCHIP LAN966X OIC DRIVER +M: Herve Codina <herve.codina@bootlin.com> +S: Maintained +F: Documentation/devicetree/bindings/interrupt-controller/microchip,lan966x-oic.yaml +F: drivers/irqchip/irq-lan966x-oic.c + MICROCHIP LCDFB DRIVER M: Nicolas Ferre <nicolas.ferre@microchip.com> L: linux-fbdev@vger.kernel.org @@ -18743,6 +18758,13 @@ S: Maintained F: Documentation/devicetree/bindings/power/avs/qcom,cpr.yaml F: drivers/pmdomain/qcom/cpr.c +QUALCOMM CPUCP MAILBOX DRIVER +M: Sibi Sankar <quic_sibis@quicinc.com> +L: linux-arm-msm@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml +F: drivers/mailbox/qcom-cpucp-mbox.c + QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096 M: Ilia Lin <ilia.lin@kernel.org> L: linux-pm@vger.kernel.org @@ -11,8 +11,8 @@ NAME = Baby Opossum Posse # Comments in this file are targeted only to the developer, do not # expect to learn how to build the kernel reading this file. -ifeq ($(filter undefine,$(.FEATURES)),) -$(error GNU Make >= 3.82 is required. Your Make version is $(MAKE_VERSION)) +ifeq ($(filter output-sync,$(.FEATURES)),) +$(error GNU Make >= 4.0 is required. Your Make version is $(MAKE_VERSION)) endif $(if $(filter __%, $(MAKECMDGOALS)), \ @@ -93,15 +93,7 @@ endif # If the user is running make -s (silent mode), suppress echoing of # commands -# make-4.0 (and later) keep single letter options in the 1st word of MAKEFLAGS. - -ifeq ($(filter 3.%,$(MAKE_VERSION)),) -short-opts := $(firstword -$(MAKEFLAGS)) -else -short-opts := $(filter-out --%,$(MAKEFLAGS)) -endif - -ifneq ($(findstring s,$(short-opts)),) +ifneq ($(findstring s,$(firstword -$(MAKEFLAGS))),) quiet=silent_ override KBUILD_VERBOSE := endif @@ -201,14 +193,6 @@ ifneq ($(words $(subst :, ,$(abs_srctree))), 1) $(error source directory cannot contain spaces or colons) endif -ifneq ($(filter 3.%,$(MAKE_VERSION)),) -# 'MAKEFLAGS += -rR' does not immediately become effective for GNU Make 3.x -# We need to invoke sub-make to avoid implicit rules in the top Makefile. -need-sub-make := 1 -# Cancel implicit rules for this Makefile. -$(this-makefile): ; -endif - export sub_make_done := 1 endif # sub_make_done @@ -474,8 +458,10 @@ export rust_common_flags := --edition=2021 \ -Dclippy::no_mangle_with_rust_abi \ -Wclippy::dbg_macro -KBUILD_HOSTCFLAGS := $(KBUILD_USERHOSTCFLAGS) $(HOST_LFS_CFLAGS) $(HOSTCFLAGS) -KBUILD_HOSTCXXFLAGS := -Wall -O2 $(HOST_LFS_CFLAGS) $(HOSTCXXFLAGS) +KBUILD_HOSTCFLAGS := $(KBUILD_USERHOSTCFLAGS) $(HOST_LFS_CFLAGS) \ + $(HOSTCFLAGS) -I $(srctree)/scripts/include +KBUILD_HOSTCXXFLAGS := -Wall -O2 $(HOST_LFS_CFLAGS) $(HOSTCXXFLAGS) \ + -I $(srctree)/scripts/include KBUILD_HOSTRUSTFLAGS := $(rust_common_flags) -O -Cstrip=debuginfo \ -Zallow-features= $(HOSTRUSTFLAGS) KBUILD_HOSTLDFLAGS := $(HOST_LFS_LDFLAGS) $(HOSTLDFLAGS) @@ -1344,6 +1330,12 @@ prepare: tools/bpf/resolve_btfids endif endif +# The tools build system is not a part of Kbuild and tends to introduce +# its own unique issues. If you need to integrate a new tool into Kbuild, +# please consider locating that tool outside the tools/ tree and using the +# standard Kbuild "hostprogs" syntax instead of adding a new tools/* entry +# here. See Documentation/kbuild/makefiles.rst for details. + PHONY += resolve_btfids_clean resolve_btfids_O = $(abspath $(objtree))/tools/bpf/resolve_btfids @@ -1497,7 +1489,7 @@ CLEAN_FILES += vmlinux.symvers modules-only.symvers \ # Directories & files removed with 'make mrproper' MRPROPER_FILES += include/config include/generated \ arch/$(SRCARCH)/include/generated .objdiff \ - debian snap tar-install \ + debian snap tar-install PKGBUILD pacman \ .config .config.old .version \ Module.symvers \ certs/signing_key.pem \ diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index a867a7d967aa..954a1916a500 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1483,7 +1483,8 @@ config ARM_ATAG_DTB_COMPAT from the ATAG list and store it at run time into the appended DTB. choice - prompt "Kernel command line type" if ARM_ATAG_DTB_COMPAT + prompt "Kernel command line type" + depends on ARM_ATAG_DTB_COMPAT default ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER config ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER @@ -1512,7 +1513,8 @@ config CMDLINE memory size and the root device (e.g., mem=64M root=/dev/nfs). choice - prompt "Kernel command line type" if CMDLINE != "" + prompt "Kernel command line type" + depends on CMDLINE != "" default CMDLINE_FROM_BOOTLOADER config CMDLINE_FROM_BOOTLOADER diff --git a/arch/arm/boot/install.sh b/arch/arm/boot/install.sh index 9ec11fac7d8d..34e2c6e31fd1 100755 --- a/arch/arm/boot/install.sh +++ b/arch/arm/boot/install.sh @@ -17,6 +17,8 @@ # $3 - kernel map file # $4 - default install path (blank if root directory) +set -e + if [ "$(basename $2)" = "zImage" ]; then # Compressed install echo "Installing compressed kernel" diff --git a/arch/arm/mach-stm32/Kconfig b/arch/arm/mach-stm32/Kconfig index ae21a9f78f9c..630b992f32b1 100644 --- a/arch/arm/mach-stm32/Kconfig +++ b/arch/arm/mach-stm32/Kconfig @@ -11,7 +11,7 @@ menuconfig ARCH_STM32 select CLKSRC_STM32 select PINCTRL select RESET_CONTROLLER - select STM32_EXTI + select STM32_EXTI if ARM_SINGLE_ARMV7M select STM32_FIREWALL help Support for STMicroelectronics STM32 processors. diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 79a656a62cbc..7fd70be0463f 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -2290,7 +2290,8 @@ config CMDLINE root device (e.g. root=/dev/nfs). choice - prompt "Kernel command line type" if CMDLINE != "" + prompt "Kernel command line type" + depends on CMDLINE != "" default CMDLINE_FROM_BOOTLOADER help Choose how the kernel will handle the provided default kernel diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index 56f440a06b21..6c6d11536b42 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -309,7 +309,6 @@ config ARCH_STM32 select GPIOLIB select PINCTRL select PINCTRL_STM32MP257 - select STM32_EXTI select ARM_SMC_MBOX select ARM_SCMI_PROTOCOL select REGULATOR diff --git a/arch/arm64/boot/install.sh b/arch/arm64/boot/install.sh index 9b7a09808a3d..cc2f4ccca6c0 100755 --- a/arch/arm64/boot/install.sh +++ b/arch/arm64/boot/install.sh @@ -17,6 +17,8 @@ # $3 - kernel map file # $4 - default install path (blank if root directory) +set -e + if [ "$(basename $2)" = "Image.gz" ] || [ "$(basename $2)" = "vmlinuz.efi" ] then # Compressed install diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index b81d0eba5c7e..ebdb7156560c 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -16,12 +16,14 @@ config LOONGARCH select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI select ARCH_HAS_CPU_FINALIZE_INIT select ARCH_HAS_CURRENT_STACK_POINTER + select ARCH_HAS_DEBUG_VM_PGTABLE select ARCH_HAS_FAST_MULTIPLIER select ARCH_HAS_FORTIFY_SOURCE select ARCH_HAS_KCOV select ARCH_HAS_KERNEL_FPU_SUPPORT if CPU_HAS_FPU select ARCH_HAS_NMI_SAFE_THIS_CPU_OPS select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE + select ARCH_HAS_PTE_DEVMAP select ARCH_HAS_PTE_SPECIAL select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST select ARCH_INLINE_READ_LOCK if !PREEMPTION @@ -106,6 +108,7 @@ config LOONGARCH select HAVE_ARCH_KFENCE select HAVE_ARCH_KGDB if PERF_EVENTS select HAVE_ARCH_MMAP_RND_BITS if MMU + select HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET select HAVE_ARCH_SECCOMP select HAVE_ARCH_SECCOMP_FILTER select HAVE_ARCH_TRACEHOOK @@ -607,6 +610,7 @@ config ARCH_HAS_GENERIC_CRASHKERNEL_RESERVATION config RELOCATABLE bool "Relocatable kernel" + select ARCH_HAS_RELR help This builds the kernel as a Position Independent Executable (PIE), which retains all relocation metadata required, so as to relocate @@ -710,6 +714,7 @@ config ARCH_HIBERNATION_POSSIBLE source "kernel/power/Kconfig" source "drivers/acpi/Kconfig" +source "drivers/cpufreq/Kconfig" endmenu diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile index 8674e7e24c4a..ae3f80622f4c 100644 --- a/arch/loongarch/Makefile +++ b/arch/loongarch/Makefile @@ -105,7 +105,8 @@ KBUILD_CFLAGS += -fno-jump-tables endif KBUILD_RUSTFLAGS += --target=loongarch64-unknown-none-softfloat -KBUILD_RUSTFLAGS_MODULE += -Crelocation-model=pic +KBUILD_RUSTFLAGS_KERNEL += -Zdirect-access-external-data=yes +KBUILD_RUSTFLAGS_MODULE += -Zdirect-access-external-data=no ifeq ($(CONFIG_RELOCATABLE),y) KBUILD_CFLAGS_KERNEL += -fPIE diff --git a/arch/loongarch/include/asm/addrspace.h b/arch/loongarch/include/asm/addrspace.h index 7bd47d65bf7a..fe198b473f84 100644 --- a/arch/loongarch/include/asm/addrspace.h +++ b/arch/loongarch/include/asm/addrspace.h @@ -37,6 +37,10 @@ extern unsigned long vm_map_base; #define UNCACHE_BASE CSR_DMW0_BASE #endif +#ifndef WRITECOMBINE_BASE +#define WRITECOMBINE_BASE CSR_DMW2_BASE +#endif + #define DMW_PABITS 48 #define TO_PHYS_MASK ((1ULL << DMW_PABITS) - 1) diff --git a/arch/loongarch/include/asm/asmmacro.h b/arch/loongarch/include/asm/asmmacro.h index 655db7d7a427..8d7f501b0a12 100644 --- a/arch/loongarch/include/asm/asmmacro.h +++ b/arch/loongarch/include/asm/asmmacro.h @@ -609,6 +609,7 @@ lu32i.d \reg, 0 lu52i.d \reg, \reg, 0 .pushsection ".la_abs", "aw", %progbits + .p2align 3 .dword 766b .dword \sym .popsection diff --git a/arch/loongarch/include/asm/hardirq.h b/arch/loongarch/include/asm/hardirq.h index d41138abcf26..1d7feb719515 100644 --- a/arch/loongarch/include/asm/hardirq.h +++ b/arch/loongarch/include/asm/hardirq.h @@ -12,11 +12,12 @@ extern void ack_bad_irq(unsigned int irq); #define ack_bad_irq ack_bad_irq -#define NR_IPI 2 +#define NR_IPI 3 enum ipi_msg_type { IPI_RESCHEDULE, IPI_CALL_FUNCTION, + IPI_IRQ_WORK, }; typedef struct { diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h index c3993fd88aba..944482063f14 100644 --- a/arch/loongarch/include/asm/inst.h +++ b/arch/loongarch/include/asm/inst.h @@ -532,6 +532,9 @@ static inline void emit_##NAME(union loongarch_instruction *insn, \ DEF_EMIT_REG0I15_FORMAT(break, break_op) +/* like emit_break(imm) but returns a constant expression */ +#define __emit_break(imm) ((u32)((imm) | (break_op << 15))) + #define DEF_EMIT_REG0I26_FORMAT(NAME, OP) \ static inline void emit_##NAME(union loongarch_instruction *insn, \ int offset) \ diff --git a/arch/loongarch/include/asm/io.h b/arch/loongarch/include/asm/io.h index c2f9979b2979..5e95a60df180 100644 --- a/arch/loongarch/include/asm/io.h +++ b/arch/loongarch/include/asm/io.h @@ -25,10 +25,16 @@ extern void __init early_iounmap(void __iomem *addr, unsigned long size); static inline void __iomem *ioremap_prot(phys_addr_t offset, unsigned long size, unsigned long prot_val) { - if (prot_val & _CACHE_CC) + switch (prot_val & _CACHE_MASK) { + case _CACHE_CC: return (void __iomem *)(unsigned long)(CACHE_BASE + offset); - else + case _CACHE_SUC: return (void __iomem *)(unsigned long)(UNCACHE_BASE + offset); + case _CACHE_WUC: + return (void __iomem *)(unsigned long)(WRITECOMBINE_BASE + offset); + default: + return NULL; + } } #define ioremap(offset, size) \ diff --git a/arch/loongarch/include/asm/irq_work.h b/arch/loongarch/include/asm/irq_work.h new file mode 100644 index 000000000000..d63076e9160d --- /dev/null +++ b/arch/loongarch/include/asm/irq_work.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_LOONGARCH_IRQ_WORK_H +#define _ASM_LOONGARCH_IRQ_WORK_H + +static inline bool arch_irq_work_has_interrupt(void) +{ + return IS_ENABLED(CONFIG_SMP); +} + +#endif /* _ASM_LOONGARCH_IRQ_WORK_H */ diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h index 7a4633ef284b..04a78010fc72 100644 --- a/arch/loongarch/include/asm/loongarch.h +++ b/arch/loongarch/include/asm/loongarch.h @@ -878,7 +878,7 @@ #define LOONGARCH_CSR_DMWIN2 0x182 /* 64 direct map win2: MEM */ #define LOONGARCH_CSR_DMWIN3 0x183 /* 64 direct map win3: MEM */ -/* Direct Map window 0/1 */ +/* Direct Map window 0/1/2/3 */ #define CSR_DMW0_PLV0 _CONST64_(1 << 0) #define CSR_DMW0_VSEG _CONST64_(0x8000) #define CSR_DMW0_BASE (CSR_DMW0_VSEG << DMW_PABITS) @@ -890,6 +890,14 @@ #define CSR_DMW1_BASE (CSR_DMW1_VSEG << DMW_PABITS) #define CSR_DMW1_INIT (CSR_DMW1_BASE | CSR_DMW1_MAT | CSR_DMW1_PLV0) +#define CSR_DMW2_PLV0 _CONST64_(1 << 0) +#define CSR_DMW2_MAT _CONST64_(2 << 4) +#define CSR_DMW2_VSEG _CONST64_(0xa000) +#define CSR_DMW2_BASE (CSR_DMW2_VSEG << DMW_PABITS) +#define CSR_DMW2_INIT (CSR_DMW2_BASE | CSR_DMW2_MAT | CSR_DMW2_PLV0) + +#define CSR_DMW3_INIT 0x0 + /* Performance Counter registers */ #define LOONGARCH_CSR_PERFCTRL0 0x200 /* 32 perf event 0 config */ #define LOONGARCH_CSR_PERFCNTR0 0x201 /* 64 perf event 0 count value */ @@ -1054,11 +1062,14 @@ #define LOONGARCH_IOCSR_NODECNT 0x408 #define LOONGARCH_IOCSR_MISC_FUNC 0x420 +#define IOCSR_MISC_FUNC_SOFT_INT BIT_ULL(10) #define IOCSR_MISC_FUNC_TIMER_RESET BIT_ULL(21) #define IOCSR_MISC_FUNC_EXT_IOI_EN BIT_ULL(48) #define LOONGARCH_IOCSR_CPUTEMP 0x428 +#define LOONGARCH_IOCSR_SMCMBX 0x51c + /* PerCore CSR, only accessible by local cores */ #define LOONGARCH_IOCSR_IPI_STATUS 0x1000 #define LOONGARCH_IOCSR_IPI_EN 0x1004 diff --git a/arch/loongarch/include/asm/pgtable-bits.h b/arch/loongarch/include/asm/pgtable-bits.h index 21319c1e045c..82cd3a9f094b 100644 --- a/arch/loongarch/include/asm/pgtable-bits.h +++ b/arch/loongarch/include/asm/pgtable-bits.h @@ -22,6 +22,7 @@ #define _PAGE_PFN_SHIFT 12 #define _PAGE_SWP_EXCLUSIVE_SHIFT 23 #define _PAGE_PFN_END_SHIFT 48 +#define _PAGE_DEVMAP_SHIFT 59 #define _PAGE_PRESENT_INVALID_SHIFT 60 #define _PAGE_NO_READ_SHIFT 61 #define _PAGE_NO_EXEC_SHIFT 62 @@ -35,6 +36,7 @@ #define _PAGE_MODIFIED (_ULCAST_(1) << _PAGE_MODIFIED_SHIFT) #define _PAGE_PROTNONE (_ULCAST_(1) << _PAGE_PROTNONE_SHIFT) #define _PAGE_SPECIAL (_ULCAST_(1) << _PAGE_SPECIAL_SHIFT) +#define _PAGE_DEVMAP (_ULCAST_(1) << _PAGE_DEVMAP_SHIFT) /* We borrow bit 23 to store the exclusive marker in swap PTEs. */ #define _PAGE_SWP_EXCLUSIVE (_ULCAST_(1) << _PAGE_SWP_EXCLUSIVE_SHIFT) @@ -74,8 +76,8 @@ #define __READABLE (_PAGE_VALID) #define __WRITEABLE (_PAGE_DIRTY | _PAGE_WRITE) -#define _PAGE_CHG_MASK (_PAGE_MODIFIED | _PAGE_SPECIAL | _PFN_MASK | _CACHE_MASK | _PAGE_PLV) -#define _HPAGE_CHG_MASK (_PAGE_MODIFIED | _PAGE_SPECIAL | _PFN_MASK | _CACHE_MASK | _PAGE_PLV | _PAGE_HUGE) +#define _PAGE_CHG_MASK (_PAGE_MODIFIED | _PAGE_SPECIAL | _PAGE_DEVMAP | _PFN_MASK | _CACHE_MASK | _PAGE_PLV) +#define _HPAGE_CHG_MASK (_PAGE_MODIFIED | _PAGE_SPECIAL | _PAGE_DEVMAP | _PFN_MASK | _CACHE_MASK | _PAGE_PLV | _PAGE_HUGE) #define PAGE_NONE __pgprot(_PAGE_PROTNONE | _PAGE_NO_READ | \ _PAGE_USER | _CACHE_CC) diff --git a/arch/loongarch/include/asm/pgtable.h b/arch/loongarch/include/asm/pgtable.h index 161dd6e10479..3fbf1f37c58e 100644 --- a/arch/loongarch/include/asm/pgtable.h +++ b/arch/loongarch/include/asm/pgtable.h @@ -424,6 +424,9 @@ static inline int pte_special(pte_t pte) { return pte_val(pte) & _PAGE_SPECIAL; static inline pte_t pte_mkspecial(pte_t pte) { pte_val(pte) |= _PAGE_SPECIAL; return pte; } #endif /* CONFIG_ARCH_HAS_PTE_SPECIAL */ +static inline int pte_devmap(pte_t pte) { return !!(pte_val(pte) & _PAGE_DEVMAP); } +static inline pte_t pte_mkdevmap(pte_t pte) { pte_val(pte) |= _PAGE_DEVMAP; return pte; } + #define pte_accessible pte_accessible static inline unsigned long pte_accessible(struct mm_struct *mm, pte_t a) { @@ -558,6 +561,17 @@ static inline pmd_t pmd_mkyoung(pmd_t pmd) return pmd; } +static inline int pmd_devmap(pmd_t pmd) +{ + return !!(pmd_val(pmd) & _PAGE_DEVMAP); +} + +static inline pmd_t pmd_mkdevmap(pmd_t pmd) +{ + pmd_val(pmd) |= _PAGE_DEVMAP; + return pmd; +} + static inline struct page *pmd_page(pmd_t pmd) { if (pmd_trans_huge(pmd)) @@ -613,6 +627,11 @@ static inline long pmd_protnone(pmd_t pmd) #define pmd_leaf(pmd) ((pmd_val(pmd) & _PAGE_HUGE) != 0) #define pud_leaf(pud) ((pud_val(pud) & _PAGE_HUGE) != 0) +#ifdef CONFIG_TRANSPARENT_HUGEPAGE +#define pud_devmap(pud) (0) +#define pgd_devmap(pgd) (0) +#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ + /* * We provide our own get_unmapped area to cope with the virtual aliasing * constraints placed on us by the cache architecture. diff --git a/arch/loongarch/include/asm/setup.h b/arch/loongarch/include/asm/setup.h index ee52fb1e9963..3c2fb16b11b6 100644 --- a/arch/loongarch/include/asm/setup.h +++ b/arch/loongarch/include/asm/setup.h @@ -34,6 +34,11 @@ extern long __la_abs_end; extern long __rela_dyn_begin; extern long __rela_dyn_end; +#ifdef CONFIG_RELR +extern long __relr_dyn_begin; +extern long __relr_dyn_end; +#endif + extern unsigned long __init relocate_kernel(void); #endif diff --git a/arch/loongarch/include/asm/smp.h b/arch/loongarch/include/asm/smp.h index 278700cfee88..50db503f44e3 100644 --- a/arch/loongarch/include/asm/smp.h +++ b/arch/loongarch/include/asm/smp.h @@ -69,9 +69,11 @@ extern int __cpu_logical_map[NR_CPUS]; #define ACTION_BOOT_CPU 0 #define ACTION_RESCHEDULE 1 #define ACTION_CALL_FUNCTION 2 +#define ACTION_IRQ_WORK 3 #define SMP_BOOT_CPU BIT(ACTION_BOOT_CPU) #define SMP_RESCHEDULE BIT(ACTION_RESCHEDULE) #define SMP_CALL_FUNCTION BIT(ACTION_CALL_FUNCTION) +#define SMP_IRQ_WORK BIT(ACTION_IRQ_WORK) struct secondary_data { unsigned long stack; diff --git a/arch/loongarch/include/asm/stackframe.h b/arch/loongarch/include/asm/stackframe.h index d9eafd3ee3d1..66736837085b 100644 --- a/arch/loongarch/include/asm/stackframe.h +++ b/arch/loongarch/include/asm/stackframe.h @@ -38,6 +38,17 @@ cfi_restore \reg \offset \docfi .endm + .macro SETUP_DMWINS temp + li.d \temp, CSR_DMW0_INIT # WUC, PLV0, 0x8000 xxxx xxxx xxxx + csrwr \temp, LOONGARCH_CSR_DMWIN0 + li.d \temp, CSR_DMW1_INIT # CAC, PLV0, 0x9000 xxxx xxxx xxxx + csrwr \temp, LOONGARCH_CSR_DMWIN1 + li.d \temp, CSR_DMW2_INIT # WUC, PLV0, 0xa000 xxxx xxxx xxxx + csrwr \temp, LOONGARCH_CSR_DMWIN2 + li.d \temp, CSR_DMW3_INIT # 0x0, unused + csrwr \temp, LOONGARCH_CSR_DMWIN3 + .endm + /* Jump to the runtime virtual address. */ .macro JUMP_VIRT_ADDR temp1 temp2 li.d \temp1, CACHE_BASE diff --git a/arch/loongarch/include/asm/unistd.h b/arch/loongarch/include/asm/unistd.h index fc0a481a7416..e2c0f3d86c7b 100644 --- a/arch/loongarch/include/asm/unistd.h +++ b/arch/loongarch/include/asm/unistd.h @@ -8,6 +8,7 @@ #include <uapi/asm/unistd.h> +#define __ARCH_WANT_NEW_STAT #define __ARCH_WANT_SYS_CLONE #define NR_syscalls (__NR_syscalls) diff --git a/arch/loongarch/include/asm/uprobes.h b/arch/loongarch/include/asm/uprobes.h index c8f59983f702..99a0d198927f 100644 --- a/arch/loongarch/include/asm/uprobes.h +++ b/arch/loongarch/include/asm/uprobes.h @@ -9,10 +9,10 @@ typedef u32 uprobe_opcode_t; #define MAX_UINSN_BYTES 8 #define UPROBE_XOL_SLOT_BYTES MAX_UINSN_BYTES -#define UPROBE_SWBP_INSN larch_insn_gen_break(BRK_UPROBE_BP) +#define UPROBE_SWBP_INSN __emit_break(BRK_UPROBE_BP) #define UPROBE_SWBP_INSN_SIZE LOONGARCH_INSN_SIZE -#define UPROBE_XOLBP_INSN larch_insn_gen_break(BRK_UPROBE_XOLBP) +#define UPROBE_XOLBP_INSN __emit_break(BRK_UPROBE_XOLBP) struct arch_uprobe { unsigned long resume_era; diff --git a/arch/loongarch/kernel/Makefile.syscalls b/arch/loongarch/kernel/Makefile.syscalls index ab7d9baa2915..523bb411a3bc 100644 --- a/arch/loongarch/kernel/Makefile.syscalls +++ b/arch/loongarch/kernel/Makefile.syscalls @@ -1,4 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -# No special ABIs on loongarch so far -syscall_abis_64 += +syscall_abis_64 += newstat diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c index 5cf59c617126..929a497c987e 100644 --- a/arch/loongarch/kernel/acpi.c +++ b/arch/loongarch/kernel/acpi.c @@ -57,15 +57,22 @@ void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size) return ioremap_cache(phys, size); } +static int cpu_enumerated = 0; + #ifdef CONFIG_SMP static int set_processor_mask(u32 id, u32 flags) { - + int nr_cpus; int cpu, cpuid = id; - if (num_processors >= nr_cpu_ids) { - pr_warn(PREFIX "nr_cpus/possible_cpus limit of %i reached." - " processor 0x%x ignored.\n", nr_cpu_ids, cpuid); + if (!cpu_enumerated) + nr_cpus = NR_CPUS; + else + nr_cpus = nr_cpu_ids; + + if (num_processors >= nr_cpus) { + pr_warn(PREFIX "nr_cpus limit of %i reached." + " processor 0x%x ignored.\n", nr_cpus, cpuid); return -ENODEV; @@ -73,11 +80,13 @@ static int set_processor_mask(u32 id, u32 flags) if (cpuid == loongson_sysconf.boot_cpu_id) cpu = 0; else - cpu = cpumask_next_zero(-1, cpu_present_mask); + cpu = find_first_zero_bit(cpumask_bits(cpu_present_mask), NR_CPUS); + + if (!cpu_enumerated) + set_cpu_possible(cpu, true); if (flags & ACPI_MADT_ENABLED) { num_processors++; - set_cpu_possible(cpu, true); set_cpu_present(cpu, true); __cpu_number_map[cpuid] = cpu; __cpu_logical_map[cpu] = cpuid; @@ -138,6 +147,7 @@ static void __init acpi_process_madt(void) acpi_table_parse_madt(ACPI_MADT_TYPE_EIO_PIC, acpi_parse_eio_master, MAX_IO_PICS); + cpu_enumerated = 1; loongson_sysconf.nr_cpus = num_processors; } diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S index 4677ea8fa8e9..506a99a5bbc7 100644 --- a/arch/loongarch/kernel/head.S +++ b/arch/loongarch/kernel/head.S @@ -44,11 +44,7 @@ SYM_DATA(kernel_fsize, .long _kernel_fsize); SYM_CODE_START(kernel_entry) # kernel entry point /* Config direct window and set PG */ - li.d t0, CSR_DMW0_INIT # UC, PLV0, 0x8000 xxxx xxxx xxxx - csrwr t0, LOONGARCH_CSR_DMWIN0 - li.d t0, CSR_DMW1_INIT # CA, PLV0, 0x9000 xxxx xxxx xxxx - csrwr t0, LOONGARCH_CSR_DMWIN1 - + SETUP_DMWINS t0 JUMP_VIRT_ADDR t0, t1 /* Enable PG */ @@ -124,11 +120,8 @@ SYM_CODE_END(kernel_entry) * function after setting up the stack and tp registers. */ SYM_CODE_START(smpboot_entry) - li.d t0, CSR_DMW0_INIT # UC, PLV0 - csrwr t0, LOONGARCH_CSR_DMWIN0 - li.d t0, CSR_DMW1_INIT # CA, PLV0 - csrwr t0, LOONGARCH_CSR_DMWIN1 + SETUP_DMWINS t0 JUMP_VIRT_ADDR t0, t1 #ifdef CONFIG_PAGE_SIZE_4KB diff --git a/arch/loongarch/kernel/hw_breakpoint.c b/arch/loongarch/kernel/hw_breakpoint.c index 621ad7634df7..a6e4b605bfa8 100644 --- a/arch/loongarch/kernel/hw_breakpoint.c +++ b/arch/loongarch/kernel/hw_breakpoint.c @@ -221,7 +221,7 @@ static int hw_breakpoint_control(struct perf_event *bp, } enable = csr_read64(LOONGARCH_CSR_CRMD); csr_write64(CSR_CRMD_WE | enable, LOONGARCH_CSR_CRMD); - if (bp->hw.target) + if (bp->hw.target && test_tsk_thread_flag(bp->hw.target, TIF_LOAD_WATCH)) regs->csr_prmd |= CSR_PRMD_PWE; break; case HW_BREAKPOINT_UNINSTALL: diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c index 17b040bd6067..8ba391cfabb0 100644 --- a/arch/loongarch/kernel/kprobes.c +++ b/arch/loongarch/kernel/kprobes.c @@ -4,8 +4,8 @@ #include <linux/preempt.h> #include <asm/break.h> -#define KPROBE_BP_INSN larch_insn_gen_break(BRK_KPROBE_BP) -#define KPROBE_SSTEPBP_INSN larch_insn_gen_break(BRK_KPROBE_SSTEPBP) +#define KPROBE_BP_INSN __emit_break(BRK_KPROBE_BP) +#define KPROBE_SSTEPBP_INSN __emit_break(BRK_KPROBE_SSTEPBP) DEFINE_PER_CPU(struct kprobe *, current_kprobe); DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); diff --git a/arch/loongarch/kernel/paravirt.c b/arch/loongarch/kernel/paravirt.c index 9abe8b71aa48..9c9b75b76f62 100644 --- a/arch/loongarch/kernel/paravirt.c +++ b/arch/loongarch/kernel/paravirt.c @@ -2,6 +2,7 @@ #include <linux/export.h> #include <linux/types.h> #include <linux/interrupt.h> +#include <linux/irq_work.h> #include <linux/jump_label.h> #include <linux/kvm_para.h> #include <linux/reboot.h> @@ -128,6 +129,11 @@ static irqreturn_t pv_ipi_interrupt(int irq, void *dev) info->ipi_irqs[IPI_CALL_FUNCTION]++; } + if (action & SMP_IRQ_WORK) { + irq_work_run(); + info->ipi_irqs[IPI_IRQ_WORK]++; + } + return IRQ_HANDLED; } diff --git a/arch/loongarch/kernel/ptrace.c b/arch/loongarch/kernel/ptrace.c index 200109de1971..19dc6eff45cc 100644 --- a/arch/loongarch/kernel/ptrace.c +++ b/arch/loongarch/kernel/ptrace.c @@ -589,6 +589,7 @@ static int ptrace_hbp_set_ctrl(unsigned int note_type, struct perf_event *bp; struct perf_event_attr attr; struct arch_hw_breakpoint_ctrl ctrl; + struct thread_info *ti = task_thread_info(tsk); bp = ptrace_hbp_get_initialised_bp(note_type, tsk, idx); if (IS_ERR(bp)) @@ -613,8 +614,10 @@ static int ptrace_hbp_set_ctrl(unsigned int note_type, if (err) return err; attr.disabled = 0; + set_ti_thread_flag(ti, TIF_LOAD_WATCH); } else { attr.disabled = 1; + clear_ti_thread_flag(ti, TIF_LOAD_WATCH); } return modify_user_hw_breakpoint(bp, &attr); diff --git a/arch/loongarch/kernel/relocate.c b/arch/loongarch/kernel/relocate.c index 1acfa704c8d0..50c469067f3a 100644 --- a/arch/loongarch/kernel/relocate.c +++ b/arch/loongarch/kernel/relocate.c @@ -13,6 +13,7 @@ #include <asm/bootinfo.h> #include <asm/early_ioremap.h> #include <asm/inst.h> +#include <asm/io.h> #include <asm/sections.h> #include <asm/setup.h> @@ -34,11 +35,27 @@ static inline void __init relocate_relative(void) if (rela->r_info != R_LARCH_RELATIVE) continue; - if (relocated_addr >= VMLINUX_LOAD_ADDRESS) - relocated_addr = (Elf64_Addr)RELOCATED(relocated_addr); - + relocated_addr = (Elf64_Addr)RELOCATED(relocated_addr); *(Elf64_Addr *)RELOCATED(addr) = relocated_addr; } + +#ifdef CONFIG_RELR + u64 *addr = NULL; + u64 *relr = (u64 *)&__relr_dyn_begin; + u64 *relr_end = (u64 *)&__relr_dyn_end; + + for ( ; relr < relr_end; relr++) { + if ((*relr & 1) == 0) { + addr = (u64 *)(*relr + reloc_offset); + *addr++ += reloc_offset; + } else { + for (u64 *p = addr, r = *relr >> 1; r; p++, r >>= 1) + if (r & 1) + *p += reloc_offset; + addr += 63; + } + } +#endif } static inline void __init relocate_absolute(long random_offset) @@ -123,6 +140,32 @@ static inline __init bool kaslr_disabled(void) if (str == boot_command_line || (str > boot_command_line && *(str - 1) == ' ')) return true; +#ifdef CONFIG_HIBERNATION + str = strstr(builtin_cmdline, "nohibernate"); + if (str == builtin_cmdline || (str > builtin_cmdline && *(str - 1) == ' ')) + return false; + + str = strstr(boot_command_line, "nohibernate"); + if (str == boot_command_line || (str > boot_command_line && *(str - 1) == ' ')) + return false; + + str = strstr(builtin_cmdline, "noresume"); + if (str == builtin_cmdline || (str > builtin_cmdline && *(str - 1) == ' ')) + return false; + + str = strstr(boot_command_line, "noresume"); + if (str == boot_command_line || (str > boot_command_line && *(str - 1) == ' ')) + return false; + + str = strstr(builtin_cmdline, "resume="); + if (str == builtin_cmdline || (str > builtin_cmdline && *(str - 1) == ' ')) + return true; + + str = strstr(boot_command_line, "resume="); + if (str == boot_command_line || (str > boot_command_line && *(str - 1) == ' ')) + return true; +#endif + return false; } @@ -170,7 +213,7 @@ unsigned long __init relocate_kernel(void) unsigned long kernel_length; unsigned long random_offset = 0; void *location_new = _text; /* Default to original kernel start */ - char *cmdline = early_ioremap(fw_arg1, COMMAND_LINE_SIZE); /* Boot command line is passed in fw_arg1 */ + char *cmdline = early_memremap_ro(fw_arg1, COMMAND_LINE_SIZE); /* Boot command line is passed in fw_arg1 */ strscpy(boot_command_line, cmdline, COMMAND_LINE_SIZE); @@ -182,6 +225,7 @@ unsigned long __init relocate_kernel(void) random_offset = (unsigned long)location_new - (unsigned long)(_text); #endif reloc_offset = (unsigned long)_text - VMLINUX_LOAD_ADDRESS; + early_memunmap(cmdline, COMMAND_LINE_SIZE); if (random_offset) { kernel_length = (long)(_end) - (long)(_text); diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index 3d048f1be143..0f0740f0be27 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -576,8 +576,10 @@ static void __init prefill_possible_map(void) for (i = 0; i < possible; i++) set_cpu_possible(i, true); - for (; i < NR_CPUS; i++) + for (; i < NR_CPUS; i++) { + set_cpu_present(i, false); set_cpu_possible(i, false); + } set_nr_cpu_ids(possible); } diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index 1436d2465939..ca405ab86aae 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -13,6 +13,7 @@ #include <linux/cpumask.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/irq_work.h> #include <linux/profile.h> #include <linux/seq_file.h> #include <linux/smp.h> @@ -70,6 +71,7 @@ static DEFINE_PER_CPU(int, cpu_state); static const char *ipi_types[NR_IPI] __tracepoint_string = { [IPI_RESCHEDULE] = "Rescheduling interrupts", [IPI_CALL_FUNCTION] = "Function call interrupts", + [IPI_IRQ_WORK] = "IRQ work interrupts", }; void show_ipi_list(struct seq_file *p, int prec) @@ -217,6 +219,13 @@ void arch_smp_send_reschedule(int cpu) } EXPORT_SYMBOL_GPL(arch_smp_send_reschedule); +#ifdef CONFIG_IRQ_WORK +void arch_irq_work_raise(void) +{ + mp_ops.send_ipi_single(smp_processor_id(), ACTION_IRQ_WORK); +} +#endif + static irqreturn_t loongson_ipi_interrupt(int irq, void *dev) { unsigned int action; @@ -234,6 +243,11 @@ static irqreturn_t loongson_ipi_interrupt(int irq, void *dev) per_cpu(irq_stat, cpu).ipi_irqs[IPI_CALL_FUNCTION]++; } + if (action & SMP_IRQ_WORK) { + irq_work_run(); + per_cpu(irq_stat, cpu).ipi_irqs[IPI_IRQ_WORK]++; + } + return IRQ_HANDLED; } @@ -271,11 +285,10 @@ static void __init fdt_smp_setup(void) if (cpuid >= nr_cpu_ids) continue; - if (cpuid == loongson_sysconf.boot_cpu_id) { + if (cpuid == loongson_sysconf.boot_cpu_id) cpu = 0; - } else { - cpu = cpumask_next_zero(-1, cpu_present_mask); - } + else + cpu = find_first_zero_bit(cpumask_bits(cpu_present_mask), NR_CPUS); num_processors++; set_cpu_possible(cpu, true); diff --git a/arch/loongarch/kernel/syscall.c b/arch/loongarch/kernel/syscall.c index ec17cd5163b7..ba5d0930a74f 100644 --- a/arch/loongarch/kernel/syscall.c +++ b/arch/loongarch/kernel/syscall.c @@ -9,11 +9,14 @@ #include <linux/entry-common.h> #include <linux/errno.h> #include <linux/linkage.h> +#include <linux/objtool.h> +#include <linux/randomize_kstack.h> #include <linux/syscalls.h> #include <linux/unistd.h> #include <asm/asm.h> #include <asm/exception.h> +#include <asm/loongarch.h> #include <asm/signal.h> #include <asm/switch_to.h> #include <asm-generic/syscalls.h> @@ -39,7 +42,7 @@ void *sys_call_table[__NR_syscalls] = { typedef long (*sys_call_fn)(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); -void noinstr do_syscall(struct pt_regs *regs) +void noinstr __no_stack_protector do_syscall(struct pt_regs *regs) { unsigned long nr; sys_call_fn syscall_fn; @@ -55,11 +58,28 @@ void noinstr do_syscall(struct pt_regs *regs) nr = syscall_enter_from_user_mode(regs, nr); + add_random_kstack_offset(); + if (nr < NR_syscalls) { syscall_fn = sys_call_table[nr]; regs->regs[4] = syscall_fn(regs->orig_a0, regs->regs[5], regs->regs[6], regs->regs[7], regs->regs[8], regs->regs[9]); } + /* + * This value will get limited by KSTACK_OFFSET_MAX(), which is 10 + * bits. The actual entropy will be further reduced by the compiler + * when applying stack alignment constraints: 16-bytes (i.e. 4-bits) + * aligned, which will remove the 4 low bits from any entropy chosen + * here. + * + * The resulting 6 bits of entropy is seen in SP[9:4]. + */ + choose_random_kstack_offset(drdtime()); + syscall_exit_to_user_mode(regs); } + +#ifdef CONFIG_RANDOMIZE_KSTACK_OFFSET +STACK_FRAME_NON_STANDARD(do_syscall); +#endif diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S index 3c7595342730..08ea921cdec1 100644 --- a/arch/loongarch/kernel/vmlinux.lds.S +++ b/arch/loongarch/kernel/vmlinux.lds.S @@ -113,6 +113,14 @@ SECTIONS __rela_dyn_end = .; } +#ifdef CONFIG_RELR + .relr.dyn : ALIGN(8) { + __relr_dyn_begin = .; + *(.relr.dyn) + __relr_dyn_end = .; + } +#endif + .data.rel : { *(.data.rel*) } #ifdef CONFIG_RELOCATABLE diff --git a/arch/loongarch/power/platform.c b/arch/loongarch/power/platform.c index 3ea8e07aa225..0909729dc2e1 100644 --- a/arch/loongarch/power/platform.c +++ b/arch/loongarch/power/platform.c @@ -34,22 +34,49 @@ void enable_pci_wakeup(void) acpi_write_bit_register(ACPI_BITREG_PCIEXP_WAKE_DISABLE, 0); } +static struct platform_device loongson3_cpufreq_device = { + .name = "loongson3_cpufreq", + .id = -1, +}; + +static int __init loongson_cpufreq_init(void) +{ + if (!cpu_has_scalefreq) + return -ENODEV; + + return platform_device_register(&loongson3_cpufreq_device); +} + +arch_initcall(loongson_cpufreq_init); + +static void default_suspend_addr(void) +{ + acpi_enter_sleep_state(ACPI_STATE_S3); +} + static int __init loongson3_acpi_suspend_init(void) { #ifdef CONFIG_ACPI acpi_status status; uint64_t suspend_addr = 0; - if (acpi_disabled || acpi_gbl_reduced_hardware) + if (acpi_disabled) + return 0; + + if (!acpi_gbl_reduced_hardware) + acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1); + + if (!acpi_sleep_state_supported(ACPI_STATE_S3)) return 0; - acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1); status = acpi_evaluate_integer(NULL, "\\SADR", NULL, &suspend_addr); if (ACPI_FAILURE(status) || !suspend_addr) { - pr_err("ACPI S3 is not support!\n"); - return -1; + pr_info("ACPI S3 supported with hardware register default\n"); + loongson_sysconf.suspend_addr = (u64)default_suspend_addr; + } else { + pr_info("ACPI S3 supported with Loongson ACPI SADR extension\n"); + loongson_sysconf.suspend_addr = (u64)phys_to_virt(PHYSADDR(suspend_addr)); } - loongson_sysconf.suspend_addr = (u64)phys_to_virt(PHYSADDR(suspend_addr)); #endif return 0; } diff --git a/arch/loongarch/power/suspend_asm.S b/arch/loongarch/power/suspend_asm.S index e2fc3b4e31f0..9fe28d5a0270 100644 --- a/arch/loongarch/power/suspend_asm.S +++ b/arch/loongarch/power/suspend_asm.S @@ -66,18 +66,14 @@ SYM_FUNC_START(loongarch_suspend_enter) la.pcrel a0, loongarch_wakeup_start la.pcrel t0, loongarch_suspend_addr ld.d t0, t0, 0 - jirl a0, t0, 0 /* Call BIOS's STR sleep routine */ + jirl ra, t0, 0 /* Call BIOS's STR sleep routine */ /* * This is where we return upon wakeup. * Reload all of the registers and return. */ SYM_INNER_LABEL(loongarch_wakeup_start, SYM_L_GLOBAL) - li.d t0, CSR_DMW0_INIT # UC, PLV0 - csrwr t0, LOONGARCH_CSR_DMWIN0 - li.d t0, CSR_DMW1_INIT # CA, PLV0 - csrwr t0, LOONGARCH_CSR_DMWIN1 - + SETUP_DMWINS t0 JUMP_VIRT_ADDR t0, t1 /* Enable PG */ diff --git a/arch/m68k/install.sh b/arch/m68k/install.sh index af65e16e5147..b6829b3942b3 100755 --- a/arch/m68k/install.sh +++ b/arch/m68k/install.sh @@ -16,6 +16,8 @@ # $3 - kernel map file # $4 - default install path (blank if root directory) +set -e + if [ -f $4/vmlinuz ]; then mv $4/vmlinuz $4/vmlinuz.old fi diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 28af3d9e6bc0..f9d252b6ede1 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -2927,7 +2927,8 @@ config BUILTIN_DTB bool choice - prompt "Kernel appended dtb support" if USE_OF + prompt "Kernel appended dtb support" + depends on USE_OF default MIPS_NO_APPENDED_DTB config MIPS_NO_APPENDED_DTB @@ -2968,7 +2969,8 @@ choice endchoice choice - prompt "Kernel command line type" if !CMDLINE_OVERRIDE + prompt "Kernel command line type" + depends on !CMDLINE_OVERRIDE default MIPS_CMDLINE_FROM_DTB if USE_OF && !ATH79 && !MACH_INGENIC && \ !MACH_LOONGSON64 && !MIPS_MALTA && \ !CAVIUM_OCTEON_SOC diff --git a/arch/nios2/boot/install.sh b/arch/nios2/boot/install.sh index 34a2feec42c8..1161f2bf59ec 100755 --- a/arch/nios2/boot/install.sh +++ b/arch/nios2/boot/install.sh @@ -16,6 +16,8 @@ # $3 - kernel map file # $4 - default install path (blank if root directory) +set -e + if [ -f $4/vmlinuz ]; then mv $4/vmlinuz $4/vmlinuz.old fi diff --git a/arch/parisc/install.sh b/arch/parisc/install.sh index 933d031c249a..664c2d77f776 100755 --- a/arch/parisc/install.sh +++ b/arch/parisc/install.sh @@ -16,6 +16,8 @@ # $3 - kernel map file # $4 - default install path (blank if root directory) +set -e + if [ "$(basename $2)" = "vmlinuz" ]; then # Compressed install echo "Installing compressed kernel" diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index bc5a1612be72..d7b09b064a8a 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -964,7 +964,8 @@ config CMDLINE most cases you will need to specify the root device here. choice - prompt "Kernel command line type" if CMDLINE != "" + prompt "Kernel command line type" + depends on CMDLINE != "" default CMDLINE_FROM_BOOTLOADER config CMDLINE_FROM_BOOTLOADER diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 736457a5898a..3ceec2ca84fa 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -960,7 +960,8 @@ config CMDLINE line here and choose how the kernel should use it later on. choice - prompt "Built-in command line usage" if CMDLINE != "" + prompt "Built-in command line usage" + depends on CMDLINE != "" default CMDLINE_FALLBACK help Choose how the kernel will handle the provided built-in command diff --git a/arch/riscv/boot/install.sh b/arch/riscv/boot/install.sh index a8df7591513a..4b3d8bf91cc6 100755 --- a/arch/riscv/boot/install.sh +++ b/arch/riscv/boot/install.sh @@ -17,6 +17,8 @@ # $3 - kernel map file # $4 - default install path (blank if root directory) +set -e + case "${2##*/}" in # Compressed install Image.*|vmlinuz.efi) diff --git a/arch/s390/boot/install.sh b/arch/s390/boot/install.sh index a13dd2f2aa1c..fa41486258ee 100755 --- a/arch/s390/boot/install.sh +++ b/arch/s390/boot/install.sh @@ -15,6 +15,8 @@ # $3 - kernel map file # $4 - default install path (blank if root directory) +set -e + echo "Warning: '${INSTALLKERNEL}' command not available - additional " \ "bootloader config required" >&2 if [ -f "$4/vmlinuz-$1" ]; then mv -- "$4/vmlinuz-$1" "$4/vmlinuz-$1.old"; fi diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 5e6a3ead51fb..1aa3c4a0c5b2 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -3,8 +3,6 @@ config SUPERH def_bool y select ARCH_32BIT_OFF_T select ARCH_HAS_CPU_CACHE_ALIASING - select ARCH_ENABLE_MEMORY_HOTPLUG if SPARSEMEM && MMU - select ARCH_ENABLE_MEMORY_HOTREMOVE if SPARSEMEM && MMU select ARCH_HAVE_NMI_SAFE_CMPXCHG if (GUSA_RB || CPU_SH4A) select ARCH_HAS_BINFMT_FLAT if !MMU select ARCH_HAS_CPU_FINALIZE_INIT diff --git a/arch/sh/configs/apsh4ad0a_defconfig b/arch/sh/configs/apsh4ad0a_defconfig index 05d21d91f41d..137573610ec4 100644 --- a/arch/sh/configs/apsh4ad0a_defconfig +++ b/arch/sh/configs/apsh4ad0a_defconfig @@ -24,8 +24,6 @@ CONFIG_BFQ_GROUP_IOSCHED=y CONFIG_CPU_SUBTYPE_SH7786=y CONFIG_MEMORY_SIZE=0x10000000 CONFIG_HUGETLB_PAGE_SIZE_1MB=y -CONFIG_MEMORY_HOTPLUG=y -CONFIG_MEMORY_HOTREMOVE=y CONFIG_KSM=y CONFIG_SH_STORE_QUEUES=y CONFIG_SH_APSH4AD0A=y diff --git a/arch/sh/configs/sdk7786_defconfig b/arch/sh/configs/sdk7786_defconfig index 7b427c17fbfe..07894f13441e 100644 --- a/arch/sh/configs/sdk7786_defconfig +++ b/arch/sh/configs/sdk7786_defconfig @@ -43,8 +43,6 @@ CONFIG_MEMORY_SIZE=0x20000000 CONFIG_PMB=y CONFIG_NUMA=y CONFIG_HUGETLB_PAGE_SIZE_64MB=y -CONFIG_MEMORY_HOTPLUG=y -CONFIG_MEMORY_HOTREMOVE=y CONFIG_KSM=y CONFIG_SH_STORE_QUEUES=y CONFIG_SPECULATIVE_EXECUTION=y diff --git a/arch/sh/configs/shx3_defconfig b/arch/sh/configs/shx3_defconfig index aa353dff7f19..9a0df5ea3866 100644 --- a/arch/sh/configs/shx3_defconfig +++ b/arch/sh/configs/shx3_defconfig @@ -31,8 +31,6 @@ CONFIG_CPU_SUBTYPE_SHX3=y CONFIG_MEMORY_START=0x0c000000 CONFIG_NUMA=y CONFIG_PAGE_SIZE_64KB=y -CONFIG_MEMORY_HOTPLUG=y -CONFIG_MEMORY_HOTREMOVE=y CONFIG_SH_STORE_QUEUES=y CONFIG_SH_X3PROTO=y CONFIG_NO_HZ=y diff --git a/arch/sh/drivers/push-switch.c b/arch/sh/drivers/push-switch.c index 362e4860bf52..1dea43381b5a 100644 --- a/arch/sh/drivers/push-switch.c +++ b/arch/sh/drivers/push-switch.c @@ -131,4 +131,5 @@ module_exit(switch_exit); MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR("Paul Mundt"); +MODULE_DESCRIPTION("Generic push-switch framework"); MODULE_LICENSE("GPL v2"); diff --git a/arch/sh/mm/Kconfig b/arch/sh/mm/Kconfig index f32a1963ff0c..1862411665ab 100644 --- a/arch/sh/mm/Kconfig +++ b/arch/sh/mm/Kconfig @@ -144,10 +144,6 @@ config ARCH_SPARSEMEM_DEFAULT config ARCH_SELECT_MEMORY_MODEL def_bool y -config ARCH_MEMORY_PROBE - def_bool y - depends on MEMORY_HOTPLUG - config IOREMAP_FIXED def_bool y depends on X2TLB diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index bf1b54055316..d1fe90b2f5ff 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -395,31 +395,3 @@ void __init mem_init(void) mem_init_done = 1; } - -#ifdef CONFIG_MEMORY_HOTPLUG -int arch_add_memory(int nid, u64 start, u64 size, - struct mhp_params *params) -{ - unsigned long start_pfn = PFN_DOWN(start); - unsigned long nr_pages = size >> PAGE_SHIFT; - int ret; - - if (WARN_ON_ONCE(params->pgprot.pgprot != PAGE_KERNEL.pgprot)) - return -EINVAL; - - /* We only have ZONE_NORMAL, so this is easy.. */ - ret = __add_pages(nid, start_pfn, nr_pages, params); - if (unlikely(ret)) - printk("%s: Failed, __add_pages() == %d\n", __func__, ret); - - return ret; -} - -void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) -{ - unsigned long start_pfn = PFN_DOWN(start); - unsigned long nr_pages = size >> PAGE_SHIFT; - - __remove_pages(start_pfn, nr_pages, altmap); -} -#endif /* CONFIG_MEMORY_HOTPLUG */ diff --git a/arch/sparc/boot/install.sh b/arch/sparc/boot/install.sh index 4f130f3f30d6..68de67c5621e 100755 --- a/arch/sparc/boot/install.sh +++ b/arch/sparc/boot/install.sh @@ -16,6 +16,8 @@ # $3 - kernel map file # $4 - default install path (blank if root directory) +set -e + if [ -f $4/vmlinuz ]; then mv $4/vmlinuz $4/vmlinuz.old fi diff --git a/arch/um/drivers/virt-pci.c b/arch/um/drivers/virt-pci.c index b16a75c94b8b..6100819681b5 100644 --- a/arch/um/drivers/virt-pci.c +++ b/arch/um/drivers/virt-pci.c @@ -988,6 +988,11 @@ static struct resource virt_platform_resource = { static int __init um_pci_init(void) { + struct irq_domain_info inner_domain_info = { + .size = MAX_MSI_VECTORS, + .hwirq_max = MAX_MSI_VECTORS, + .ops = &um_pci_inner_domain_ops, + }; int err, i; WARN_ON(logic_iomem_add_region(&virt_cfgspace_resource, @@ -1017,11 +1022,10 @@ static int __init um_pci_init(void) goto free; } - um_pci_inner_domain = __irq_domain_add(um_pci_fwnode, MAX_MSI_VECTORS, - MAX_MSI_VECTORS, 0, - &um_pci_inner_domain_ops, NULL); - if (!um_pci_inner_domain) { - err = -ENOMEM; + inner_domain_info.fwnode = um_pci_fwnode; + um_pci_inner_domain = irq_domain_instantiate(&inner_domain_info); + if (IS_ERR(um_pci_inner_domain)) { + err = PTR_ERR(um_pci_inner_domain); goto free; } @@ -1058,7 +1062,7 @@ static int __init um_pci_init(void) goto free; return 0; free: - if (um_pci_inner_domain) + if (!IS_ERR_OR_NULL(um_pci_inner_domain)) irq_domain_remove(um_pci_inner_domain); if (um_pci_fwnode) irq_domain_free_fwnode(um_pci_fwnode); diff --git a/arch/x86/boot/install.sh b/arch/x86/boot/install.sh index 0849f4b42745..93784abcd66d 100755 --- a/arch/x86/boot/install.sh +++ b/arch/x86/boot/install.sh @@ -16,6 +16,8 @@ # $3 - kernel map file # $4 - default install path (blank if root directory) +set -e + if [ -f $4/vmlinuz ]; then mv $4/vmlinuz $4/vmlinuz.old fi diff --git a/block/bio-integrity.c b/block/bio-integrity.c index b78c145eb026..8d1fb38f745f 100644 --- a/block/bio-integrity.c +++ b/block/bio-integrity.c @@ -22,9 +22,17 @@ void blk_flush_integrity(void) flush_workqueue(kintegrityd_wq); } -static void __bio_integrity_free(struct bio_set *bs, - struct bio_integrity_payload *bip) +/** + * bio_integrity_free - Free bio integrity payload + * @bio: bio containing bip to be freed + * + * Description: Free the integrity portion of a bio. + */ +void bio_integrity_free(struct bio *bio) { + struct bio_integrity_payload *bip = bio_integrity(bio); + struct bio_set *bs = bio->bi_pool; + if (bs && mempool_initialized(&bs->bio_integrity_pool)) { if (bip->bip_vec) bvec_free(&bs->bvec_integrity_pool, bip->bip_vec, @@ -33,6 +41,8 @@ static void __bio_integrity_free(struct bio_set *bs, } else { kfree(bip); } + bio->bi_integrity = NULL; + bio->bi_opf &= ~REQ_INTEGRITY; } /** @@ -86,7 +96,10 @@ struct bio_integrity_payload *bio_integrity_alloc(struct bio *bio, return bip; err: - __bio_integrity_free(bs, bip); + if (bs && mempool_initialized(&bs->bio_integrity_pool)) + mempool_free(bip, &bs->bio_integrity_pool); + else + kfree(bip); return ERR_PTR(-ENOMEM); } EXPORT_SYMBOL(bio_integrity_alloc); @@ -118,63 +131,26 @@ static void bio_integrity_uncopy_user(struct bio_integrity_payload *bip) bio_integrity_unpin_bvec(copy, nr_vecs, true); } -static void bio_integrity_unmap_user(struct bio_integrity_payload *bip) -{ - bool dirty = bio_data_dir(bip->bip_bio) == READ; - - if (bip->bip_flags & BIP_COPY_USER) { - if (dirty) - bio_integrity_uncopy_user(bip); - kfree(bvec_virt(bip->bip_vec)); - return; - } - - bio_integrity_unpin_bvec(bip->bip_vec, bip->bip_max_vcnt, dirty); -} - /** - * bio_integrity_free - Free bio integrity payload - * @bio: bio containing bip to be freed + * bio_integrity_unmap_user - Unmap user integrity payload + * @bio: bio containing bip to be unmapped * - * Description: Used to free the integrity portion of a bio. Usually - * called from bio_free(). + * Unmap the user mapped integrity portion of a bio. */ -void bio_integrity_free(struct bio *bio) +void bio_integrity_unmap_user(struct bio *bio) { struct bio_integrity_payload *bip = bio_integrity(bio); - struct bio_set *bs = bio->bi_pool; - if (bip->bip_flags & BIP_INTEGRITY_USER) - return; - if (bip->bip_flags & BIP_BLOCK_INTEGRITY) + if (bip->bip_flags & BIP_COPY_USER) { + if (bio_data_dir(bio) == READ) + bio_integrity_uncopy_user(bip); kfree(bvec_virt(bip->bip_vec)); - - __bio_integrity_free(bs, bip); - bio->bi_integrity = NULL; - bio->bi_opf &= ~REQ_INTEGRITY; -} - -/** - * bio_integrity_unmap_free_user - Unmap and free bio user integrity payload - * @bio: bio containing bip to be unmapped and freed - * - * Description: Used to unmap and free the user mapped integrity portion of a - * bio. Submitter attaching the user integrity buffer is responsible for - * unmapping and freeing it during completion. - */ -void bio_integrity_unmap_free_user(struct bio *bio) -{ - struct bio_integrity_payload *bip = bio_integrity(bio); - struct bio_set *bs = bio->bi_pool; - - if (WARN_ON_ONCE(!(bip->bip_flags & BIP_INTEGRITY_USER))) return; - bio_integrity_unmap_user(bip); - __bio_integrity_free(bs, bip); - bio->bi_integrity = NULL; - bio->bi_opf &= ~REQ_INTEGRITY; + } + + bio_integrity_unpin_bvec(bip->bip_vec, bip->bip_max_vcnt, + bio_data_dir(bio) == READ); } -EXPORT_SYMBOL(bio_integrity_unmap_free_user); /** * bio_integrity_add_page - Attach integrity metadata @@ -274,7 +250,7 @@ static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec, goto free_bip; } - bip->bip_flags |= BIP_INTEGRITY_USER | BIP_COPY_USER; + bip->bip_flags |= BIP_COPY_USER; bip->bip_iter.bi_sector = seed; bip->bip_vcnt = nr_vecs; return 0; @@ -295,7 +271,6 @@ static int bio_integrity_init_user(struct bio *bio, struct bio_vec *bvec, return PTR_ERR(bip); memcpy(bip->bip_vec, bvec, nr_vecs * sizeof(*bvec)); - bip->bip_flags |= BIP_INTEGRITY_USER; bip->bip_iter.bi_sector = seed; bip->bip_iter.bi_size = len; bip->bip_vcnt = nr_vecs; @@ -503,6 +478,8 @@ static void bio_integrity_verify_fn(struct work_struct *work) struct bio *bio = bip->bip_bio; blk_integrity_verify(bio); + + kfree(bvec_virt(bip->bip_vec)); bio_integrity_free(bio); bio_endio(bio); } @@ -523,13 +500,13 @@ bool __bio_integrity_endio(struct bio *bio) struct blk_integrity *bi = blk_get_integrity(bio->bi_bdev->bd_disk); struct bio_integrity_payload *bip = bio_integrity(bio); - if (bio_op(bio) == REQ_OP_READ && !bio->bi_status && - (bip->bip_flags & BIP_BLOCK_INTEGRITY) && bi->csum_type) { + if (bio_op(bio) == REQ_OP_READ && !bio->bi_status && bi->csum_type) { INIT_WORK(&bip->bip_work, bio_integrity_verify_fn); queue_work(kintegrityd_wq, &bip->bip_work); return false; } + kfree(bvec_virt(bip->bip_vec)); bio_integrity_free(bio); return true; } diff --git a/block/bio.c b/block/bio.c index a3b1b2266c50..c4053d49679a 100644 --- a/block/bio.c +++ b/block/bio.c @@ -4,7 +4,7 @@ */ #include <linux/mm.h> #include <linux/swap.h> -#include <linux/bio.h> +#include <linux/bio-integrity.h> #include <linux/blkdev.h> #include <linux/uio.h> #include <linux/iocontext.h> @@ -1630,8 +1630,18 @@ again: goto again; } - /* release cgroup info */ - bio_uninit(bio); +#ifdef CONFIG_BLK_CGROUP + /* + * Release cgroup info. We shouldn't have to do this here, but quite + * a few callers of bio_init fail to call bio_uninit, so we cover up + * for that here at least for now. + */ + if (bio->bi_blkg) { + blkg_put(bio->bi_blkg); + bio->bi_blkg = NULL; + } +#endif + if (bio->bi_end_io) bio->bi_end_io(bio); } diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 37e6cc91d576..69e70964398c 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -2182,12 +2182,13 @@ void blk_cgroup_bio_start(struct bio *bio) bool blk_cgroup_congested(void) { - struct cgroup_subsys_state *css; + struct blkcg *blkcg; bool ret = false; rcu_read_lock(); - for (css = blkcg_css(); css; css = css->parent) { - if (atomic_read(&css->cgroup->congestion_count)) { + for (blkcg = css_to_blkcg(blkcg_css()); blkcg; + blkcg = blkcg_parent(blkcg)) { + if (atomic_read(&blkcg->congestion_count)) { ret = true; break; } diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index bd472a30bc61..864fad4a850b 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -95,6 +95,8 @@ struct blkcg { struct cgroup_subsys_state css; spinlock_t lock; refcount_t online_pin; + /* If there is block congestion on this cgroup. */ + atomic_t congestion_count; struct radix_tree_root blkg_tree; struct blkcg_gq __rcu *blkg_hint; @@ -374,7 +376,7 @@ static inline void blkcg_use_delay(struct blkcg_gq *blkg) if (WARN_ON_ONCE(atomic_read(&blkg->use_delay) < 0)) return; if (atomic_add_return(1, &blkg->use_delay) == 1) - atomic_inc(&blkg->blkcg->css.cgroup->congestion_count); + atomic_inc(&blkg->blkcg->congestion_count); } static inline int blkcg_unuse_delay(struct blkcg_gq *blkg) @@ -399,7 +401,7 @@ static inline int blkcg_unuse_delay(struct blkcg_gq *blkg) if (old == 0) return 0; if (old == 1) - atomic_dec(&blkg->blkcg->css.cgroup->congestion_count); + atomic_dec(&blkg->blkcg->congestion_count); return 1; } @@ -418,7 +420,7 @@ static inline void blkcg_set_delay(struct blkcg_gq *blkg, u64 delay) /* We only want 1 person setting the congestion count for this blkg. */ if (!old && atomic_try_cmpxchg(&blkg->use_delay, &old, -1)) - atomic_inc(&blkg->blkcg->css.cgroup->congestion_count); + atomic_inc(&blkg->blkcg->congestion_count); atomic64_set(&blkg->delay_nsec, delay); } @@ -435,7 +437,7 @@ static inline void blkcg_clear_delay(struct blkcg_gq *blkg) /* We only want 1 person clearing the congestion count for this blkg. */ if (old && atomic_try_cmpxchg(&blkg->use_delay, &old, 0)) - atomic_dec(&blkg->blkcg->css.cgroup->congestion_count); + atomic_dec(&blkg->blkcg->congestion_count); } /** diff --git a/block/blk-core.c b/block/blk-core.c index 02bceeb36f2c..1217c2cd66dd 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -791,8 +791,11 @@ void submit_bio_noacct(struct bio *bio) } } - if (!(q->limits.features & BLK_FEAT_POLL)) + if (!(q->limits.features & BLK_FEAT_POLL) && + (bio->bi_opf & REQ_POLLED)) { bio_clear_polled(bio); + goto not_supported; + } switch (bio_op(bio)) { case REQ_OP_READ: diff --git a/block/blk-map.c b/block/blk-map.c index bce144091128..0e1167b23934 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -757,6 +757,9 @@ int blk_rq_unmap_user(struct bio *bio) bio_release_pages(bio, bio_data_dir(bio) == READ); } + if (bio_integrity(bio)) + bio_integrity_unmap_user(bio); + next_bio = bio; bio = bio->bi_next; blk_mq_map_bio_put(next_bio); diff --git a/block/blk-mq-debugfs.c b/block/blk-mq-debugfs.c index 344f9e503bdb..5463697a8442 100644 --- a/block/blk-mq-debugfs.c +++ b/block/blk-mq-debugfs.c @@ -5,6 +5,7 @@ #include <linux/kernel.h> #include <linux/blkdev.h> +#include <linux/build_bug.h> #include <linux/debugfs.h> #include "blk.h" @@ -79,7 +80,6 @@ static int queue_pm_only_show(void *data, struct seq_file *m) #define QUEUE_FLAG_NAME(name) [QUEUE_FLAG_##name] = #name static const char *const blk_queue_flag_name[] = { - QUEUE_FLAG_NAME(STOPPED), QUEUE_FLAG_NAME(DYING), QUEUE_FLAG_NAME(NOMERGES), QUEUE_FLAG_NAME(SAME_COMP), @@ -100,6 +100,7 @@ static int queue_state_show(void *data, struct seq_file *m) { struct request_queue *q = data; + BUILD_BUG_ON(ARRAY_SIZE(blk_queue_flag_name) != QUEUE_FLAG_MAX); blk_flags_show(m, q->queue_flags, blk_queue_flag_name, ARRAY_SIZE(blk_queue_flag_name)); seq_puts(m, "\n"); @@ -164,6 +165,7 @@ static int hctx_state_show(void *data, struct seq_file *m) { struct blk_mq_hw_ctx *hctx = data; + BUILD_BUG_ON(ARRAY_SIZE(hctx_state_name) != BLK_MQ_S_MAX); blk_flags_show(m, hctx->state, hctx_state_name, ARRAY_SIZE(hctx_state_name)); seq_puts(m, "\n"); @@ -181,10 +183,11 @@ static const char *const alloc_policy_name[] = { static const char *const hctx_flag_name[] = { HCTX_FLAG_NAME(SHOULD_MERGE), HCTX_FLAG_NAME(TAG_QUEUE_SHARED), - HCTX_FLAG_NAME(BLOCKING), - HCTX_FLAG_NAME(NO_SCHED), HCTX_FLAG_NAME(STACKING), HCTX_FLAG_NAME(TAG_HCTX_SHARED), + HCTX_FLAG_NAME(BLOCKING), + HCTX_FLAG_NAME(NO_SCHED), + HCTX_FLAG_NAME(NO_SCHED_BY_DEFAULT), }; #undef HCTX_FLAG_NAME @@ -193,6 +196,10 @@ static int hctx_flags_show(void *data, struct seq_file *m) struct blk_mq_hw_ctx *hctx = data; const int alloc_policy = BLK_MQ_FLAG_TO_ALLOC_POLICY(hctx->flags); + BUILD_BUG_ON(ARRAY_SIZE(hctx_flag_name) != + BLK_MQ_F_ALLOC_POLICY_START_BIT); + BUILD_BUG_ON(ARRAY_SIZE(alloc_policy_name) != BLK_TAG_ALLOC_MAX); + seq_puts(m, "alloc_policy="); if (alloc_policy < ARRAY_SIZE(alloc_policy_name) && alloc_policy_name[alloc_policy]) @@ -223,12 +230,17 @@ static const char *const cmd_flag_name[] = { CMD_FLAG_NAME(RAHEAD), CMD_FLAG_NAME(BACKGROUND), CMD_FLAG_NAME(NOWAIT), - CMD_FLAG_NAME(NOUNMAP), CMD_FLAG_NAME(POLLED), + CMD_FLAG_NAME(ALLOC_CACHE), + CMD_FLAG_NAME(SWAP), + CMD_FLAG_NAME(DRV), + CMD_FLAG_NAME(FS_PRIVATE), + CMD_FLAG_NAME(ATOMIC), + CMD_FLAG_NAME(NOUNMAP), }; #undef CMD_FLAG_NAME -#define RQF_NAME(name) [ilog2((__force u32)RQF_##name)] = #name +#define RQF_NAME(name) [__RQF_##name] = #name static const char *const rqf_name[] = { RQF_NAME(STARTED), RQF_NAME(FLUSH_SEQ), @@ -243,6 +255,7 @@ static const char *const rqf_name[] = { RQF_NAME(HASHED), RQF_NAME(STATS), RQF_NAME(SPECIAL_PAYLOAD), + RQF_NAME(ZONE_WRITE_PLUGGING), RQF_NAME(TIMED_OUT), RQF_NAME(RESV), }; @@ -268,6 +281,9 @@ int __blk_mq_debugfs_rq_show(struct seq_file *m, struct request *rq) const enum req_op op = req_op(rq); const char *op_str = blk_op_str(op); + BUILD_BUG_ON(ARRAY_SIZE(cmd_flag_name) != __REQ_NR_BITS); + BUILD_BUG_ON(ARRAY_SIZE(rqf_name) != __RQF_BITS); + seq_printf(m, "%p {.op=", rq); if (strcmp(op_str, "UNKNOWN") == 0) seq_printf(m, "%u", op); diff --git a/block/blk-mq.h b/block/blk-mq.h index 260beea8e332..3bd43b10032f 100644 --- a/block/blk-mq.h +++ b/block/blk-mq.h @@ -36,6 +36,8 @@ enum { BLK_MQ_TAG_MAX = BLK_MQ_NO_TAG - 1, }; +#define BLK_MQ_CPU_WORK_BATCH (8) + typedef unsigned int __bitwise blk_insert_t; #define BLK_MQ_INSERT_AT_HEAD ((__force blk_insert_t)0x01) diff --git a/block/blk.h b/block/blk.h index 8e8936e97307..e180863f918b 100644 --- a/block/blk.h +++ b/block/blk.h @@ -2,6 +2,7 @@ #ifndef BLK_INTERNAL_H #define BLK_INTERNAL_H +#include <linux/bio-integrity.h> #include <linux/blk-crypto.h> #include <linux/memblock.h> /* for max_pfn/max_low_pfn */ #include <linux/sched/sysctl.h> @@ -201,11 +202,20 @@ static inline unsigned int blk_queue_get_max_sectors(struct request *rq) #ifdef CONFIG_BLK_DEV_INTEGRITY void blk_flush_integrity(void); -bool __bio_integrity_endio(struct bio *); void bio_integrity_free(struct bio *bio); + +/* + * Integrity payloads can either be owned by the submitter, in which case + * bio_uninit will free them, or owned and generated by the block layer, + * in which case we'll verify them here (for reads) and free them before + * the bio is handed back to the submitted. + */ +bool __bio_integrity_endio(struct bio *bio); static inline bool bio_integrity_endio(struct bio *bio) { - if (bio_integrity(bio)) + struct bio_integrity_payload *bip = bio_integrity(bio); + + if (bip && (bip->bip_flags & BIP_BLOCK_INTEGRITY)) return __bio_integrity_endio(bio); return true; } diff --git a/block/bounce.c b/block/bounce.c index d6a5219f29dd..0d898cd5ec49 100644 --- a/block/bounce.c +++ b/block/bounce.c @@ -10,7 +10,7 @@ #include <linux/export.h> #include <linux/swap.h> #include <linux/gfp.h> -#include <linux/bio.h> +#include <linux/bio-integrity.h> #include <linux/pagemap.h> #include <linux/mempool.h> #include <linux/blkdev.h> diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index b04b684f3190..9b6b71a2ffb5 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -216,6 +216,21 @@ phys_cpuid_t __init acpi_map_madt_entry(u32 acpi_id) return rv; } +int __init acpi_get_madt_revision(void) +{ + struct acpi_table_header *madt = NULL; + int revision; + + if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_MADT, 0, &madt))) + return -EINVAL; + + revision = madt->revision; + + acpi_put_table(madt); + + return revision; +} + static phys_cpuid_t map_mat_entry(acpi_handle handle, int type, u32 acpi_id) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c index 11f5fdf65b9e..0e60dd650b5e 100644 --- a/drivers/base/platform-msi.c +++ b/drivers/base/platform-msi.c @@ -4,346 +4,12 @@ * * Copyright (C) 2015 ARM Limited, All Rights Reserved. * Author: Marc Zyngier <marc.zyngier@arm.com> + * Copyright (C) 2022 Linutronix GmbH */ #include <linux/device.h> -#include <linux/idr.h> -#include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/msi.h> -#include <linux/slab.h> - -/* Begin of removal area. Once everything is converted over. Cleanup the includes too! */ - -#define DEV_ID_SHIFT 21 -#define MAX_DEV_MSIS (1 << (32 - DEV_ID_SHIFT)) - -/* - * Internal data structure containing a (made up, but unique) devid - * and the callback to write the MSI message. - */ -struct platform_msi_priv_data { - struct device *dev; - void *host_data; - msi_alloc_info_t arg; - irq_write_msi_msg_t write_msg; - int devid; -}; - -/* The devid allocator */ -static DEFINE_IDA(platform_msi_devid_ida); - -#ifdef GENERIC_MSI_DOMAIN_OPS -/* - * Convert an msi_desc to a globaly unique identifier (per-device - * devid + msi_desc position in the msi_list). - */ -static irq_hw_number_t platform_msi_calc_hwirq(struct msi_desc *desc) -{ - u32 devid = desc->dev->msi.data->platform_data->devid; - - return (devid << (32 - DEV_ID_SHIFT)) | desc->msi_index; -} - -static void platform_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) -{ - arg->desc = desc; - arg->hwirq = platform_msi_calc_hwirq(desc); -} - -static int platform_msi_init(struct irq_domain *domain, - struct msi_domain_info *info, - unsigned int virq, irq_hw_number_t hwirq, - msi_alloc_info_t *arg) -{ - return irq_domain_set_hwirq_and_chip(domain, virq, hwirq, - info->chip, info->chip_data); -} - -static void platform_msi_set_proxy_dev(msi_alloc_info_t *arg) -{ - arg->flags |= MSI_ALLOC_FLAGS_PROXY_DEVICE; -} -#else -#define platform_msi_set_desc NULL -#define platform_msi_init NULL -#define platform_msi_set_proxy_dev(x) do {} while(0) -#endif - -static void platform_msi_update_dom_ops(struct msi_domain_info *info) -{ - struct msi_domain_ops *ops = info->ops; - - BUG_ON(!ops); - - if (ops->msi_init == NULL) - ops->msi_init = platform_msi_init; - if (ops->set_desc == NULL) - ops->set_desc = platform_msi_set_desc; -} - -static void platform_msi_write_msg(struct irq_data *data, struct msi_msg *msg) -{ - struct msi_desc *desc = irq_data_get_msi_desc(data); - - desc->dev->msi.data->platform_data->write_msg(desc, msg); -} - -static void platform_msi_update_chip_ops(struct msi_domain_info *info) -{ - struct irq_chip *chip = info->chip; - - BUG_ON(!chip); - if (!chip->irq_mask) - chip->irq_mask = irq_chip_mask_parent; - if (!chip->irq_unmask) - chip->irq_unmask = irq_chip_unmask_parent; - if (!chip->irq_eoi) - chip->irq_eoi = irq_chip_eoi_parent; - if (!chip->irq_set_affinity) - chip->irq_set_affinity = msi_domain_set_affinity; - if (!chip->irq_write_msi_msg) - chip->irq_write_msi_msg = platform_msi_write_msg; - if (WARN_ON((info->flags & MSI_FLAG_LEVEL_CAPABLE) && - !(chip->flags & IRQCHIP_SUPPORTS_LEVEL_MSI))) - info->flags &= ~MSI_FLAG_LEVEL_CAPABLE; -} - -/** - * platform_msi_create_irq_domain - Create a platform MSI interrupt domain - * @fwnode: Optional fwnode of the interrupt controller - * @info: MSI domain info - * @parent: Parent irq domain - * - * Updates the domain and chip ops and creates a platform MSI - * interrupt domain. - * - * Returns: - * A domain pointer or NULL in case of failure. - */ -struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode, - struct msi_domain_info *info, - struct irq_domain *parent) -{ - struct irq_domain *domain; - - if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) - platform_msi_update_dom_ops(info); - if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) - platform_msi_update_chip_ops(info); - info->flags |= MSI_FLAG_DEV_SYSFS | MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS | - MSI_FLAG_FREE_MSI_DESCS; - - domain = msi_create_irq_domain(fwnode, info, parent); - if (domain) - irq_domain_update_bus_token(domain, DOMAIN_BUS_PLATFORM_MSI); - - return domain; -} -EXPORT_SYMBOL_GPL(platform_msi_create_irq_domain); - -static int platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec, - irq_write_msi_msg_t write_msi_msg) -{ - struct platform_msi_priv_data *datap; - int err; - - /* - * Limit the number of interrupts to 2048 per device. Should we - * need to bump this up, DEV_ID_SHIFT should be adjusted - * accordingly (which would impact the max number of MSI - * capable devices). - */ - if (!dev->msi.domain || !write_msi_msg || !nvec || nvec > MAX_DEV_MSIS) - return -EINVAL; - - if (dev->msi.domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) { - dev_err(dev, "Incompatible msi_domain, giving up\n"); - return -EINVAL; - } - - err = msi_setup_device_data(dev); - if (err) - return err; - - /* Already initialized? */ - if (dev->msi.data->platform_data) - return -EBUSY; - - datap = kzalloc(sizeof(*datap), GFP_KERNEL); - if (!datap) - return -ENOMEM; - - datap->devid = ida_alloc_max(&platform_msi_devid_ida, - (1 << DEV_ID_SHIFT) - 1, GFP_KERNEL); - if (datap->devid < 0) { - err = datap->devid; - kfree(datap); - return err; - } - - datap->write_msg = write_msi_msg; - datap->dev = dev; - dev->msi.data->platform_data = datap; - return 0; -} - -static void platform_msi_free_priv_data(struct device *dev) -{ - struct platform_msi_priv_data *data = dev->msi.data->platform_data; - - dev->msi.data->platform_data = NULL; - ida_free(&platform_msi_devid_ida, data->devid); - kfree(data); -} - -/** - * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev - * @dev: The device for which to allocate interrupts - * @nvec: The number of interrupts to allocate - * @write_msi_msg: Callback to write an interrupt message for @dev - * - * Returns: - * Zero for success, or an error code in case of failure - */ -static int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec, - irq_write_msi_msg_t write_msi_msg) -{ - int err; - - err = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg); - if (err) - return err; - - err = msi_domain_alloc_irqs_range(dev, MSI_DEFAULT_DOMAIN, 0, nvec - 1); - if (err) - platform_msi_free_priv_data(dev); - - return err; -} - -/** - * platform_msi_get_host_data - Query the private data associated with - * a platform-msi domain - * @domain: The platform-msi domain - * - * Return: The private data provided when calling - * platform_msi_create_device_domain(). - */ -void *platform_msi_get_host_data(struct irq_domain *domain) -{ - struct platform_msi_priv_data *data = domain->host_data; - - return data->host_data; -} - -static struct lock_class_key platform_device_msi_lock_class; - -/** - * __platform_msi_create_device_domain - Create a platform-msi device domain - * - * @dev: The device generating the MSIs - * @nvec: The number of MSIs that need to be allocated - * @is_tree: flag to indicate tree hierarchy - * @write_msi_msg: Callback to write an interrupt message for @dev - * @ops: The hierarchy domain operations to use - * @host_data: Private data associated to this domain - * - * Return: An irqdomain for @nvec interrupts on success, NULL in case of error. - * - * This is for interrupt domains which stack on a platform-msi domain - * created by platform_msi_create_irq_domain(). @dev->msi.domain points to - * that platform-msi domain which is the parent for the new domain. - */ -struct irq_domain * -__platform_msi_create_device_domain(struct device *dev, - unsigned int nvec, - bool is_tree, - irq_write_msi_msg_t write_msi_msg, - const struct irq_domain_ops *ops, - void *host_data) -{ - struct platform_msi_priv_data *data; - struct irq_domain *domain; - int err; - - err = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg); - if (err) - return NULL; - - /* - * Use a separate lock class for the MSI descriptor mutex on - * platform MSI device domains because the descriptor mutex nests - * into the domain mutex. See alloc/free below. - */ - lockdep_set_class(&dev->msi.data->mutex, &platform_device_msi_lock_class); - - data = dev->msi.data->platform_data; - data->host_data = host_data; - domain = irq_domain_create_hierarchy(dev->msi.domain, 0, - is_tree ? 0 : nvec, - dev->fwnode, ops, data); - if (!domain) - goto free_priv; - - platform_msi_set_proxy_dev(&data->arg); - err = msi_domain_prepare_irqs(domain->parent, dev, nvec, &data->arg); - if (err) - goto free_domain; - - return domain; - -free_domain: - irq_domain_remove(domain); -free_priv: - platform_msi_free_priv_data(dev); - return NULL; -} - -/** - * platform_msi_device_domain_free - Free interrupts associated with a platform-msi - * device domain - * - * @domain: The platform-msi device domain - * @virq: The base irq from which to perform the free operation - * @nr_irqs: How many interrupts to free from @virq - */ -void platform_msi_device_domain_free(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs) -{ - struct platform_msi_priv_data *data = domain->host_data; - - msi_lock_descs(data->dev); - msi_domain_depopulate_descs(data->dev, virq, nr_irqs); - irq_domain_free_irqs_common(domain, virq, nr_irqs); - msi_free_msi_descs_range(data->dev, virq, virq + nr_irqs - 1); - msi_unlock_descs(data->dev); -} - -/** - * platform_msi_device_domain_alloc - Allocate interrupts associated with - * a platform-msi device domain - * - * @domain: The platform-msi device domain - * @virq: The base irq from which to perform the allocate operation - * @nr_irqs: How many interrupts to allocate from @virq - * - * Return 0 on success, or an error code on failure. Must be called - * with irq_domain_mutex held (which can only be done as part of a - * top-level interrupt allocation). - */ -int platform_msi_device_domain_alloc(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs) -{ - struct platform_msi_priv_data *data = domain->host_data; - struct device *dev = data->dev; - - return msi_domain_populate_irqs(domain->parent, dev, virq, nr_irqs, &data->arg); -} - -/* End of removal area */ - -/* Real per device domain interfaces */ /* * This indirection can go when platform_device_msi_init_and_alloc_irqs() @@ -357,7 +23,7 @@ static void platform_msi_write_msi_msg(struct irq_data *d, struct msi_msg *msg) cb(irq_data_get_msi_desc(d), msg); } -static void platform_msi_set_desc_byindex(msi_alloc_info_t *arg, struct msi_desc *desc) +static void platform_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) { arg->desc = desc; arg->hwirq = desc->msi_index; @@ -373,7 +39,7 @@ static const struct msi_domain_template platform_msi_template = { }, .ops = { - .set_desc = platform_msi_set_desc_byindex, + .set_desc = platform_msi_set_desc, }, .info = { @@ -408,10 +74,6 @@ int platform_device_msi_init_and_alloc_irqs(struct device *dev, unsigned int nve if (!domain || !write_msi_msg) return -EINVAL; - /* Migration support. Will go away once everything is converted */ - if (!irq_domain_is_msi_parent(domain)) - return platform_msi_domain_alloc_irqs(dev, nvec, write_msi_msg); - /* * @write_msi_msg is stored in the resulting msi_domain_info::data. * The underlying domain creation mechanism will assign that @@ -432,12 +94,6 @@ EXPORT_SYMBOL_GPL(platform_device_msi_init_and_alloc_irqs); */ void platform_device_msi_free_irqs_all(struct device *dev) { - struct irq_domain *domain = dev->msi.domain; - msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN); - - /* Migration support. Will go away once everything is converted */ - if (!irq_domain_is_msi_parent(domain)) - platform_msi_free_priv_data(dev); } EXPORT_SYMBOL_GPL(platform_device_msi_free_irqs_all); diff --git a/drivers/firmware/efi/libstub/loongarch.c b/drivers/firmware/efi/libstub/loongarch.c index d0ef93551c44..3782d0a187d1 100644 --- a/drivers/firmware/efi/libstub/loongarch.c +++ b/drivers/firmware/efi/libstub/loongarch.c @@ -74,6 +74,8 @@ efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image, /* Config Direct Mapping */ csr_write64(CSR_DMW0_INIT, LOONGARCH_CSR_DMWIN0); csr_write64(CSR_DMW1_INIT, LOONGARCH_CSR_DMWIN1); + csr_write64(CSR_DMW2_INIT, LOONGARCH_CSR_DMWIN2); + csr_write64(CSR_DMW3_INIT, LOONGARCH_CSR_DMWIN3); real_kernel_entry = (void *)kernel_entry_address(kernel_addr, image); diff --git a/drivers/hsi/clients/ssi_protocol.c b/drivers/hsi/clients/ssi_protocol.c index 10926359e6d2..afe470f3661c 100644 --- a/drivers/hsi/clients/ssi_protocol.c +++ b/drivers/hsi/clients/ssi_protocol.c @@ -14,7 +14,6 @@ #include <linux/clk.h> #include <linux/device.h> #include <linux/err.h> -#include <linux/gpio.h> #include <linux/if_ether.h> #include <linux/if_arp.h> #include <linux/if_phonet.h> diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c index 0c0a932c00f3..6505261e6068 100644 --- a/drivers/hwspinlock/hwspinlock_core.c +++ b/drivers/hwspinlock/hwspinlock_core.c @@ -306,6 +306,34 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags) EXPORT_SYMBOL_GPL(__hwspin_unlock); /** + * hwspin_lock_bust() - bust a specific hwspinlock + * @hwlock: a previously-acquired hwspinlock which we want to bust + * @id: identifier of the remote lock holder, if applicable + * + * This function will bust a hwspinlock that was previously acquired as + * long as the current owner of the lock matches the id given by the caller. + * + * Context: Process context. + * + * Returns: 0 on success, or -EINVAL if the hwspinlock does not exist, or + * the bust operation fails, and -EOPNOTSUPP if the bust operation is not + * defined for the hwspinlock. + */ +int hwspin_lock_bust(struct hwspinlock *hwlock, unsigned int id) +{ + if (WARN_ON(!hwlock)) + return -EINVAL; + + if (!hwlock->bank->ops->bust) { + pr_err("bust operation not defined\n"); + return -EOPNOTSUPP; + } + + return hwlock->bank->ops->bust(hwlock, id); +} +EXPORT_SYMBOL_GPL(hwspin_lock_bust); + +/** * of_hwspin_lock_simple_xlate - translate hwlock_spec to return a lock id * @hwlock_spec: hwlock specifier as found in the device tree * diff --git a/drivers/hwspinlock/hwspinlock_internal.h b/drivers/hwspinlock/hwspinlock_internal.h index 29892767bb7a..f298fc0ee5ad 100644 --- a/drivers/hwspinlock/hwspinlock_internal.h +++ b/drivers/hwspinlock/hwspinlock_internal.h @@ -21,6 +21,8 @@ struct hwspinlock_device; * @trylock: make a single attempt to take the lock. returns 0 on * failure and true on success. may _not_ sleep. * @unlock: release the lock. always succeed. may _not_ sleep. + * @bust: optional, platform-specific bust handler, called by hwspinlock + * core to bust a specific lock. * @relax: optional, platform-specific relax handler, called by hwspinlock * core while spinning on a lock, between two successive * invocations of @trylock. may _not_ sleep. @@ -28,6 +30,7 @@ struct hwspinlock_device; struct hwspinlock_ops { int (*trylock)(struct hwspinlock *lock); void (*unlock)(struct hwspinlock *lock); + int (*bust)(struct hwspinlock *lock, unsigned int id); void (*relax)(struct hwspinlock *lock); }; diff --git a/drivers/hwspinlock/qcom_hwspinlock.c b/drivers/hwspinlock/qcom_hwspinlock.c index 814dfe8697bf..0390979fd765 100644 --- a/drivers/hwspinlock/qcom_hwspinlock.c +++ b/drivers/hwspinlock/qcom_hwspinlock.c @@ -64,9 +64,34 @@ static void qcom_hwspinlock_unlock(struct hwspinlock *lock) pr_err("%s: failed to unlock spinlock\n", __func__); } +static int qcom_hwspinlock_bust(struct hwspinlock *lock, unsigned int id) +{ + struct regmap_field *field = lock->priv; + u32 owner; + int ret; + + ret = regmap_field_read(field, &owner); + if (ret) { + dev_err(lock->bank->dev, "unable to query spinlock owner\n"); + return ret; + } + + if (owner != id) + return 0; + + ret = regmap_field_write(field, 0); + if (ret) { + dev_err(lock->bank->dev, "failed to bust spinlock\n"); + return ret; + } + + return 0; +} + static const struct hwspinlock_ops qcom_hwspinlock_ops = { .trylock = qcom_hwspinlock_trylock, .unlock = qcom_hwspinlock_unlock, + .bust = qcom_hwspinlock_bust, }; static const struct regmap_config sfpb_mutex_config = { diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 3e32fb882101..a22f9125322a 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -196,6 +196,7 @@ config I2C_ISMT config I2C_PIIX4 tristate "Intel PIIX4 and compatible (ATI/AMD/Serverworks/Broadcom/SMSC)" depends on PCI && HAS_IOPORT + select I2C_SMBUS help If you say yes to this option, support will be included for the Intel PIIX4 family of mainboard I2C interfaces. Specifically, the following diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 84aa18d1003b..4e32d57ae0bf 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -29,6 +29,7 @@ #include <linux/stddef.h> #include <linux/ioport.h> #include <linux/i2c.h> +#include <linux/i2c-smbus.h> #include <linux/slab.h> #include <linux/dmi.h> #include <linux/acpi.h> @@ -982,6 +983,14 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba, return retval; } + /* + * The AUX bus can not be probed as on some platforms it reports all + * devices present and all reads return "0". + * This would allow the ee1004 to be probed incorrectly. + */ + if (port == 0) + i2c_register_spd(adap); + *padap = adap; return 0; } diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c index 1cb137b9181d..7e4203df83ed 100644 --- a/drivers/i2c/i2c-smbus.c +++ b/drivers/i2c/i2c-smbus.c @@ -352,18 +352,11 @@ void i2c_register_spd(struct i2c_adapter *adap) return; /* - * If we're a child adapter on a muxed segment, then limit slots to 8, - * as this is the max number of SPD EEPROMs that can be addressed per bus. + * The max number of SPD EEPROMs that can be addressed per bus is 8. + * If more slots are present either muxed or multiple busses are + * necessary or the additional slots are ignored. */ - if (i2c_parent_is_i2c_adapter(adap)) { - slot_count = 8; - } else { - if (slot_count > 8) { - dev_warn(&adap->dev, - "More than 8 memory slots on a single bus, contact i801 maintainer to add missing mux config\n"); - return; - } - } + slot_count = min(slot_count, 8); /* * Memory types could be found at section 7.18.2 (Memory Device — Type), table 78 diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c index d6bbb8b68333..944577bb09c1 100644 --- a/drivers/i2c/muxes/i2c-mux-gpio.c +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -5,16 +5,17 @@ * Peter Korsgaard <peter.korsgaard@barco.com> */ +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio/driver.h> #include <linux/i2c.h> #include <linux/i2c-mux.h> +#include <linux/module.h> #include <linux/overflow.h> #include <linux/platform_data/i2c-mux-gpio.h> #include <linux/platform_device.h> -#include <linux/module.h> #include <linux/slab.h> -#include <linux/bits.h> -#include <linux/gpio/consumer.h> -#include <linux/gpio/driver.h> struct gpiomux { struct i2c_mux_gpio_platform_data data; @@ -37,6 +38,9 @@ static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan) i2c_mux_gpio_set(mux, chan); + if (mux->data.settle_time) + fsleep(mux->data.settle_time); + return 0; } @@ -116,6 +120,8 @@ static int i2c_mux_gpio_probe_fw(struct gpiomux *mux, if (device_property_read_u32(dev, "idle-state", &mux->data.idle)) mux->data.idle = I2C_MUX_GPIO_NO_IDLE; + device_property_read_u32(dev, "settle-time-us", &mux->data.settle_time); + return 0; } diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 14464716bacb..d078bdc48c38 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -26,6 +26,7 @@ config ARM_GIC_V2M bool depends on PCI select ARM_GIC + select IRQ_MSI_LIB select PCI_MSI config GIC_NON_BANKED @@ -41,6 +42,7 @@ config ARM_GIC_V3 config ARM_GIC_V3_ITS bool select GENERIC_MSI_IRQ + select IRQ_MSI_LIB default ARM_GIC_V3 config ARM_GIC_V3_ITS_PCI @@ -74,6 +76,9 @@ config ARM_VIC_NR The maximum number of VICs available in the system, for power management. +config IRQ_MSI_LIB + bool + config ARMADA_370_XP_IRQ bool select GENERIC_IRQ_CHIP @@ -169,6 +174,18 @@ config IXP4XX_IRQ select IRQ_DOMAIN select SPARSE_IRQ +config LAN966X_OIC + tristate "Microchip LAN966x OIC Support" + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN + help + Enable support for the LAN966x Outbound Interrupt Controller. + This controller is present on the Microchip LAN966x PCI device and + maps the internal interrupts sources to PCIe interrupt. + + To compile this driver as a module, choose M here: the module + will be called irq-lan966x-oic. + config MADERA_IRQ tristate @@ -366,6 +383,7 @@ config MSCC_OCELOT_IRQ select GENERIC_IRQ_CHIP config MVEBU_GICP + select IRQ_MSI_LIB bool config MVEBU_ICU @@ -373,6 +391,7 @@ config MVEBU_ICU config MVEBU_ODMI bool + select IRQ_MSI_LIB select GENERIC_MSI_IRQ config MVEBU_PIC @@ -392,6 +411,15 @@ config LS_SCFG_MSI config PARTITION_PERCPU bool +config STM32MP_EXTI + tristate "STM32MP extended interrupts and event controller" + depends on (ARCH_STM32 && !ARM_SINGLE_ARMV7M) || COMPILE_TEST + default y + select IRQ_DOMAIN_HIERARCHY + select GENERIC_IRQ_CHIP + help + Support STM32MP EXTI (extended interrupts and event) controller. + config STM32_EXTI bool select IRQ_DOMAIN @@ -487,6 +515,7 @@ config IMX_MU_MSI select IRQ_DOMAIN select IRQ_DOMAIN_HIERARCHY select GENERIC_MSI_IRQ + select IRQ_MSI_LIB help Provide a driver for the i.MX Messaging Unit block used as a CPU-to-CPU MSI controller. This requires a specially crafted DT diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index d9dc3d99aaa8..15635812b2d6 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -29,10 +29,10 @@ obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o obj-$(CONFIG_ARM_GIC_PM) += irq-gic-pm.o obj-$(CONFIG_ARCH_REALVIEW) += irq-gic-realview.o +obj-$(CONFIG_IRQ_MSI_LIB) += irq-msi-lib.o obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-v3-mbi.o irq-gic-common.o -obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v3-its-platform-msi.o irq-gic-v4.o -obj-$(CONFIG_ARM_GIC_V3_ITS_PCI) += irq-gic-v3-its-pci-msi.o +obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v4.o irq-gic-v3-its-msi-parent.o obj-$(CONFIG_ARM_GIC_V3_ITS_FSL_MC) += irq-gic-v3-its-fsl-mc-msi.o obj-$(CONFIG_PARTITION_PERCPU) += irq-partition-percpu.o obj-$(CONFIG_HISILICON_IRQ_MBIGEN) += irq-mbigen.o @@ -84,6 +84,7 @@ obj-$(CONFIG_MVEBU_SEI) += irq-mvebu-sei.o obj-$(CONFIG_LS_EXTIRQ) += irq-ls-extirq.o obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o irq-aspeed-i2c-ic.o irq-aspeed-scu-ic.o +obj-$(CONFIG_STM32MP_EXTI) += irq-stm32mp-exti.o obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o obj-$(CONFIG_QCOM_IRQ_COMBINER) += qcom-irq-combiner.o obj-$(CONFIG_IRQ_UNIPHIER_AIDET) += irq-uniphier-aidet.o @@ -104,6 +105,7 @@ obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o obj-$(CONFIG_IMX_INTMUX) += irq-imx-intmux.o obj-$(CONFIG_IMX_MU_MSI) += irq-imx-mu-msi.o obj-$(CONFIG_MADERA_IRQ) += irq-madera.o +obj-$(CONFIG_LAN966X_OIC) += irq-lan966x-oic.o obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 4b021a67bdfe..dce2b80bf439 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -13,6 +13,7 @@ * warranty of any kind, whether express or implied. */ +#include <linux/bits.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -29,6 +30,7 @@ #include <linux/slab.h> #include <linux/syscore_ops.h> #include <linux/msi.h> +#include <linux/types.h> #include <asm/mach/arch.h> #include <asm/exception.h> #include <asm/smp_plat.h> @@ -135,6 +137,7 @@ #define ARMADA_370_XP_MAX_PER_CPU_IRQS (28) +/* IPI and MSI interrupt definitions for IPI platforms */ #define IPI_DOORBELL_START (0) #define IPI_DOORBELL_END (8) #define IPI_DOORBELL_MASK 0xFF @@ -143,6 +146,14 @@ #define PCI_MSI_DOORBELL_END (32) #define PCI_MSI_DOORBELL_MASK 0xFFFF0000 +/* MSI interrupt definitions for non-IPI platforms */ +#define PCI_MSI_FULL_DOORBELL_START 0 +#define PCI_MSI_FULL_DOORBELL_NR 32 +#define PCI_MSI_FULL_DOORBELL_END 32 +#define PCI_MSI_FULL_DOORBELL_MASK GENMASK(31, 0) +#define PCI_MSI_FULL_DOORBELL_SRC0_MASK GENMASK(15, 0) +#define PCI_MSI_FULL_DOORBELL_SRC1_MASK GENMASK(31, 16) + static void __iomem *per_cpu_int_base; static void __iomem *main_int_base; static struct irq_domain *armada_370_xp_mpic_domain; @@ -151,11 +162,46 @@ static int parent_irq; #ifdef CONFIG_PCI_MSI static struct irq_domain *armada_370_xp_msi_domain; static struct irq_domain *armada_370_xp_msi_inner_domain; -static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); +static DECLARE_BITMAP(msi_used, PCI_MSI_FULL_DOORBELL_NR); static DEFINE_MUTEX(msi_used_lock); static phys_addr_t msi_doorbell_addr; #endif +static inline bool is_ipi_available(void) +{ + /* + * We distinguish IPI availability in the IC by the IC not having a + * parent irq defined. If a parent irq is defined, there is a parent + * interrupt controller (e.g. GIC) that takes care of inter-processor + * interrupts. + */ + return parent_irq <= 0; +} + +static inline u32 msi_doorbell_mask(void) +{ + return is_ipi_available() ? PCI_MSI_DOORBELL_MASK : + PCI_MSI_FULL_DOORBELL_MASK; +} + +static inline unsigned int msi_doorbell_start(void) +{ + return is_ipi_available() ? PCI_MSI_DOORBELL_START : + PCI_MSI_FULL_DOORBELL_START; +} + +static inline unsigned int msi_doorbell_size(void) +{ + return is_ipi_available() ? PCI_MSI_DOORBELL_NR : + PCI_MSI_FULL_DOORBELL_NR; +} + +static inline unsigned int msi_doorbell_end(void) +{ + return is_ipi_available() ? PCI_MSI_DOORBELL_END : + PCI_MSI_FULL_DOORBELL_END; +} + static inline bool is_percpu_irq(irq_hw_number_t irq) { if (irq <= ARMADA_370_XP_MAX_PER_CPU_IRQS) @@ -213,7 +259,7 @@ static void armada_370_xp_compose_msi_msg(struct irq_data *data, struct msi_msg msg->address_lo = lower_32_bits(msi_doorbell_addr); msg->address_hi = upper_32_bits(msi_doorbell_addr); - msg->data = BIT(cpu + 8) | (data->hwirq + PCI_MSI_DOORBELL_START); + msg->data = BIT(cpu + 8) | (data->hwirq + msi_doorbell_start()); } static int armada_370_xp_msi_set_affinity(struct irq_data *irq_data, @@ -246,7 +292,7 @@ static int armada_370_xp_msi_alloc(struct irq_domain *domain, unsigned int virq, int hwirq, i; mutex_lock(&msi_used_lock); - hwirq = bitmap_find_free_region(msi_used, PCI_MSI_DOORBELL_NR, + hwirq = bitmap_find_free_region(msi_used, msi_doorbell_size(), order_base_2(nr_irqs)); mutex_unlock(&msi_used_lock); @@ -283,9 +329,10 @@ static void armada_370_xp_msi_reenable_percpu(void) u32 reg; /* Enable MSI doorbell mask and combined cpu local interrupt */ - reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS) - | PCI_MSI_DOORBELL_MASK; + reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); + reg |= msi_doorbell_mask(); writel(reg, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); + /* Unmask local doorbell interrupt */ writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); } @@ -297,7 +344,7 @@ static int armada_370_xp_msi_init(struct device_node *node, ARMADA_370_XP_SW_TRIG_INT_OFFS; armada_370_xp_msi_inner_domain = - irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR, + irq_domain_add_linear(NULL, msi_doorbell_size(), &armada_370_xp_msi_domain_ops, NULL); if (!armada_370_xp_msi_inner_domain) return -ENOMEM; @@ -313,6 +360,10 @@ static int armada_370_xp_msi_init(struct device_node *node, armada_370_xp_msi_reenable_percpu(); + /* Unmask low 16 MSI irqs on non-IPI platforms */ + if (!is_ipi_available()) + writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); + return 0; } #else @@ -461,24 +512,18 @@ static __init void armada_xp_ipi_init(struct device_node *node) set_smp_ipi_range(base_ipi, IPI_DOORBELL_END); } -static DEFINE_RAW_SPINLOCK(irq_controller_lock); - static int armada_xp_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force) { irq_hw_number_t hwirq = irqd_to_hwirq(d); - unsigned long reg, mask; int cpu; /* Select a single core from the affinity mask which is online */ cpu = cpumask_any_and(mask_val, cpu_online_mask); - mask = 1UL << cpu_logical_map(cpu); - raw_spin_lock(&irq_controller_lock); - reg = readl(main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); - reg = (reg & (~ARMADA_370_XP_INT_SOURCE_CPU_MASK)) | mask; - writel(reg, main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); - raw_spin_unlock(&irq_controller_lock); + atomic_io_modify(main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq), + ARMADA_370_XP_INT_SOURCE_CPU_MASK, + BIT(cpu_logical_map(cpu))); irq_data_update_effective_affinity(d, cpumask_of(cpu)); @@ -496,6 +541,9 @@ static void armada_xp_mpic_smp_cpu_init(void) for (i = 0; i < nr_irqs; i++) writel(i, per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS); + if (!is_ipi_available()) + return; + /* Disable all IPIs */ writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); @@ -527,7 +575,8 @@ static void armada_xp_mpic_reenable_percpu(void) armada_370_xp_irq_unmask(data); } - ipi_resume(); + if (is_ipi_available()) + ipi_resume(); armada_370_xp_msi_reenable_percpu(); } @@ -566,6 +615,10 @@ static struct irq_chip armada_370_xp_irq_chip = { static int armada_370_xp_mpic_irq_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) { + /* IRQs 0 and 1 cannot be mapped, they are handled internally */ + if (hw <= 1) + return -EINVAL; + armada_370_xp_irq_mask(irq_get_irq_data(virq)); if (!is_percpu_irq(hw)) writel(hw, per_cpu_int_base + @@ -599,20 +652,20 @@ static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained) u32 msimask, msinr; msimask = readl_relaxed(per_cpu_int_base + - ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) - & PCI_MSI_DOORBELL_MASK; + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); + msimask &= msi_doorbell_mask(); writel(~msimask, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); - for (msinr = PCI_MSI_DOORBELL_START; - msinr < PCI_MSI_DOORBELL_END; msinr++) { + for (msinr = msi_doorbell_start(); + msinr < msi_doorbell_end(); msinr++) { unsigned int irq; if (!(msimask & BIT(msinr))) continue; - irq = msinr - PCI_MSI_DOORBELL_START; + irq = msinr - msi_doorbell_start(); generic_handle_domain_irq(armada_370_xp_msi_inner_domain, irq); } @@ -641,7 +694,7 @@ static void armada_370_xp_mpic_handle_cascade_irq(struct irq_desc *desc) if (!(irqsrc & ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid))) continue; - if (irqn == 1) { + if (irqn == 0 || irqn == 1) { armada_370_xp_handle_msi_irq(NULL, true); continue; } @@ -702,6 +755,7 @@ static int armada_370_xp_mpic_suspend(void) static void armada_370_xp_mpic_resume(void) { + bool src0, src1; int nirqs; irq_hw_number_t irq; @@ -741,12 +795,22 @@ static void armada_370_xp_mpic_resume(void) /* Reconfigure doorbells for IPIs and MSIs */ writel(doorbell_mask_reg, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); - if (doorbell_mask_reg & IPI_DOORBELL_MASK) + + if (is_ipi_available()) { + src0 = doorbell_mask_reg & IPI_DOORBELL_MASK; + src1 = doorbell_mask_reg & PCI_MSI_DOORBELL_MASK; + } else { + src0 = doorbell_mask_reg & PCI_MSI_FULL_DOORBELL_SRC0_MASK; + src1 = doorbell_mask_reg & PCI_MSI_FULL_DOORBELL_SRC1_MASK; + } + + if (src0) writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); - if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK) + if (src1) writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); - ipi_resume(); + if (is_ipi_available()) + ipi_resume(); } static struct syscore_ops armada_370_xp_mpic_syscore_ops = { @@ -791,13 +855,18 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, BUG_ON(!armada_370_xp_mpic_domain); irq_domain_update_bus_token(armada_370_xp_mpic_domain, DOMAIN_BUS_WIRED); + /* + * Initialize parent_irq before calling any other functions, since it is + * used to distinguish between IPI and non-IPI platforms. + */ + parent_irq = irq_of_parse_and_map(node, 0); + /* Setup for the boot CPU */ armada_xp_mpic_perf_init(); armada_xp_mpic_smp_cpu_init(); armada_370_xp_msi_init(node, main_int_res.start); - parent_irq = irq_of_parse_and_map(node, 0); if (parent_irq <= 0) { irq_set_default_host(armada_370_xp_mpic_domain); set_handle_irq(armada_370_xp_handle_irq); diff --git a/drivers/irqchip/irq-bcm2835.c b/drivers/irqchip/irq-bcm2835.c index e94e2882286c..6c20604c2242 100644 --- a/drivers/irqchip/irq-bcm2835.c +++ b/drivers/irqchip/irq-bcm2835.c @@ -102,7 +102,9 @@ static void armctrl_unmask_irq(struct irq_data *d) static struct irq_chip armctrl_chip = { .name = "ARMCTRL-level", .irq_mask = armctrl_mask_irq, - .irq_unmask = armctrl_unmask_irq + .irq_unmask = armctrl_unmask_irq, + .flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SKIP_SET_WAKE, }; static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr, diff --git a/drivers/irqchip/irq-gic-common.h b/drivers/irqchip/irq-gic-common.h index e8eab72ef195..020ecdf16901 100644 --- a/drivers/irqchip/irq-gic-common.h +++ b/drivers/irqchip/irq-gic-common.h @@ -8,6 +8,7 @@ #include <linux/of.h> #include <linux/irqdomain.h> +#include <linux/msi.h> #include <linux/irqchip/arm-gic-common.h> struct gic_quirk { @@ -28,6 +29,8 @@ void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks, void gic_enable_of_quirks(const struct device_node *np, const struct gic_quirk *quirks, void *data); +extern const struct msi_parent_ops gic_v3_its_msi_parent_ops; + #define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0) #define RDIST_FLAGS_RD_TABLES_PREALLOCATED (1 << 1) #define RDIST_FLAGS_FORCE_NON_SHAREABLE (1 << 2) diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index f2ff4387870d..51af63c046ed 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c @@ -26,6 +26,8 @@ #include <linux/irqchip/arm-gic.h> #include <linux/irqchip/arm-gic-common.h> +#include "irq-msi-lib.h" + /* * MSI_TYPER: * [31:26] Reserved @@ -72,31 +74,6 @@ struct v2m_data { u32 flags; /* v2m flags for specific implementation */ }; -static void gicv2m_mask_msi_irq(struct irq_data *d) -{ - pci_msi_mask_irq(d); - irq_chip_mask_parent(d); -} - -static void gicv2m_unmask_msi_irq(struct irq_data *d) -{ - pci_msi_unmask_irq(d); - irq_chip_unmask_parent(d); -} - -static struct irq_chip gicv2m_msi_irq_chip = { - .name = "MSI", - .irq_mask = gicv2m_mask_msi_irq, - .irq_unmask = gicv2m_unmask_msi_irq, - .irq_eoi = irq_chip_eoi_parent, -}; - -static struct msi_domain_info gicv2m_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), - .chip = &gicv2m_msi_irq_chip, -}; - static phys_addr_t gicv2m_get_msi_addr(struct v2m_data *v2m, int hwirq) { if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) @@ -230,6 +207,7 @@ static void gicv2m_irq_domain_free(struct irq_domain *domain, } static const struct irq_domain_ops gicv2m_domain_ops = { + .select = msi_lib_irq_domain_select, .alloc = gicv2m_irq_domain_alloc, .free = gicv2m_irq_domain_free, }; @@ -250,19 +228,6 @@ static bool is_msi_spi_valid(u32 base, u32 num) return true; } -static struct irq_chip gicv2m_pmsi_irq_chip = { - .name = "pMSI", -}; - -static struct msi_domain_ops gicv2m_pmsi_ops = { -}; - -static struct msi_domain_info gicv2m_pmsi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS), - .ops = &gicv2m_pmsi_ops, - .chip = &gicv2m_pmsi_irq_chip, -}; - static void __init gicv2m_teardown(void) { struct v2m_data *v2m, *tmp; @@ -278,9 +243,27 @@ static void __init gicv2m_teardown(void) } } + +#define GICV2M_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS | \ + MSI_FLAG_PCI_MSI_MASK_PARENT) + +#define GICV2M_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \ + MSI_FLAG_PCI_MSIX | \ + MSI_FLAG_MULTI_PCI_MSI) + +static struct msi_parent_ops gicv2m_msi_parent_ops = { + .supported_flags = GICV2M_MSI_FLAGS_SUPPORTED, + .required_flags = GICV2M_MSI_FLAGS_REQUIRED, + .bus_select_token = DOMAIN_BUS_NEXUS, + .bus_select_mask = MATCH_PCI_MSI | MATCH_PLATFORM_MSI, + .prefix = "GICv2m-", + .init_dev_msi_info = msi_lib_init_dev_msi_info, +}; + static __init int gicv2m_allocate_domains(struct irq_domain *parent) { - struct irq_domain *inner_domain, *pci_domain, *plat_domain; + struct irq_domain *inner_domain; struct v2m_data *v2m; v2m = list_first_entry_or_null(&v2m_nodes, struct v2m_data, entry); @@ -295,22 +278,8 @@ static __init int gicv2m_allocate_domains(struct irq_domain *parent) } irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS); - pci_domain = pci_msi_create_irq_domain(v2m->fwnode, - &gicv2m_msi_domain_info, - inner_domain); - plat_domain = platform_msi_create_irq_domain(v2m->fwnode, - &gicv2m_pmsi_domain_info, - inner_domain); - if (!pci_domain || !plat_domain) { - pr_err("Failed to create MSI domains\n"); - if (plat_domain) - irq_domain_remove(plat_domain); - if (pci_domain) - irq_domain_remove(pci_domain); - irq_domain_remove(inner_domain); - return -ENOMEM; - } - + inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; + inner_domain->msi_parent_ops = &gicv2m_msi_parent_ops; return 0; } @@ -511,7 +480,7 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header, pr_info("applying Amazon Graviton quirk\n"); res.end = res.start + SZ_8K - 1; flags |= GICV2M_GRAVITON_ADDRESS_ONLY; - gicv2m_msi_domain_info.flags &= ~MSI_FLAG_MULTI_PCI_MSI; + gicv2m_msi_parent_ops.supported_flags &= ~MSI_FLAG_MULTI_PCI_MSI; } if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) { diff --git a/drivers/irqchip/irq-gic-v3-its-msi-parent.c b/drivers/irqchip/irq-gic-v3-its-msi-parent.c new file mode 100644 index 000000000000..e150365fbe89 --- /dev/null +++ b/drivers/irqchip/irq-gic-v3-its-msi-parent.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2013-2015 ARM Limited, All Rights Reserved. +// Author: Marc Zyngier <marc.zyngier@arm.com> +// Copyright (C) 2022 Linutronix GmbH +// Copyright (C) 2022 Intel + +#include <linux/acpi_iort.h> +#include <linux/pci.h> + +#include "irq-gic-common.h" +#include "irq-msi-lib.h" + +#define ITS_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS | \ + MSI_FLAG_PCI_MSI_MASK_PARENT) + +#define ITS_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \ + MSI_FLAG_PCI_MSIX | \ + MSI_FLAG_MULTI_PCI_MSI) + +#ifdef CONFIG_PCI_MSI +static int its_pci_msi_vec_count(struct pci_dev *pdev, void *data) +{ + int msi, msix, *count = data; + + msi = max(pci_msi_vec_count(pdev), 0); + msix = max(pci_msix_vec_count(pdev), 0); + *count += max(msi, msix); + + return 0; +} + +static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data) +{ + struct pci_dev **alias_dev = data; + + *alias_dev = pdev; + + return 0; +} + +static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev, + int nvec, msi_alloc_info_t *info) +{ + struct pci_dev *pdev, *alias_dev; + struct msi_domain_info *msi_info; + int alias_count = 0, minnvec = 1; + + if (!dev_is_pci(dev)) + return -EINVAL; + + pdev = to_pci_dev(dev); + /* + * If pdev is downstream of any aliasing bridges, take an upper + * bound of how many other vectors could map to the same DevID. + * Also tell the ITS that the signalling will come from a proxy + * device, and that special allocation rules apply. + */ + pci_for_each_dma_alias(pdev, its_get_pci_alias, &alias_dev); + if (alias_dev != pdev) { + if (alias_dev->subordinate) + pci_walk_bus(alias_dev->subordinate, + its_pci_msi_vec_count, &alias_count); + info->flags |= MSI_ALLOC_FLAGS_PROXY_DEVICE; + } + + /* ITS specific DeviceID, as the core ITS ignores dev. */ + info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain->parent, pdev); + + /* + * @domain->msi_domain_info->hwsize contains the size of the + * MSI[-X] domain, but vector allocation happens one by one. This + * needs some thought when MSI comes into play as the size of MSI + * might be unknown at domain creation time and therefore set to + * MSI_MAX_INDEX. + */ + msi_info = msi_get_domain_info(domain); + if (msi_info->hwsize > nvec) + nvec = msi_info->hwsize; + + /* + * Always allocate a power of 2, and special case device 0 for + * broken systems where the DevID is not wired (and all devices + * appear as DevID 0). For that reason, we generously allocate a + * minimum of 32 MSIs for DevID 0. If you want more because all + * your devices are aliasing to DevID 0, consider fixing your HW. + */ + nvec = max(nvec, alias_count); + if (!info->scratchpad[0].ul) + minnvec = 32; + nvec = max_t(int, minnvec, roundup_pow_of_two(nvec)); + + msi_info = msi_get_domain_info(domain->parent); + return msi_info->ops->msi_prepare(domain->parent, dev, nvec, info); +} +#else /* CONFIG_PCI_MSI */ +#define its_pci_msi_prepare NULL +#endif /* !CONFIG_PCI_MSI */ + +static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev, + u32 *dev_id) +{ + int ret, index = 0; + + /* Suck the DeviceID out of the msi-parent property */ + do { + struct of_phandle_args args; + + ret = of_parse_phandle_with_args(dev->of_node, + "msi-parent", "#msi-cells", + index, &args); + if (args.np == irq_domain_get_of_node(domain)) { + if (WARN_ON(args.args_count != 1)) + return -EINVAL; + *dev_id = args.args[0]; + break; + } + index++; + } while (!ret); + + return ret; +} + +int __weak iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) +{ + return -1; +} + +static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev, + int nvec, msi_alloc_info_t *info) +{ + struct msi_domain_info *msi_info; + u32 dev_id; + int ret; + + if (dev->of_node) + ret = of_pmsi_get_dev_id(domain->parent, dev, &dev_id); + else + ret = iort_pmsi_get_dev_id(dev, &dev_id); + if (ret) + return ret; + + /* ITS specific DeviceID, as the core ITS ignores dev. */ + info->scratchpad[0].ul = dev_id; + + /* + * @domain->msi_domain_info->hwsize contains the size of the device + * domain, but vector allocation happens one by one. + */ + msi_info = msi_get_domain_info(domain); + if (msi_info->hwsize > nvec) + nvec = msi_info->hwsize; + + /* Allocate at least 32 MSIs, and always as a power of 2 */ + nvec = max_t(int, 32, roundup_pow_of_two(nvec)); + + msi_info = msi_get_domain_info(domain->parent); + return msi_info->ops->msi_prepare(domain->parent, + dev, nvec, info); +} + +static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain, + struct irq_domain *real_parent, struct msi_domain_info *info) +{ + if (!msi_lib_init_dev_msi_info(dev, domain, real_parent, info)) + return false; + + switch(info->bus_token) { + case DOMAIN_BUS_PCI_DEVICE_MSI: + case DOMAIN_BUS_PCI_DEVICE_MSIX: + /* + * FIXME: This probably should be done after a (not yet + * existing) post domain creation callback once to make + * support for dynamic post-enable MSI-X allocations + * work without having to reevaluate the domain size + * over and over. It is known already at allocation + * time via info->hwsize. + * + * That should work perfectly fine for MSI/MSI-X but needs + * some thoughts for purely software managed MSI domains + * where the index space is only limited artificially via + * %MSI_MAX_INDEX. + */ + info->ops->msi_prepare = its_pci_msi_prepare; + break; + case DOMAIN_BUS_DEVICE_MSI: + case DOMAIN_BUS_WIRED_TO_MSI: + /* + * FIXME: See the above PCI prepare comment. The domain + * size is also known at domain creation time. + */ + info->ops->msi_prepare = its_pmsi_prepare; + break; + default: + /* Confused. How did the lib return true? */ + WARN_ON_ONCE(1); + return false; + } + + return true; +} + +const struct msi_parent_ops gic_v3_its_msi_parent_ops = { + .supported_flags = ITS_MSI_FLAGS_SUPPORTED, + .required_flags = ITS_MSI_FLAGS_REQUIRED, + .bus_select_token = DOMAIN_BUS_NEXUS, + .bus_select_mask = MATCH_PCI_MSI | MATCH_PLATFORM_MSI, + .prefix = "ITS-", + .init_dev_msi_info = its_init_dev_msi_info, +}; diff --git a/drivers/irqchip/irq-gic-v3-its-pci-msi.c b/drivers/irqchip/irq-gic-v3-its-pci-msi.c deleted file mode 100644 index 93f77a8196da..000000000000 --- a/drivers/irqchip/irq-gic-v3-its-pci-msi.c +++ /dev/null @@ -1,202 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2013-2015 ARM Limited, All Rights Reserved. - * Author: Marc Zyngier <marc.zyngier@arm.com> - */ - -#include <linux/acpi_iort.h> -#include <linux/pci.h> -#include <linux/msi.h> -#include <linux/of.h> -#include <linux/of_irq.h> -#include <linux/of_pci.h> - -static void its_mask_msi_irq(struct irq_data *d) -{ - pci_msi_mask_irq(d); - irq_chip_mask_parent(d); -} - -static void its_unmask_msi_irq(struct irq_data *d) -{ - pci_msi_unmask_irq(d); - irq_chip_unmask_parent(d); -} - -static struct irq_chip its_msi_irq_chip = { - .name = "ITS-MSI", - .irq_unmask = its_unmask_msi_irq, - .irq_mask = its_mask_msi_irq, - .irq_eoi = irq_chip_eoi_parent, -}; - -static int its_pci_msi_vec_count(struct pci_dev *pdev, void *data) -{ - int msi, msix, *count = data; - - msi = max(pci_msi_vec_count(pdev), 0); - msix = max(pci_msix_vec_count(pdev), 0); - *count += max(msi, msix); - - return 0; -} - -static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data) -{ - struct pci_dev **alias_dev = data; - - *alias_dev = pdev; - - return 0; -} - -static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev, - int nvec, msi_alloc_info_t *info) -{ - struct pci_dev *pdev, *alias_dev; - struct msi_domain_info *msi_info; - int alias_count = 0, minnvec = 1; - - if (!dev_is_pci(dev)) - return -EINVAL; - - msi_info = msi_get_domain_info(domain->parent); - - pdev = to_pci_dev(dev); - /* - * If pdev is downstream of any aliasing bridges, take an upper - * bound of how many other vectors could map to the same DevID. - * Also tell the ITS that the signalling will come from a proxy - * device, and that special allocation rules apply. - */ - pci_for_each_dma_alias(pdev, its_get_pci_alias, &alias_dev); - if (alias_dev != pdev) { - if (alias_dev->subordinate) - pci_walk_bus(alias_dev->subordinate, - its_pci_msi_vec_count, &alias_count); - info->flags |= MSI_ALLOC_FLAGS_PROXY_DEVICE; - } - - /* ITS specific DeviceID, as the core ITS ignores dev. */ - info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain, pdev); - - /* - * Always allocate a power of 2, and special case device 0 for - * broken systems where the DevID is not wired (and all devices - * appear as DevID 0). For that reason, we generously allocate a - * minimum of 32 MSIs for DevID 0. If you want more because all - * your devices are aliasing to DevID 0, consider fixing your HW. - */ - nvec = max(nvec, alias_count); - if (!info->scratchpad[0].ul) - minnvec = 32; - nvec = max_t(int, minnvec, roundup_pow_of_two(nvec)); - return msi_info->ops->msi_prepare(domain->parent, dev, nvec, info); -} - -static struct msi_domain_ops its_pci_msi_ops = { - .msi_prepare = its_pci_msi_prepare, -}; - -static struct msi_domain_info its_pci_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX), - .ops = &its_pci_msi_ops, - .chip = &its_msi_irq_chip, -}; - -static struct of_device_id its_device_id[] = { - { .compatible = "arm,gic-v3-its", }, - {}, -}; - -static int __init its_pci_msi_init_one(struct fwnode_handle *handle, - const char *name) -{ - struct irq_domain *parent; - - parent = irq_find_matching_fwnode(handle, DOMAIN_BUS_NEXUS); - if (!parent || !msi_get_domain_info(parent)) { - pr_err("%s: Unable to locate ITS domain\n", name); - return -ENXIO; - } - - if (!pci_msi_create_irq_domain(handle, &its_pci_msi_domain_info, - parent)) { - pr_err("%s: Unable to create PCI domain\n", name); - return -ENOMEM; - } - - return 0; -} - -static int __init its_pci_of_msi_init(void) -{ - struct device_node *np; - - for (np = of_find_matching_node(NULL, its_device_id); np; - np = of_find_matching_node(np, its_device_id)) { - if (!of_device_is_available(np)) - continue; - if (!of_property_read_bool(np, "msi-controller")) - continue; - - if (its_pci_msi_init_one(of_node_to_fwnode(np), np->full_name)) - continue; - - pr_info("PCI/MSI: %pOF domain created\n", np); - } - - return 0; -} - -#ifdef CONFIG_ACPI - -static int __init -its_pci_msi_parse_madt(union acpi_subtable_headers *header, - const unsigned long end) -{ - struct acpi_madt_generic_translator *its_entry; - struct fwnode_handle *dom_handle; - const char *node_name; - int err = -ENXIO; - - its_entry = (struct acpi_madt_generic_translator *)header; - node_name = kasprintf(GFP_KERNEL, "ITS@0x%lx", - (long)its_entry->base_address); - dom_handle = iort_find_domain_token(its_entry->translation_id); - if (!dom_handle) { - pr_err("%s: Unable to locate ITS domain handle\n", node_name); - goto out; - } - - err = its_pci_msi_init_one(dom_handle, node_name); - if (!err) - pr_info("PCI/MSI: %s domain created\n", node_name); - -out: - kfree(node_name); - return err; -} - -static int __init its_pci_acpi_msi_init(void) -{ - acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR, - its_pci_msi_parse_madt, 0); - return 0; -} -#else -static int __init its_pci_acpi_msi_init(void) -{ - return 0; -} -#endif - -static int __init its_pci_msi_init(void) -{ - its_pci_of_msi_init(); - its_pci_acpi_msi_init(); - - return 0; -} -early_initcall(its_pci_msi_init); diff --git a/drivers/irqchip/irq-gic-v3-its-platform-msi.c b/drivers/irqchip/irq-gic-v3-its-platform-msi.c deleted file mode 100644 index daa6d5053bc3..000000000000 --- a/drivers/irqchip/irq-gic-v3-its-platform-msi.c +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2013-2015 ARM Limited, All Rights Reserved. - * Author: Marc Zyngier <marc.zyngier@arm.com> - */ - -#include <linux/acpi_iort.h> -#include <linux/device.h> -#include <linux/msi.h> -#include <linux/of.h> -#include <linux/of_irq.h> - -static struct irq_chip its_pmsi_irq_chip = { - .name = "ITS-pMSI", -}; - -static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev, - u32 *dev_id) -{ - int ret, index = 0; - - /* Suck the DeviceID out of the msi-parent property */ - do { - struct of_phandle_args args; - - ret = of_parse_phandle_with_args(dev->of_node, - "msi-parent", "#msi-cells", - index, &args); - if (args.np == irq_domain_get_of_node(domain)) { - if (WARN_ON(args.args_count != 1)) - return -EINVAL; - *dev_id = args.args[0]; - break; - } - index++; - } while (!ret); - - return ret; -} - -int __weak iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) -{ - return -1; -} - -static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev, - int nvec, msi_alloc_info_t *info) -{ - struct msi_domain_info *msi_info; - u32 dev_id; - int ret; - - msi_info = msi_get_domain_info(domain->parent); - - if (dev->of_node) - ret = of_pmsi_get_dev_id(domain, dev, &dev_id); - else - ret = iort_pmsi_get_dev_id(dev, &dev_id); - if (ret) - return ret; - - /* ITS specific DeviceID, as the core ITS ignores dev. */ - info->scratchpad[0].ul = dev_id; - - /* Allocate at least 32 MSIs, and always as a power of 2 */ - nvec = max_t(int, 32, roundup_pow_of_two(nvec)); - return msi_info->ops->msi_prepare(domain->parent, - dev, nvec, info); -} - -static struct msi_domain_ops its_pmsi_ops = { - .msi_prepare = its_pmsi_prepare, -}; - -static struct msi_domain_info its_pmsi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS), - .ops = &its_pmsi_ops, - .chip = &its_pmsi_irq_chip, -}; - -static const struct of_device_id its_device_id[] = { - { .compatible = "arm,gic-v3-its", }, - {}, -}; - -static int __init its_pmsi_init_one(struct fwnode_handle *fwnode, - const char *name) -{ - struct irq_domain *parent; - - parent = irq_find_matching_fwnode(fwnode, DOMAIN_BUS_NEXUS); - if (!parent || !msi_get_domain_info(parent)) { - pr_err("%s: unable to locate ITS domain\n", name); - return -ENXIO; - } - - if (!platform_msi_create_irq_domain(fwnode, &its_pmsi_domain_info, - parent)) { - pr_err("%s: unable to create platform domain\n", name); - return -ENXIO; - } - - pr_info("Platform MSI: %s domain created\n", name); - return 0; -} - -#ifdef CONFIG_ACPI -static int __init -its_pmsi_parse_madt(union acpi_subtable_headers *header, - const unsigned long end) -{ - struct acpi_madt_generic_translator *its_entry; - struct fwnode_handle *domain_handle; - const char *node_name; - int err = -ENXIO; - - its_entry = (struct acpi_madt_generic_translator *)header; - node_name = kasprintf(GFP_KERNEL, "ITS@0x%lx", - (long)its_entry->base_address); - domain_handle = iort_find_domain_token(its_entry->translation_id); - if (!domain_handle) { - pr_err("%s: Unable to locate ITS domain handle\n", node_name); - goto out; - } - - err = its_pmsi_init_one(domain_handle, node_name); - -out: - kfree(node_name); - return err; -} - -static void __init its_pmsi_acpi_init(void) -{ - acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR, - its_pmsi_parse_madt, 0); -} -#else -static inline void its_pmsi_acpi_init(void) { } -#endif - -static void __init its_pmsi_of_init(void) -{ - struct device_node *np; - - for (np = of_find_matching_node(NULL, its_device_id); np; - np = of_find_matching_node(np, its_device_id)) { - if (!of_device_is_available(np)) - continue; - if (!of_property_read_bool(np, "msi-controller")) - continue; - - its_pmsi_init_one(of_node_to_fwnode(np), np->full_name); - } -} - -static int __init its_pmsi_init(void) -{ - its_pmsi_of_init(); - its_pmsi_acpi_init(); - return 0; -} -early_initcall(its_pmsi_init); diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 42e63272154e..9b34596b3542 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -38,6 +38,7 @@ #include <asm/exception.h> #include "irq-gic-common.h" +#include "irq-msi-lib.h" #define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1ULL << 0) #define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1) @@ -1317,7 +1318,6 @@ static void its_send_vmovp(struct its_vpe *vpe) { struct its_cmd_desc desc = {}; struct its_node *its; - unsigned long flags; int col_id = vpe->col_idx; desc.its_vmovp_cmd.vpe = vpe; @@ -1330,6 +1330,12 @@ static void its_send_vmovp(struct its_vpe *vpe) } /* + * Protect against concurrent updates of the mapping state on + * individual VMs. + */ + guard(raw_spinlock_irqsave)(&vpe->its_vm->vmapp_lock); + + /* * Yet another marvel of the architecture. If using the * its_list "feature", we need to make sure that all ITSs * receive all VMOVP commands in the same order. The only way @@ -1337,8 +1343,7 @@ static void its_send_vmovp(struct its_vpe *vpe) * * Wall <-- Head. */ - raw_spin_lock_irqsave(&vmovp_lock, flags); - + guard(raw_spinlock)(&vmovp_lock); desc.its_vmovp_cmd.seq_num = vmovp_seq_num++; desc.its_vmovp_cmd.its_list = get_its_list(vpe->its_vm); @@ -1353,8 +1358,6 @@ static void its_send_vmovp(struct its_vpe *vpe) desc.its_vmovp_cmd.col = &its->collections[col_id]; its_send_single_vcommand(its, its_build_vmovp_cmd, &desc); } - - raw_spin_unlock_irqrestore(&vmovp_lock, flags); } static void its_send_vinvall(struct its_node *its, struct its_vpe *vpe) @@ -1791,12 +1794,10 @@ static bool gic_requires_eager_mapping(void) static void its_map_vm(struct its_node *its, struct its_vm *vm) { - unsigned long flags; - if (gic_requires_eager_mapping()) return; - raw_spin_lock_irqsave(&vmovp_lock, flags); + guard(raw_spinlock_irqsave)(&vm->vmapp_lock); /* * If the VM wasn't mapped yet, iterate over the vpes and get @@ -1809,37 +1810,31 @@ static void its_map_vm(struct its_node *its, struct its_vm *vm) for (i = 0; i < vm->nr_vpes; i++) { struct its_vpe *vpe = vm->vpes[i]; - struct irq_data *d = irq_get_irq_data(vpe->irq); - /* Map the VPE to the first possible CPU */ - vpe->col_idx = cpumask_first(cpu_online_mask); - its_send_vmapp(its, vpe, true); + scoped_guard(raw_spinlock, &vpe->vpe_lock) + its_send_vmapp(its, vpe, true); + its_send_vinvall(its, vpe); - irq_data_update_effective_affinity(d, cpumask_of(vpe->col_idx)); } } - - raw_spin_unlock_irqrestore(&vmovp_lock, flags); } static void its_unmap_vm(struct its_node *its, struct its_vm *vm) { - unsigned long flags; - /* Not using the ITS list? Everything is always mapped. */ if (gic_requires_eager_mapping()) return; - raw_spin_lock_irqsave(&vmovp_lock, flags); + guard(raw_spinlock_irqsave)(&vm->vmapp_lock); if (!--vm->vlpi_count[its->list_nr]) { int i; - for (i = 0; i < vm->nr_vpes; i++) + for (i = 0; i < vm->nr_vpes; i++) { + guard(raw_spinlock)(&vm->vpes[i]->vpe_lock); its_send_vmapp(its, vm->vpes[i], false); + } } - - raw_spin_unlock_irqrestore(&vmovp_lock, flags); } static int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info) @@ -3688,6 +3683,7 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq, } static const struct irq_domain_ops its_domain_ops = { + .select = msi_lib_irq_domain_select, .alloc = its_irq_domain_alloc, .free = its_irq_domain_free, .activate = its_irq_domain_activate, @@ -3926,6 +3922,8 @@ static void its_vpe_invall(struct its_vpe *vpe) { struct its_node *its; + guard(raw_spinlock_irqsave)(&vpe->its_vm->vmapp_lock); + list_for_each_entry(its, &its_nodes, entry) { if (!is_v4(its)) continue; @@ -4531,6 +4529,7 @@ static int its_vpe_irq_domain_alloc(struct irq_domain *domain, unsigned int virq vm->db_lpi_base = base; vm->nr_db_lpis = nr_ids; vm->vprop_page = vprop_page; + raw_spin_lock_init(&vm->vmapp_lock); if (gic_rdists->has_rvpeid) irqchip = &its_vpe_4_1_irq_chip; @@ -4562,6 +4561,10 @@ static int its_vpe_irq_domain_activate(struct irq_domain *domain, struct its_vpe *vpe = irq_data_get_irq_chip_data(d); struct its_node *its; + /* Map the VPE to the first possible CPU */ + vpe->col_idx = cpumask_first(cpu_online_mask); + irq_data_update_effective_affinity(d, cpumask_of(vpe->col_idx)); + /* * If we use the list map, we issue VMAPP on demand... Unless * we're on a GICv4.1 and we eagerly map the VPE on all ITSs @@ -4570,9 +4573,6 @@ static int its_vpe_irq_domain_activate(struct irq_domain *domain, if (!gic_requires_eager_mapping()) return 0; - /* Map the VPE to the first possible CPU */ - vpe->col_idx = cpumask_first(cpu_online_mask); - list_for_each_entry(its, &its_nodes, entry) { if (!is_v4(its)) continue; @@ -4581,8 +4581,6 @@ static int its_vpe_irq_domain_activate(struct irq_domain *domain, its_send_vinvall(its, vpe); } - irq_data_update_effective_affinity(d, cpumask_of(vpe->col_idx)); - return 0; } @@ -4993,6 +4991,9 @@ static int its_init_domain(struct its_node *its) irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS); + inner_domain->msi_parent_ops = &gic_v3_its_msi_parent_ops; + inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; + return 0; } @@ -5580,6 +5581,10 @@ static int __init gic_acpi_parse_madt_its(union acpi_subtable_headers *header, goto node_err; } + if (acpi_get_madt_revision() >= 7 && + (its_entry->flags & ACPI_MADT_ITS_NON_COHERENT)) + its->flags |= ITS_FLAGS_FORCE_NON_SHAREABLE; + err = its_probe_one(its); if (!err) return 0; diff --git a/drivers/irqchip/irq-gic-v3-mbi.c b/drivers/irqchip/irq-gic-v3-mbi.c index dbb8b1efda44..3fe870f8ee17 100644 --- a/drivers/irqchip/irq-gic-v3-mbi.c +++ b/drivers/irqchip/irq-gic-v3-mbi.c @@ -18,6 +18,8 @@ #include <linux/irqchip/arm-gic-v3.h> +#include "irq-msi-lib.h" + struct mbi_range { u32 spi_start; u32 nr_spis; @@ -138,6 +140,7 @@ static void mbi_irq_domain_free(struct irq_domain *domain, } static const struct irq_domain_ops mbi_domain_ops = { + .select = msi_lib_irq_domain_select, .alloc = mbi_irq_domain_alloc, .free = mbi_irq_domain_free, }; @@ -151,54 +154,6 @@ static void mbi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg); } -#ifdef CONFIG_PCI_MSI -/* PCI-specific irqchip */ -static void mbi_mask_msi_irq(struct irq_data *d) -{ - pci_msi_mask_irq(d); - irq_chip_mask_parent(d); -} - -static void mbi_unmask_msi_irq(struct irq_data *d) -{ - pci_msi_unmask_irq(d); - irq_chip_unmask_parent(d); -} - -static struct irq_chip mbi_msi_irq_chip = { - .name = "MSI", - .irq_mask = mbi_mask_msi_irq, - .irq_unmask = mbi_unmask_msi_irq, - .irq_eoi = irq_chip_eoi_parent, - .irq_compose_msi_msg = mbi_compose_msi_msg, -}; - -static struct msi_domain_info mbi_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), - .chip = &mbi_msi_irq_chip, -}; - -static int mbi_allocate_pci_domain(struct irq_domain *nexus_domain, - struct irq_domain **pci_domain) -{ - *pci_domain = pci_msi_create_irq_domain(nexus_domain->parent->fwnode, - &mbi_msi_domain_info, - nexus_domain); - if (!*pci_domain) - return -ENOMEM; - - return 0; -} -#else -static int mbi_allocate_pci_domain(struct irq_domain *nexus_domain, - struct irq_domain **pci_domain) -{ - *pci_domain = NULL; - return 0; -} -#endif - static void mbi_compose_mbi_msg(struct irq_data *data, struct msi_msg *msg) { mbi_compose_msi_msg(data, msg); @@ -210,28 +165,51 @@ static void mbi_compose_mbi_msg(struct irq_data *data, struct msi_msg *msg) iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), &msg[1]); } -/* Platform-MSI specific irqchip */ -static struct irq_chip mbi_pmsi_irq_chip = { - .name = "pMSI", - .irq_set_type = irq_chip_set_type_parent, - .irq_compose_msi_msg = mbi_compose_mbi_msg, - .flags = IRQCHIP_SUPPORTS_LEVEL_MSI, -}; - -static struct msi_domain_ops mbi_pmsi_ops = { -}; +static bool mbi_init_dev_msi_info(struct device *dev, struct irq_domain *domain, + struct irq_domain *real_parent, struct msi_domain_info *info) +{ + if (!msi_lib_init_dev_msi_info(dev, domain, real_parent, info)) + return false; + + switch (info->bus_token) { + case DOMAIN_BUS_PCI_DEVICE_MSI: + case DOMAIN_BUS_PCI_DEVICE_MSIX: + info->chip->irq_compose_msi_msg = mbi_compose_msi_msg; + return true; + + case DOMAIN_BUS_DEVICE_MSI: + info->chip->irq_compose_msi_msg = mbi_compose_mbi_msg; + info->chip->irq_set_type = irq_chip_set_type_parent; + info->chip->flags |= IRQCHIP_SUPPORTS_LEVEL_MSI; + info->flags |= MSI_FLAG_LEVEL_CAPABLE; + return true; + + default: + WARN_ON_ONCE(1); + return false; + } +} -static struct msi_domain_info mbi_pmsi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_LEVEL_CAPABLE), - .ops = &mbi_pmsi_ops, - .chip = &mbi_pmsi_irq_chip, +#define MBI_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS | \ + MSI_FLAG_PCI_MSI_MASK_PARENT) + +#define MBI_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \ + MSI_FLAG_PCI_MSIX | \ + MSI_FLAG_MULTI_PCI_MSI) + +static const struct msi_parent_ops gic_v3_mbi_msi_parent_ops = { + .supported_flags = MBI_MSI_FLAGS_SUPPORTED, + .required_flags = MBI_MSI_FLAGS_REQUIRED, + .bus_select_token = DOMAIN_BUS_NEXUS, + .bus_select_mask = MATCH_PCI_MSI | MATCH_PLATFORM_MSI, + .prefix = "MBI-", + .init_dev_msi_info = mbi_init_dev_msi_info, }; -static int mbi_allocate_domains(struct irq_domain *parent) +static int mbi_allocate_domain(struct irq_domain *parent) { - struct irq_domain *nexus_domain, *pci_domain, *plat_domain; - int err; + struct irq_domain *nexus_domain; nexus_domain = irq_domain_create_hierarchy(parent, 0, 0, parent->fwnode, &mbi_domain_ops, NULL); @@ -239,22 +217,8 @@ static int mbi_allocate_domains(struct irq_domain *parent) return -ENOMEM; irq_domain_update_bus_token(nexus_domain, DOMAIN_BUS_NEXUS); - - err = mbi_allocate_pci_domain(nexus_domain, &pci_domain); - - plat_domain = platform_msi_create_irq_domain(parent->fwnode, - &mbi_pmsi_domain_info, - nexus_domain); - - if (err || !plat_domain) { - if (plat_domain) - irq_domain_remove(plat_domain); - if (pci_domain) - irq_domain_remove(pci_domain); - irq_domain_remove(nexus_domain); - return -ENOMEM; - } - + nexus_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; + nexus_domain->msi_parent_ops = &gic_v3_mbi_msi_parent_ops; return 0; } @@ -317,7 +281,7 @@ int __init mbi_init(struct fwnode_handle *fwnode, struct irq_domain *parent) pr_info("Using MBI frame %pa\n", &mbi_phys_base); - ret = mbi_allocate_domains(parent); + ret = mbi_allocate_domain(parent); if (ret) goto err_free_mbi; diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 6393f3d780e9..c19083bfb943 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -2203,11 +2203,10 @@ out_put_node: of_node_put(parts_node); } -static void __init gic_of_setup_kvm_info(struct device_node *node) +static void __init gic_of_setup_kvm_info(struct device_node *node, u32 nr_redist_regions) { int ret; struct resource r; - u32 gicv_idx; gic_v3_kvm_info.type = GIC_V3; @@ -2215,12 +2214,8 @@ static void __init gic_of_setup_kvm_info(struct device_node *node) if (!gic_v3_kvm_info.maint_irq) return; - if (of_property_read_u32(node, "#redistributor-regions", - &gicv_idx)) - gicv_idx = 1; - - gicv_idx += 3; /* Also skip GICD, GICC, GICH */ - ret = of_address_to_resource(node, gicv_idx, &r); + /* Also skip GICD, GICC, GICH */ + ret = of_address_to_resource(node, nr_redist_regions + 3, &r); if (!ret) gic_v3_kvm_info.vcpu = r; @@ -2310,7 +2305,7 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare gic_populate_ppi_partitions(node); if (static_branch_likely(&supports_deactivate_key)) - gic_of_setup_kvm_info(node); + gic_of_setup_kvm_info(node, nr_redist_regions); return 0; out_unmap_rdist: @@ -2362,6 +2357,11 @@ gic_acpi_parse_madt_redist(union acpi_subtable_headers *header, pr_err("Couldn't map GICR region @%llx\n", redist->base_address); return -ENOMEM; } + + if (acpi_get_madt_revision() >= 7 && + (redist->flags & ACPI_MADT_GICR_NON_COHERENT)) + gic_data.rdists.flags |= RDIST_FLAGS_FORCE_NON_SHAREABLE; + gic_request_region(redist->base_address, redist->length, "GICR"); gic_acpi_register_redist(redist->base_address, redist_base); @@ -2402,6 +2402,10 @@ gic_acpi_parse_madt_gicc(union acpi_subtable_headers *header, return -ENOMEM; gic_request_region(gicc->gicr_base_address, size, "GICR"); + if (acpi_get_madt_revision() >= 7 && + (gicc->flags & ACPI_MADT_GICC_NON_COHERENT)) + gic_data.rdists.flags |= RDIST_FLAGS_FORCE_NON_SHAREABLE; + gic_acpi_register_redist(gicc->gicr_base_address, redist_base); return 0; } diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c index 20cf7a9e9ece..75a0e980ff35 100644 --- a/drivers/irqchip/irq-imx-irqsteer.c +++ b/drivers/irqchip/irq-imx-irqsteer.c @@ -36,6 +36,7 @@ struct irqsteer_data { int channel; struct irq_domain *domain; u32 *saved_reg; + struct device *dev; }; static int imx_irqsteer_get_reg_index(struct irqsteer_data *data, @@ -72,10 +73,26 @@ static void imx_irqsteer_irq_mask(struct irq_data *d) raw_spin_unlock_irqrestore(&data->lock, flags); } +static void imx_irqsteer_irq_bus_lock(struct irq_data *d) +{ + struct irqsteer_data *data = d->chip_data; + + pm_runtime_get_sync(data->dev); +} + +static void imx_irqsteer_irq_bus_sync_unlock(struct irq_data *d) +{ + struct irqsteer_data *data = d->chip_data; + + pm_runtime_put_autosuspend(data->dev); +} + static const struct irq_chip imx_irqsteer_irq_chip = { - .name = "irqsteer", - .irq_mask = imx_irqsteer_irq_mask, - .irq_unmask = imx_irqsteer_irq_unmask, + .name = "irqsteer", + .irq_mask = imx_irqsteer_irq_mask, + .irq_unmask = imx_irqsteer_irq_unmask, + .irq_bus_lock = imx_irqsteer_irq_bus_lock, + .irq_bus_sync_unlock = imx_irqsteer_irq_bus_sync_unlock, }; static int imx_irqsteer_irq_map(struct irq_domain *h, unsigned int irq, @@ -150,6 +167,7 @@ static int imx_irqsteer_probe(struct platform_device *pdev) if (!data) return -ENOMEM; + data->dev = &pdev->dev; data->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(data->regs)) { dev_err(&pdev->dev, "failed to initialize reg\n"); diff --git a/drivers/irqchip/irq-imx-mu-msi.c b/drivers/irqchip/irq-imx-mu-msi.c index 90d41c1407ac..4342a21de1eb 100644 --- a/drivers/irqchip/irq-imx-mu-msi.c +++ b/drivers/irqchip/irq-imx-mu-msi.c @@ -24,6 +24,8 @@ #include <linux/pm_domain.h> #include <linux/spinlock.h> +#include "irq-msi-lib.h" + #define IMX_MU_CHANS 4 enum imx_mu_xcr { @@ -114,20 +116,6 @@ static void imx_mu_msi_parent_ack_irq(struct irq_data *data) imx_mu_read(msi_data, msi_data->cfg->xRR + data->hwirq * 4); } -static struct irq_chip imx_mu_msi_irq_chip = { - .name = "MU-MSI", - .irq_ack = irq_chip_ack_parent, -}; - -static struct msi_domain_ops imx_mu_msi_irq_ops = { -}; - -static struct msi_domain_info imx_mu_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS), - .ops = &imx_mu_msi_irq_ops, - .chip = &imx_mu_msi_irq_chip, -}; - static void imx_mu_msi_parent_compose_msg(struct irq_data *data, struct msi_msg *msg) { @@ -195,6 +183,7 @@ static void imx_mu_msi_domain_irq_free(struct irq_domain *domain, } static const struct irq_domain_ops imx_mu_msi_domain_ops = { + .select = msi_lib_irq_domain_select, .alloc = imx_mu_msi_domain_irq_alloc, .free = imx_mu_msi_domain_irq_free, }; @@ -216,35 +205,38 @@ static void imx_mu_msi_irq_handler(struct irq_desc *desc) chained_irq_exit(chip, desc); } +#define IMX_MU_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS | \ + MSI_FLAG_PARENT_PM_DEV) + +#define IMX_MU_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK) + +static const struct msi_parent_ops imx_mu_msi_parent_ops = { + .supported_flags = IMX_MU_MSI_FLAGS_SUPPORTED, + .required_flags = IMX_MU_MSI_FLAGS_REQUIRED, + .bus_select_token = DOMAIN_BUS_NEXUS, + .bus_select_mask = MATCH_PLATFORM_MSI, + .prefix = "MU-MSI-", + .init_dev_msi_info = msi_lib_init_dev_msi_info, +}; + static int imx_mu_msi_domains_init(struct imx_mu_msi *msi_data, struct device *dev) { struct fwnode_handle *fwnodes = dev_fwnode(dev); struct irq_domain *parent; /* Initialize MSI domain parent */ - parent = irq_domain_create_linear(fwnodes, - IMX_MU_CHANS, - &imx_mu_msi_domain_ops, - msi_data); + parent = irq_domain_create_linear(fwnodes, IMX_MU_CHANS, + &imx_mu_msi_domain_ops, msi_data); if (!parent) { dev_err(dev, "failed to create IRQ domain\n"); return -ENOMEM; } irq_domain_update_bus_token(parent, DOMAIN_BUS_NEXUS); - - msi_data->msi_domain = platform_msi_create_irq_domain(fwnodes, - &imx_mu_msi_domain_info, - parent); - - if (!msi_data->msi_domain) { - dev_err(dev, "failed to create MSI domain\n"); - irq_domain_remove(parent); - return -ENOMEM; - } - - irq_domain_set_pm_device(msi_data->msi_domain, dev); - + parent->dev = parent->pm_dev = dev; + parent->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; + parent->msi_parent_ops = &imx_mu_msi_parent_ops; return 0; } diff --git a/drivers/irqchip/irq-lan966x-oic.c b/drivers/irqchip/irq-lan966x-oic.c new file mode 100644 index 000000000000..41ac880e3b87 --- /dev/null +++ b/drivers/irqchip/irq-lan966x-oic.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Microchip LAN966x outbound interrupt controller + * + * Copyright (c) 2024 Technology Inc. and its subsidiaries. + * + * Authors: + * Horatiu Vultur <horatiu.vultur@microchip.com> + * Clément Léger <clement.leger@bootlin.com> + * Herve Codina <herve.codina@bootlin.com> + */ + +#include <linux/interrupt.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqchip.h> +#include <linux/irq.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +struct lan966x_oic_chip_regs { + int reg_off_ena_set; + int reg_off_ena_clr; + int reg_off_sticky; + int reg_off_ident; + int reg_off_map; +}; + +struct lan966x_oic_data { + void __iomem *regs; + int irq; +}; + +#define LAN966X_OIC_NR_IRQ 86 + +/* Interrupt sticky status */ +#define LAN966X_OIC_INTR_STICKY 0x30 +#define LAN966X_OIC_INTR_STICKY1 0x34 +#define LAN966X_OIC_INTR_STICKY2 0x38 + +/* Interrupt enable */ +#define LAN966X_OIC_INTR_ENA 0x48 +#define LAN966X_OIC_INTR_ENA1 0x4c +#define LAN966X_OIC_INTR_ENA2 0x50 + +/* Atomic clear of interrupt enable */ +#define LAN966X_OIC_INTR_ENA_CLR 0x54 +#define LAN966X_OIC_INTR_ENA_CLR1 0x58 +#define LAN966X_OIC_INTR_ENA_CLR2 0x5c + +/* Atomic set of interrupt */ +#define LAN966X_OIC_INTR_ENA_SET 0x60 +#define LAN966X_OIC_INTR_ENA_SET1 0x64 +#define LAN966X_OIC_INTR_ENA_SET2 0x68 + +/* Mapping of source to destination interrupts (_n = 0..8) */ +#define LAN966X_OIC_DST_INTR_MAP(_n) (0x78 + (_n) * 4) +#define LAN966X_OIC_DST_INTR_MAP1(_n) (0x9c + (_n) * 4) +#define LAN966X_OIC_DST_INTR_MAP2(_n) (0xc0 + (_n) * 4) + +/* Currently active interrupt sources per destination (_n = 0..8) */ +#define LAN966X_OIC_DST_INTR_IDENT(_n) (0xe4 + (_n) * 4) +#define LAN966X_OIC_DST_INTR_IDENT1(_n) (0x108 + (_n) * 4) +#define LAN966X_OIC_DST_INTR_IDENT2(_n) (0x12c + (_n) * 4) + +static unsigned int lan966x_oic_irq_startup(struct irq_data *data) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); + struct irq_chip_type *ct = irq_data_get_chip_type(data); + struct lan966x_oic_chip_regs *chip_regs = gc->private; + u32 map; + + irq_gc_lock(gc); + + /* Map the source interrupt to the destination */ + map = irq_reg_readl(gc, chip_regs->reg_off_map); + map |= data->mask; + irq_reg_writel(gc, map, chip_regs->reg_off_map); + + irq_gc_unlock(gc); + + ct->chip.irq_ack(data); + ct->chip.irq_unmask(data); + + return 0; +} + +static void lan966x_oic_irq_shutdown(struct irq_data *data) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); + struct irq_chip_type *ct = irq_data_get_chip_type(data); + struct lan966x_oic_chip_regs *chip_regs = gc->private; + u32 map; + + ct->chip.irq_mask(data); + + irq_gc_lock(gc); + + /* Unmap the interrupt */ + map = irq_reg_readl(gc, chip_regs->reg_off_map); + map &= ~data->mask; + irq_reg_writel(gc, map, chip_regs->reg_off_map); + + irq_gc_unlock(gc); +} + +static int lan966x_oic_irq_set_type(struct irq_data *data, + unsigned int flow_type) +{ + if (flow_type != IRQ_TYPE_LEVEL_HIGH) { + pr_err("lan966x oic doesn't support flow type %d\n", flow_type); + return -EINVAL; + } + + return 0; +} + +static void lan966x_oic_irq_handler_domain(struct irq_domain *d, u32 first_irq) +{ + struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, first_irq); + struct lan966x_oic_chip_regs *chip_regs = gc->private; + unsigned long ident; + unsigned int hwirq; + + ident = irq_reg_readl(gc, chip_regs->reg_off_ident); + if (!ident) + return; + + for_each_set_bit(hwirq, &ident, 32) + generic_handle_domain_irq(d, hwirq + first_irq); +} + +static void lan966x_oic_irq_handler(struct irq_desc *desc) +{ + struct irq_domain *d = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + + chained_irq_enter(chip, desc); + lan966x_oic_irq_handler_domain(d, 0); + lan966x_oic_irq_handler_domain(d, 32); + lan966x_oic_irq_handler_domain(d, 64); + chained_irq_exit(chip, desc); +} + +static struct lan966x_oic_chip_regs lan966x_oic_chip_regs[3] = { + { + .reg_off_ena_set = LAN966X_OIC_INTR_ENA_SET, + .reg_off_ena_clr = LAN966X_OIC_INTR_ENA_CLR, + .reg_off_sticky = LAN966X_OIC_INTR_STICKY, + .reg_off_ident = LAN966X_OIC_DST_INTR_IDENT(0), + .reg_off_map = LAN966X_OIC_DST_INTR_MAP(0), + }, { + .reg_off_ena_set = LAN966X_OIC_INTR_ENA_SET1, + .reg_off_ena_clr = LAN966X_OIC_INTR_ENA_CLR1, + .reg_off_sticky = LAN966X_OIC_INTR_STICKY1, + .reg_off_ident = LAN966X_OIC_DST_INTR_IDENT1(0), + .reg_off_map = LAN966X_OIC_DST_INTR_MAP1(0), + }, { + .reg_off_ena_set = LAN966X_OIC_INTR_ENA_SET2, + .reg_off_ena_clr = LAN966X_OIC_INTR_ENA_CLR2, + .reg_off_sticky = LAN966X_OIC_INTR_STICKY2, + .reg_off_ident = LAN966X_OIC_DST_INTR_IDENT2(0), + .reg_off_map = LAN966X_OIC_DST_INTR_MAP2(0), + } +}; + +static int lan966x_oic_chip_init(struct irq_chip_generic *gc) +{ + struct lan966x_oic_data *lan966x_oic = gc->domain->host_data; + struct lan966x_oic_chip_regs *chip_regs; + + chip_regs = &lan966x_oic_chip_regs[gc->irq_base / 32]; + + gc->reg_base = lan966x_oic->regs; + gc->chip_types[0].regs.enable = chip_regs->reg_off_ena_set; + gc->chip_types[0].regs.disable = chip_regs->reg_off_ena_clr; + gc->chip_types[0].regs.ack = chip_regs->reg_off_sticky; + gc->chip_types[0].chip.irq_startup = lan966x_oic_irq_startup; + gc->chip_types[0].chip.irq_shutdown = lan966x_oic_irq_shutdown; + gc->chip_types[0].chip.irq_set_type = lan966x_oic_irq_set_type; + gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg; + gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg; + gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit; + gc->private = chip_regs; + + /* Disable all interrupts handled by this chip */ + irq_reg_writel(gc, ~0U, chip_regs->reg_off_ena_clr); + + return 0; +} + +static void lan966x_oic_chip_exit(struct irq_chip_generic *gc) +{ + /* Disable and ack all interrupts handled by this chip */ + irq_reg_writel(gc, ~0U, gc->chip_types[0].regs.disable); + irq_reg_writel(gc, ~0U, gc->chip_types[0].regs.ack); +} + +static int lan966x_oic_domain_init(struct irq_domain *d) +{ + struct lan966x_oic_data *lan966x_oic = d->host_data; + + irq_set_chained_handler_and_data(lan966x_oic->irq, lan966x_oic_irq_handler, d); + + return 0; +} + +static void lan966x_oic_domain_exit(struct irq_domain *d) +{ + struct lan966x_oic_data *lan966x_oic = d->host_data; + + irq_set_chained_handler_and_data(lan966x_oic->irq, NULL, NULL); +} + +static int lan966x_oic_probe(struct platform_device *pdev) +{ + struct irq_domain_chip_generic_info dgc_info = { + .name = "lan966x-oic", + .handler = handle_level_irq, + .irqs_per_chip = 32, + .num_ct = 1, + .init = lan966x_oic_chip_init, + .exit = lan966x_oic_chip_exit, + }; + struct irq_domain_info d_info = { + .fwnode = of_node_to_fwnode(pdev->dev.of_node), + .domain_flags = IRQ_DOMAIN_FLAG_DESTROY_GC, + .size = LAN966X_OIC_NR_IRQ, + .hwirq_max = LAN966X_OIC_NR_IRQ, + .ops = &irq_generic_chip_ops, + .dgc_info = &dgc_info, + .init = lan966x_oic_domain_init, + .exit = lan966x_oic_domain_exit, + }; + struct lan966x_oic_data *lan966x_oic; + struct device *dev = &pdev->dev; + struct irq_domain *domain; + + lan966x_oic = devm_kmalloc(dev, sizeof(*lan966x_oic), GFP_KERNEL); + if (!lan966x_oic) + return -ENOMEM; + + lan966x_oic->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(lan966x_oic->regs)) + return dev_err_probe(dev, PTR_ERR(lan966x_oic->regs), + "failed to map resource\n"); + + lan966x_oic->irq = platform_get_irq(pdev, 0); + if (lan966x_oic->irq < 0) + return dev_err_probe(dev, lan966x_oic->irq, "failed to get the IRQ\n"); + + d_info.host_data = lan966x_oic; + domain = devm_irq_domain_instantiate(dev, &d_info); + if (IS_ERR(domain)) + return dev_err_probe(dev, PTR_ERR(domain), + "failed to instantiate the IRQ domain\n"); + return 0; +} + +static const struct of_device_id lan966x_oic_of_match[] = { + { .compatible = "microchip,lan966x-oic" }, + {} /* sentinel */ +}; +MODULE_DEVICE_TABLE(of, lan966x_oic_of_match); + +static struct platform_driver lan966x_oic_driver = { + .probe = lan966x_oic_probe, + .driver = { + .name = "lan966x-oic", + .of_match_table = lan966x_oic_of_match, + }, +}; +module_platform_driver(lan966x_oic_driver); + +MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>"); +MODULE_DESCRIPTION("Microchip LAN966x OIC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c index 58881d313979..093fd42893a7 100644 --- a/drivers/irqchip/irq-mbigen.c +++ b/drivers/irqchip/irq-mbigen.c @@ -135,24 +135,14 @@ static int mbigen_set_type(struct irq_data *data, unsigned int type) return 0; } -static struct irq_chip mbigen_irq_chip = { - .name = "mbigen-v2", - .irq_mask = irq_chip_mask_parent, - .irq_unmask = irq_chip_unmask_parent, - .irq_eoi = mbigen_eoi_irq, - .irq_set_type = mbigen_set_type, - .irq_set_affinity = irq_chip_set_affinity_parent, -}; - -static void mbigen_write_msg(struct msi_desc *desc, struct msi_msg *msg) +static void mbigen_write_msi_msg(struct irq_data *d, struct msi_msg *msg) { - struct irq_data *d = irq_get_irq_data(desc->irq); void __iomem *base = d->chip_data; u32 val; if (!msg->address_lo && !msg->address_hi) return; - + base += get_mbigen_vec_reg(d->hwirq); val = readl_relaxed(base); @@ -165,10 +155,8 @@ static void mbigen_write_msg(struct msi_desc *desc, struct msi_msg *msg) writel_relaxed(val, base); } -static int mbigen_domain_translate(struct irq_domain *d, - struct irq_fwspec *fwspec, - unsigned long *hwirq, - unsigned int *type) +static int mbigen_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec, + unsigned long *hwirq, unsigned int *type) { if (is_of_node(fwspec->fwnode) || is_acpi_device_node(fwspec->fwnode)) { if (fwspec->param_count != 2) @@ -192,51 +180,48 @@ static int mbigen_domain_translate(struct irq_domain *d, return -EINVAL; } -static int mbigen_irq_domain_alloc(struct irq_domain *domain, - unsigned int virq, - unsigned int nr_irqs, - void *args) +static void mbigen_domain_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) { - struct irq_fwspec *fwspec = args; - irq_hw_number_t hwirq; - unsigned int type; - struct mbigen_device *mgn_chip; - int i, err; - - err = mbigen_domain_translate(domain, fwspec, &hwirq, &type); - if (err) - return err; - - err = platform_msi_device_domain_alloc(domain, virq, nr_irqs); - if (err) - return err; + arg->desc = desc; + arg->hwirq = (u32)desc->data.icookie.value; +} - mgn_chip = platform_msi_get_host_data(domain); +static const struct msi_domain_template mbigen_msi_template = { + .chip = { + .name = "mbigen-v2", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = mbigen_eoi_irq, + .irq_set_type = mbigen_set_type, + .irq_write_msi_msg = mbigen_write_msi_msg, + }, - for (i = 0; i < nr_irqs; i++) - irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, - &mbigen_irq_chip, mgn_chip->base); + .ops = { + .set_desc = mbigen_domain_set_desc, + .msi_translate = mbigen_domain_translate, + }, - return 0; -} + .info = { + .bus_token = DOMAIN_BUS_WIRED_TO_MSI, + .flags = MSI_FLAG_USE_DEV_FWNODE, + }, +}; -static void mbigen_irq_domain_free(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs) +static bool mbigen_create_device_domain(struct device *dev, unsigned int size, + struct mbigen_device *mgn_chip) { - platform_msi_device_domain_free(domain, virq, nr_irqs); -} + if (WARN_ON_ONCE(!dev->msi.domain)) + return false; -static const struct irq_domain_ops mbigen_domain_ops = { - .translate = mbigen_domain_translate, - .alloc = mbigen_irq_domain_alloc, - .free = mbigen_irq_domain_free, -}; + return msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN, + &mbigen_msi_template, size, + NULL, mgn_chip->base); +} static int mbigen_of_create_domain(struct platform_device *pdev, struct mbigen_device *mgn_chip) { struct platform_device *child; - struct irq_domain *domain; struct device_node *np; u32 num_pins; int ret = 0; @@ -258,11 +243,7 @@ static int mbigen_of_create_domain(struct platform_device *pdev, break; } - domain = platform_msi_create_device_domain(&child->dev, num_pins, - mbigen_write_msg, - &mbigen_domain_ops, - mgn_chip); - if (!domain) { + if (!mbigen_create_device_domain(&child->dev, num_pins, mgn_chip)) { ret = -ENOMEM; break; } @@ -284,7 +265,6 @@ MODULE_DEVICE_TABLE(acpi, mbigen_acpi_match); static int mbigen_acpi_create_domain(struct platform_device *pdev, struct mbigen_device *mgn_chip) { - struct irq_domain *domain; u32 num_pins = 0; int ret; @@ -315,11 +295,7 @@ static int mbigen_acpi_create_domain(struct platform_device *pdev, if (ret || num_pins == 0) return -EINVAL; - domain = platform_msi_create_device_domain(&pdev->dev, num_pins, - mbigen_write_msg, - &mbigen_domain_ops, - mgn_chip); - if (!domain) + if (!mbigen_create_device_domain(&pdev->dev, num_pins, mgn_chip)) return -ENOMEM; return 0; diff --git a/drivers/irqchip/irq-meson-gpio.c b/drivers/irqchip/irq-meson-gpio.c index 9a1791908598..27e30ce41db3 100644 --- a/drivers/irqchip/irq-meson-gpio.c +++ b/drivers/irqchip/irq-meson-gpio.c @@ -608,5 +608,6 @@ IRQCHIP_MATCH("amlogic,meson-gpio-intc", meson_gpio_irq_of_init) IRQCHIP_PLATFORM_DRIVER_END(meson_gpio_intc) MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_DESCRIPTION("Meson GPIO Interrupt Multiplexer driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:meson-gpio-intc"); diff --git a/drivers/irqchip/irq-msi-lib.c b/drivers/irqchip/irq-msi-lib.c new file mode 100644 index 000000000000..b5b90003311a --- /dev/null +++ b/drivers/irqchip/irq-msi-lib.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2022 Linutronix GmbH +// Copyright (C) 2022 Intel + +#include <linux/export.h> + +#include "irq-msi-lib.h" + +/** + * msi_lib_init_dev_msi_info - Domain info setup for MSI domains + * @dev: The device for which the domain is created for + * @domain: The domain providing this callback + * @real_parent: The real parent domain of the domain to be initialized + * which might be a domain built on top of @domain or + * @domain itself + * @info: The domain info for the domain to be initialize + * + * This function is to be used for all types of MSI domains above the root + * parent domain and any intermediates. The topmost parent domain specific + * functionality is determined via @real_parent. + * + * All intermediate domains between the root and the device domain must + * have either msi_parent_ops.init_dev_msi_info = msi_parent_init_dev_msi_info + * or invoke it down the line. + */ +bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain, + struct irq_domain *real_parent, + struct msi_domain_info *info) +{ + const struct msi_parent_ops *pops = real_parent->msi_parent_ops; + u32 required_flags; + + /* Parent ops available? */ + if (WARN_ON_ONCE(!pops)) + return false; + + /* + * MSI parent domain specific settings. For now there is only the + * root parent domain, e.g. NEXUS, acting as a MSI parent, but it is + * possible to stack MSI parents. See x86 vector -> irq remapping + */ + if (domain->bus_token == pops->bus_select_token) { + if (WARN_ON_ONCE(domain != real_parent)) + return false; + } else { + WARN_ON_ONCE(1); + return false; + } + + required_flags = pops->required_flags; + + /* Is the target domain bus token supported? */ + switch(info->bus_token) { + case DOMAIN_BUS_PCI_DEVICE_MSI: + case DOMAIN_BUS_PCI_DEVICE_MSIX: + if (WARN_ON_ONCE(!IS_ENABLED(CONFIG_PCI_MSI))) + return false; + + break; + case DOMAIN_BUS_DEVICE_MSI: + /* + * Per device MSI should never have any MSI feature bits + * set. It's sole purpose is to create a dumb interrupt + * chip which has a device specific irq_write_msi_msg() + * callback. + */ + if (WARN_ON_ONCE(info->flags)) + return false; + + /* Core managed MSI descriptors */ + info->flags = MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS | MSI_FLAG_FREE_MSI_DESCS; + fallthrough; + case DOMAIN_BUS_WIRED_TO_MSI: + /* Remove PCI specific flags */ + required_flags &= ~MSI_FLAG_PCI_MSI_MASK_PARENT; + break; + default: + /* + * This should never be reached. See + * msi_lib_irq_domain_select() + */ + WARN_ON_ONCE(1); + return false; + } + + /* + * Mask out the domain specific MSI feature flags which are not + * supported by the real parent. + */ + info->flags &= pops->supported_flags; + /* Enforce the required flags */ + info->flags |= required_flags; + + /* Chip updates for all child bus types */ + if (!info->chip->irq_eoi) + info->chip->irq_eoi = irq_chip_eoi_parent; + if (!info->chip->irq_ack) + info->chip->irq_ack = irq_chip_ack_parent; + + /* + * The device MSI domain can never have a set affinity callback. It + * always has to rely on the parent domain to handle affinity + * settings. The device MSI domain just has to write the resulting + * MSI message into the hardware which is the whole purpose of the + * device MSI domain aside of mask/unmask which is provided e.g. by + * PCI/MSI device domains. + */ + info->chip->irq_set_affinity = msi_domain_set_affinity; + return true; +} +EXPORT_SYMBOL_GPL(msi_lib_init_dev_msi_info); + +/** + * msi_lib_irq_domain_select - Shared select function for NEXUS domains + * @d: Pointer to the irq domain on which select is invoked + * @fwspec: Firmware spec describing what is searched + * @bus_token: The bus token for which a matching irq domain is looked up + * + * Returns: %0 if @d is not what is being looked for + * + * %1 if @d is either the domain which is directly searched for or + * if @d is providing the parent MSI domain for the functionality + * requested with @bus_token. + */ +int msi_lib_irq_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec, + enum irq_domain_bus_token bus_token) +{ + const struct msi_parent_ops *ops = d->msi_parent_ops; + u32 busmask = BIT(bus_token); + + if (fwspec->fwnode != d->fwnode || fwspec->param_count != 0) + return 0; + + /* Handle pure domain searches */ + if (bus_token == ops->bus_select_token) + return 1; + + return ops && !!(ops->bus_select_mask & busmask); +} +EXPORT_SYMBOL_GPL(msi_lib_irq_domain_select); diff --git a/drivers/irqchip/irq-msi-lib.h b/drivers/irqchip/irq-msi-lib.h new file mode 100644 index 000000000000..681ceabb7bc7 --- /dev/null +++ b/drivers/irqchip/irq-msi-lib.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2022 Linutronix GmbH +// Copyright (C) 2022 Intel + +#ifndef _DRIVERS_IRQCHIP_IRQ_MSI_LIB_H +#define _DRIVERS_IRQCHIP_IRQ_MSI_LIB_H + +#include <linux/bits.h> +#include <linux/irqdomain.h> +#include <linux/msi.h> + +#ifdef CONFIG_PCI_MSI +#define MATCH_PCI_MSI BIT(DOMAIN_BUS_PCI_MSI) +#else +#define MATCH_PCI_MSI (0) +#endif + +#define MATCH_PLATFORM_MSI BIT(DOMAIN_BUS_PLATFORM_MSI) + +int msi_lib_irq_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec, + enum irq_domain_bus_token bus_token); + +bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain, + struct irq_domain *real_parent, + struct msi_domain_info *info); + +#endif /* _DRIVERS_IRQCHIP_IRQ_MSI_LIB_H */ diff --git a/drivers/irqchip/irq-mvebu-gicp.c b/drivers/irqchip/irq-mvebu-gicp.c index c43a345061d5..2b6183919ea4 100644 --- a/drivers/irqchip/irq-mvebu-gicp.c +++ b/drivers/irqchip/irq-mvebu-gicp.c @@ -17,6 +17,8 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> +#include "irq-msi-lib.h" + #include <dt-bindings/interrupt-controller/arm-gic.h> #define GICP_SETSPI_NSR_OFFSET 0x0 @@ -145,32 +147,32 @@ static void gicp_irq_domain_free(struct irq_domain *domain, } static const struct irq_domain_ops gicp_domain_ops = { + .select = msi_lib_irq_domain_select, .alloc = gicp_irq_domain_alloc, .free = gicp_irq_domain_free, }; -static struct irq_chip gicp_msi_irq_chip = { - .name = "GICP", - .irq_set_type = irq_chip_set_type_parent, - .flags = IRQCHIP_SUPPORTS_LEVEL_MSI, -}; +#define GICP_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS) -static struct msi_domain_ops gicp_msi_ops = { -}; +#define GICP_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \ + MSI_FLAG_LEVEL_CAPABLE) -static struct msi_domain_info gicp_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_LEVEL_CAPABLE), - .ops = &gicp_msi_ops, - .chip = &gicp_msi_irq_chip, +static const struct msi_parent_ops gicp_msi_parent_ops = { + .supported_flags = GICP_MSI_FLAGS_SUPPORTED, + .required_flags = GICP_MSI_FLAGS_REQUIRED, + .bus_select_token = DOMAIN_BUS_GENERIC_MSI, + .bus_select_mask = MATCH_PLATFORM_MSI, + .prefix = "GICP-", + .init_dev_msi_info = msi_lib_init_dev_msi_info, }; static int mvebu_gicp_probe(struct platform_device *pdev) { - struct mvebu_gicp *gicp; - struct irq_domain *inner_domain, *plat_domain, *parent_domain; + struct irq_domain *inner_domain, *parent_domain; struct device_node *node = pdev->dev.of_node; struct device_node *irq_parent_dn; + struct mvebu_gicp *gicp; int ret, i; gicp = devm_kzalloc(&pdev->dev, sizeof(*gicp), GFP_KERNEL); @@ -234,17 +236,9 @@ static int mvebu_gicp_probe(struct platform_device *pdev) if (!inner_domain) return -ENOMEM; - - plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node), - &gicp_msi_domain_info, - inner_domain); - if (!plat_domain) { - irq_domain_remove(inner_domain); - return -ENOMEM; - } - - platform_set_drvdata(pdev, gicp); - + irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_GENERIC_MSI); + inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; + inner_domain->msi_parent_ops = &gicp_msi_parent_ops; return 0; } diff --git a/drivers/irqchip/irq-mvebu-icu.c b/drivers/irqchip/irq-mvebu-icu.c index 3c77acc7ec6a..b337f6c05f18 100644 --- a/drivers/irqchip/irq-mvebu-icu.c +++ b/drivers/irqchip/irq-mvebu-icu.c @@ -20,6 +20,8 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> +#include "irq-msi-lib.h" + #include <dt-bindings/interrupt-controller/mvebu-icu.h> /* ICU registers */ @@ -60,99 +62,13 @@ struct mvebu_icu_msi_data { const struct mvebu_icu_subset_data *subset_data; }; -struct mvebu_icu_irq_data { - struct mvebu_icu *icu; - unsigned int icu_group; - unsigned int type; -}; - static DEFINE_STATIC_KEY_FALSE(legacy_bindings); -static void mvebu_icu_init(struct mvebu_icu *icu, - struct mvebu_icu_msi_data *msi_data, - struct msi_msg *msg) -{ - const struct mvebu_icu_subset_data *subset = msi_data->subset_data; - - if (atomic_cmpxchg(&msi_data->initialized, false, true)) - return; - - /* Set 'SET' ICU SPI message address in AP */ - writel_relaxed(msg[0].address_hi, icu->base + subset->offset_set_ah); - writel_relaxed(msg[0].address_lo, icu->base + subset->offset_set_al); - - if (subset->icu_group != ICU_GRP_NSR) - return; - - /* Set 'CLEAR' ICU SPI message address in AP (level-MSI only) */ - writel_relaxed(msg[1].address_hi, icu->base + subset->offset_clr_ah); - writel_relaxed(msg[1].address_lo, icu->base + subset->offset_clr_al); -} - -static void mvebu_icu_write_msg(struct msi_desc *desc, struct msi_msg *msg) -{ - struct irq_data *d = irq_get_irq_data(desc->irq); - struct mvebu_icu_msi_data *msi_data = platform_msi_get_host_data(d->domain); - struct mvebu_icu_irq_data *icu_irqd = d->chip_data; - struct mvebu_icu *icu = icu_irqd->icu; - unsigned int icu_int; - - if (msg->address_lo || msg->address_hi) { - /* One off initialization per domain */ - mvebu_icu_init(icu, msi_data, msg); - /* Configure the ICU with irq number & type */ - icu_int = msg->data | ICU_INT_ENABLE; - if (icu_irqd->type & IRQ_TYPE_EDGE_RISING) - icu_int |= ICU_IS_EDGE; - icu_int |= icu_irqd->icu_group << ICU_GROUP_SHIFT; - } else { - /* De-configure the ICU */ - icu_int = 0; - } - - writel_relaxed(icu_int, icu->base + ICU_INT_CFG(d->hwirq)); - - /* - * The SATA unit has 2 ports, and a dedicated ICU entry per - * port. The ahci sata driver supports only one irq interrupt - * per SATA unit. To solve this conflict, we configure the 2 - * SATA wired interrupts in the south bridge into 1 GIC - * interrupt in the north bridge. Even if only a single port - * is enabled, if sata node is enabled, both interrupts are - * configured (regardless of which port is actually in use). - */ - if (d->hwirq == ICU_SATA0_ICU_ID || d->hwirq == ICU_SATA1_ICU_ID) { - writel_relaxed(icu_int, - icu->base + ICU_INT_CFG(ICU_SATA0_ICU_ID)); - writel_relaxed(icu_int, - icu->base + ICU_INT_CFG(ICU_SATA1_ICU_ID)); - } -} - -static struct irq_chip mvebu_icu_nsr_chip = { - .name = "ICU-NSR", - .irq_mask = irq_chip_mask_parent, - .irq_unmask = irq_chip_unmask_parent, - .irq_eoi = irq_chip_eoi_parent, - .irq_set_type = irq_chip_set_type_parent, - .irq_set_affinity = irq_chip_set_affinity_parent, -}; - -static struct irq_chip mvebu_icu_sei_chip = { - .name = "ICU-SEI", - .irq_ack = irq_chip_ack_parent, - .irq_mask = irq_chip_mask_parent, - .irq_unmask = irq_chip_unmask_parent, - .irq_set_type = irq_chip_set_type_parent, - .irq_set_affinity = irq_chip_set_affinity_parent, -}; - -static int -mvebu_icu_irq_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec, +static int mvebu_icu_translate(struct irq_domain *d, struct irq_fwspec *fwspec, unsigned long *hwirq, unsigned int *type) { unsigned int param_count = static_branch_unlikely(&legacy_bindings) ? 3 : 2; - struct mvebu_icu_msi_data *msi_data = platform_msi_get_host_data(d); + struct mvebu_icu_msi_data *msi_data = d->host_data; struct mvebu_icu *icu = msi_data->icu; /* Check the count of the parameters in dt */ @@ -192,81 +108,126 @@ mvebu_icu_irq_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec, return 0; } -static int -mvebu_icu_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs, void *args) +static void mvebu_icu_init(struct mvebu_icu *icu, + struct mvebu_icu_msi_data *msi_data, + struct msi_msg *msg) { - int err; - unsigned long hwirq; - struct irq_fwspec *fwspec = args; - struct mvebu_icu_msi_data *msi_data = platform_msi_get_host_data(domain); - struct mvebu_icu *icu = msi_data->icu; - struct mvebu_icu_irq_data *icu_irqd; - struct irq_chip *chip = &mvebu_icu_nsr_chip; + const struct mvebu_icu_subset_data *subset = msi_data->subset_data; - icu_irqd = kmalloc(sizeof(*icu_irqd), GFP_KERNEL); - if (!icu_irqd) - return -ENOMEM; + if (atomic_cmpxchg(&msi_data->initialized, false, true)) + return; - err = mvebu_icu_irq_domain_translate(domain, fwspec, &hwirq, - &icu_irqd->type); - if (err) { - dev_err(icu->dev, "failed to translate ICU parameters\n"); - goto free_irqd; - } + /* Set 'SET' ICU SPI message address in AP */ + writel_relaxed(msg[0].address_hi, icu->base + subset->offset_set_ah); + writel_relaxed(msg[0].address_lo, icu->base + subset->offset_set_al); - if (static_branch_unlikely(&legacy_bindings)) - icu_irqd->icu_group = fwspec->param[0]; - else - icu_irqd->icu_group = msi_data->subset_data->icu_group; - icu_irqd->icu = icu; + if (subset->icu_group != ICU_GRP_NSR) + return; - err = platform_msi_device_domain_alloc(domain, virq, nr_irqs); - if (err) { - dev_err(icu->dev, "failed to allocate ICU interrupt in parent domain\n"); - goto free_irqd; - } + /* Set 'CLEAR' ICU SPI message address in AP (level-MSI only) */ + writel_relaxed(msg[1].address_hi, icu->base + subset->offset_clr_ah); + writel_relaxed(msg[1].address_lo, icu->base + subset->offset_clr_al); +} - /* Make sure there is no interrupt left pending by the firmware */ - err = irq_set_irqchip_state(virq, IRQCHIP_STATE_PENDING, false); - if (err) - goto free_msi; +static int mvebu_icu_msi_init(struct irq_domain *domain, struct msi_domain_info *info, + unsigned int virq, irq_hw_number_t hwirq, msi_alloc_info_t *arg) +{ + irq_domain_set_hwirq_and_chip(domain, virq, hwirq, info->chip, info->chip_data); + return irq_set_irqchip_state(virq, IRQCHIP_STATE_PENDING, false); +} - if (icu_irqd->icu_group == ICU_GRP_SEI) - chip = &mvebu_icu_sei_chip; +static void mvebu_icu_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) +{ + arg->desc = desc; + arg->hwirq = (u32)desc->data.icookie.value; +} - err = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, - chip, icu_irqd); - if (err) { - dev_err(icu->dev, "failed to set the data to IRQ domain\n"); - goto free_msi; +static void mvebu_icu_write_msi_msg(struct irq_data *d, struct msi_msg *msg) +{ + struct mvebu_icu_msi_data *msi_data = d->chip_data; + unsigned int icu_group = msi_data->subset_data->icu_group; + struct msi_desc *desc = irq_data_get_msi_desc(d); + struct mvebu_icu *icu = msi_data->icu; + unsigned int type; + u32 icu_int; + + if (msg->address_lo || msg->address_hi) { + /* One off initialization per domain */ + mvebu_icu_init(icu, msi_data, msg); + /* Configure the ICU with irq number & type */ + icu_int = msg->data | ICU_INT_ENABLE; + type = (unsigned int)(desc->data.icookie.value >> 32); + if (type & IRQ_TYPE_EDGE_RISING) + icu_int |= ICU_IS_EDGE; + icu_int |= icu_group << ICU_GROUP_SHIFT; + } else { + /* De-configure the ICU */ + icu_int = 0; } - return 0; + writel_relaxed(icu_int, icu->base + ICU_INT_CFG(d->hwirq)); -free_msi: - platform_msi_device_domain_free(domain, virq, nr_irqs); -free_irqd: - kfree(icu_irqd); - return err; + /* + * The SATA unit has 2 ports, and a dedicated ICU entry per + * port. The ahci sata driver supports only one irq interrupt + * per SATA unit. To solve this conflict, we configure the 2 + * SATA wired interrupts in the south bridge into 1 GIC + * interrupt in the north bridge. Even if only a single port + * is enabled, if sata node is enabled, both interrupts are + * configured (regardless of which port is actually in use). + */ + if (d->hwirq == ICU_SATA0_ICU_ID || d->hwirq == ICU_SATA1_ICU_ID) { + writel_relaxed(icu_int, icu->base + ICU_INT_CFG(ICU_SATA0_ICU_ID)); + writel_relaxed(icu_int, icu->base + ICU_INT_CFG(ICU_SATA1_ICU_ID)); + } } -static void -mvebu_icu_irq_domain_free(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs) -{ - struct irq_data *d = irq_get_irq_data(virq); - struct mvebu_icu_irq_data *icu_irqd = d->chip_data; +static const struct msi_domain_template mvebu_icu_nsr_msi_template = { + .chip = { + .name = "ICU-NSR", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = irq_chip_eoi_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_write_msi_msg = mvebu_icu_write_msi_msg, + .flags = IRQCHIP_SUPPORTS_LEVEL_MSI, + }, - kfree(icu_irqd); + .ops = { + .msi_translate = mvebu_icu_translate, + .msi_init = mvebu_icu_msi_init, + .set_desc = mvebu_icu_set_desc, + }, - platform_msi_device_domain_free(domain, virq, nr_irqs); -} + .info = { + .bus_token = DOMAIN_BUS_WIRED_TO_MSI, + .flags = MSI_FLAG_LEVEL_CAPABLE | + MSI_FLAG_USE_DEV_FWNODE, + }, +}; + +static const struct msi_domain_template mvebu_icu_sei_msi_template = { + .chip = { + .name = "ICU-SEI", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_ack = irq_chip_ack_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_write_msi_msg = mvebu_icu_write_msi_msg, + .flags = IRQCHIP_SUPPORTS_LEVEL_MSI, + }, -static const struct irq_domain_ops mvebu_icu_domain_ops = { - .translate = mvebu_icu_irq_domain_translate, - .alloc = mvebu_icu_irq_domain_alloc, - .free = mvebu_icu_irq_domain_free, + .ops = { + .msi_translate = mvebu_icu_translate, + .msi_init = mvebu_icu_msi_init, + .set_desc = mvebu_icu_set_desc, + }, + + .info = { + .bus_token = DOMAIN_BUS_WIRED_TO_MSI, + .flags = MSI_FLAG_LEVEL_CAPABLE | + MSI_FLAG_USE_DEV_FWNODE, + }, }; static const struct mvebu_icu_subset_data mvebu_icu_nsr_subset_data = { @@ -297,10 +258,10 @@ static const struct of_device_id mvebu_icu_subset_of_match[] = { static int mvebu_icu_subset_probe(struct platform_device *pdev) { + const struct msi_domain_template *tmpl; struct mvebu_icu_msi_data *msi_data; - struct device_node *msi_parent_dn; struct device *dev = &pdev->dev; - struct irq_domain *irq_domain; + bool sei; msi_data = devm_kzalloc(dev, sizeof(*msi_data), GFP_KERNEL); if (!msi_data) @@ -314,20 +275,18 @@ static int mvebu_icu_subset_probe(struct platform_device *pdev) msi_data->subset_data = of_device_get_match_data(dev); } - dev->msi.domain = of_msi_get_domain(dev, dev->of_node, - DOMAIN_BUS_PLATFORM_MSI); + dev->msi.domain = of_msi_get_domain(dev, dev->of_node, DOMAIN_BUS_PLATFORM_MSI); if (!dev->msi.domain) return -EPROBE_DEFER; - msi_parent_dn = irq_domain_get_of_node(dev->msi.domain); - if (!msi_parent_dn) + if (!irq_domain_get_of_node(dev->msi.domain)) return -ENODEV; - irq_domain = platform_msi_create_device_tree_domain(dev, ICU_MAX_IRQS, - mvebu_icu_write_msg, - &mvebu_icu_domain_ops, - msi_data); - if (!irq_domain) { + sei = msi_data->subset_data->icu_group == ICU_GRP_SEI; + tmpl = sei ? &mvebu_icu_sei_msi_template : &mvebu_icu_nsr_msi_template; + + if (!msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN, tmpl, + ICU_MAX_IRQS, NULL, msi_data)) { dev_err(dev, "Failed to create ICU MSI domain\n"); return -ENOMEM; } diff --git a/drivers/irqchip/irq-mvebu-odmi.c b/drivers/irqchip/irq-mvebu-odmi.c index 108091533e10..ff19bfd258dc 100644 --- a/drivers/irqchip/irq-mvebu-odmi.c +++ b/drivers/irqchip/irq-mvebu-odmi.c @@ -17,6 +17,9 @@ #include <linux/msi.h> #include <linux/of_address.h> #include <linux/slab.h> + +#include "irq-msi-lib.h" + #include <dt-bindings/interrupt-controller/arm-gic.h> #define GICP_ODMIN_SET 0x40 @@ -141,27 +144,29 @@ static void odmi_irq_domain_free(struct irq_domain *domain, } static const struct irq_domain_ops odmi_domain_ops = { + .select = msi_lib_irq_domain_select, .alloc = odmi_irq_domain_alloc, .free = odmi_irq_domain_free, }; -static struct irq_chip odmi_msi_irq_chip = { - .name = "ODMI", -}; +#define ODMI_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS) -static struct msi_domain_ops odmi_msi_ops = { -}; +#define ODMI_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK) -static struct msi_domain_info odmi_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS), - .ops = &odmi_msi_ops, - .chip = &odmi_msi_irq_chip, +static const struct msi_parent_ops odmi_msi_parent_ops = { + .supported_flags = ODMI_MSI_FLAGS_SUPPORTED, + .required_flags = ODMI_MSI_FLAGS_REQUIRED, + .bus_select_token = DOMAIN_BUS_GENERIC_MSI, + .bus_select_mask = MATCH_PLATFORM_MSI, + .prefix = "ODMI-", + .init_dev_msi_info = msi_lib_init_dev_msi_info, }; static int __init mvebu_odmi_init(struct device_node *node, struct device_node *parent) { - struct irq_domain *parent_domain, *inner_domain, *plat_domain; + struct irq_domain *parent_domain, *inner_domain; int ret, i; if (of_property_read_u32(node, "marvell,odmi-frames", &odmis_count)) @@ -208,18 +213,12 @@ static int __init mvebu_odmi_init(struct device_node *node, goto err_unmap; } - plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node), - &odmi_msi_domain_info, - inner_domain); - if (!plat_domain) { - ret = -ENOMEM; - goto err_remove_inner; - } + irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_GENERIC_MSI); + inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; + inner_domain->msi_parent_ops = &odmi_msi_parent_ops; return 0; -err_remove_inner: - irq_domain_remove(inner_domain); err_unmap: for (i = 0; i < odmis_count; i++) { struct odmi_data *odmi = &odmis[i]; diff --git a/drivers/irqchip/irq-mvebu-pic.c b/drivers/irqchip/irq-mvebu-pic.c index d17d9c0e2880..08b0cc862adf 100644 --- a/drivers/irqchip/irq-mvebu-pic.c +++ b/drivers/irqchip/irq-mvebu-pic.c @@ -193,6 +193,7 @@ module_platform_driver(mvebu_pic_driver); MODULE_AUTHOR("Yehuda Yitschak <yehuday@marvell.com>"); MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>"); +MODULE_DESCRIPTION("Marvell Armada 7K/8K PIC driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:mvebu_pic"); diff --git a/drivers/irqchip/irq-mvebu-sei.c b/drivers/irqchip/irq-mvebu-sei.c index a48dbe91b036..f8c70f2d100a 100644 --- a/drivers/irqchip/irq-mvebu-sei.c +++ b/drivers/irqchip/irq-mvebu-sei.c @@ -14,6 +14,8 @@ #include <linux/of_irq.h> #include <linux/of_platform.h> +#include "irq-msi-lib.h" + /* Cause register */ #define GICP_SECR(idx) (0x0 + ((idx) * 0x4)) /* Mask register */ @@ -190,6 +192,7 @@ static void mvebu_sei_domain_free(struct irq_domain *domain, unsigned int virq, } static const struct irq_domain_ops mvebu_sei_domain_ops = { + .select = msi_lib_irq_domain_select, .alloc = mvebu_sei_domain_alloc, .free = mvebu_sei_domain_free, }; @@ -307,21 +310,6 @@ static const struct irq_domain_ops mvebu_sei_cp_domain_ops = { .free = mvebu_sei_cp_domain_free, }; -static struct irq_chip mvebu_sei_msi_irq_chip = { - .name = "SEI pMSI", - .irq_ack = irq_chip_ack_parent, - .irq_set_type = irq_chip_set_type_parent, -}; - -static struct msi_domain_ops mvebu_sei_msi_ops = { -}; - -static struct msi_domain_info mvebu_sei_msi_domain_info = { - .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS, - .ops = &mvebu_sei_msi_ops, - .chip = &mvebu_sei_msi_irq_chip, -}; - static void mvebu_sei_handle_cascade_irq(struct irq_desc *desc) { struct mvebu_sei *sei = irq_desc_get_handler_data(desc); @@ -360,10 +348,23 @@ static void mvebu_sei_reset(struct mvebu_sei *sei) } } +#define SEI_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS) + +#define SEI_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK) + +static const struct msi_parent_ops sei_msi_parent_ops = { + .supported_flags = SEI_MSI_FLAGS_SUPPORTED, + .required_flags = SEI_MSI_FLAGS_REQUIRED, + .bus_select_mask = MATCH_PLATFORM_MSI, + .bus_select_token = DOMAIN_BUS_GENERIC_MSI, + .prefix = "SEI-", + .init_dev_msi_info = msi_lib_init_dev_msi_info, +}; + static int mvebu_sei_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; - struct irq_domain *plat_domain; struct mvebu_sei *sei; u32 parent_irq; int ret; @@ -440,33 +441,20 @@ static int mvebu_sei_probe(struct platform_device *pdev) } irq_domain_update_bus_token(sei->cp_domain, DOMAIN_BUS_GENERIC_MSI); - - plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node), - &mvebu_sei_msi_domain_info, - sei->cp_domain); - if (!plat_domain) { - pr_err("Failed to create CPs MSI domain\n"); - ret = -ENOMEM; - goto remove_cp_domain; - } + sei->cp_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; + sei->cp_domain->msi_parent_ops = &sei_msi_parent_ops; mvebu_sei_reset(sei); - irq_set_chained_handler_and_data(parent_irq, - mvebu_sei_handle_cascade_irq, - sei); - + irq_set_chained_handler_and_data(parent_irq, mvebu_sei_handle_cascade_irq, sei); return 0; -remove_cp_domain: - irq_domain_remove(sei->cp_domain); remove_ap_domain: irq_domain_remove(sei->ap_domain); remove_sei_domain: irq_domain_remove(sei->sei_domain); dispose_irq: irq_dispose_mapping(parent_irq); - return ret; } diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index f6484bf15e0b..693ff285ca2c 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -37,6 +37,8 @@ #define TSSEL_SHIFT(n) (8 * (n)) #define TSSEL_MASK GENMASK(7, 0) #define IRQ_MASK 0x3 +#define IMSK 0x10010 +#define TMSK 0x10020 #define TSSR_OFFSET(n) ((n) % 4) #define TSSR_INDEX(n) ((n) / 4) @@ -69,12 +71,14 @@ struct rzg2l_irqc_reg_cache { /** * struct rzg2l_irqc_priv - IRQ controller private data structure * @base: Controller's base address + * @irqchip: Pointer to struct irq_chip * @fwspec: IRQ firmware specific data * @lock: Lock to serialize access to hardware registers * @cache: Registers cache for suspend/resume */ static struct rzg2l_irqc_priv { void __iomem *base; + const struct irq_chip *irqchip; struct irq_fwspec fwspec[IRQC_NUM_IRQ]; raw_spinlock_t lock; struct rzg2l_irqc_reg_cache cache; @@ -138,6 +142,111 @@ static void rzg2l_irqc_eoi(struct irq_data *d) irq_chip_eoi_parent(d); } +static void rzfive_irqc_mask_irq_interrupt(struct rzg2l_irqc_priv *priv, + unsigned int hwirq) +{ + u32 bit = BIT(hwirq - IRQC_IRQ_START); + + writel_relaxed(readl_relaxed(priv->base + IMSK) | bit, priv->base + IMSK); +} + +static void rzfive_irqc_unmask_irq_interrupt(struct rzg2l_irqc_priv *priv, + unsigned int hwirq) +{ + u32 bit = BIT(hwirq - IRQC_IRQ_START); + + writel_relaxed(readl_relaxed(priv->base + IMSK) & ~bit, priv->base + IMSK); +} + +static void rzfive_irqc_mask_tint_interrupt(struct rzg2l_irqc_priv *priv, + unsigned int hwirq) +{ + u32 bit = BIT(hwirq - IRQC_TINT_START); + + writel_relaxed(readl_relaxed(priv->base + TMSK) | bit, priv->base + TMSK); +} + +static void rzfive_irqc_unmask_tint_interrupt(struct rzg2l_irqc_priv *priv, + unsigned int hwirq) +{ + u32 bit = BIT(hwirq - IRQC_TINT_START); + + writel_relaxed(readl_relaxed(priv->base + TMSK) & ~bit, priv->base + TMSK); +} + +static void rzfive_irqc_mask(struct irq_data *d) +{ + struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); + unsigned int hwirq = irqd_to_hwirq(d); + + raw_spin_lock(&priv->lock); + if (hwirq >= IRQC_IRQ_START && hwirq <= IRQC_IRQ_COUNT) + rzfive_irqc_mask_irq_interrupt(priv, hwirq); + else if (hwirq >= IRQC_TINT_START && hwirq < IRQC_NUM_IRQ) + rzfive_irqc_mask_tint_interrupt(priv, hwirq); + raw_spin_unlock(&priv->lock); + irq_chip_mask_parent(d); +} + +static void rzfive_irqc_unmask(struct irq_data *d) +{ + struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); + unsigned int hwirq = irqd_to_hwirq(d); + + raw_spin_lock(&priv->lock); + if (hwirq >= IRQC_IRQ_START && hwirq <= IRQC_IRQ_COUNT) + rzfive_irqc_unmask_irq_interrupt(priv, hwirq); + else if (hwirq >= IRQC_TINT_START && hwirq < IRQC_NUM_IRQ) + rzfive_irqc_unmask_tint_interrupt(priv, hwirq); + raw_spin_unlock(&priv->lock); + irq_chip_unmask_parent(d); +} + +static void rzfive_tint_irq_endisable(struct irq_data *d, bool enable) +{ + struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); + unsigned int hwirq = irqd_to_hwirq(d); + + if (hwirq >= IRQC_TINT_START && hwirq < IRQC_NUM_IRQ) { + u32 offset = hwirq - IRQC_TINT_START; + u32 tssr_offset = TSSR_OFFSET(offset); + u8 tssr_index = TSSR_INDEX(offset); + u32 reg; + + raw_spin_lock(&priv->lock); + if (enable) + rzfive_irqc_unmask_tint_interrupt(priv, hwirq); + else + rzfive_irqc_mask_tint_interrupt(priv, hwirq); + reg = readl_relaxed(priv->base + TSSR(tssr_index)); + if (enable) + reg |= TIEN << TSSEL_SHIFT(tssr_offset); + else + reg &= ~(TIEN << TSSEL_SHIFT(tssr_offset)); + writel_relaxed(reg, priv->base + TSSR(tssr_index)); + raw_spin_unlock(&priv->lock); + } else { + raw_spin_lock(&priv->lock); + if (enable) + rzfive_irqc_unmask_irq_interrupt(priv, hwirq); + else + rzfive_irqc_mask_irq_interrupt(priv, hwirq); + raw_spin_unlock(&priv->lock); + } +} + +static void rzfive_irqc_irq_disable(struct irq_data *d) +{ + irq_chip_disable_parent(d); + rzfive_tint_irq_endisable(d, false); +} + +static void rzfive_irqc_irq_enable(struct irq_data *d) +{ + rzfive_tint_irq_endisable(d, true); + irq_chip_enable_parent(d); +} + static void rzg2l_tint_irq_endisable(struct irq_data *d, bool enable) { unsigned int hw_irq = irqd_to_hwirq(d); @@ -162,8 +271,8 @@ static void rzg2l_tint_irq_endisable(struct irq_data *d, bool enable) static void rzg2l_irqc_irq_disable(struct irq_data *d) { - rzg2l_tint_irq_endisable(d, false); irq_chip_disable_parent(d); + rzg2l_tint_irq_endisable(d, false); } static void rzg2l_irqc_irq_enable(struct irq_data *d) @@ -321,7 +430,7 @@ static struct syscore_ops rzg2l_irqc_syscore_ops = { .resume = rzg2l_irqc_irq_resume, }; -static const struct irq_chip irqc_chip = { +static const struct irq_chip rzg2l_irqc_chip = { .name = "rzg2l-irqc", .irq_eoi = rzg2l_irqc_eoi, .irq_mask = irq_chip_mask_parent, @@ -338,6 +447,23 @@ static const struct irq_chip irqc_chip = { IRQCHIP_SKIP_SET_WAKE, }; +static const struct irq_chip rzfive_irqc_chip = { + .name = "rzfive-irqc", + .irq_eoi = rzg2l_irqc_eoi, + .irq_mask = rzfive_irqc_mask, + .irq_unmask = rzfive_irqc_unmask, + .irq_disable = rzfive_irqc_irq_disable, + .irq_enable = rzfive_irqc_irq_enable, + .irq_get_irqchip_state = irq_chip_get_parent_state, + .irq_set_irqchip_state = irq_chip_set_parent_state, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_type = rzg2l_irqc_set_type, + .irq_set_affinity = irq_chip_set_affinity_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SET_TYPE_MASKED | + IRQCHIP_SKIP_SET_WAKE, +}; + static int rzg2l_irqc_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *arg) { @@ -369,7 +495,7 @@ static int rzg2l_irqc_alloc(struct irq_domain *domain, unsigned int virq, if (hwirq > (IRQC_NUM_IRQ - 1)) return -EINVAL; - ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &irqc_chip, + ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, priv->irqchip, (void *)(uintptr_t)tint); if (ret) return ret; @@ -401,7 +527,8 @@ static int rzg2l_irqc_parse_interrupts(struct rzg2l_irqc_priv *priv, return 0; } -static int rzg2l_irqc_init(struct device_node *node, struct device_node *parent) +static int rzg2l_irqc_common_init(struct device_node *node, struct device_node *parent, + const struct irq_chip *irq_chip) { struct irq_domain *irq_domain, *parent_domain; struct platform_device *pdev; @@ -422,6 +549,8 @@ static int rzg2l_irqc_init(struct device_node *node, struct device_node *parent) if (!rzg2l_irqc_data) return -ENOMEM; + rzg2l_irqc_data->irqchip = irq_chip; + rzg2l_irqc_data->base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 0, NULL); if (IS_ERR(rzg2l_irqc_data->base)) return PTR_ERR(rzg2l_irqc_data->base); @@ -472,8 +601,21 @@ pm_disable: return ret; } +static int __init rzg2l_irqc_init(struct device_node *node, + struct device_node *parent) +{ + return rzg2l_irqc_common_init(node, parent, &rzg2l_irqc_chip); +} + +static int __init rzfive_irqc_init(struct device_node *node, + struct device_node *parent) +{ + return rzg2l_irqc_common_init(node, parent, &rzfive_irqc_chip); +} + IRQCHIP_PLATFORM_DRIVER_BEGIN(rzg2l_irqc) IRQCHIP_MATCH("renesas,rzg2l-irqc", rzg2l_irqc_init) +IRQCHIP_MATCH("renesas,r9a07g043f-irqc", rzfive_irqc_init) IRQCHIP_PLATFORM_DRIVER_END(rzg2l_irqc) MODULE_AUTHOR("Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>"); MODULE_DESCRIPTION("Renesas RZ/G2L IRQC Driver"); diff --git a/drivers/irqchip/irq-riscv-aplic-main.c b/drivers/irqchip/irq-riscv-aplic-main.c index 774a0c97fdab..28dd175b5764 100644 --- a/drivers/irqchip/irq-riscv-aplic-main.c +++ b/drivers/irqchip/irq-riscv-aplic-main.c @@ -127,6 +127,7 @@ static void aplic_init_hw_irqs(struct aplic_priv *priv) int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem *regs) { + struct device_node *np = to_of_node(dev->fwnode); struct of_phandle_args parent; int rc; @@ -134,7 +135,7 @@ int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem * * Currently, only OF fwnode is supported so extend this * function for ACPI support. */ - if (!is_of_node(dev->fwnode)) + if (!np) return -EINVAL; /* Save device pointer and register base */ @@ -142,8 +143,7 @@ int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem * priv->regs = regs; /* Find out number of interrupt sources */ - rc = of_property_read_u32(to_of_node(dev->fwnode), "riscv,num-sources", - &priv->nr_irqs); + rc = of_property_read_u32(np, "riscv,num-sources", &priv->nr_irqs); if (rc) { dev_err(dev, "failed to get number of interrupt sources\n"); return rc; @@ -155,8 +155,8 @@ int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem * * If "msi-parent" property is present then we ignore the * APLIC IDCs which forces the APLIC driver to use MSI mode. */ - if (!of_property_present(to_of_node(dev->fwnode), "msi-parent")) { - while (!of_irq_parse_one(to_of_node(dev->fwnode), priv->nr_idcs, &parent)) + if (!of_property_present(np, "msi-parent")) { + while (!of_irq_parse_one(np, priv->nr_idcs, &parent)) priv->nr_idcs++; } @@ -184,8 +184,7 @@ static int aplic_probe(struct platform_device *pdev) * If msi-parent property is present then setup APLIC MSI * mode otherwise setup APLIC direct mode. */ - if (is_of_node(dev->fwnode)) - msi_mode = of_property_present(to_of_node(dev->fwnode), "msi-parent"); + msi_mode = of_property_present(to_of_node(dev->fwnode), "msi-parent"); if (msi_mode) rc = aplic_msi_setup(dev, regs); else diff --git a/drivers/irqchip/irq-riscv-intc.c b/drivers/irqchip/irq-riscv-intc.c index 4f3a12383a1e..47f3200476da 100644 --- a/drivers/irqchip/irq-riscv-intc.c +++ b/drivers/irqchip/irq-riscv-intc.c @@ -26,7 +26,7 @@ static unsigned int riscv_intc_nr_irqs __ro_after_init = BITS_PER_LONG; static unsigned int riscv_intc_custom_base __ro_after_init = BITS_PER_LONG; static unsigned int riscv_intc_custom_nr_irqs __ro_after_init; -static asmlinkage void riscv_intc_irq(struct pt_regs *regs) +static void riscv_intc_irq(struct pt_regs *regs) { unsigned long cause = regs->cause & ~CAUSE_IRQ_FLAG; @@ -34,7 +34,7 @@ static asmlinkage void riscv_intc_irq(struct pt_regs *regs) pr_warn_ratelimited("Failed to handle interrupt (cause: %ld)\n", cause); } -static asmlinkage void riscv_intc_aia_irq(struct pt_regs *regs) +static void riscv_intc_aia_irq(struct pt_regs *regs) { unsigned long topi; diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index 2cc9f3b7d669..7c6a0080c330 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c @@ -1,45 +1,22 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) Maxime Coquelin 2015 - * Copyright (C) STMicroelectronics 2017 + * Copyright (C) STMicroelectronics 2017-2024 * Author: Maxime Coquelin <mcoquelin.stm32@gmail.com> */ #include <linux/bitops.h> -#include <linux/delay.h> -#include <linux/hwspinlock.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/irqchip.h> #include <linux/irqchip/chained_irq.h> #include <linux/irqdomain.h> -#include <linux/mod_devicetable.h> -#include <linux/module.h> #include <linux/of_address.h> #include <linux/of_irq.h> -#include <linux/platform_device.h> -#include <linux/pm.h> - -#include <dt-bindings/interrupt-controller/arm-gic.h> #define IRQS_PER_BANK 32 -#define HWSPNLCK_TIMEOUT 1000 /* usec */ - -#define EXTI_EnCIDCFGR(n) (0x180 + (n) * 4) -#define EXTI_HWCFGR1 0x3f0 - -/* Register: EXTI_EnCIDCFGR(n) */ -#define EXTI_CIDCFGR_CFEN_MASK BIT(0) -#define EXTI_CIDCFGR_CID_MASK GENMASK(6, 4) -#define EXTI_CIDCFGR_CID_SHIFT 4 - -/* Register: EXTI_HWCFGR1 */ -#define EXTI_HWCFGR1_CIDWIDTH_MASK GENMASK(27, 24) - -#define EXTI_CID1 1 - struct stm32_exti_bank { u32 imr_ofst; u32 emr_ofst; @@ -47,13 +24,8 @@ struct stm32_exti_bank { u32 ftsr_ofst; u32 swier_ofst; u32 rpr_ofst; - u32 fpr_ofst; - u32 trg_ofst; - u32 seccfgr_ofst; }; -#define UNDEF_REG ~0 - struct stm32_exti_drv_data { const struct stm32_exti_bank **exti_banks; const u8 *desc_irqs; @@ -63,7 +35,6 @@ struct stm32_exti_drv_data { struct stm32_exti_chip_data { struct stm32_exti_host_data *host_data; const struct stm32_exti_bank *reg_bank; - struct raw_spinlock rlock; u32 wake_active; u32 mask_cache; u32 rtsr_cache; @@ -76,8 +47,6 @@ struct stm32_exti_host_data { struct device *dev; struct stm32_exti_chip_data *chips_data; const struct stm32_exti_drv_data *drv_data; - struct hwspinlock *hwlock; - bool dt_has_irqs_desc; /* skip internal desc_irqs array and get it from DT */ }; static const struct stm32_exti_bank stm32f4xx_exti_b1 = { @@ -87,9 +56,6 @@ static const struct stm32_exti_bank stm32f4xx_exti_b1 = { .ftsr_ofst = 0x0C, .swier_ofst = 0x10, .rpr_ofst = 0x14, - .fpr_ofst = UNDEF_REG, - .trg_ofst = UNDEF_REG, - .seccfgr_ofst = UNDEF_REG, }; static const struct stm32_exti_bank *stm32f4xx_exti_banks[] = { @@ -108,9 +74,6 @@ static const struct stm32_exti_bank stm32h7xx_exti_b1 = { .ftsr_ofst = 0x04, .swier_ofst = 0x08, .rpr_ofst = 0x88, - .fpr_ofst = UNDEF_REG, - .trg_ofst = UNDEF_REG, - .seccfgr_ofst = UNDEF_REG, }; static const struct stm32_exti_bank stm32h7xx_exti_b2 = { @@ -120,9 +83,6 @@ static const struct stm32_exti_bank stm32h7xx_exti_b2 = { .ftsr_ofst = 0x24, .swier_ofst = 0x28, .rpr_ofst = 0x98, - .fpr_ofst = UNDEF_REG, - .trg_ofst = UNDEF_REG, - .seccfgr_ofst = UNDEF_REG, }; static const struct stm32_exti_bank stm32h7xx_exti_b3 = { @@ -132,9 +92,6 @@ static const struct stm32_exti_bank stm32h7xx_exti_b3 = { .ftsr_ofst = 0x44, .swier_ofst = 0x48, .rpr_ofst = 0xA8, - .fpr_ofst = UNDEF_REG, - .trg_ofst = UNDEF_REG, - .seccfgr_ofst = UNDEF_REG, }; static const struct stm32_exti_bank *stm32h7xx_exti_banks[] = { @@ -148,183 +105,12 @@ static const struct stm32_exti_drv_data stm32h7xx_drv_data = { .bank_nr = ARRAY_SIZE(stm32h7xx_exti_banks), }; -static const struct stm32_exti_bank stm32mp1_exti_b1 = { - .imr_ofst = 0x80, - .emr_ofst = UNDEF_REG, - .rtsr_ofst = 0x00, - .ftsr_ofst = 0x04, - .swier_ofst = 0x08, - .rpr_ofst = 0x0C, - .fpr_ofst = 0x10, - .trg_ofst = 0x3EC, - .seccfgr_ofst = 0x14, -}; - -static const struct stm32_exti_bank stm32mp1_exti_b2 = { - .imr_ofst = 0x90, - .emr_ofst = UNDEF_REG, - .rtsr_ofst = 0x20, - .ftsr_ofst = 0x24, - .swier_ofst = 0x28, - .rpr_ofst = 0x2C, - .fpr_ofst = 0x30, - .trg_ofst = 0x3E8, - .seccfgr_ofst = 0x34, -}; - -static const struct stm32_exti_bank stm32mp1_exti_b3 = { - .imr_ofst = 0xA0, - .emr_ofst = UNDEF_REG, - .rtsr_ofst = 0x40, - .ftsr_ofst = 0x44, - .swier_ofst = 0x48, - .rpr_ofst = 0x4C, - .fpr_ofst = 0x50, - .trg_ofst = 0x3E4, - .seccfgr_ofst = 0x54, -}; - -static const struct stm32_exti_bank *stm32mp1_exti_banks[] = { - &stm32mp1_exti_b1, - &stm32mp1_exti_b2, - &stm32mp1_exti_b3, -}; - -static struct irq_chip stm32_exti_h_chip; -static struct irq_chip stm32_exti_h_chip_direct; - -#define EXTI_INVALID_IRQ U8_MAX -#define STM32MP1_DESC_IRQ_SIZE (ARRAY_SIZE(stm32mp1_exti_banks) * IRQS_PER_BANK) - -/* - * Use some intentionally tricky logic here to initialize the whole array to - * EXTI_INVALID_IRQ, but then override certain fields, requiring us to indicate - * that we "know" that there are overrides in this structure, and we'll need to - * disable that warning from W=1 builds. - */ -__diag_push(); -__diag_ignore_all("-Woverride-init", - "logic to initialize all and then override some is OK"); - -static const u8 stm32mp1_desc_irq[] = { - /* default value */ - [0 ... (STM32MP1_DESC_IRQ_SIZE - 1)] = EXTI_INVALID_IRQ, - - [0] = 6, - [1] = 7, - [2] = 8, - [3] = 9, - [4] = 10, - [5] = 23, - [6] = 64, - [7] = 65, - [8] = 66, - [9] = 67, - [10] = 40, - [11] = 42, - [12] = 76, - [13] = 77, - [14] = 121, - [15] = 127, - [16] = 1, - [19] = 3, - [21] = 31, - [22] = 33, - [23] = 72, - [24] = 95, - [25] = 107, - [26] = 37, - [27] = 38, - [28] = 39, - [29] = 71, - [30] = 52, - [31] = 53, - [32] = 82, - [33] = 83, - [46] = 151, - [47] = 93, - [48] = 138, - [50] = 139, - [52] = 140, - [53] = 141, - [54] = 135, - [61] = 100, - [65] = 144, - [68] = 143, - [70] = 62, - [73] = 129, -}; - -static const u8 stm32mp13_desc_irq[] = { - /* default value */ - [0 ... (STM32MP1_DESC_IRQ_SIZE - 1)] = EXTI_INVALID_IRQ, - - [0] = 6, - [1] = 7, - [2] = 8, - [3] = 9, - [4] = 10, - [5] = 24, - [6] = 65, - [7] = 66, - [8] = 67, - [9] = 68, - [10] = 41, - [11] = 43, - [12] = 77, - [13] = 78, - [14] = 106, - [15] = 109, - [16] = 1, - [19] = 3, - [21] = 32, - [22] = 34, - [23] = 73, - [24] = 93, - [25] = 114, - [26] = 38, - [27] = 39, - [28] = 40, - [29] = 72, - [30] = 53, - [31] = 54, - [32] = 83, - [33] = 84, - [44] = 96, - [47] = 92, - [48] = 116, - [50] = 117, - [52] = 118, - [53] = 119, - [68] = 63, - [70] = 98, -}; - -__diag_pop(); - -static const struct stm32_exti_drv_data stm32mp1_drv_data = { - .exti_banks = stm32mp1_exti_banks, - .bank_nr = ARRAY_SIZE(stm32mp1_exti_banks), - .desc_irqs = stm32mp1_desc_irq, -}; - -static const struct stm32_exti_drv_data stm32mp13_drv_data = { - .exti_banks = stm32mp1_exti_banks, - .bank_nr = ARRAY_SIZE(stm32mp1_exti_banks), - .desc_irqs = stm32mp13_desc_irq, -}; - static unsigned long stm32_exti_pending(struct irq_chip_generic *gc) { struct stm32_exti_chip_data *chip_data = gc->private; const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; - unsigned long pending; - pending = irq_reg_readl(gc, stm32_bank->rpr_ofst); - if (stm32_bank->fpr_ofst != UNDEF_REG) - pending |= irq_reg_readl(gc, stm32_bank->fpr_ofst); - - return pending; + return irq_reg_readl(gc, stm32_bank->rpr_ofst); } static void stm32_irq_handler(struct irq_desc *desc) @@ -380,33 +166,21 @@ static int stm32_irq_set_type(struct irq_data *d, unsigned int type) struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct stm32_exti_chip_data *chip_data = gc->private; const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; - struct hwspinlock *hwlock = chip_data->host_data->hwlock; u32 rtsr, ftsr; int err; irq_gc_lock(gc); - if (hwlock) { - err = hwspin_lock_timeout_in_atomic(hwlock, HWSPNLCK_TIMEOUT); - if (err) { - pr_err("%s can't get hwspinlock (%d)\n", __func__, err); - goto unlock; - } - } - rtsr = irq_reg_readl(gc, stm32_bank->rtsr_ofst); ftsr = irq_reg_readl(gc, stm32_bank->ftsr_ofst); err = stm32_exti_set_type(d, type, &rtsr, &ftsr); if (err) - goto unspinlock; + goto unlock; irq_reg_writel(gc, rtsr, stm32_bank->rtsr_ofst); irq_reg_writel(gc, ftsr, stm32_bank->ftsr_ofst); -unspinlock: - if (hwlock) - hwspin_unlock_in_atomic(hwlock); unlock: irq_gc_unlock(gc); @@ -494,287 +268,10 @@ static void stm32_irq_ack(struct irq_data *d) irq_gc_lock(gc); irq_reg_writel(gc, d->mask, stm32_bank->rpr_ofst); - if (stm32_bank->fpr_ofst != UNDEF_REG) - irq_reg_writel(gc, d->mask, stm32_bank->fpr_ofst); irq_gc_unlock(gc); } -/* directly set the target bit without reading first. */ -static inline void stm32_exti_write_bit(struct irq_data *d, u32 reg) -{ - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - void __iomem *base = chip_data->host_data->base; - u32 val = BIT(d->hwirq % IRQS_PER_BANK); - - writel_relaxed(val, base + reg); -} - -static inline u32 stm32_exti_set_bit(struct irq_data *d, u32 reg) -{ - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - void __iomem *base = chip_data->host_data->base; - u32 val; - - val = readl_relaxed(base + reg); - val |= BIT(d->hwirq % IRQS_PER_BANK); - writel_relaxed(val, base + reg); - - return val; -} - -static inline u32 stm32_exti_clr_bit(struct irq_data *d, u32 reg) -{ - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - void __iomem *base = chip_data->host_data->base; - u32 val; - - val = readl_relaxed(base + reg); - val &= ~BIT(d->hwirq % IRQS_PER_BANK); - writel_relaxed(val, base + reg); - - return val; -} - -static void stm32_exti_h_eoi(struct irq_data *d) -{ - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; - - raw_spin_lock(&chip_data->rlock); - - stm32_exti_write_bit(d, stm32_bank->rpr_ofst); - if (stm32_bank->fpr_ofst != UNDEF_REG) - stm32_exti_write_bit(d, stm32_bank->fpr_ofst); - - raw_spin_unlock(&chip_data->rlock); - - if (d->parent_data->chip) - irq_chip_eoi_parent(d); -} - -static void stm32_exti_h_mask(struct irq_data *d) -{ - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; - - raw_spin_lock(&chip_data->rlock); - chip_data->mask_cache = stm32_exti_clr_bit(d, stm32_bank->imr_ofst); - raw_spin_unlock(&chip_data->rlock); - - if (d->parent_data->chip) - irq_chip_mask_parent(d); -} - -static void stm32_exti_h_unmask(struct irq_data *d) -{ - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; - - raw_spin_lock(&chip_data->rlock); - chip_data->mask_cache = stm32_exti_set_bit(d, stm32_bank->imr_ofst); - raw_spin_unlock(&chip_data->rlock); - - if (d->parent_data->chip) - irq_chip_unmask_parent(d); -} - -static int stm32_exti_h_set_type(struct irq_data *d, unsigned int type) -{ - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; - struct hwspinlock *hwlock = chip_data->host_data->hwlock; - void __iomem *base = chip_data->host_data->base; - u32 rtsr, ftsr; - int err; - - raw_spin_lock(&chip_data->rlock); - - if (hwlock) { - err = hwspin_lock_timeout_in_atomic(hwlock, HWSPNLCK_TIMEOUT); - if (err) { - pr_err("%s can't get hwspinlock (%d)\n", __func__, err); - goto unlock; - } - } - - rtsr = readl_relaxed(base + stm32_bank->rtsr_ofst); - ftsr = readl_relaxed(base + stm32_bank->ftsr_ofst); - - err = stm32_exti_set_type(d, type, &rtsr, &ftsr); - if (err) - goto unspinlock; - - writel_relaxed(rtsr, base + stm32_bank->rtsr_ofst); - writel_relaxed(ftsr, base + stm32_bank->ftsr_ofst); - -unspinlock: - if (hwlock) - hwspin_unlock_in_atomic(hwlock); -unlock: - raw_spin_unlock(&chip_data->rlock); - - return err; -} - -static int stm32_exti_h_set_wake(struct irq_data *d, unsigned int on) -{ - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - u32 mask = BIT(d->hwirq % IRQS_PER_BANK); - - raw_spin_lock(&chip_data->rlock); - - if (on) - chip_data->wake_active |= mask; - else - chip_data->wake_active &= ~mask; - - raw_spin_unlock(&chip_data->rlock); - - return 0; -} - -static int stm32_exti_h_set_affinity(struct irq_data *d, - const struct cpumask *dest, bool force) -{ - if (d->parent_data->chip) - return irq_chip_set_affinity_parent(d, dest, force); - - return IRQ_SET_MASK_OK_DONE; -} - -static int stm32_exti_h_suspend(struct device *dev) -{ - struct stm32_exti_host_data *host_data = dev_get_drvdata(dev); - struct stm32_exti_chip_data *chip_data; - int i; - - for (i = 0; i < host_data->drv_data->bank_nr; i++) { - chip_data = &host_data->chips_data[i]; - stm32_chip_suspend(chip_data, chip_data->wake_active); - } - - return 0; -} - -static int stm32_exti_h_resume(struct device *dev) -{ - struct stm32_exti_host_data *host_data = dev_get_drvdata(dev); - struct stm32_exti_chip_data *chip_data; - int i; - - for (i = 0; i < host_data->drv_data->bank_nr; i++) { - chip_data = &host_data->chips_data[i]; - stm32_chip_resume(chip_data, chip_data->mask_cache); - } - - return 0; -} - -static int stm32_exti_h_retrigger(struct irq_data *d) -{ - struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; - void __iomem *base = chip_data->host_data->base; - u32 mask = BIT(d->hwirq % IRQS_PER_BANK); - - writel_relaxed(mask, base + stm32_bank->swier_ofst); - - return 0; -} - -static struct irq_chip stm32_exti_h_chip = { - .name = "stm32-exti-h", - .irq_eoi = stm32_exti_h_eoi, - .irq_mask = stm32_exti_h_mask, - .irq_unmask = stm32_exti_h_unmask, - .irq_retrigger = stm32_exti_h_retrigger, - .irq_set_type = stm32_exti_h_set_type, - .irq_set_wake = stm32_exti_h_set_wake, - .flags = IRQCHIP_MASK_ON_SUSPEND, - .irq_set_affinity = IS_ENABLED(CONFIG_SMP) ? stm32_exti_h_set_affinity : NULL, -}; - -static struct irq_chip stm32_exti_h_chip_direct = { - .name = "stm32-exti-h-direct", - .irq_eoi = irq_chip_eoi_parent, - .irq_ack = irq_chip_ack_parent, - .irq_mask = stm32_exti_h_mask, - .irq_unmask = stm32_exti_h_unmask, - .irq_retrigger = irq_chip_retrigger_hierarchy, - .irq_set_type = irq_chip_set_type_parent, - .irq_set_wake = stm32_exti_h_set_wake, - .flags = IRQCHIP_MASK_ON_SUSPEND, - .irq_set_affinity = IS_ENABLED(CONFIG_SMP) ? irq_chip_set_affinity_parent : NULL, -}; - -static int stm32_exti_h_domain_alloc(struct irq_domain *dm, - unsigned int virq, - unsigned int nr_irqs, void *data) -{ - struct stm32_exti_host_data *host_data = dm->host_data; - struct stm32_exti_chip_data *chip_data; - u8 desc_irq; - struct irq_fwspec *fwspec = data; - struct irq_fwspec p_fwspec; - irq_hw_number_t hwirq; - int bank; - u32 event_trg; - struct irq_chip *chip; - - hwirq = fwspec->param[0]; - if (hwirq >= host_data->drv_data->bank_nr * IRQS_PER_BANK) - return -EINVAL; - - bank = hwirq / IRQS_PER_BANK; - chip_data = &host_data->chips_data[bank]; - - /* Check if event is reserved (Secure) */ - if (chip_data->event_reserved & BIT(hwirq % IRQS_PER_BANK)) { - dev_err(host_data->dev, "event %lu is reserved, secure\n", hwirq); - return -EPERM; - } - - event_trg = readl_relaxed(host_data->base + chip_data->reg_bank->trg_ofst); - chip = (event_trg & BIT(hwirq % IRQS_PER_BANK)) ? - &stm32_exti_h_chip : &stm32_exti_h_chip_direct; - - irq_domain_set_hwirq_and_chip(dm, virq, hwirq, chip, chip_data); - - if (host_data->dt_has_irqs_desc) { - struct of_phandle_args out_irq; - int ret; - - ret = of_irq_parse_one(host_data->dev->of_node, hwirq, &out_irq); - if (ret) - return ret; - /* we only support one parent, so far */ - if (of_node_to_fwnode(out_irq.np) != dm->parent->fwnode) - return -EINVAL; - - of_phandle_args_to_fwspec(out_irq.np, out_irq.args, - out_irq.args_count, &p_fwspec); - - return irq_domain_alloc_irqs_parent(dm, virq, 1, &p_fwspec); - } - - if (!host_data->drv_data->desc_irqs) - return -EINVAL; - - desc_irq = host_data->drv_data->desc_irqs[hwirq]; - if (desc_irq != EXTI_INVALID_IRQ) { - p_fwspec.fwnode = dm->parent->fwnode; - p_fwspec.param_count = 3; - p_fwspec.param[0] = GIC_SPI; - p_fwspec.param[1] = desc_irq; - p_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; - - return irq_domain_alloc_irqs_parent(dm, virq, 1, &p_fwspec); - } - - return 0; -} - static struct stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd, struct device_node *node) @@ -822,19 +319,12 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, chip_data->host_data = h_data; chip_data->reg_bank = stm32_bank; - raw_spin_lock_init(&chip_data->rlock); - /* * This IP has no reset, so after hot reboot we should * clear registers to avoid residue */ writel_relaxed(0, base + stm32_bank->imr_ofst); - if (stm32_bank->emr_ofst != UNDEF_REG) - writel_relaxed(0, base + stm32_bank->emr_ofst); - - /* reserve Secure events */ - if (stm32_bank->seccfgr_ofst != UNDEF_REG) - chip_data->event_reserved = readl_relaxed(base + stm32_bank->seccfgr_ofst); + writel_relaxed(0, base + stm32_bank->emr_ofst); pr_info("%pOF: bank%d\n", node, bank_idx); @@ -914,158 +404,6 @@ out_unmap: return ret; } -static const struct irq_domain_ops stm32_exti_h_domain_ops = { - .alloc = stm32_exti_h_domain_alloc, - .free = irq_domain_free_irqs_common, - .xlate = irq_domain_xlate_twocell, -}; - -static void stm32_exti_check_rif(struct stm32_exti_host_data *host_data) -{ - unsigned int bank, i, event; - u32 cid, cidcfgr, hwcfgr1; - - /* quit on CID not supported */ - hwcfgr1 = readl_relaxed(host_data->base + EXTI_HWCFGR1); - if ((hwcfgr1 & EXTI_HWCFGR1_CIDWIDTH_MASK) == 0) - return; - - for (bank = 0; bank < host_data->drv_data->bank_nr; bank++) { - for (i = 0; i < IRQS_PER_BANK; i++) { - event = bank * IRQS_PER_BANK + i; - cidcfgr = readl_relaxed(host_data->base + EXTI_EnCIDCFGR(event)); - cid = (cidcfgr & EXTI_CIDCFGR_CID_MASK) >> EXTI_CIDCFGR_CID_SHIFT; - if ((cidcfgr & EXTI_CIDCFGR_CFEN_MASK) && cid != EXTI_CID1) - host_data->chips_data[bank].event_reserved |= BIT(i); - } - } -} - -static void stm32_exti_remove_irq(void *data) -{ - struct irq_domain *domain = data; - - irq_domain_remove(domain); -} - -static int stm32_exti_probe(struct platform_device *pdev) -{ - int ret, i; - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct irq_domain *parent_domain, *domain; - struct stm32_exti_host_data *host_data; - const struct stm32_exti_drv_data *drv_data; - - host_data = devm_kzalloc(dev, sizeof(*host_data), GFP_KERNEL); - if (!host_data) - return -ENOMEM; - - dev_set_drvdata(dev, host_data); - host_data->dev = dev; - - /* check for optional hwspinlock which may be not available yet */ - ret = of_hwspin_lock_get_id(np, 0); - if (ret == -EPROBE_DEFER) - /* hwspinlock framework not yet ready */ - return ret; - - if (ret >= 0) { - host_data->hwlock = devm_hwspin_lock_request_specific(dev, ret); - if (!host_data->hwlock) { - dev_err(dev, "Failed to request hwspinlock\n"); - return -EINVAL; - } - } else if (ret != -ENOENT) { - /* note: ENOENT is a valid case (means 'no hwspinlock') */ - dev_err(dev, "Failed to get hwspinlock\n"); - return ret; - } - - /* initialize host_data */ - drv_data = of_device_get_match_data(dev); - if (!drv_data) { - dev_err(dev, "no of match data\n"); - return -ENODEV; - } - host_data->drv_data = drv_data; - - host_data->chips_data = devm_kcalloc(dev, drv_data->bank_nr, - sizeof(*host_data->chips_data), - GFP_KERNEL); - if (!host_data->chips_data) - return -ENOMEM; - - host_data->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(host_data->base)) - return PTR_ERR(host_data->base); - - for (i = 0; i < drv_data->bank_nr; i++) - stm32_exti_chip_init(host_data, i, np); - - stm32_exti_check_rif(host_data); - - parent_domain = irq_find_host(of_irq_find_parent(np)); - if (!parent_domain) { - dev_err(dev, "GIC interrupt-parent not found\n"); - return -EINVAL; - } - - domain = irq_domain_add_hierarchy(parent_domain, 0, - drv_data->bank_nr * IRQS_PER_BANK, - np, &stm32_exti_h_domain_ops, - host_data); - - if (!domain) { - dev_err(dev, "Could not register exti domain\n"); - return -ENOMEM; - } - - ret = devm_add_action_or_reset(dev, stm32_exti_remove_irq, domain); - if (ret) - return ret; - - if (of_property_read_bool(np, "interrupts-extended")) - host_data->dt_has_irqs_desc = true; - - return 0; -} - -/* platform driver only for MP1 */ -static const struct of_device_id stm32_exti_ids[] = { - { .compatible = "st,stm32mp1-exti", .data = &stm32mp1_drv_data}, - { .compatible = "st,stm32mp13-exti", .data = &stm32mp13_drv_data}, - {}, -}; -MODULE_DEVICE_TABLE(of, stm32_exti_ids); - -static const struct dev_pm_ops stm32_exti_dev_pm_ops = { - NOIRQ_SYSTEM_SLEEP_PM_OPS(stm32_exti_h_suspend, stm32_exti_h_resume) -}; - -static struct platform_driver stm32_exti_driver = { - .probe = stm32_exti_probe, - .driver = { - .name = "stm32_exti", - .of_match_table = stm32_exti_ids, - .pm = &stm32_exti_dev_pm_ops, - }, -}; - -static int __init stm32_exti_arch_init(void) -{ - return platform_driver_register(&stm32_exti_driver); -} - -static void __exit stm32_exti_arch_exit(void) -{ - return platform_driver_unregister(&stm32_exti_driver); -} - -arch_initcall(stm32_exti_arch_init); -module_exit(stm32_exti_arch_exit); - -/* no platform driver for F4 and H7 */ static int __init stm32f4_exti_of_init(struct device_node *np, struct device_node *parent) { diff --git a/drivers/irqchip/irq-stm32mp-exti.c b/drivers/irqchip/irq-stm32mp-exti.c new file mode 100644 index 000000000000..33e0cfdea654 --- /dev/null +++ b/drivers/irqchip/irq-stm32mp-exti.c @@ -0,0 +1,729 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Maxime Coquelin 2015 + * Copyright (C) STMicroelectronics 2017-2024 + * Author: Maxime Coquelin <mcoquelin.stm32@gmail.com> + */ + +#include <linux/bitops.h> +#include <linux/hwspinlock.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqchip.h> +#include <linux/irqdomain.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/pm.h> + +#include <dt-bindings/interrupt-controller/arm-gic.h> + +#define IRQS_PER_BANK 32 + +#define HWSPNLCK_TIMEOUT 1000 /* usec */ + +#define EXTI_EnCIDCFGR(n) (0x180 + (n) * 4) +#define EXTI_HWCFGR1 0x3f0 + +/* Register: EXTI_EnCIDCFGR(n) */ +#define EXTI_CIDCFGR_CFEN_MASK BIT(0) +#define EXTI_CIDCFGR_CID_MASK GENMASK(6, 4) +#define EXTI_CIDCFGR_CID_SHIFT 4 + +/* Register: EXTI_HWCFGR1 */ +#define EXTI_HWCFGR1_CIDWIDTH_MASK GENMASK(27, 24) + +#define EXTI_CID1 1 + +struct stm32mp_exti_bank { + u32 imr_ofst; + u32 rtsr_ofst; + u32 ftsr_ofst; + u32 swier_ofst; + u32 rpr_ofst; + u32 fpr_ofst; + u32 trg_ofst; + u32 seccfgr_ofst; +}; + +struct stm32mp_exti_drv_data { + const struct stm32mp_exti_bank **exti_banks; + const u8 *desc_irqs; + u32 bank_nr; +}; + +struct stm32mp_exti_chip_data { + struct stm32mp_exti_host_data *host_data; + const struct stm32mp_exti_bank *reg_bank; + struct raw_spinlock rlock; + u32 wake_active; + u32 mask_cache; + u32 rtsr_cache; + u32 ftsr_cache; + u32 event_reserved; +}; + +struct stm32mp_exti_host_data { + void __iomem *base; + struct device *dev; + struct stm32mp_exti_chip_data *chips_data; + const struct stm32mp_exti_drv_data *drv_data; + struct hwspinlock *hwlock; + /* skip internal desc_irqs array and get it from DT */ + bool dt_has_irqs_desc; +}; + +static const struct stm32mp_exti_bank stm32mp_exti_b1 = { + .imr_ofst = 0x80, + .rtsr_ofst = 0x00, + .ftsr_ofst = 0x04, + .swier_ofst = 0x08, + .rpr_ofst = 0x0C, + .fpr_ofst = 0x10, + .trg_ofst = 0x3EC, + .seccfgr_ofst = 0x14, +}; + +static const struct stm32mp_exti_bank stm32mp_exti_b2 = { + .imr_ofst = 0x90, + .rtsr_ofst = 0x20, + .ftsr_ofst = 0x24, + .swier_ofst = 0x28, + .rpr_ofst = 0x2C, + .fpr_ofst = 0x30, + .trg_ofst = 0x3E8, + .seccfgr_ofst = 0x34, +}; + +static const struct stm32mp_exti_bank stm32mp_exti_b3 = { + .imr_ofst = 0xA0, + .rtsr_ofst = 0x40, + .ftsr_ofst = 0x44, + .swier_ofst = 0x48, + .rpr_ofst = 0x4C, + .fpr_ofst = 0x50, + .trg_ofst = 0x3E4, + .seccfgr_ofst = 0x54, +}; + +static const struct stm32mp_exti_bank *stm32mp_exti_banks[] = { + &stm32mp_exti_b1, + &stm32mp_exti_b2, + &stm32mp_exti_b3, +}; + +static struct irq_chip stm32mp_exti_chip; +static struct irq_chip stm32mp_exti_chip_direct; + +#define EXTI_INVALID_IRQ U8_MAX +#define STM32MP_DESC_IRQ_SIZE (ARRAY_SIZE(stm32mp_exti_banks) * IRQS_PER_BANK) + +/* + * Use some intentionally tricky logic here to initialize the whole array to + * EXTI_INVALID_IRQ, but then override certain fields, requiring us to indicate + * that we "know" that there are overrides in this structure, and we'll need to + * disable that warning from W=1 builds. + */ +__diag_push(); +__diag_ignore_all("-Woverride-init", + "logic to initialize all and then override some is OK"); + +static const u8 stm32mp1_desc_irq[] = { + /* default value */ + [0 ... (STM32MP_DESC_IRQ_SIZE - 1)] = EXTI_INVALID_IRQ, + + [0] = 6, + [1] = 7, + [2] = 8, + [3] = 9, + [4] = 10, + [5] = 23, + [6] = 64, + [7] = 65, + [8] = 66, + [9] = 67, + [10] = 40, + [11] = 42, + [12] = 76, + [13] = 77, + [14] = 121, + [15] = 127, + [16] = 1, + [19] = 3, + [21] = 31, + [22] = 33, + [23] = 72, + [24] = 95, + [25] = 107, + [26] = 37, + [27] = 38, + [28] = 39, + [29] = 71, + [30] = 52, + [31] = 53, + [32] = 82, + [33] = 83, + [46] = 151, + [47] = 93, + [48] = 138, + [50] = 139, + [52] = 140, + [53] = 141, + [54] = 135, + [61] = 100, + [65] = 144, + [68] = 143, + [70] = 62, + [73] = 129, +}; + +static const u8 stm32mp13_desc_irq[] = { + /* default value */ + [0 ... (STM32MP_DESC_IRQ_SIZE - 1)] = EXTI_INVALID_IRQ, + + [0] = 6, + [1] = 7, + [2] = 8, + [3] = 9, + [4] = 10, + [5] = 24, + [6] = 65, + [7] = 66, + [8] = 67, + [9] = 68, + [10] = 41, + [11] = 43, + [12] = 77, + [13] = 78, + [14] = 106, + [15] = 109, + [16] = 1, + [19] = 3, + [21] = 32, + [22] = 34, + [23] = 73, + [24] = 93, + [25] = 114, + [26] = 38, + [27] = 39, + [28] = 40, + [29] = 72, + [30] = 53, + [31] = 54, + [32] = 83, + [33] = 84, + [44] = 96, + [47] = 92, + [48] = 116, + [50] = 117, + [52] = 118, + [53] = 119, + [68] = 63, + [70] = 98, +}; + +__diag_pop(); + +static const struct stm32mp_exti_drv_data stm32mp1_drv_data = { + .exti_banks = stm32mp_exti_banks, + .bank_nr = ARRAY_SIZE(stm32mp_exti_banks), + .desc_irqs = stm32mp1_desc_irq, +}; + +static const struct stm32mp_exti_drv_data stm32mp13_drv_data = { + .exti_banks = stm32mp_exti_banks, + .bank_nr = ARRAY_SIZE(stm32mp_exti_banks), + .desc_irqs = stm32mp13_desc_irq, +}; + +static int stm32mp_exti_convert_type(struct irq_data *d, unsigned int type, u32 *rtsr, u32 *ftsr) +{ + u32 mask = BIT(d->hwirq % IRQS_PER_BANK); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + *rtsr |= mask; + *ftsr &= ~mask; + break; + case IRQ_TYPE_EDGE_FALLING: + *rtsr &= ~mask; + *ftsr |= mask; + break; + case IRQ_TYPE_EDGE_BOTH: + *rtsr |= mask; + *ftsr |= mask; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void stm32mp_chip_suspend(struct stm32mp_exti_chip_data *chip_data, u32 wake_active) +{ + const struct stm32mp_exti_bank *bank = chip_data->reg_bank; + void __iomem *base = chip_data->host_data->base; + + /* save rtsr, ftsr registers */ + chip_data->rtsr_cache = readl_relaxed(base + bank->rtsr_ofst); + chip_data->ftsr_cache = readl_relaxed(base + bank->ftsr_ofst); + + writel_relaxed(wake_active, base + bank->imr_ofst); +} + +static void stm32mp_chip_resume(struct stm32mp_exti_chip_data *chip_data, u32 mask_cache) +{ + const struct stm32mp_exti_bank *bank = chip_data->reg_bank; + void __iomem *base = chip_data->host_data->base; + + /* restore rtsr, ftsr, registers */ + writel_relaxed(chip_data->rtsr_cache, base + bank->rtsr_ofst); + writel_relaxed(chip_data->ftsr_cache, base + bank->ftsr_ofst); + + writel_relaxed(mask_cache, base + bank->imr_ofst); +} + +/* directly set the target bit without reading first. */ +static inline void stm32mp_exti_write_bit(struct irq_data *d, u32 reg) +{ + struct stm32mp_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + void __iomem *base = chip_data->host_data->base; + u32 val = BIT(d->hwirq % IRQS_PER_BANK); + + writel_relaxed(val, base + reg); +} + +static inline u32 stm32mp_exti_set_bit(struct irq_data *d, u32 reg) +{ + struct stm32mp_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + void __iomem *base = chip_data->host_data->base; + u32 val; + + val = readl_relaxed(base + reg); + val |= BIT(d->hwirq % IRQS_PER_BANK); + writel_relaxed(val, base + reg); + + return val; +} + +static inline u32 stm32mp_exti_clr_bit(struct irq_data *d, u32 reg) +{ + struct stm32mp_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + void __iomem *base = chip_data->host_data->base; + u32 val; + + val = readl_relaxed(base + reg); + val &= ~BIT(d->hwirq % IRQS_PER_BANK); + writel_relaxed(val, base + reg); + + return val; +} + +static void stm32mp_exti_eoi(struct irq_data *d) +{ + struct stm32mp_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32mp_exti_bank *bank = chip_data->reg_bank; + + raw_spin_lock(&chip_data->rlock); + + stm32mp_exti_write_bit(d, bank->rpr_ofst); + stm32mp_exti_write_bit(d, bank->fpr_ofst); + + raw_spin_unlock(&chip_data->rlock); + + if (d->parent_data->chip) + irq_chip_eoi_parent(d); +} + +static void stm32mp_exti_mask(struct irq_data *d) +{ + struct stm32mp_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32mp_exti_bank *bank = chip_data->reg_bank; + + raw_spin_lock(&chip_data->rlock); + chip_data->mask_cache = stm32mp_exti_clr_bit(d, bank->imr_ofst); + raw_spin_unlock(&chip_data->rlock); + + if (d->parent_data->chip) + irq_chip_mask_parent(d); +} + +static void stm32mp_exti_unmask(struct irq_data *d) +{ + struct stm32mp_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32mp_exti_bank *bank = chip_data->reg_bank; + + raw_spin_lock(&chip_data->rlock); + chip_data->mask_cache = stm32mp_exti_set_bit(d, bank->imr_ofst); + raw_spin_unlock(&chip_data->rlock); + + if (d->parent_data->chip) + irq_chip_unmask_parent(d); +} + +static int stm32mp_exti_set_type(struct irq_data *d, unsigned int type) +{ + struct stm32mp_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32mp_exti_bank *bank = chip_data->reg_bank; + struct hwspinlock *hwlock = chip_data->host_data->hwlock; + void __iomem *base = chip_data->host_data->base; + u32 rtsr, ftsr; + int err; + + raw_spin_lock(&chip_data->rlock); + + if (hwlock) { + err = hwspin_lock_timeout_in_atomic(hwlock, HWSPNLCK_TIMEOUT); + if (err) { + pr_err("%s can't get hwspinlock (%d)\n", __func__, err); + goto unlock; + } + } + + rtsr = readl_relaxed(base + bank->rtsr_ofst); + ftsr = readl_relaxed(base + bank->ftsr_ofst); + + err = stm32mp_exti_convert_type(d, type, &rtsr, &ftsr); + if (!err) { + writel_relaxed(rtsr, base + bank->rtsr_ofst); + writel_relaxed(ftsr, base + bank->ftsr_ofst); + } + + if (hwlock) + hwspin_unlock_in_atomic(hwlock); +unlock: + raw_spin_unlock(&chip_data->rlock); + return err; +} + +static int stm32mp_exti_set_wake(struct irq_data *d, unsigned int on) +{ + struct stm32mp_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + u32 mask = BIT(d->hwirq % IRQS_PER_BANK); + + raw_spin_lock(&chip_data->rlock); + + if (on) + chip_data->wake_active |= mask; + else + chip_data->wake_active &= ~mask; + + raw_spin_unlock(&chip_data->rlock); + + return 0; +} + +static int stm32mp_exti_set_affinity(struct irq_data *d, const struct cpumask *dest, bool force) +{ + if (d->parent_data->chip) + return irq_chip_set_affinity_parent(d, dest, force); + + return IRQ_SET_MASK_OK_DONE; +} + +static int stm32mp_exti_suspend(struct device *dev) +{ + struct stm32mp_exti_host_data *host_data = dev_get_drvdata(dev); + struct stm32mp_exti_chip_data *chip_data; + int i; + + for (i = 0; i < host_data->drv_data->bank_nr; i++) { + chip_data = &host_data->chips_data[i]; + stm32mp_chip_suspend(chip_data, chip_data->wake_active); + } + + return 0; +} + +static int stm32mp_exti_resume(struct device *dev) +{ + struct stm32mp_exti_host_data *host_data = dev_get_drvdata(dev); + struct stm32mp_exti_chip_data *chip_data; + int i; + + for (i = 0; i < host_data->drv_data->bank_nr; i++) { + chip_data = &host_data->chips_data[i]; + stm32mp_chip_resume(chip_data, chip_data->mask_cache); + } + + return 0; +} + +static int stm32mp_exti_retrigger(struct irq_data *d) +{ + struct stm32mp_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32mp_exti_bank *bank = chip_data->reg_bank; + void __iomem *base = chip_data->host_data->base; + u32 mask = BIT(d->hwirq % IRQS_PER_BANK); + + writel_relaxed(mask, base + bank->swier_ofst); + + return 0; +} + +static struct irq_chip stm32mp_exti_chip = { + .name = "stm32mp-exti", + .irq_eoi = stm32mp_exti_eoi, + .irq_mask = stm32mp_exti_mask, + .irq_unmask = stm32mp_exti_unmask, + .irq_retrigger = stm32mp_exti_retrigger, + .irq_set_type = stm32mp_exti_set_type, + .irq_set_wake = stm32mp_exti_set_wake, + .flags = IRQCHIP_MASK_ON_SUSPEND, + .irq_set_affinity = IS_ENABLED(CONFIG_SMP) ? stm32mp_exti_set_affinity : NULL, +}; + +static struct irq_chip stm32mp_exti_chip_direct = { + .name = "stm32mp-exti-direct", + .irq_eoi = irq_chip_eoi_parent, + .irq_ack = irq_chip_ack_parent, + .irq_mask = stm32mp_exti_mask, + .irq_unmask = stm32mp_exti_unmask, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_wake = stm32mp_exti_set_wake, + .flags = IRQCHIP_MASK_ON_SUSPEND, + .irq_set_affinity = IS_ENABLED(CONFIG_SMP) ? irq_chip_set_affinity_parent : NULL, +}; + +static int stm32mp_exti_domain_alloc(struct irq_domain *dm, + unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct stm32mp_exti_host_data *host_data = dm->host_data; + struct stm32mp_exti_chip_data *chip_data; + struct irq_fwspec *fwspec = data; + struct irq_fwspec p_fwspec; + irq_hw_number_t hwirq; + struct irq_chip *chip; + u32 event_trg; + u8 desc_irq; + int bank; + + hwirq = fwspec->param[0]; + if (hwirq >= host_data->drv_data->bank_nr * IRQS_PER_BANK) + return -EINVAL; + + bank = hwirq / IRQS_PER_BANK; + chip_data = &host_data->chips_data[bank]; + + /* Check if event is reserved (Secure) */ + if (chip_data->event_reserved & BIT(hwirq % IRQS_PER_BANK)) { + dev_err(host_data->dev, "event %lu is reserved, secure\n", hwirq); + return -EPERM; + } + + event_trg = readl_relaxed(host_data->base + chip_data->reg_bank->trg_ofst); + chip = (event_trg & BIT(hwirq % IRQS_PER_BANK)) ? + &stm32mp_exti_chip : &stm32mp_exti_chip_direct; + + irq_domain_set_hwirq_and_chip(dm, virq, hwirq, chip, chip_data); + + if (host_data->dt_has_irqs_desc) { + struct of_phandle_args out_irq; + int ret; + + ret = of_irq_parse_one(host_data->dev->of_node, hwirq, &out_irq); + if (ret) + return ret; + /* we only support one parent, so far */ + if (of_node_to_fwnode(out_irq.np) != dm->parent->fwnode) + return -EINVAL; + + of_phandle_args_to_fwspec(out_irq.np, out_irq.args, + out_irq.args_count, &p_fwspec); + + return irq_domain_alloc_irqs_parent(dm, virq, 1, &p_fwspec); + } + + if (!host_data->drv_data->desc_irqs) + return -EINVAL; + + desc_irq = host_data->drv_data->desc_irqs[hwirq]; + if (desc_irq != EXTI_INVALID_IRQ) { + p_fwspec.fwnode = dm->parent->fwnode; + p_fwspec.param_count = 3; + p_fwspec.param[0] = GIC_SPI; + p_fwspec.param[1] = desc_irq; + p_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; + + return irq_domain_alloc_irqs_parent(dm, virq, 1, &p_fwspec); + } + + return 0; +} + +static struct stm32mp_exti_chip_data *stm32mp_exti_chip_init(struct stm32mp_exti_host_data *h_data, + u32 bank_idx, struct device_node *node) +{ + struct stm32mp_exti_chip_data *chip_data; + const struct stm32mp_exti_bank *bank; + void __iomem *base = h_data->base; + + bank = h_data->drv_data->exti_banks[bank_idx]; + chip_data = &h_data->chips_data[bank_idx]; + chip_data->host_data = h_data; + chip_data->reg_bank = bank; + + raw_spin_lock_init(&chip_data->rlock); + + /* + * This IP has no reset, so after hot reboot we should + * clear registers to avoid residue + */ + writel_relaxed(0, base + bank->imr_ofst); + + /* reserve Secure events */ + chip_data->event_reserved = readl_relaxed(base + bank->seccfgr_ofst); + + pr_info("%pOF: bank%d\n", node, bank_idx); + + return chip_data; +} + +static const struct irq_domain_ops stm32mp_exti_domain_ops = { + .alloc = stm32mp_exti_domain_alloc, + .free = irq_domain_free_irqs_common, + .xlate = irq_domain_xlate_twocell, +}; + +static void stm32mp_exti_check_rif(struct stm32mp_exti_host_data *host_data) +{ + unsigned int bank, i, event; + u32 cid, cidcfgr, hwcfgr1; + + /* quit on CID not supported */ + hwcfgr1 = readl_relaxed(host_data->base + EXTI_HWCFGR1); + if ((hwcfgr1 & EXTI_HWCFGR1_CIDWIDTH_MASK) == 0) + return; + + for (bank = 0; bank < host_data->drv_data->bank_nr; bank++) { + for (i = 0; i < IRQS_PER_BANK; i++) { + event = bank * IRQS_PER_BANK + i; + cidcfgr = readl_relaxed(host_data->base + EXTI_EnCIDCFGR(event)); + cid = (cidcfgr & EXTI_CIDCFGR_CID_MASK) >> EXTI_CIDCFGR_CID_SHIFT; + if ((cidcfgr & EXTI_CIDCFGR_CFEN_MASK) && cid != EXTI_CID1) + host_data->chips_data[bank].event_reserved |= BIT(i); + } + } +} + +static void stm32mp_exti_remove_irq(void *data) +{ + struct irq_domain *domain = data; + + irq_domain_remove(domain); +} + +static int stm32mp_exti_probe(struct platform_device *pdev) +{ + const struct stm32mp_exti_drv_data *drv_data; + struct irq_domain *parent_domain, *domain; + struct stm32mp_exti_host_data *host_data; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + int ret, i; + + host_data = devm_kzalloc(dev, sizeof(*host_data), GFP_KERNEL); + if (!host_data) + return -ENOMEM; + + dev_set_drvdata(dev, host_data); + host_data->dev = dev; + + /* check for optional hwspinlock which may be not available yet */ + ret = of_hwspin_lock_get_id(np, 0); + if (ret == -EPROBE_DEFER) + /* hwspinlock framework not yet ready */ + return ret; + + if (ret >= 0) { + host_data->hwlock = devm_hwspin_lock_request_specific(dev, ret); + if (!host_data->hwlock) { + dev_err(dev, "Failed to request hwspinlock\n"); + return -EINVAL; + } + } else if (ret != -ENOENT) { + /* note: ENOENT is a valid case (means 'no hwspinlock') */ + dev_err(dev, "Failed to get hwspinlock\n"); + return ret; + } + + /* initialize host_data */ + drv_data = of_device_get_match_data(dev); + if (!drv_data) { + dev_err(dev, "no of match data\n"); + return -ENODEV; + } + host_data->drv_data = drv_data; + + host_data->chips_data = devm_kcalloc(dev, drv_data->bank_nr, + sizeof(*host_data->chips_data), + GFP_KERNEL); + if (!host_data->chips_data) + return -ENOMEM; + + host_data->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(host_data->base)) + return PTR_ERR(host_data->base); + + for (i = 0; i < drv_data->bank_nr; i++) + stm32mp_exti_chip_init(host_data, i, np); + + stm32mp_exti_check_rif(host_data); + + parent_domain = irq_find_host(of_irq_find_parent(np)); + if (!parent_domain) { + dev_err(dev, "GIC interrupt-parent not found\n"); + return -EINVAL; + } + + domain = irq_domain_add_hierarchy(parent_domain, 0, + drv_data->bank_nr * IRQS_PER_BANK, + np, &stm32mp_exti_domain_ops, + host_data); + + if (!domain) { + dev_err(dev, "Could not register exti domain\n"); + return -ENOMEM; + } + + ret = devm_add_action_or_reset(dev, stm32mp_exti_remove_irq, domain); + if (ret) + return ret; + + if (of_property_read_bool(np, "interrupts-extended")) + host_data->dt_has_irqs_desc = true; + + return 0; +} + +static const struct of_device_id stm32mp_exti_ids[] = { + { .compatible = "st,stm32mp1-exti", .data = &stm32mp1_drv_data}, + { .compatible = "st,stm32mp13-exti", .data = &stm32mp13_drv_data}, + {}, +}; +MODULE_DEVICE_TABLE(of, stm32mp_exti_ids); + +static const struct dev_pm_ops stm32mp_exti_dev_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(stm32mp_exti_suspend, stm32mp_exti_resume) +}; + +static struct platform_driver stm32mp_exti_driver = { + .probe = stm32mp_exti_probe, + .driver = { + .name = "stm32mp_exti", + .of_match_table = stm32mp_exti_ids, + .pm = &stm32mp_exti_dev_pm_ops, + }, +}; + +module_platform_driver(stm32mp_exti_driver); + +MODULE_AUTHOR("Maxime Coquelin <mcoquelin.stm32@gmail.com>"); +MODULE_DESCRIPTION("STM32MP EXTI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/irqchip/irq-ts4800.c b/drivers/irqchip/irq-ts4800.c index 57f610dab6b8..b5dddb3c1568 100644 --- a/drivers/irqchip/irq-ts4800.c +++ b/drivers/irqchip/irq-ts4800.c @@ -163,5 +163,6 @@ static struct platform_driver ts4800_ic_driver = { module_platform_driver(ts4800_ic_driver); MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>"); +MODULE_DESCRIPTION("Multiplexed-IRQs driver for TS-4800's FPGA"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:ts4800_irqc"); diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 3b8842c4a340..4eed97295927 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -276,6 +276,14 @@ config SPRD_MBOX to send message between application processors and MCU. Say Y here if you want to build the Spreatrum mailbox controller driver. +config QCOM_CPUCP_MBOX + tristate "Qualcomm Technologies, Inc. CPUCP mailbox driver" + depends on (ARCH_QCOM || COMPILE_TEST) && 64BIT + help + Qualcomm Technologies, Inc. CPUSS Control Processor (CPUCP) mailbox + controller driver enables communication between AP and CPUCP. Say + Y here if you want to build this driver. + config QCOM_IPCC tristate "Qualcomm Technologies, Inc. IPCC driver" depends on ARCH_QCOM || COMPILE_TEST diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 5cf2f54debaf..3c3c27d54c13 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -61,4 +61,6 @@ obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o +obj-$(CONFIG_QCOM_CPUCP_MBOX) += qcom-cpucp-mbox.o + obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o diff --git a/drivers/mailbox/bcm-pdc-mailbox.c b/drivers/mailbox/bcm-pdc-mailbox.c index 242e7504a628..a873672a9082 100644 --- a/drivers/mailbox/bcm-pdc-mailbox.c +++ b/drivers/mailbox/bcm-pdc-mailbox.c @@ -158,10 +158,6 @@ enum pdc_hw { PDC_HW /* PDC/MDE hardware (i.e. Northstar 2, Pegasus) */ }; -struct pdc_dma_map { - void *ctx; /* opaque context associated with frame */ -}; - /* dma descriptor */ struct dma64dd { u32 ctrl1; /* misc control bits */ diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 933727f89431..d17efb1dd0cb 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -225,6 +225,8 @@ static int imx_mu_generic_tx(struct imx_mu_priv *priv, void *data) { u32 *arg = data; + u32 val; + int ret; switch (cp->type) { case IMX_MU_TYPE_TX: @@ -236,7 +238,13 @@ static int imx_mu_generic_tx(struct imx_mu_priv *priv, queue_work(system_bh_wq, &cp->txdb_work); break; case IMX_MU_TYPE_TXDB_V2: - imx_mu_xcr_rmw(priv, IMX_MU_GCR, IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx), 0); + imx_mu_write(priv, IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx), + priv->dcfg->xCR[IMX_MU_GCR]); + ret = readl_poll_timeout(priv->base + priv->dcfg->xCR[IMX_MU_GCR], val, + !(val & IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx)), + 0, 1000); + if (ret) + dev_warn_ratelimited(priv->dev, "channel type: %d failure\n", cp->type); break; default: dev_warn_ratelimited(priv->dev, "Send data on wrong channel type: %d\n", cp->type); diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index 4aa394e91109..4bff73532085 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -22,7 +22,6 @@ #define CMDQ_OP_CODE_MASK (0xff << CMDQ_OP_CODE_SHIFT) #define CMDQ_NUM_CMD(t) (t->cmd_buf_size / CMDQ_INST_SIZE) -#define CMDQ_GCE_NUM_MAX (2) #define CMDQ_CURR_IRQ_STATUS 0x10 #define CMDQ_SYNC_TOKEN_UPDATE 0x68 @@ -81,7 +80,7 @@ struct cmdq { u32 irq_mask; const struct gce_plat *pdata; struct cmdq_thread *thread; - struct clk_bulk_data clocks[CMDQ_GCE_NUM_MAX]; + struct clk_bulk_data *clocks; bool suspended; }; @@ -578,16 +577,64 @@ static struct mbox_chan *cmdq_xlate(struct mbox_controller *mbox, return &mbox->chans[ind]; } +static int cmdq_get_clocks(struct device *dev, struct cmdq *cmdq) +{ + static const char * const gce_name = "gce"; + struct device_node *node, *parent = dev->of_node->parent; + struct clk_bulk_data *clks; + + cmdq->clocks = devm_kcalloc(dev, cmdq->pdata->gce_num, + sizeof(cmdq->clocks), GFP_KERNEL); + if (!cmdq->clocks) + return -ENOMEM; + + if (cmdq->pdata->gce_num == 1) { + clks = &cmdq->clocks[0]; + + clks->id = gce_name; + clks->clk = devm_clk_get(dev, NULL); + if (IS_ERR(clks->clk)) + return dev_err_probe(dev, PTR_ERR(clks->clk), + "failed to get gce clock\n"); + + return 0; + } + + /* + * If there is more than one GCE, get the clocks for the others too, + * as the clock of the main GCE must be enabled for additional IPs + * to be reachable. + */ + for_each_child_of_node(parent, node) { + int alias_id = of_alias_get_id(node, gce_name); + + if (alias_id < 0 || alias_id >= cmdq->pdata->gce_num) + continue; + + clks = &cmdq->clocks[alias_id]; + + clks->id = devm_kasprintf(dev, GFP_KERNEL, "gce%d", alias_id); + if (!clks->id) { + of_node_put(node); + return -ENOMEM; + } + + clks->clk = of_clk_get(node, 0); + if (IS_ERR(clks->clk)) { + of_node_put(node); + return dev_err_probe(dev, PTR_ERR(clks->clk), + "failed to get gce%d clock\n", alias_id); + } + } + + return 0; +} + static int cmdq_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct cmdq *cmdq; int err, i; - struct device_node *phandle = dev->of_node; - struct device_node *node; - int alias_id = 0; - static const char * const clk_name = "gce"; - static const char * const clk_names[] = { "gce0", "gce1" }; cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL); if (!cmdq) @@ -612,29 +659,9 @@ static int cmdq_probe(struct platform_device *pdev) dev_dbg(dev, "cmdq device: addr:0x%p, va:0x%p, irq:%d\n", dev, cmdq->base, cmdq->irq); - if (cmdq->pdata->gce_num > 1) { - for_each_child_of_node(phandle->parent, node) { - alias_id = of_alias_get_id(node, clk_name); - if (alias_id >= 0 && alias_id < cmdq->pdata->gce_num) { - cmdq->clocks[alias_id].id = clk_names[alias_id]; - cmdq->clocks[alias_id].clk = of_clk_get(node, 0); - if (IS_ERR(cmdq->clocks[alias_id].clk)) { - of_node_put(node); - return dev_err_probe(dev, - PTR_ERR(cmdq->clocks[alias_id].clk), - "failed to get gce clk: %d\n", - alias_id); - } - } - } - } else { - cmdq->clocks[alias_id].id = clk_name; - cmdq->clocks[alias_id].clk = devm_clk_get(&pdev->dev, clk_name); - if (IS_ERR(cmdq->clocks[alias_id].clk)) { - return dev_err_probe(dev, PTR_ERR(cmdq->clocks[alias_id].clk), - "failed to get gce clk\n"); - } - } + err = cmdq_get_clocks(dev, cmdq); + if (err) + return err; cmdq->mbox.dev = dev; cmdq->mbox.chans = devm_kcalloc(dev, cmdq->pdata->thread_nr, @@ -662,12 +689,6 @@ static int cmdq_probe(struct platform_device *pdev) cmdq->mbox.chans[i].con_priv = (void *)&cmdq->thread[i]; } - err = devm_mbox_controller_register(dev, &cmdq->mbox); - if (err < 0) { - dev_err(dev, "failed to register mailbox: %d\n", err); - return err; - } - platform_set_drvdata(pdev, cmdq); WARN_ON(clk_bulk_prepare(cmdq->pdata->gce_num, cmdq->clocks)); @@ -695,6 +716,12 @@ static int cmdq_probe(struct platform_device *pdev) pm_runtime_set_autosuspend_delay(dev, CMDQ_MBOX_AUTOSUSPEND_DELAY_MS); pm_runtime_use_autosuspend(dev); + err = devm_mbox_controller_register(dev, &cmdq->mbox); + if (err < 0) { + dev_err(dev, "failed to register mailbox: %d\n", err); + return err; + } + return 0; } @@ -790,4 +817,5 @@ static void __exit cmdq_drv_exit(void) subsys_initcall(cmdq_drv_init); module_exit(cmdq_drv_exit); +MODULE_DESCRIPTION("Mediatek Command Queue(CMDQ) Mailbox driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c index 46747559b438..7a87424657a1 100644 --- a/drivers/mailbox/omap-mailbox.c +++ b/drivers/mailbox/omap-mailbox.c @@ -230,7 +230,8 @@ static int omap_mbox_startup(struct omap_mbox *mbox) int ret = 0; ret = request_threaded_irq(mbox->irq, NULL, mbox_interrupt, - IRQF_ONESHOT, mbox->name, mbox); + IRQF_SHARED | IRQF_ONESHOT, mbox->name, + mbox); if (unlikely(ret)) { pr_err("failed to register mailbox interrupt:%d\n", ret); return ret; diff --git a/drivers/mailbox/qcom-cpucp-mbox.c b/drivers/mailbox/qcom-cpucp-mbox.c new file mode 100644 index 000000000000..e5437c294803 --- /dev/null +++ b/drivers/mailbox/qcom-cpucp-mbox.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/bitops.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/mailbox_controller.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#define APSS_CPUCP_IPC_CHAN_SUPPORTED 3 +#define APSS_CPUCP_MBOX_CMD_OFF 0x4 + +/* Tx Registers */ +#define APSS_CPUCP_TX_MBOX_CMD(i) (0x100 + ((i) * 8)) + +/* Rx Registers */ +#define APSS_CPUCP_RX_MBOX_CMD(i) (0x100 + ((i) * 8)) +#define APSS_CPUCP_RX_MBOX_MAP 0x4000 +#define APSS_CPUCP_RX_MBOX_STAT 0x4400 +#define APSS_CPUCP_RX_MBOX_CLEAR 0x4800 +#define APSS_CPUCP_RX_MBOX_EN 0x4c00 +#define APSS_CPUCP_RX_MBOX_CMD_MASK GENMASK_ULL(63, 0) + +/** + * struct qcom_cpucp_mbox - Holder for the mailbox driver + * @chans: The mailbox channel + * @mbox: The mailbox controller + * @tx_base: Base address of the CPUCP tx registers + * @rx_base: Base address of the CPUCP rx registers + */ +struct qcom_cpucp_mbox { + struct mbox_chan chans[APSS_CPUCP_IPC_CHAN_SUPPORTED]; + struct mbox_controller mbox; + void __iomem *tx_base; + void __iomem *rx_base; +}; + +static inline int channel_number(struct mbox_chan *chan) +{ + return chan - chan->mbox->chans; +} + +static irqreturn_t qcom_cpucp_mbox_irq_fn(int irq, void *data) +{ + struct qcom_cpucp_mbox *cpucp = data; + u64 status; + int i; + + status = readq(cpucp->rx_base + APSS_CPUCP_RX_MBOX_STAT); + + for_each_set_bit(i, (unsigned long *)&status, APSS_CPUCP_IPC_CHAN_SUPPORTED) { + u32 val = readl(cpucp->rx_base + APSS_CPUCP_RX_MBOX_CMD(i) + APSS_CPUCP_MBOX_CMD_OFF); + struct mbox_chan *chan = &cpucp->chans[i]; + unsigned long flags; + + /* Provide mutual exclusion with changes to chan->cl */ + spin_lock_irqsave(&chan->lock, flags); + if (chan->cl) + mbox_chan_received_data(chan, &val); + writeq(BIT(i), cpucp->rx_base + APSS_CPUCP_RX_MBOX_CLEAR); + spin_unlock_irqrestore(&chan->lock, flags); + } + + return IRQ_HANDLED; +} + +static int qcom_cpucp_mbox_startup(struct mbox_chan *chan) +{ + struct qcom_cpucp_mbox *cpucp = container_of(chan->mbox, struct qcom_cpucp_mbox, mbox); + unsigned long chan_id = channel_number(chan); + u64 val; + + val = readq(cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN); + val |= BIT(chan_id); + writeq(val, cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN); + + return 0; +} + +static void qcom_cpucp_mbox_shutdown(struct mbox_chan *chan) +{ + struct qcom_cpucp_mbox *cpucp = container_of(chan->mbox, struct qcom_cpucp_mbox, mbox); + unsigned long chan_id = channel_number(chan); + u64 val; + + val = readq(cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN); + val &= ~BIT(chan_id); + writeq(val, cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN); +} + +static int qcom_cpucp_mbox_send_data(struct mbox_chan *chan, void *data) +{ + struct qcom_cpucp_mbox *cpucp = container_of(chan->mbox, struct qcom_cpucp_mbox, mbox); + unsigned long chan_id = channel_number(chan); + u32 *val = data; + + writel(*val, cpucp->tx_base + APSS_CPUCP_TX_MBOX_CMD(chan_id) + APSS_CPUCP_MBOX_CMD_OFF); + + return 0; +} + +static const struct mbox_chan_ops qcom_cpucp_mbox_chan_ops = { + .startup = qcom_cpucp_mbox_startup, + .send_data = qcom_cpucp_mbox_send_data, + .shutdown = qcom_cpucp_mbox_shutdown +}; + +static int qcom_cpucp_mbox_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct qcom_cpucp_mbox *cpucp; + struct mbox_controller *mbox; + int irq, ret; + + cpucp = devm_kzalloc(dev, sizeof(*cpucp), GFP_KERNEL); + if (!cpucp) + return -ENOMEM; + + cpucp->rx_base = devm_of_iomap(dev, dev->of_node, 0, NULL); + if (IS_ERR(cpucp->rx_base)) + return PTR_ERR(cpucp->rx_base); + + cpucp->tx_base = devm_of_iomap(dev, dev->of_node, 1, NULL); + if (IS_ERR(cpucp->tx_base)) + return PTR_ERR(cpucp->tx_base); + + writeq(0, cpucp->rx_base + APSS_CPUCP_RX_MBOX_EN); + writeq(0, cpucp->rx_base + APSS_CPUCP_RX_MBOX_CLEAR); + writeq(0, cpucp->rx_base + APSS_CPUCP_RX_MBOX_MAP); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, qcom_cpucp_mbox_irq_fn, + IRQF_TRIGGER_HIGH, "apss_cpucp_mbox", cpucp); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to register irq: %d\n", irq); + + writeq(APSS_CPUCP_RX_MBOX_CMD_MASK, cpucp->rx_base + APSS_CPUCP_RX_MBOX_MAP); + + mbox = &cpucp->mbox; + mbox->dev = dev; + mbox->num_chans = APSS_CPUCP_IPC_CHAN_SUPPORTED; + mbox->chans = cpucp->chans; + mbox->ops = &qcom_cpucp_mbox_chan_ops; + + ret = devm_mbox_controller_register(dev, mbox); + if (ret) + return dev_err_probe(dev, ret, "Failed to create mailbox\n"); + + return 0; +} + +static const struct of_device_id qcom_cpucp_mbox_of_match[] = { + { .compatible = "qcom,x1e80100-cpucp-mbox" }, + {} +}; +MODULE_DEVICE_TABLE(of, qcom_cpucp_mbox_of_match); + +static struct platform_driver qcom_cpucp_mbox_driver = { + .probe = qcom_cpucp_mbox_probe, + .driver = { + .name = "qcom_cpucp_mbox", + .of_match_table = qcom_cpucp_mbox_of_match, + }, +}; + +static int __init qcom_cpucp_mbox_init(void) +{ + return platform_driver_register(&qcom_cpucp_mbox_driver); +} +core_initcall(qcom_cpucp_mbox_init); + +static void __exit qcom_cpucp_mbox_exit(void) +{ + platform_driver_unregister(&qcom_cpucp_mbox_driver); +} +module_exit(qcom_cpucp_mbox_exit); + +MODULE_DESCRIPTION("QTI CPUCP MBOX Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mailbox/zynqmp-ipi-mailbox.c b/drivers/mailbox/zynqmp-ipi-mailbox.c index 4acf5612487c..521d08b9ab47 100644 --- a/drivers/mailbox/zynqmp-ipi-mailbox.c +++ b/drivers/mailbox/zynqmp-ipi-mailbox.c @@ -64,6 +64,13 @@ #define MAX_SGI 16 +/* + * Module parameters + */ +static int tx_poll_period = 5; +module_param_named(tx_poll_period, tx_poll_period, int, 0644); +MODULE_PARM_DESC(tx_poll_period, "Poll period waiting for ack after send."); + /** * struct zynqmp_ipi_mchan - Description of a Xilinx ZynqMP IPI mailbox channel * @is_opened: indicate if the IPI channel is opened @@ -537,7 +544,7 @@ static int zynqmp_ipi_mbox_probe(struct zynqmp_ipi_mbox *ipi_mbox, mbox->num_chans = 2; mbox->txdone_irq = false; mbox->txdone_poll = true; - mbox->txpoll_period = 5; + mbox->txpoll_period = tx_poll_period; mbox->of_xlate = zynqmp_ipi_of_xlate; chans = devm_kzalloc(mdev, 2 * sizeof(*chans), GFP_KERNEL); if (!chans) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index abc478b649b7..97fab2087df8 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -11,6 +11,7 @@ #include "dm-uevent.h" #include "dm-ima.h" +#include <linux/bio-integrity.h> #include <linux/init.h> #include <linux/module.h> #include <linux/mutex.h> diff --git a/drivers/md/md-cluster.c b/drivers/md/md-cluster.c index c1ea214bfc91..1d0db62f0351 100644 --- a/drivers/md/md-cluster.c +++ b/drivers/md/md-cluster.c @@ -15,6 +15,7 @@ #define LVB_SIZE 64 #define NEW_DEV_TIMEOUT 5000 +#define WAIT_DLM_LOCK_TIMEOUT (30 * HZ) struct dlm_lock_resource { dlm_lockspace_t *ls; @@ -56,6 +57,7 @@ struct resync_info { #define MD_CLUSTER_ALREADY_IN_CLUSTER 6 #define MD_CLUSTER_PENDING_RECV_EVENT 7 #define MD_CLUSTER_HOLDING_MUTEX_FOR_RECVD 8 +#define MD_CLUSTER_WAITING_FOR_SYNC 9 struct md_cluster_info { struct mddev *mddev; /* the md device which md_cluster_info belongs to */ @@ -91,6 +93,7 @@ struct md_cluster_info { sector_t sync_hi; }; +/* For compatibility, add the new msg_type at the end. */ enum msg_type { METADATA_UPDATED = 0, RESYNCING, @@ -100,6 +103,7 @@ enum msg_type { BITMAP_NEEDS_SYNC, CHANGE_CAPACITY, BITMAP_RESIZE, + RESYNCING_START, }; struct cluster_msg { @@ -130,8 +134,13 @@ static int dlm_lock_sync(struct dlm_lock_resource *res, int mode) 0, sync_ast, res, res->bast); if (ret) return ret; - wait_event(res->sync_locking, res->sync_locking_done); + ret = wait_event_timeout(res->sync_locking, res->sync_locking_done, + WAIT_DLM_LOCK_TIMEOUT); res->sync_locking_done = false; + if (!ret) { + pr_err("locking DLM '%s' timeout!\n", res->name); + return -EBUSY; + } if (res->lksb.sb_status == 0) res->mode = mode; return res->lksb.sb_status; @@ -455,6 +464,7 @@ static void process_suspend_info(struct mddev *mddev, clear_bit(MD_RESYNCING_REMOTE, &mddev->recovery); remove_suspend_info(mddev, slot); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + clear_bit(MD_CLUSTER_WAITING_FOR_SYNC, &cinfo->state); md_wakeup_thread(mddev->thread); return; } @@ -525,6 +535,7 @@ static int process_add_new_disk(struct mddev *mddev, struct cluster_msg *cmsg) res = -1; } clear_bit(MD_CLUSTER_WAITING_FOR_NEWDISK, &cinfo->state); + set_bit(MD_CLUSTER_WAITING_FOR_SYNC, &cinfo->state); return res; } @@ -593,6 +604,9 @@ static int process_recvd_msg(struct mddev *mddev, struct cluster_msg *msg) case CHANGE_CAPACITY: set_capacity_and_notify(mddev->gendisk, mddev->array_sectors); break; + case RESYNCING_START: + clear_bit(MD_CLUSTER_WAITING_FOR_SYNC, &mddev->cluster_info->state); + break; case RESYNCING: set_bit(MD_RESYNCING_REMOTE, &mddev->recovery); process_suspend_info(mddev, le32_to_cpu(msg->slot), @@ -743,7 +757,7 @@ static void unlock_comm(struct md_cluster_info *cinfo) */ static int __sendmsg(struct md_cluster_info *cinfo, struct cluster_msg *cmsg) { - int error; + int error, unlock_error; int slot = cinfo->slot_number - 1; cmsg->slot = cpu_to_le32(slot); @@ -751,7 +765,7 @@ static int __sendmsg(struct md_cluster_info *cinfo, struct cluster_msg *cmsg) error = dlm_lock_sync(cinfo->message_lockres, DLM_LOCK_EX); if (error) { pr_err("md-cluster: failed to get EX on MESSAGE (%d)\n", error); - goto failed_message; + return error; } memcpy(cinfo->message_lockres->lksb.sb_lvbptr, (void *)cmsg, @@ -781,14 +795,10 @@ static int __sendmsg(struct md_cluster_info *cinfo, struct cluster_msg *cmsg) } failed_ack: - error = dlm_unlock_sync(cinfo->message_lockres); - if (unlikely(error != 0)) { + while ((unlock_error = dlm_unlock_sync(cinfo->message_lockres))) pr_err("md-cluster: failed convert to NL on MESSAGE(%d)\n", - error); - /* in case the message can't be released due to some reason */ - goto failed_ack; - } -failed_message: + unlock_error); + return error; } @@ -1343,6 +1353,23 @@ static void resync_info_get(struct mddev *mddev, sector_t *lo, sector_t *hi) spin_unlock_irq(&cinfo->suspend_lock); } +static int resync_status_get(struct mddev *mddev) +{ + struct md_cluster_info *cinfo = mddev->cluster_info; + + return test_bit(MD_CLUSTER_WAITING_FOR_SYNC, &cinfo->state); +} + +static int resync_start_notify(struct mddev *mddev) +{ + struct md_cluster_info *cinfo = mddev->cluster_info; + struct cluster_msg cmsg = {0}; + + cmsg.type = cpu_to_le32(RESYNCING_START); + + return sendmsg(cinfo, &cmsg, 0); +} + static int resync_info_update(struct mddev *mddev, sector_t lo, sector_t hi) { struct md_cluster_info *cinfo = mddev->cluster_info; @@ -1577,6 +1604,8 @@ static const struct md_cluster_operations cluster_ops = { .resync_start = resync_start, .resync_finish = resync_finish, .resync_info_update = resync_info_update, + .resync_start_notify = resync_start_notify, + .resync_status_get = resync_status_get, .resync_info_get = resync_info_get, .metadata_update_start = metadata_update_start, .metadata_update_finish = metadata_update_finish, diff --git a/drivers/md/md-cluster.h b/drivers/md/md-cluster.h index a78e3021775d..470bf18ffde5 100644 --- a/drivers/md/md-cluster.h +++ b/drivers/md/md-cluster.h @@ -14,6 +14,8 @@ struct md_cluster_operations { int (*leave)(struct mddev *mddev); int (*slot_number)(struct mddev *mddev); int (*resync_info_update)(struct mddev *mddev, sector_t lo, sector_t hi); + int (*resync_start_notify)(struct mddev *mddev); + int (*resync_status_get)(struct mddev *mddev); void (*resync_info_get)(struct mddev *mddev, sector_t *lo, sector_t *hi); int (*metadata_update_start)(struct mddev *mddev); int (*metadata_update_finish)(struct mddev *mddev); diff --git a/drivers/md/md.c b/drivers/md/md.c index 64693913ed18..d3a837506a36 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -8978,7 +8978,8 @@ void md_do_sync(struct md_thread *thread) * This will mean we have to start checking from the beginning again. * */ - + if (mddev_is_clustered(mddev)) + md_cluster_ops->resync_start_notify(mddev); do { int mddev2_minor = -1; mddev->curr_resync = MD_RESYNC_DELAYED; @@ -9992,8 +9993,18 @@ static void check_sb_changes(struct mddev *mddev, struct md_rdev *rdev) */ if (rdev2->raid_disk == -1 && role != MD_DISK_ROLE_SPARE && !(le32_to_cpu(sb->feature_map) & - MD_FEATURE_RESHAPE_ACTIVE)) { - rdev2->saved_raid_disk = role; + MD_FEATURE_RESHAPE_ACTIVE) && + !md_cluster_ops->resync_status_get(mddev)) { + /* + * -1 to make raid1_add_disk() set conf->fullsync + * to 1. This could avoid skipping sync when the + * remote node is down during resyncing. + */ + if ((le32_to_cpu(sb->feature_map) + & MD_FEATURE_RECOVERY_OFFSET)) + rdev2->saved_raid_disk = -1; + else + rdev2->saved_raid_disk = role; ret = remove_and_add_spares(mddev, rdev2); pr_info("Activated spare: %pg\n", rdev2->bdev); diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 04a0c2ca1732..7acfe7c9dc8d 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -680,6 +680,7 @@ static int choose_slow_rdev(struct r1conf *conf, struct r1bio *r1_bio, len = r1_bio->sectors; read_len = raid1_check_read_range(rdev, this_sector, &len); if (read_len == r1_bio->sectors) { + *max_sectors = read_len; update_read_sectors(conf, disk, this_sector, read_len); return disk; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index ed0796aff722..d92470960b38 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -621,8 +621,14 @@ static int iwl_mvm_tzone_get_temp(struct thermal_zone_device *device, guard(mvm)(mvm); if (!iwl_mvm_firmware_running(mvm) || - mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) - return -ENODATA; + mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) { + /* + * Tell the core that there is no valid temperature value to + * return, but it need not worry about this. + */ + *temperature = THERMAL_TEMP_INVALID; + return 0; + } ret = iwl_mvm_get_temp(mvm, &temp); if (ret) diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c index 8b69427a4476..f1d58e70933f 100644 --- a/drivers/nvme/host/ioctl.c +++ b/drivers/nvme/host/ioctl.c @@ -3,6 +3,7 @@ * Copyright (c) 2011-2014, Intel Corporation. * Copyright (c) 2017-2021 Christoph Hellwig. */ +#include <linux/bio-integrity.h> #include <linux/ptrace.h> /* for force_successful_syscall_return */ #include <linux/nvme_ioctl.h> #include <linux/io_uring/cmd.h> @@ -111,13 +112,6 @@ static struct request *nvme_alloc_user_request(struct request_queue *q, return req; } -static void nvme_unmap_bio(struct bio *bio) -{ - if (bio_integrity(bio)) - bio_integrity_unmap_free_user(bio); - blk_rq_unmap_user(bio); -} - static int nvme_map_user_request(struct request *req, u64 ubuffer, unsigned bufflen, void __user *meta_buffer, unsigned meta_len, u32 meta_seed, struct io_uring_cmd *ioucmd, unsigned int flags) @@ -164,7 +158,7 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer, out_unmap: if (bio) - nvme_unmap_bio(bio); + blk_rq_unmap_user(bio); out: blk_mq_free_request(req); return ret; @@ -202,7 +196,7 @@ static int nvme_submit_user_cmd(struct request_queue *q, if (result) *result = le64_to_cpu(nvme_req(req)->result.u64); if (bio) - nvme_unmap_bio(bio); + blk_rq_unmap_user(bio); blk_mq_free_request(req); if (effects) @@ -413,7 +407,7 @@ static void nvme_uring_task_cb(struct io_uring_cmd *ioucmd, struct nvme_uring_cmd_pdu *pdu = nvme_uring_cmd_pdu(ioucmd); if (pdu->bio) - nvme_unmap_bio(pdu->bio); + blk_rq_unmap_user(pdu->bio); io_uring_cmd_done(ioucmd, pdu->status, pdu->result, issue_flags); } @@ -439,7 +433,7 @@ static enum rq_end_io_ret nvme_uring_cmd_end_io(struct request *req, */ if (blk_rq_is_poll(req)) { if (pdu->bio) - nvme_unmap_bio(pdu->bio); + blk_rq_unmap_user(pdu->bio); io_uring_cmd_iopoll_done(ioucmd, pdu->result, pdu->status); } else { io_uring_cmd_do_in_task_lazy(ioucmd, nvme_uring_task_cb); diff --git a/drivers/pci/msi/irqdomain.c b/drivers/pci/msi/irqdomain.c index 03d2dd25790d..569125726b3e 100644 --- a/drivers/pci/msi/irqdomain.c +++ b/drivers/pci/msi/irqdomain.c @@ -148,17 +148,35 @@ static void pci_device_domain_set_desc(msi_alloc_info_t *arg, struct msi_desc *d arg->hwirq = desc->msi_index; } +static __always_inline void cond_mask_parent(struct irq_data *data) +{ + struct msi_domain_info *info = data->domain->host_data; + + if (unlikely(info->flags & MSI_FLAG_PCI_MSI_MASK_PARENT)) + irq_chip_mask_parent(data); +} + +static __always_inline void cond_unmask_parent(struct irq_data *data) +{ + struct msi_domain_info *info = data->domain->host_data; + + if (unlikely(info->flags & MSI_FLAG_PCI_MSI_MASK_PARENT)) + irq_chip_unmask_parent(data); +} + static void pci_irq_mask_msi(struct irq_data *data) { struct msi_desc *desc = irq_data_get_msi_desc(data); pci_msi_mask(desc, BIT(data->irq - desc->irq)); + cond_mask_parent(data); } static void pci_irq_unmask_msi(struct irq_data *data) { struct msi_desc *desc = irq_data_get_msi_desc(data); + cond_unmask_parent(data); pci_msi_unmask(desc, BIT(data->irq - desc->irq)); } @@ -195,10 +213,12 @@ static const struct msi_domain_template pci_msi_template = { static void pci_irq_mask_msix(struct irq_data *data) { pci_msix_mask(irq_data_get_msi_desc(data)); + cond_mask_parent(data); } static void pci_irq_unmask_msix(struct irq_data *data) { + cond_unmask_parent(data); pci_msix_unmask(irq_data_get_msi_desc(data)); } diff --git a/drivers/pcmcia/bcm63xx_pcmcia.c b/drivers/pcmcia/bcm63xx_pcmcia.c index a5414441834a..5bda3e6d43d8 100644 --- a/drivers/pcmcia/bcm63xx_pcmcia.c +++ b/drivers/pcmcia/bcm63xx_pcmcia.c @@ -456,7 +456,6 @@ struct platform_driver bcm63xx_pcmcia_driver = { .remove_new = bcm63xx_drv_pcmcia_remove, .driver = { .name = "bcm63xx_pcmcia", - .owner = THIS_MODULE, }, }; diff --git a/drivers/pcmcia/i82092.c b/drivers/pcmcia/i82092.c index a335748bdef5..a947ffb2df55 100644 --- a/drivers/pcmcia/i82092.c +++ b/drivers/pcmcia/i82092.c @@ -23,6 +23,7 @@ #include "i82092aa.h" #include "i82365.h" +MODULE_DESCRIPTION("Driver for Intel I82092AA PCI-PCMCIA bridge"); MODULE_LICENSE("GPL"); /* PCI core routines */ diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c index 891ccea2cccb..86a357837a7b 100644 --- a/drivers/pcmcia/i82365.c +++ b/drivers/pcmcia/i82365.c @@ -1342,5 +1342,6 @@ static void __exit exit_i82365(void) module_init(init_i82365); module_exit(exit_i82365); +MODULE_DESCRIPTION("Driver for Intel 82365 and compatible PC Card controllers"); MODULE_LICENSE("Dual MPL/GPL"); /*====================================================================*/ diff --git a/drivers/pcmcia/max1600.c b/drivers/pcmcia/max1600.c index 379875a5e7cd..7be9068f6191 100644 --- a/drivers/pcmcia/max1600.c +++ b/drivers/pcmcia/max1600.c @@ -119,4 +119,5 @@ int max1600_configure(struct max1600 *m, unsigned int vcc, unsigned int vpp) } EXPORT_SYMBOL_GPL(max1600_configure); +MODULE_DESCRIPTION("MAX1600 PCMCIA power switch library"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index 252893216e50..3a1d2baa466f 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -66,5 +66,6 @@ EXPORT_SYMBOL(pccard_static_ops); MODULE_AUTHOR("David A. Hinds, Dominik Brodowski"); +MODULE_DESCRIPTION("PCMCIA resource management routines"); MODULE_LICENSE("GPL"); MODULE_ALIAS("rsrc_nonstatic"); diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 1365eaa20ff4..020ea86c24ec 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -638,11 +638,11 @@ static int yenta_search_one_res(struct resource *root, struct resource *res, start = PCIBIOS_MIN_CARDBUS_IO; end = ~0U; } else { - unsigned long avail = root->end - root->start; + unsigned long avail = resource_size(root); int i; size = BRIDGE_MEM_MAX; - if (size > avail/8) { - size = (avail+1)/8; + if (size > (avail - 1) / 8) { + size = avail / 8; /* round size down to next power of 2 */ i = 0; while ((size /= 2) != 0) @@ -1452,4 +1452,5 @@ static struct pci_driver yenta_cardbus_driver = { module_pci_driver(yenta_cardbus_driver); +MODULE_DESCRIPTION("Driver for CardBus yenta-compatible bridges"); MODULE_LICENSE("GPL"); diff --git a/drivers/power/reset/piix4-poweroff.c b/drivers/power/reset/piix4-poweroff.c index 7f308292d7e3..e6822c021000 100644 --- a/drivers/power/reset/piix4-poweroff.c +++ b/drivers/power/reset/piix4-poweroff.c @@ -106,4 +106,5 @@ static struct pci_driver piix4_poweroff_driver = { module_pci_driver(piix4_poweroff_driver); MODULE_AUTHOR("Paul Burton <paul.burton@mips.com>"); +MODULE_DESCRIPTION("Intel PIIX4 power-off driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index f6321a42aa53..bcfa63fb9f1e 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -167,6 +167,15 @@ config BATTERY_LEGO_EV3 help Say Y here to enable support for the LEGO MINDSTORMS EV3 battery. +config BATTERY_LENOVO_YOGA_C630 + tristate "Lenovo Yoga C630 battery" + depends on EC_LENOVO_YOGA_C630 + help + This driver enables battery support on the Lenovo Yoga C630 laptop. + + To compile the driver as a module, choose M here: the module will be + called lenovo_yoga_c630_battery. + config BATTERY_PMU tristate "Apple PMU battery" depends on PPC32 && ADB_PMU @@ -402,6 +411,18 @@ config BATTERY_MAX17042 Driver can be build as a module (max17042_battery). +config BATTERY_MAX1720X + tristate "Maxim MAX17201/MAX17205 Fuel Gauge" + depends on I2C + select REGMAP_I2C + help + MAX1720x is a family of fuel-gauge systems for lithium-ion (Li+) + batteries in handheld and portable equipment. MAX17201 are + configured to operate with a single lithium cell, the MAX17205 + can operate with multiple cells. + + Say Y to include support for the MAX17201/MAX17205 Fuel Gauges. + config BATTERY_MAX1721X tristate "MAX17211/MAX17215 standalone gas-gauge" depends on W1 diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 31ca6653a564..8dcb41545317 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o obj-$(CONFIG_BATTERY_GAUGE_LTC2941) += ltc2941-battery-gauge.o obj-$(CONFIG_BATTERY_GOLDFISH) += goldfish_battery.o obj-$(CONFIG_BATTERY_LEGO_EV3) += lego_ev3_battery.o +obj-$(CONFIG_BATTERY_LENOVO_YOGA_C630) += lenovo_yoga_c630_battery.o obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_QCOM_BATTMGR) += qcom_battmgr.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o @@ -52,6 +53,7 @@ obj-$(CONFIG_CHARGER_DA9150) += da9150-charger.o obj-$(CONFIG_BATTERY_DA9150) += da9150-fg.o obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o +obj-$(CONFIG_BATTERY_MAX1720X) += max1720x_battery.o obj-$(CONFIG_BATTERY_MAX1721X) += max1721x_battery.o obj-$(CONFIG_BATTERY_RT5033) += rt5033_battery.o obj-$(CONFIG_CHARGER_RT5033) += rt5033_charger.o diff --git a/drivers/power/supply/ab8500_chargalg.c b/drivers/power/supply/ab8500_chargalg.c index 55ab7a28056e..854491ad3ecd 100644 --- a/drivers/power/supply/ab8500_chargalg.c +++ b/drivers/power/supply/ab8500_chargalg.c @@ -1225,8 +1225,8 @@ static bool ab8500_chargalg_time_to_restart(struct ab8500_chargalg *di) */ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di) { + const struct power_supply_maintenance_charge_table *mt; struct power_supply_battery_info *bi = di->bm->bi; - struct power_supply_maintenance_charge_table *mt; int charger_status; int ret; diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index 9b34d1a60f66..93181ebfb324 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -487,14 +487,17 @@ static int ab8500_charger_get_ac_voltage(struct ab8500_charger *di) /* Only measure voltage if the charger is connected */ if (di->ac.charger_connected) { - ret = iio_read_channel_processed(di->adc_main_charger_v, &vch); - if (ret < 0) - dev_err(di->dev, "%s ADC conv failed,\n", __func__); + /* Convert to microvolt, IIO returns millivolt */ + ret = iio_read_channel_processed_scale(di->adc_main_charger_v, + &vch, 1000); + if (ret < 0) { + dev_err(di->dev, "%s ADC conv failed\n", __func__); + return ret; + } } else { vch = 0; } - /* Convert to microvolt, IIO returns millivolt */ - return vch * 1000; + return vch; } /** @@ -539,14 +542,17 @@ static int ab8500_charger_get_vbus_voltage(struct ab8500_charger *di) /* Only measure voltage if the charger is connected */ if (di->usb.charger_connected) { - ret = iio_read_channel_processed(di->adc_vbus_v, &vch); - if (ret < 0) - dev_err(di->dev, "%s ADC conv failed,\n", __func__); + /* Convert to microvolt, IIO returns millivolt */ + ret = iio_read_channel_processed_scale(di->adc_vbus_v, + &vch, 1000); + if (ret < 0) { + dev_err(di->dev, "%s ADC conv failed\n", __func__); + return ret; + } } else { vch = 0; } - /* Convert to microvolt, IIO returns millivolt */ - return vch * 1000; + return vch; } /** @@ -562,14 +568,17 @@ static int ab8500_charger_get_usb_current(struct ab8500_charger *di) /* Only measure current if the charger is online */ if (di->usb.charger_online) { - ret = iio_read_channel_processed(di->adc_usb_charger_c, &ich); - if (ret < 0) - dev_err(di->dev, "%s ADC conv failed,\n", __func__); + /* Return microamperes */ + ret = iio_read_channel_processed_scale(di->adc_usb_charger_c, + &ich, 1000); + if (ret < 0) { + dev_err(di->dev, "%s ADC conv failed\n", __func__); + return ret; + } } else { ich = 0; } - /* Return microamperes */ - return ich * 1000; + return ich; } /** @@ -585,14 +594,17 @@ static int ab8500_charger_get_ac_current(struct ab8500_charger *di) /* Only measure current if the charger is online */ if (di->ac.charger_online) { - ret = iio_read_channel_processed(di->adc_main_charger_c, &ich); - if (ret < 0) - dev_err(di->dev, "%s ADC conv failed,\n", __func__); + /* Return microamperes */ + ret = iio_read_channel_processed_scale(di->adc_main_charger_c, + &ich, 1000); + if (ret < 0) { + dev_err(di->dev, "%s ADC conv failed\n", __func__); + return ret; + } } else { ich = 0; } - /* Return microamperes */ - return ich * 1000; + return ich; } /** diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index 2ccaf6116c09..270874eeb934 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -149,11 +149,6 @@ struct ab8500_fg_flags { bool batt_id_received; }; -struct inst_curr_result_list { - struct list_head list; - int *result; -}; - /** * struct ab8500_fg - ab8500 FG device information * @dev: Pointer to the structure device diff --git a/drivers/power/supply/adp5061.c b/drivers/power/supply/adp5061.c index 3e3a0d118ce5..dac9875d993c 100644 --- a/drivers/power/supply/adp5061.c +++ b/drivers/power/supply/adp5061.c @@ -727,7 +727,7 @@ static int adp5061_probe(struct i2c_client *client) } static const struct i2c_device_id adp5061_id[] = { - { "adp5061", 0}, + { "adp5061" }, { } }; MODULE_DEVICE_TABLE(i2c, adp5061_id); diff --git a/drivers/power/supply/bd99954-charger.c b/drivers/power/supply/bd99954-charger.c index 1ed1d9f99fb3..54bf88262510 100644 --- a/drivers/power/supply/bd99954-charger.c +++ b/drivers/power/supply/bd99954-charger.c @@ -70,13 +70,6 @@ #include "bd99954-charger.h" -struct battery_data { - u16 precharge_current; /* Trickle-charge Current */ - u16 fc_reg_voltage; /* Fast Charging Regulation Voltage */ - u16 voltage_min; - u16 voltage_max; -}; - /* Initial field values, converted to initial register values */ struct bd9995x_init_data { u16 vsysreg_set; /* VSYS Regulation Setting */ diff --git a/drivers/power/supply/bq24735-charger.c b/drivers/power/supply/bq24735-charger.c index 8efceeae864c..73a7fc867b03 100644 --- a/drivers/power/supply/bq24735-charger.c +++ b/drivers/power/supply/bq24735-charger.c @@ -489,7 +489,7 @@ static int bq24735_charger_probe(struct i2c_client *client) } static const struct i2c_device_id bq24735_charger_id[] = { - { "bq24735-charger", 0 }, + { "bq24735-charger" }, {} }; MODULE_DEVICE_TABLE(i2c, bq24735_charger_id); diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 03fa11a1c9b6..2f5ceaf00b94 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -1617,11 +1617,11 @@ static const struct dev_pm_ops bq25890_pm = { }; static const struct i2c_device_id bq25890_i2c_ids[] = { - { "bq25890", 0 }, - { "bq25892", 0 }, - { "bq25895", 0 }, - { "bq25896", 0 }, - {}, + { "bq25890" }, + { "bq25892" }, + { "bq25895" }, + { "bq25896" }, + {} }; MODULE_DEVICE_TABLE(i2c, bq25890_i2c_ids); diff --git a/drivers/power/supply/cw2015_battery.c b/drivers/power/supply/cw2015_battery.c index 99f3ccdc30a6..f63c3c410451 100644 --- a/drivers/power/supply/cw2015_battery.c +++ b/drivers/power/supply/cw2015_battery.c @@ -731,7 +731,7 @@ static int __maybe_unused cw_bat_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(cw_bat_pm_ops, cw_bat_suspend, cw_bat_resume); static const struct i2c_device_id cw_bat_id_table[] = { - { "cw2015", 0 }, + { "cw2015" }, { } }; diff --git a/drivers/power/supply/ingenic-battery.c b/drivers/power/supply/ingenic-battery.c index 2e7fdfde47ec..0a40f425c277 100644 --- a/drivers/power/supply/ingenic-battery.c +++ b/drivers/power/supply/ingenic-battery.c @@ -31,8 +31,9 @@ static int ingenic_battery_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_HEALTH: - ret = iio_read_channel_processed(bat->channel, &val->intval); - val->intval *= 1000; + ret = iio_read_channel_processed_scale(bat->channel, + &val->intval, + 1000); if (val->intval < info->voltage_min_design_uv) val->intval = POWER_SUPPLY_HEALTH_DEAD; else if (val->intval > info->voltage_max_design_uv) @@ -41,8 +42,9 @@ static int ingenic_battery_get_property(struct power_supply *psy, val->intval = POWER_SUPPLY_HEALTH_GOOD; return ret; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - ret = iio_read_channel_processed(bat->channel, &val->intval); - val->intval *= 1000; + ret = iio_read_channel_processed_scale(bat->channel, + &val->intval, + 1000); return ret; case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: val->intval = info->voltage_min_design_uv; diff --git a/drivers/power/supply/lenovo_yoga_c630_battery.c b/drivers/power/supply/lenovo_yoga_c630_battery.c new file mode 100644 index 000000000000..d4d422cc5353 --- /dev/null +++ b/drivers/power/supply/lenovo_yoga_c630_battery.c @@ -0,0 +1,501 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022-2024, Linaro Ltd + * Authors: + * Bjorn Andersson + * Dmitry Baryshkov + */ +#include <linux/auxiliary_bus.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/notifier.h> +#include <linux/power_supply.h> +#include <linux/platform_data/lenovo-yoga-c630.h> + +struct yoga_c630_psy { + struct yoga_c630_ec *ec; + struct device *dev; + struct fwnode_handle *fwnode; + struct notifier_block nb; + + /* guards all battery properties and registration of power supplies */ + struct mutex lock; + + struct power_supply *adp_psy; + struct power_supply *bat_psy; + + unsigned long last_status_update; + + bool adapter_online; + + bool unit_mA; + + bool bat_present; + unsigned int bat_status; + unsigned int design_capacity; + unsigned int design_voltage; + unsigned int full_charge_capacity; + + unsigned int capacity_now; + unsigned int voltage_now; + + int current_now; + int rate_now; +}; + +#define LENOVO_EC_CACHE_TIME (10 * HZ) + +#define LENOVO_EC_ADPT_STATUS 0xa3 +#define LENOVO_EC_ADPT_STATUS_PRESENT BIT(7) +#define LENOVO_EC_BAT_ATTRIBUTES 0xc0 +#define LENOVO_EC_BAT_ATTRIBUTES_UNIT_IS_MA BIT(1) +#define LENOVO_EC_BAT_STATUS 0xc1 +#define LENOVO_EC_BAT_STATUS_DISCHARGING BIT(0) +#define LENOVO_EC_BAT_STATUS_CHARGING BIT(1) +#define LENOVO_EC_BAT_REMAIN_CAPACITY 0xc2 +#define LENOVO_EC_BAT_VOLTAGE 0xc6 +#define LENOVO_EC_BAT_DESIGN_VOLTAGE 0xc8 +#define LENOVO_EC_BAT_DESIGN_CAPACITY 0xca +#define LENOVO_EC_BAT_FULL_CAPACITY 0xcc +#define LENOVO_EC_BAT_CURRENT 0xd2 +#define LENOVO_EC_BAT_FULL_FACTORY 0xd6 +#define LENOVO_EC_BAT_PRESENT 0xda +#define LENOVO_EC_BAT_PRESENT_IS_PRESENT BIT(0) +#define LENOVO_EC_BAT_FULL_REGISTER 0xdb +#define LENOVO_EC_BAT_FULL_REGISTER_IS_FACTORY BIT(0) + +static int yoga_c630_psy_update_bat_info(struct yoga_c630_psy *ecbat) +{ + struct yoga_c630_ec *ec = ecbat->ec; + int val; + + lockdep_assert_held(&ecbat->lock); + + val = yoga_c630_ec_read8(ec, LENOVO_EC_BAT_PRESENT); + if (val < 0) + return val; + ecbat->bat_present = !!(val & LENOVO_EC_BAT_PRESENT_IS_PRESENT); + if (!ecbat->bat_present) + return val; + + val = yoga_c630_ec_read8(ec, LENOVO_EC_BAT_ATTRIBUTES); + if (val < 0) + return val; + ecbat->unit_mA = val & LENOVO_EC_BAT_ATTRIBUTES_UNIT_IS_MA; + + val = yoga_c630_ec_read16(ec, LENOVO_EC_BAT_DESIGN_CAPACITY); + if (val < 0) + return val; + ecbat->design_capacity = val * 1000; + + /* + * DSDT has delays after most of EC reads in these methods. + * Having no documentation for the EC we have to follow and sleep here. + */ + msleep(50); + + val = yoga_c630_ec_read16(ec, LENOVO_EC_BAT_DESIGN_VOLTAGE); + if (val < 0) + return val; + ecbat->design_voltage = val; + + msleep(50); + + val = yoga_c630_ec_read8(ec, LENOVO_EC_BAT_FULL_REGISTER); + if (val < 0) + return val; + val = yoga_c630_ec_read16(ec, + val & LENOVO_EC_BAT_FULL_REGISTER_IS_FACTORY ? + LENOVO_EC_BAT_FULL_FACTORY : + LENOVO_EC_BAT_FULL_CAPACITY); + if (val < 0) + return val; + + ecbat->full_charge_capacity = val * 1000; + + if (!ecbat->unit_mA) { + ecbat->design_capacity *= 10; + ecbat->full_charge_capacity *= 10; + } + + return 0; +} + +static int yoga_c630_psy_maybe_update_bat_status(struct yoga_c630_psy *ecbat) +{ + struct yoga_c630_ec *ec = ecbat->ec; + int current_mA; + int val; + + guard(mutex)(&ecbat->lock); + if (time_before(jiffies, ecbat->last_status_update + LENOVO_EC_CACHE_TIME)) + return 0; + + val = yoga_c630_ec_read8(ec, LENOVO_EC_BAT_STATUS); + if (val < 0) + return val; + ecbat->bat_status = val; + + msleep(50); + + val = yoga_c630_ec_read16(ec, LENOVO_EC_BAT_REMAIN_CAPACITY); + if (val < 0) + return val; + ecbat->capacity_now = val * 1000; + + msleep(50); + + val = yoga_c630_ec_read16(ec, LENOVO_EC_BAT_VOLTAGE); + if (val < 0) + return val; + ecbat->voltage_now = val * 1000; + + msleep(50); + + val = yoga_c630_ec_read16(ec, LENOVO_EC_BAT_CURRENT); + if (val < 0) + return val; + current_mA = sign_extend32(val, 15); + ecbat->current_now = current_mA * 1000; + ecbat->rate_now = current_mA * (ecbat->voltage_now / 1000); + + msleep(50); + + if (!ecbat->unit_mA) + ecbat->capacity_now *= 10; + + ecbat->last_status_update = jiffies; + + return 0; +} + +static int yoga_c630_psy_update_adapter_status(struct yoga_c630_psy *ecbat) +{ + struct yoga_c630_ec *ec = ecbat->ec; + int val; + + guard(mutex)(&ecbat->lock); + + val = yoga_c630_ec_read8(ec, LENOVO_EC_ADPT_STATUS); + if (val < 0) + return val; + + ecbat->adapter_online = !!(val & LENOVO_EC_ADPT_STATUS_PRESENT); + + return 0; +} + +static bool yoga_c630_psy_is_charged(struct yoga_c630_psy *ecbat) +{ + if (ecbat->bat_status != 0) + return false; + + if (ecbat->full_charge_capacity <= ecbat->capacity_now) + return true; + + if (ecbat->design_capacity <= ecbat->capacity_now) + return true; + + return false; +} + +static int yoga_c630_psy_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct yoga_c630_psy *ecbat = power_supply_get_drvdata(psy); + int rc = 0; + + if (!ecbat->bat_present && psp != POWER_SUPPLY_PROP_PRESENT) + return -ENODEV; + + rc = yoga_c630_psy_maybe_update_bat_status(ecbat); + if (rc) + return rc; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (ecbat->bat_status & LENOVO_EC_BAT_STATUS_DISCHARGING) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else if (ecbat->bat_status & LENOVO_EC_BAT_STATUS_CHARGING) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (yoga_c630_psy_is_charged(ecbat)) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = ecbat->bat_present; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = ecbat->design_voltage; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + val->intval = ecbat->design_capacity; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_ENERGY_FULL: + val->intval = ecbat->full_charge_capacity; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + case POWER_SUPPLY_PROP_ENERGY_NOW: + val->intval = ecbat->capacity_now; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = ecbat->current_now; + break; + case POWER_SUPPLY_PROP_POWER_NOW: + val->intval = ecbat->rate_now; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = ecbat->voltage_now; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = "PABAS0241231"; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = "Compal"; + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_SYSTEM; + break; + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static enum power_supply_property yoga_c630_psy_bat_mA_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_POWER_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_SCOPE, +}; + +static enum power_supply_property yoga_c630_psy_bat_mWh_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, + POWER_SUPPLY_PROP_ENERGY_FULL, + POWER_SUPPLY_PROP_ENERGY_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_POWER_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_SCOPE, +}; + +static const struct power_supply_desc yoga_c630_psy_bat_psy_desc_mA = { + .name = "yoga-c630-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = yoga_c630_psy_bat_mA_properties, + .num_properties = ARRAY_SIZE(yoga_c630_psy_bat_mA_properties), + .get_property = yoga_c630_psy_bat_get_property, +}; + +static const struct power_supply_desc yoga_c630_psy_bat_psy_desc_mWh = { + .name = "yoga-c630-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = yoga_c630_psy_bat_mWh_properties, + .num_properties = ARRAY_SIZE(yoga_c630_psy_bat_mWh_properties), + .get_property = yoga_c630_psy_bat_get_property, +}; + +static int yoga_c630_psy_adpt_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct yoga_c630_psy *ecbat = power_supply_get_drvdata(psy); + int ret = 0; + + ret = yoga_c630_psy_update_adapter_status(ecbat); + if (ret < 0) + return ret; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = ecbat->adapter_online; + break; + case POWER_SUPPLY_PROP_USB_TYPE: + val->intval = POWER_SUPPLY_USB_TYPE_C; + break; + default: + return -EINVAL; + } + + return 0; +} + +static enum power_supply_property yoga_c630_psy_adpt_properties[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_USB_TYPE, +}; + +static const enum power_supply_usb_type yoga_c630_psy_adpt_usb_type[] = { + POWER_SUPPLY_USB_TYPE_C, +}; + +static const struct power_supply_desc yoga_c630_psy_adpt_psy_desc = { + .name = "yoga-c630-adapter", + .type = POWER_SUPPLY_TYPE_USB, + .usb_types = yoga_c630_psy_adpt_usb_type, + .num_usb_types = ARRAY_SIZE(yoga_c630_psy_adpt_usb_type), + .properties = yoga_c630_psy_adpt_properties, + .num_properties = ARRAY_SIZE(yoga_c630_psy_adpt_properties), + .get_property = yoga_c630_psy_adpt_get_property, +}; + +static int yoga_c630_psy_register_bat_psy(struct yoga_c630_psy *ecbat) +{ + struct power_supply_config bat_cfg = {}; + + bat_cfg.drv_data = ecbat; + bat_cfg.fwnode = ecbat->fwnode; + ecbat->bat_psy = power_supply_register_no_ws(ecbat->dev, + ecbat->unit_mA ? + &yoga_c630_psy_bat_psy_desc_mA : + &yoga_c630_psy_bat_psy_desc_mWh, + &bat_cfg); + if (IS_ERR(ecbat->bat_psy)) { + dev_err(ecbat->dev, "failed to register battery supply\n"); + return PTR_ERR(ecbat->bat_psy); + } + + return 0; +} + +static void yoga_c630_ec_refresh_bat_info(struct yoga_c630_psy *ecbat) +{ + bool current_unit; + + guard(mutex)(&ecbat->lock); + + current_unit = ecbat->unit_mA; + + yoga_c630_psy_update_bat_info(ecbat); + + if (current_unit != ecbat->unit_mA) { + power_supply_unregister(ecbat->bat_psy); + yoga_c630_psy_register_bat_psy(ecbat); + } +} + +static int yoga_c630_psy_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct yoga_c630_psy *ecbat = container_of(nb, struct yoga_c630_psy, nb); + + switch (action) { + case LENOVO_EC_EVENT_BAT_INFO: + yoga_c630_ec_refresh_bat_info(ecbat); + break; + case LENOVO_EC_EVENT_BAT_ADPT_STATUS: + power_supply_changed(ecbat->adp_psy); + fallthrough; + case LENOVO_EC_EVENT_BAT_STATUS: + power_supply_changed(ecbat->bat_psy); + break; + } + + return NOTIFY_OK; +} + +static int yoga_c630_psy_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct yoga_c630_ec *ec = adev->dev.platform_data; + struct power_supply_config adp_cfg = {}; + struct device *dev = &adev->dev; + struct yoga_c630_psy *ecbat; + int ret; + + ecbat = devm_kzalloc(&adev->dev, sizeof(*ecbat), GFP_KERNEL); + if (!ecbat) + return -ENOMEM; + + ecbat->ec = ec; + ecbat->dev = dev; + mutex_init(&ecbat->lock); + ecbat->fwnode = adev->dev.parent->fwnode; + ecbat->nb.notifier_call = yoga_c630_psy_notify; + + auxiliary_set_drvdata(adev, ecbat); + + adp_cfg.drv_data = ecbat; + adp_cfg.fwnode = ecbat->fwnode; + adp_cfg.supplied_to = (char **)&yoga_c630_psy_bat_psy_desc_mA.name; + adp_cfg.num_supplicants = 1; + ecbat->adp_psy = devm_power_supply_register_no_ws(dev, &yoga_c630_psy_adpt_psy_desc, &adp_cfg); + if (IS_ERR(ecbat->adp_psy)) { + dev_err(dev, "failed to register AC adapter supply\n"); + return PTR_ERR(ecbat->adp_psy); + } + + scoped_guard(mutex, &ecbat->lock) { + ret = yoga_c630_psy_update_bat_info(ecbat); + if (ret) + goto err_unreg_bat; + + ret = yoga_c630_psy_register_bat_psy(ecbat); + if (ret) + goto err_unreg_bat; + } + + ret = yoga_c630_ec_register_notify(ecbat->ec, &ecbat->nb); + if (ret) + goto err_unreg_bat; + + return 0; + +err_unreg_bat: + power_supply_unregister(ecbat->bat_psy); + return ret; +} + +static void yoga_c630_psy_remove(struct auxiliary_device *adev) +{ + struct yoga_c630_psy *ecbat = auxiliary_get_drvdata(adev); + + yoga_c630_ec_unregister_notify(ecbat->ec, &ecbat->nb); + power_supply_unregister(ecbat->bat_psy); +} + +static const struct auxiliary_device_id yoga_c630_psy_id_table[] = { + { .name = YOGA_C630_MOD_NAME "." YOGA_C630_DEV_PSY, }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, yoga_c630_psy_id_table); + +static struct auxiliary_driver yoga_c630_psy_driver = { + .name = YOGA_C630_DEV_PSY, + .id_table = yoga_c630_psy_id_table, + .probe = yoga_c630_psy_probe, + .remove = yoga_c630_psy_remove, +}; + +module_auxiliary_driver(yoga_c630_psy_driver); + +MODULE_DESCRIPTION("Lenovo Yoga C630 psy"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/lp8727_charger.c b/drivers/power/supply/lp8727_charger.c index 34548a4da90b..4186fcd37512 100644 --- a/drivers/power/supply/lp8727_charger.c +++ b/drivers/power/supply/lp8727_charger.c @@ -584,7 +584,7 @@ static const struct of_device_id lp8727_dt_ids[] __maybe_unused = { MODULE_DEVICE_TABLE(of, lp8727_dt_ids); static const struct i2c_device_id lp8727_ids[] = { - {"lp8727", 0}, + { "lp8727" }, { } }; MODULE_DEVICE_TABLE(i2c, lp8727_ids); diff --git a/drivers/power/supply/ltc4162-l-charger.c b/drivers/power/supply/ltc4162-l-charger.c index f0eace731480..2e4bc74e1c4a 100644 --- a/drivers/power/supply/ltc4162-l-charger.c +++ b/drivers/power/supply/ltc4162-l-charger.c @@ -903,8 +903,8 @@ static void ltc4162l_alert(struct i2c_client *client, } static const struct i2c_device_id ltc4162l_i2c_id_table[] = { - { "ltc4162-l", 0 }, - { }, + { "ltc4162-l" }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc4162l_i2c_id_table); diff --git a/drivers/power/supply/max14656_charger_detector.c b/drivers/power/supply/max14656_charger_detector.c index 89f2af72dfcd..a5b42b42d134 100644 --- a/drivers/power/supply/max14656_charger_detector.c +++ b/drivers/power/supply/max14656_charger_detector.c @@ -300,7 +300,7 @@ static int max14656_probe(struct i2c_client *client) } static const struct i2c_device_id max14656_id[] = { - { "max14656", 0 }, + { "max14656" }, {} }; MODULE_DEVICE_TABLE(i2c, max14656_id); diff --git a/drivers/power/supply/max1720x_battery.c b/drivers/power/supply/max1720x_battery.c new file mode 100644 index 000000000000..edc262f0a62f --- /dev/null +++ b/drivers/power/supply/max1720x_battery.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Fuel gauge driver for Maxim 17201/17205 + * + * based on max1721x_battery.c + * + * Copyright (C) 2024 Liebherr-Electronics and Drives GmbH + */ + +#include <linux/bitfield.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/power_supply.h> +#include <linux/regmap.h> + +#include <asm/unaligned.h> + +/* Nonvolatile registers */ +#define MAX1720X_NRSENSE 0xCF /* RSense in 10^-5 Ohm */ + +/* ModelGauge m5 */ +#define MAX172XX_STATUS 0x00 /* Status */ +#define MAX172XX_STATUS_BAT_ABSENT BIT(3) /* Battery absent */ +#define MAX172XX_REPCAP 0x05 /* Average capacity */ +#define MAX172XX_REPSOC 0x06 /* Percentage of charge */ +#define MAX172XX_TEMP 0x08 /* Temperature */ +#define MAX172XX_CURRENT 0x0A /* Actual current */ +#define MAX172XX_AVG_CURRENT 0x0B /* Average current */ +#define MAX172XX_TTE 0x11 /* Time to empty */ +#define MAX172XX_AVG_TA 0x16 /* Average temperature */ +#define MAX172XX_CYCLES 0x17 +#define MAX172XX_DESIGN_CAP 0x18 /* Design capacity */ +#define MAX172XX_AVG_VCELL 0x19 +#define MAX172XX_TTF 0x20 /* Time to full */ +#define MAX172XX_DEV_NAME 0x21 /* Device name */ +#define MAX172XX_DEV_NAME_TYPE_MASK GENMASK(3, 0) +#define MAX172XX_DEV_NAME_TYPE_MAX17201 BIT(0) +#define MAX172XX_DEV_NAME_TYPE_MAX17205 (BIT(0) | BIT(2)) +#define MAX172XX_QR_TABLE10 0x22 +#define MAX172XX_BATT 0xDA /* Battery voltage */ +#define MAX172XX_ATAVCAP 0xDF + +static const char *const max1720x_manufacturer = "Maxim Integrated"; +static const char *const max17201_model = "MAX17201"; +static const char *const max17205_model = "MAX17205"; + +struct max1720x_device_info { + struct regmap *regmap; + int rsense; +}; + +/* + * Model Gauge M5 Algorithm output register + * Volatile data (must not be cached) + */ +static const struct regmap_range max1720x_volatile_allow[] = { + regmap_reg_range(MAX172XX_STATUS, MAX172XX_CYCLES), + regmap_reg_range(MAX172XX_AVG_VCELL, MAX172XX_TTF), + regmap_reg_range(MAX172XX_QR_TABLE10, MAX172XX_ATAVCAP), +}; + +static const struct regmap_range max1720x_readable_allow[] = { + regmap_reg_range(MAX172XX_STATUS, MAX172XX_ATAVCAP), +}; + +static const struct regmap_range max1720x_readable_deny[] = { + /* unused registers */ + regmap_reg_range(0x24, 0x26), + regmap_reg_range(0x30, 0x31), + regmap_reg_range(0x33, 0x34), + regmap_reg_range(0x37, 0x37), + regmap_reg_range(0x3B, 0x3C), + regmap_reg_range(0x40, 0x41), + regmap_reg_range(0x43, 0x44), + regmap_reg_range(0x47, 0x49), + regmap_reg_range(0x4B, 0x4C), + regmap_reg_range(0x4E, 0xAF), + regmap_reg_range(0xB1, 0xB3), + regmap_reg_range(0xB5, 0xB7), + regmap_reg_range(0xBF, 0xD0), + regmap_reg_range(0xDB, 0xDB), + regmap_reg_range(0xE0, 0xFF), +}; + +static const struct regmap_access_table max1720x_readable_regs = { + .yes_ranges = max1720x_readable_allow, + .n_yes_ranges = ARRAY_SIZE(max1720x_readable_allow), + .no_ranges = max1720x_readable_deny, + .n_no_ranges = ARRAY_SIZE(max1720x_readable_deny), +}; + +static const struct regmap_access_table max1720x_volatile_regs = { + .yes_ranges = max1720x_volatile_allow, + .n_yes_ranges = ARRAY_SIZE(max1720x_volatile_allow), + .no_ranges = max1720x_readable_deny, + .n_no_ranges = ARRAY_SIZE(max1720x_readable_deny), +}; + +static const struct regmap_config max1720x_regmap_cfg = { + .reg_bits = 8, + .val_bits = 16, + .max_register = MAX172XX_ATAVCAP, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .rd_table = &max1720x_readable_regs, + .volatile_table = &max1720x_volatile_regs, + .cache_type = REGCACHE_RBTREE, +}; + +static const enum power_supply_property max1720x_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_AVG, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, + POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + +/* Convert regs value to power_supply units */ + +static int max172xx_time_to_ps(unsigned int reg) +{ + return reg * 5625 / 1000; /* in sec. */ +} + +static int max172xx_percent_to_ps(unsigned int reg) +{ + return reg / 256; /* in percent from 0 to 100 */ +} + +static int max172xx_voltage_to_ps(unsigned int reg) +{ + return reg * 1250; /* in uV */ +} + +static int max172xx_capacity_to_ps(unsigned int reg) +{ + return reg * 500; /* in uAh */ +} + +/* + * Current and temperature is signed values, so unsigned regs + * value must be converted to signed type + */ + +static int max172xx_temperature_to_ps(unsigned int reg) +{ + int val = (int16_t)reg; + + return val * 10 / 256; /* in tenths of deg. C */ +} + +/* + * Calculating current registers resolution: + * + * RSense stored in 10^-5 Ohm, so mesaurment voltage must be + * in 10^-11 Volts for get current in uA. + * 16 bit current reg fullscale +/-51.2mV is 102400 uV. + * So: 102400 / 65535 * 10^5 = 156252 + */ +static int max172xx_current_to_voltage(unsigned int reg) +{ + int val = (int16_t)reg; + + return val * 156252; +} + +static int max1720x_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct max1720x_device_info *info = power_supply_get_drvdata(psy); + unsigned int reg_val; + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + /* + * POWER_SUPPLY_PROP_PRESENT will always readable via + * sysfs interface. Value return 0 if battery not + * present or unaccesable via I2c. + */ + ret = regmap_read(info->regmap, MAX172XX_STATUS, ®_val); + if (ret < 0) { + val->intval = 0; + return 0; + } + + val->intval = !FIELD_GET(MAX172XX_STATUS_BAT_ABSENT, reg_val); + break; + case POWER_SUPPLY_PROP_CAPACITY: + ret = regmap_read(info->regmap, MAX172XX_REPSOC, ®_val); + val->intval = max172xx_percent_to_ps(reg_val); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = regmap_read(info->regmap, MAX172XX_BATT, ®_val); + val->intval = max172xx_voltage_to_ps(reg_val); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + ret = regmap_read(info->regmap, MAX172XX_DESIGN_CAP, ®_val); + val->intval = max172xx_capacity_to_ps(reg_val); + break; + case POWER_SUPPLY_PROP_CHARGE_AVG: + ret = regmap_read(info->regmap, MAX172XX_REPCAP, ®_val); + val->intval = max172xx_capacity_to_ps(reg_val); + break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: + ret = regmap_read(info->regmap, MAX172XX_TTE, ®_val); + val->intval = max172xx_time_to_ps(reg_val); + break; + case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: + ret = regmap_read(info->regmap, MAX172XX_TTF, ®_val); + val->intval = max172xx_time_to_ps(reg_val); + break; + case POWER_SUPPLY_PROP_TEMP: + ret = regmap_read(info->regmap, MAX172XX_TEMP, ®_val); + val->intval = max172xx_temperature_to_ps(reg_val); + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = regmap_read(info->regmap, MAX172XX_CURRENT, ®_val); + val->intval = max172xx_current_to_voltage(reg_val) / info->rsense; + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + ret = regmap_read(info->regmap, MAX172XX_AVG_CURRENT, ®_val); + val->intval = max172xx_current_to_voltage(reg_val) / info->rsense; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + ret = regmap_read(info->regmap, MAX172XX_DEV_NAME, ®_val); + reg_val = FIELD_GET(MAX172XX_DEV_NAME_TYPE_MASK, reg_val); + if (reg_val == MAX172XX_DEV_NAME_TYPE_MAX17201) + val->strval = max17201_model; + else if (reg_val == MAX172XX_DEV_NAME_TYPE_MAX17205) + val->strval = max17205_model; + else + return -ENODEV; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = max1720x_manufacturer; + break; + default: + return -EINVAL; + } + + return ret; +} + +static int max1720x_probe_sense_resistor(struct i2c_client *client, + struct max1720x_device_info *info) +{ + struct device *dev = &client->dev; + struct i2c_client *ancillary; + int ret; + + ancillary = i2c_new_ancillary_device(client, "nvmem", 0xb); + if (IS_ERR(ancillary)) { + dev_err(dev, "Failed to initialize ancillary i2c device\n"); + return PTR_ERR(ancillary); + } + + ret = i2c_smbus_read_word_data(ancillary, MAX1720X_NRSENSE); + i2c_unregister_device(ancillary); + if (ret < 0) + return ret; + + info->rsense = ret; + if (!info->rsense) { + dev_warn(dev, "RSense not calibrated, set 10 mOhms!\n"); + info->rsense = 1000; /* in regs in 10^-5 */ + } + + return 0; +} + +static const struct power_supply_desc max1720x_bat_desc = { + .name = "max1720x", + .no_thermal = true, + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = max1720x_battery_props, + .num_properties = ARRAY_SIZE(max1720x_battery_props), + .get_property = max1720x_battery_get_property, +}; + +static int max1720x_probe(struct i2c_client *client) +{ + struct power_supply_config psy_cfg = {}; + struct device *dev = &client->dev; + struct max1720x_device_info *info; + struct power_supply *bat; + int ret; + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + psy_cfg.drv_data = info; + psy_cfg.fwnode = dev_fwnode(dev); + info->regmap = devm_regmap_init_i2c(client, &max1720x_regmap_cfg); + if (IS_ERR(info->regmap)) + return dev_err_probe(dev, PTR_ERR(info->regmap), + "regmap initialization failed\n"); + + ret = max1720x_probe_sense_resistor(client, info); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read sense resistor value\n"); + + bat = devm_power_supply_register(dev, &max1720x_bat_desc, &psy_cfg); + if (IS_ERR(bat)) + return dev_err_probe(dev, PTR_ERR(bat), + "Failed to register power supply\n"); + + return 0; +} + +static const struct of_device_id max1720x_of_match[] = { + { .compatible = "maxim,max17201" }, + {} +}; +MODULE_DEVICE_TABLE(of, max1720x_of_match); + +static struct i2c_driver max1720x_i2c_driver = { + .driver = { + .name = "max1720x", + .of_match_table = max1720x_of_match, + }, + .probe = max1720x_probe, +}; +module_i2c_driver(max1720x_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Dimitri Fedrau <dima.fedrau@gmail.com>"); +MODULE_DESCRIPTION("Maxim MAX17201/MAX17205 Fuel Gauge IC driver"); diff --git a/drivers/power/supply/max77976_charger.c b/drivers/power/supply/max77976_charger.c index 99659dc8f5a6..d7e520da7688 100644 --- a/drivers/power/supply/max77976_charger.c +++ b/drivers/power/supply/max77976_charger.c @@ -483,8 +483,8 @@ static int max77976_probe(struct i2c_client *client) } static const struct i2c_device_id max77976_i2c_id[] = { - { MAX77976_DRIVER_NAME, 0 }, - { }, + { MAX77976_DRIVER_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, max77976_i2c_id); diff --git a/drivers/power/supply/mm8013.c b/drivers/power/supply/mm8013.c index 20c1651ca38e..5bcfaeeda3db 100644 --- a/drivers/power/supply/mm8013.c +++ b/drivers/power/supply/mm8013.c @@ -284,7 +284,7 @@ static int mm8013_probe(struct i2c_client *client) } static const struct i2c_device_id mm8013_id_table[] = { - { "mm8013", 0 }, + { "mm8013" }, {} }; MODULE_DEVICE_TABLE(i2c, mm8013_id_table); diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index fefe938c9342..8f6025acd10a 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -1024,7 +1024,7 @@ EXPORT_SYMBOL_GPL(power_supply_temp2resist_simple); int power_supply_vbat2ri(struct power_supply_battery_info *info, int vbat_uv, bool charging) { - struct power_supply_vbat_ri_table *vbat2ri; + const struct power_supply_vbat_ri_table *vbat2ri; int table_len; int i, high, low; @@ -1072,7 +1072,7 @@ int power_supply_vbat2ri(struct power_supply_battery_info *info, } EXPORT_SYMBOL_GPL(power_supply_vbat2ri); -struct power_supply_maintenance_charge_table * +const struct power_supply_maintenance_charge_table * power_supply_get_maintenance_charging_setting(struct power_supply_battery_info *info, int index) { diff --git a/drivers/power/supply/power_supply_hwmon.c b/drivers/power/supply/power_supply_hwmon.c index c97893d4c25e..baacefbdf768 100644 --- a/drivers/power/supply/power_supply_hwmon.c +++ b/drivers/power/supply/power_supply_hwmon.c @@ -48,6 +48,18 @@ static int power_supply_hwmon_curr_to_property(u32 attr) } } +static int power_supply_hwmon_power_to_property(u32 attr) +{ + switch (attr) { + case hwmon_power_input: + return POWER_SUPPLY_PROP_POWER_NOW; + case hwmon_power_average: + return POWER_SUPPLY_PROP_POWER_AVG; + default: + return -EINVAL; + } +} + static int power_supply_hwmon_temp_to_property(u32 attr, int channel) { if (channel) { @@ -90,6 +102,8 @@ power_supply_hwmon_to_property(enum hwmon_sensor_types type, return power_supply_hwmon_in_to_property(attr); case hwmon_curr: return power_supply_hwmon_curr_to_property(attr); + case hwmon_power: + return power_supply_hwmon_power_to_property(attr); case hwmon_temp: return power_supply_hwmon_temp_to_property(attr, channel); default: @@ -229,6 +243,11 @@ power_supply_hwmon_read(struct device *dev, enum hwmon_sensor_types type, case hwmon_in: pspval.intval = DIV_ROUND_CLOSEST(pspval.intval, 1000); break; + case hwmon_power: + /* + * Power properties are already in microwatts. + */ + break; /* * Temp needs to be converted from 1/10 C to milli-C */ @@ -311,6 +330,10 @@ static const struct hwmon_channel_info * const power_supply_hwmon_info[] = { HWMON_C_MAX | HWMON_C_INPUT), + HWMON_CHANNEL_INFO(power, + HWMON_P_INPUT | + HWMON_P_AVERAGE), + HWMON_CHANNEL_INFO(in, HWMON_I_AVERAGE | HWMON_I_MIN | @@ -359,6 +382,8 @@ int power_supply_add_hwmon_sysfs(struct power_supply *psy) case POWER_SUPPLY_PROP_CURRENT_AVG: case POWER_SUPPLY_PROP_CURRENT_MAX: case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_POWER_AVG: + case POWER_SUPPLY_PROP_POWER_NOW: case POWER_SUPPLY_PROP_TEMP: case POWER_SUPPLY_PROP_TEMP_MAX: case POWER_SUPPLY_PROP_TEMP_MIN: diff --git a/drivers/power/supply/power_supply_leds.c b/drivers/power/supply/power_supply_leds.c index 73935de844d9..f4a7e566bea1 100644 --- a/drivers/power/supply/power_supply_leds.c +++ b/drivers/power/supply/power_supply_leds.c @@ -19,6 +19,76 @@ /* Battery specific LEDs triggers. */ +struct power_supply_led_trigger { + struct led_trigger trig; + struct power_supply *psy; +}; + +#define trigger_to_psy_trigger(trigger) \ + container_of(trigger, struct power_supply_led_trigger, trig) + +static int power_supply_led_trigger_activate(struct led_classdev *led_cdev) +{ + struct power_supply_led_trigger *psy_trig = + trigger_to_psy_trigger(led_cdev->trigger); + + /* Sync current power-supply state to LED being activated */ + power_supply_update_leds(psy_trig->psy); + return 0; +} + +static int power_supply_register_led_trigger(struct power_supply *psy, + const char *name_template, + struct led_trigger **tp, int *err) +{ + struct power_supply_led_trigger *psy_trig; + int ret = -ENOMEM; + + /* Bail on previous errors */ + if (err && *err) + return *err; + + psy_trig = kzalloc(sizeof(*psy_trig), GFP_KERNEL); + if (!psy_trig) + goto err_free_trigger; + + psy_trig->trig.name = kasprintf(GFP_KERNEL, name_template, psy->desc->name); + if (!psy_trig->trig.name) + goto err_free_trigger; + + psy_trig->trig.activate = power_supply_led_trigger_activate; + psy_trig->psy = psy; + + ret = led_trigger_register(&psy_trig->trig); + if (ret) + goto err_free_name; + + *tp = &psy_trig->trig; + return 0; + +err_free_name: + kfree(psy_trig->trig.name); +err_free_trigger: + kfree(psy_trig); + if (err) + *err = ret; + + return ret; +} + +static void power_supply_unregister_led_trigger(struct led_trigger *trig) +{ + struct power_supply_led_trigger *psy_trig; + + if (!trig) + return; + + psy_trig = trigger_to_psy_trigger(trig); + led_trigger_unregister(&psy_trig->trig); + kfree(psy_trig->trig.name); + kfree(psy_trig); +} + static void power_supply_update_bat_leds(struct power_supply *psy) { union power_supply_propval status; @@ -32,7 +102,7 @@ static void power_supply_update_bat_leds(struct power_supply *psy) switch (status.intval) { case POWER_SUPPLY_STATUS_FULL: - led_trigger_event(psy->charging_full_trig, LED_FULL); + led_trigger_event(psy->trig, LED_FULL); led_trigger_event(psy->charging_trig, LED_OFF); led_trigger_event(psy->full_trig, LED_FULL); /* Going from blink to LED on requires a LED_OFF event to stop blink */ @@ -44,7 +114,7 @@ static void power_supply_update_bat_leds(struct power_supply *psy) LED_FULL); break; case POWER_SUPPLY_STATUS_CHARGING: - led_trigger_event(psy->charging_full_trig, LED_FULL); + led_trigger_event(psy->trig, LED_FULL); led_trigger_event(psy->charging_trig, LED_FULL); led_trigger_event(psy->full_trig, LED_OFF); led_trigger_blink(psy->charging_blink_full_solid_trig, 0, 0); @@ -54,7 +124,7 @@ static void power_supply_update_bat_leds(struct power_supply *psy) LED_FULL); break; default: - led_trigger_event(psy->charging_full_trig, LED_OFF); + led_trigger_event(psy->trig, LED_OFF); led_trigger_event(psy->charging_trig, LED_OFF); led_trigger_event(psy->full_trig, LED_OFF); led_trigger_event(psy->charging_blink_full_solid_trig, @@ -65,69 +135,33 @@ static void power_supply_update_bat_leds(struct power_supply *psy) } } -static int power_supply_create_bat_triggers(struct power_supply *psy) +static void power_supply_remove_bat_triggers(struct power_supply *psy) { - psy->charging_full_trig_name = kasprintf(GFP_KERNEL, - "%s-charging-or-full", psy->desc->name); - if (!psy->charging_full_trig_name) - goto charging_full_failed; - - psy->charging_trig_name = kasprintf(GFP_KERNEL, - "%s-charging", psy->desc->name); - if (!psy->charging_trig_name) - goto charging_failed; - - psy->full_trig_name = kasprintf(GFP_KERNEL, "%s-full", psy->desc->name); - if (!psy->full_trig_name) - goto full_failed; - - psy->charging_blink_full_solid_trig_name = kasprintf(GFP_KERNEL, - "%s-charging-blink-full-solid", psy->desc->name); - if (!psy->charging_blink_full_solid_trig_name) - goto charging_blink_full_solid_failed; - - psy->charging_orange_full_green_trig_name = kasprintf(GFP_KERNEL, - "%s-charging-orange-full-green", psy->desc->name); - if (!psy->charging_orange_full_green_trig_name) - goto charging_red_full_green_failed; - - led_trigger_register_simple(psy->charging_full_trig_name, - &psy->charging_full_trig); - led_trigger_register_simple(psy->charging_trig_name, - &psy->charging_trig); - led_trigger_register_simple(psy->full_trig_name, - &psy->full_trig); - led_trigger_register_simple(psy->charging_blink_full_solid_trig_name, - &psy->charging_blink_full_solid_trig); - led_trigger_register_simple(psy->charging_orange_full_green_trig_name, - &psy->charging_orange_full_green_trig); - - return 0; - -charging_red_full_green_failed: - kfree(psy->charging_blink_full_solid_trig_name); -charging_blink_full_solid_failed: - kfree(psy->full_trig_name); -full_failed: - kfree(psy->charging_trig_name); -charging_failed: - kfree(psy->charging_full_trig_name); -charging_full_failed: - return -ENOMEM; + power_supply_unregister_led_trigger(psy->trig); + power_supply_unregister_led_trigger(psy->charging_trig); + power_supply_unregister_led_trigger(psy->full_trig); + power_supply_unregister_led_trigger(psy->charging_blink_full_solid_trig); + power_supply_unregister_led_trigger(psy->charging_orange_full_green_trig); } -static void power_supply_remove_bat_triggers(struct power_supply *psy) +static int power_supply_create_bat_triggers(struct power_supply *psy) { - led_trigger_unregister_simple(psy->charging_full_trig); - led_trigger_unregister_simple(psy->charging_trig); - led_trigger_unregister_simple(psy->full_trig); - led_trigger_unregister_simple(psy->charging_blink_full_solid_trig); - led_trigger_unregister_simple(psy->charging_orange_full_green_trig); - kfree(psy->charging_blink_full_solid_trig_name); - kfree(psy->full_trig_name); - kfree(psy->charging_trig_name); - kfree(psy->charging_full_trig_name); - kfree(psy->charging_orange_full_green_trig_name); + int err = 0; + + power_supply_register_led_trigger(psy, "%s-charging-or-full", + &psy->trig, &err); + power_supply_register_led_trigger(psy, "%s-charging", + &psy->charging_trig, &err); + power_supply_register_led_trigger(psy, "%s-full", + &psy->full_trig, &err); + power_supply_register_led_trigger(psy, "%s-charging-blink-full-solid", + &psy->charging_blink_full_solid_trig, &err); + power_supply_register_led_trigger(psy, "%s-charging-orange-full-green", + &psy->charging_orange_full_green_trig, &err); + if (err) + power_supply_remove_bat_triggers(psy); + + return err; } /* Generated power specific LEDs triggers. */ @@ -142,27 +176,19 @@ static void power_supply_update_gen_leds(struct power_supply *psy) dev_dbg(&psy->dev, "%s %d\n", __func__, online.intval); if (online.intval) - led_trigger_event(psy->online_trig, LED_FULL); + led_trigger_event(psy->trig, LED_FULL); else - led_trigger_event(psy->online_trig, LED_OFF); + led_trigger_event(psy->trig, LED_OFF); } static int power_supply_create_gen_triggers(struct power_supply *psy) { - psy->online_trig_name = kasprintf(GFP_KERNEL, "%s-online", - psy->desc->name); - if (!psy->online_trig_name) - return -ENOMEM; - - led_trigger_register_simple(psy->online_trig_name, &psy->online_trig); - - return 0; + return power_supply_register_led_trigger(psy, "%s-online", &psy->trig, NULL); } static void power_supply_remove_gen_triggers(struct power_supply *psy) { - led_trigger_unregister_simple(psy->online_trig); - kfree(psy->online_trig_name); + power_supply_unregister_led_trigger(psy->trig); } /* Choice what triggers to create&update. */ diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index b86e11bdc07e..3e63d165b2f7 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -379,8 +379,7 @@ static umode_t power_supply_attr_is_visible(struct kobject *kobj, int property = psy->desc->properties[i]; if (property == attrno) { - if (psy->desc->property_is_writeable && - psy->desc->property_is_writeable(psy, property) > 0) + if (power_supply_property_is_writeable(psy, property) > 0) mode |= S_IWUSR; return mode; diff --git a/drivers/power/supply/qcom_battmgr.c b/drivers/power/supply/qcom_battmgr.c index ec163d1bcd18..46f36dcb185c 100644 --- a/drivers/power/supply/qcom_battmgr.c +++ b/drivers/power/supply/qcom_battmgr.c @@ -1308,6 +1308,7 @@ static void qcom_battmgr_pdr_notify(void *priv, int state) static const struct of_device_id qcom_battmgr_of_variants[] = { { .compatible = "qcom,sc8180x-pmic-glink", .data = (void *)QCOM_BATTMGR_SC8280XP }, { .compatible = "qcom,sc8280xp-pmic-glink", .data = (void *)QCOM_BATTMGR_SC8280XP }, + { .compatible = "qcom,x1e80100-pmic-glink", .data = (void *)QCOM_BATTMGR_SC8280XP }, /* Unmatched devices falls back to QCOM_BATTMGR_SM8350 */ {} }; diff --git a/drivers/power/supply/rt9455_charger.c b/drivers/power/supply/rt9455_charger.c index e4dbacd50a43..64a23e3d7bb0 100644 --- a/drivers/power/supply/rt9455_charger.c +++ b/drivers/power/supply/rt9455_charger.c @@ -1718,8 +1718,8 @@ static void rt9455_remove(struct i2c_client *client) } static const struct i2c_device_id rt9455_i2c_id_table[] = { - { RT9455_DRIVER_NAME, 0 }, - { }, + { RT9455_DRIVER_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, rt9455_i2c_id_table); diff --git a/drivers/power/supply/samsung-sdi-battery.c b/drivers/power/supply/samsung-sdi-battery.c index b33daab798b9..b63fd2758c2f 100644 --- a/drivers/power/supply/samsung-sdi-battery.c +++ b/drivers/power/supply/samsung-sdi-battery.c @@ -25,7 +25,7 @@ struct samsung_sdi_battery { * tables apply depending on whether we are charging or not. */ -static struct power_supply_vbat_ri_table samsung_vbat2res_discharging_eb_l1m7flu[] = { +static const struct power_supply_vbat_ri_table samsung_vbat2res_discharging_eb_l1m7flu[] = { { .vbat_uv = 4240000, .ri_uohm = 160000 }, { .vbat_uv = 4210000, .ri_uohm = 179000 }, { .vbat_uv = 4180000, .ri_uohm = 183000 }, @@ -53,7 +53,7 @@ static struct power_supply_vbat_ri_table samsung_vbat2res_discharging_eb_l1m7flu { .vbat_uv = 3300000, .ri_uohm = 339000 }, }; -static struct power_supply_vbat_ri_table samsung_vbat2res_charging_eb_l1m7flu[] = { +static const struct power_supply_vbat_ri_table samsung_vbat2res_charging_eb_l1m7flu[] = { { .vbat_uv = 4302000, .ri_uohm = 230000 }, { .vbat_uv = 4276000, .ri_uohm = 345000 }, { .vbat_uv = 4227000, .ri_uohm = 345000 }, @@ -73,7 +73,7 @@ static struct power_supply_vbat_ri_table samsung_vbat2res_charging_eb_l1m7flu[] { .vbat_uv = 3590000, .ri_uohm = 164000 }, }; -static struct power_supply_vbat_ri_table samsung_vbat2res_discharging_eb425161la[] = { +static const struct power_supply_vbat_ri_table samsung_vbat2res_discharging_eb425161la[] = { { .vbat_uv = 4240000, .ri_uohm = 160000 }, { .vbat_uv = 4210000, .ri_uohm = 179000 }, { .vbat_uv = 4180000, .ri_uohm = 183000 }, @@ -105,7 +105,7 @@ static struct power_supply_vbat_ri_table samsung_vbat2res_discharging_eb425161la { .vbat_uv = 3300000, .ri_uohm = 339000 }, }; -static struct power_supply_vbat_ri_table samsung_vbat2res_charging_eb425161la[] = { +static const struct power_supply_vbat_ri_table samsung_vbat2res_charging_eb425161la[] = { { .vbat_uv = 4345000, .ri_uohm = 230000 }, { .vbat_uv = 4329000, .ri_uohm = 238000 }, { .vbat_uv = 4314000, .ri_uohm = 225000 }, @@ -182,7 +182,7 @@ static struct power_supply_vbat_ri_table samsung_vbat2res_charging_eb425161la[] { .vbat_uv = 3590000, .ri_uohm = 164000 }, }; -static struct power_supply_vbat_ri_table samsung_vbat2res_discharging_eb425161lu[] = { +static const struct power_supply_vbat_ri_table samsung_vbat2res_discharging_eb425161lu[] = { { .vbat_uv = 4240000, .ri_uohm = 160000 }, { .vbat_uv = 4210000, .ri_uohm = 179000 }, { .vbat_uv = 4180000, .ri_uohm = 183000 }, @@ -214,7 +214,7 @@ static struct power_supply_vbat_ri_table samsung_vbat2res_discharging_eb425161lu { .vbat_uv = 3300000, .ri_uohm = 339000 }, }; -static struct power_supply_vbat_ri_table samsung_vbat2res_charging_eb425161lu[] = { +static const struct power_supply_vbat_ri_table samsung_vbat2res_charging_eb425161lu[] = { { .vbat_uv = 4346000, .ri_uohm = 293000 }, { .vbat_uv = 4336000, .ri_uohm = 290000 }, { .vbat_uv = 4315000, .ri_uohm = 274000 }, @@ -244,7 +244,7 @@ static struct power_supply_vbat_ri_table samsung_vbat2res_charging_eb425161lu[] { .vbat_uv = 3590000, .ri_uohm = 164000 }, }; -static struct power_supply_vbat_ri_table samsung_vbat2res_discharging_eb485159lu[] = { +static const struct power_supply_vbat_ri_table samsung_vbat2res_discharging_eb485159lu[] = { { .vbat_uv = 4240000, .ri_uohm = 160000 }, { .vbat_uv = 4210000, .ri_uohm = 179000 }, { .vbat_uv = 4180000, .ri_uohm = 183000 }, @@ -271,7 +271,7 @@ static struct power_supply_vbat_ri_table samsung_vbat2res_discharging_eb485159lu { .vbat_uv = 3300000, .ri_uohm = 339000 }, }; -static struct power_supply_vbat_ri_table samsung_vbat2res_charging_eb485159lu[] = { +static const struct power_supply_vbat_ri_table samsung_vbat2res_charging_eb485159lu[] = { { .vbat_uv = 4302000, .ri_uohm = 200000 }, { .vbat_uv = 4258000, .ri_uohm = 206000 }, { .vbat_uv = 4200000, .ri_uohm = 231000 }, @@ -291,7 +291,7 @@ static struct power_supply_vbat_ri_table samsung_vbat2res_charging_eb485159lu[] { .vbat_uv = 3590000, .ri_uohm = 164000 }, }; -static struct power_supply_vbat_ri_table samsung_vbat2res_discharging_eb535151vu[] = { +static const struct power_supply_vbat_ri_table samsung_vbat2res_discharging_eb535151vu[] = { { .vbat_uv = 4071000, .ri_uohm = 158000 }, { .vbat_uv = 4019000, .ri_uohm = 187000 }, { .vbat_uv = 3951000, .ri_uohm = 191000 }, @@ -311,7 +311,7 @@ static struct power_supply_vbat_ri_table samsung_vbat2res_discharging_eb535151vu { .vbat_uv = 3280000, .ri_uohm = 250000 }, }; -static struct power_supply_vbat_ri_table samsung_vbat2res_charging_eb535151vu[] = { +static const struct power_supply_vbat_ri_table samsung_vbat2res_charging_eb535151vu[] = { { .vbat_uv = 4190000, .ri_uohm = 214000 }, { .vbat_uv = 4159000, .ri_uohm = 252000 }, { .vbat_uv = 4121000, .ri_uohm = 245000 }, @@ -331,7 +331,7 @@ static struct power_supply_vbat_ri_table samsung_vbat2res_charging_eb535151vu[] { .vbat_uv = 3510000, .ri_uohm = 228000 }, }; -static struct power_supply_vbat_ri_table samsung_vbat2res_discharging_eb585157lu[] = { +static const struct power_supply_vbat_ri_table samsung_vbat2res_discharging_eb585157lu[] = { { .vbat_uv = 4194000, .ri_uohm = 121000 }, { .vbat_uv = 4169000, .ri_uohm = 188000 }, { .vbat_uv = 4136000, .ri_uohm = 173000 }, @@ -401,7 +401,7 @@ static struct power_supply_vbat_ri_table samsung_vbat2res_discharging_eb585157lu { .vbat_uv = 3161000, .ri_uohm = 452000 }, }; -static struct power_supply_vbat_ri_table samsung_vbat2res_charging_eb585157lu[] = { +static const struct power_supply_vbat_ri_table samsung_vbat2res_charging_eb585157lu[] = { { .vbat_uv = 4360000, .ri_uohm = 128000 }, { .vbat_uv = 4325000, .ri_uohm = 130000 }, { .vbat_uv = 4316000, .ri_uohm = 148000 }, @@ -613,7 +613,7 @@ static struct power_supply_battery_ocv_table samsung_ocv_cap_eb585157lu[] = { { .ocv = 3300000, .capacity = 0}, }; -static struct power_supply_maintenance_charge_table samsung_maint_charge_table[] = { +static const struct power_supply_maintenance_charge_table samsung_maint_charge_table[] = { { /* Maintenance charging phase A, 60 hours */ .charge_current_max_ua = 600000, diff --git a/drivers/power/supply/sbs-charger.c b/drivers/power/supply/sbs-charger.c index f4adde449270..ab3f095d90ea 100644 --- a/drivers/power/supply/sbs-charger.c +++ b/drivers/power/supply/sbs-charger.c @@ -234,7 +234,7 @@ MODULE_DEVICE_TABLE(of, sbs_dt_ids); #endif static const struct i2c_device_id sbs_id[] = { - { "sbs-charger", 0 }, + { "sbs-charger" }, { } }; MODULE_DEVICE_TABLE(i2c, sbs_id); diff --git a/drivers/power/supply/sbs-manager.c b/drivers/power/supply/sbs-manager.c index 933b04806d10..7d2f39f19acb 100644 --- a/drivers/power/supply/sbs-manager.c +++ b/drivers/power/supply/sbs-manager.c @@ -389,8 +389,8 @@ static int sbsm_probe(struct i2c_client *client) } static const struct i2c_device_id sbsm_ids[] = { - { "sbs-manager", 0 }, - { "ltc1760", 0 }, + { "sbs-manager" }, + { "ltc1760" }, { } }; MODULE_DEVICE_TABLE(i2c, sbsm_ids); diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 48845dc8fa85..dda2ada215b7 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -166,6 +166,7 @@ config QCOM_PIL_INFO config QCOM_RPROC_COMMON tristate + select AUXILIARY_BUS config QCOM_Q6V5_COMMON tristate diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 5a3fb902acc9..144c8e9a642e 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -726,31 +726,37 @@ static int imx_rproc_addr_init(struct imx_rproc *priv, struct resource res; node = of_parse_phandle(np, "memory-region", a); + if (!node) + continue; /* Not map vdevbuffer, vdevring region */ if (!strncmp(node->name, "vdev", strlen("vdev"))) { of_node_put(node); continue; } err = of_address_to_resource(node, 0, &res); - of_node_put(node); if (err) { dev_err(dev, "unable to resolve memory region\n"); + of_node_put(node); return err; } - if (b >= IMX_RPROC_MEM_MAX) + if (b >= IMX_RPROC_MEM_MAX) { + of_node_put(node); break; + } /* Not use resource version, because we might share region */ priv->mem[b].cpu_addr = devm_ioremap_wc(&pdev->dev, res.start, resource_size(&res)); if (!priv->mem[b].cpu_addr) { dev_err(dev, "failed to remap %pr\n", &res); + of_node_put(node); return -ENOMEM; } priv->mem[b].sys_addr = res.start; priv->mem[b].size = resource_size(&res); if (!strcmp(node->name, "rsc-table")) priv->rsc_table = priv->mem[b].cpu_addr; + of_node_put(node); b++; } diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c index b8498772dba1..e744c07507ee 100644 --- a/drivers/remoteproc/mtk_scp.c +++ b/drivers/remoteproc/mtk_scp.c @@ -117,8 +117,8 @@ static void scp_ipi_handler(struct mtk_scp *scp) return; } - memset(scp->share_buf, 0, scp_sizes->ipi_share_buffer_size); memcpy_fromio(scp->share_buf, &rcv_obj->share_buf, len); + memset(&scp->share_buf[len], 0, scp_sizes->ipi_share_buffer_size - len); handler(scp->share_buf, len, ipi_desc[id].priv); scp_ipi_unlock(scp, id); @@ -1344,14 +1344,12 @@ static int scp_probe(struct platform_device *pdev) /* l1tcm is an optional memory region */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "l1tcm"); - scp_cluster->l1tcm_base = devm_ioremap_resource(dev, res); - if (IS_ERR(scp_cluster->l1tcm_base)) { - ret = PTR_ERR(scp_cluster->l1tcm_base); - if (ret != -EINVAL) - return dev_err_probe(dev, ret, "Failed to map l1tcm memory\n"); + if (res) { + scp_cluster->l1tcm_base = devm_ioremap_resource(dev, res); + if (IS_ERR(scp_cluster->l1tcm_base)) + return dev_err_probe(dev, PTR_ERR(scp_cluster->l1tcm_base), + "Failed to map l1tcm memory\n"); - scp_cluster->l1tcm_base = NULL; - } else { scp_cluster->l1tcm_size = resource_size(res); scp_cluster->l1tcm_phys = res->start; } @@ -1390,7 +1388,7 @@ static const struct mtk_scp_sizes_data default_scp_sizes = { }; static const struct mtk_scp_sizes_data mt8188_scp_sizes = { - .max_dram_size = 0x500000, + .max_dram_size = 0x800000, .ipi_share_buffer_size = 600, }; @@ -1399,6 +1397,11 @@ static const struct mtk_scp_sizes_data mt8188_scp_c1_sizes = { .ipi_share_buffer_size = 600, }; +static const struct mtk_scp_sizes_data mt8195_scp_sizes = { + .max_dram_size = 0x800000, + .ipi_share_buffer_size = 288, +}; + static const struct mtk_scp_of_data mt8183_of_data = { .scp_clk_get = mt8183_scp_clk_get, .scp_before_load = mt8183_scp_before_load, @@ -1476,7 +1479,7 @@ static const struct mtk_scp_of_data mt8195_of_data = { .scp_da_to_va = mt8192_scp_da_to_va, .host_to_scp_reg = MT8192_GIPC_IN_SET, .host_to_scp_int_bit = MT8192_HOST_IPC_INT_BIT, - .scp_sizes = &default_scp_sizes, + .scp_sizes = &mt8195_scp_sizes, }; static const struct mtk_scp_of_data mt8195_of_data_c1 = { diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index 8f50ab80e56f..9ae2e831456d 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -1277,6 +1277,13 @@ static int omap_rproc_of_get_timers(struct platform_device *pdev, return 0; } +static void omap_rproc_mem_release(void *data) +{ + struct device *dev = data; + + of_reserved_mem_device_release(dev); +} + static int omap_rproc_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -1305,8 +1312,8 @@ static int omap_rproc_probe(struct platform_device *pdev) return ret; } - rproc = rproc_alloc(&pdev->dev, dev_name(&pdev->dev), &omap_rproc_ops, - firmware, sizeof(*oproc)); + rproc = devm_rproc_alloc(&pdev->dev, dev_name(&pdev->dev), &omap_rproc_ops, + firmware, sizeof(*oproc)); if (!rproc) return -ENOMEM; @@ -1318,15 +1325,15 @@ static int omap_rproc_probe(struct platform_device *pdev) ret = omap_rproc_of_get_internal_memories(pdev, rproc); if (ret) - goto free_rproc; + return ret; ret = omap_rproc_get_boot_data(pdev, rproc); if (ret) - goto free_rproc; + return ret; ret = omap_rproc_of_get_timers(pdev, rproc); if (ret) - goto free_rproc; + return ret; init_completion(&oproc->pm_comp); oproc->autosuspend_delay = DEFAULT_AUTOSUSPEND_DELAY; @@ -1337,10 +1344,8 @@ static int omap_rproc_probe(struct platform_device *pdev) pm_runtime_set_autosuspend_delay(&pdev->dev, oproc->autosuspend_delay); oproc->fck = devm_clk_get(&pdev->dev, 0); - if (IS_ERR(oproc->fck)) { - ret = PTR_ERR(oproc->fck); - goto free_rproc; - } + if (IS_ERR(oproc->fck)) + return PTR_ERR(oproc->fck); ret = of_reserved_mem_device_init(&pdev->dev); if (ret) { @@ -1348,29 +1353,17 @@ static int omap_rproc_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "Typically this should be provided,\n"); dev_warn(&pdev->dev, "only omit if you know what you are doing.\n"); } + ret = devm_add_action_or_reset(&pdev->dev, omap_rproc_mem_release, &pdev->dev); + if (ret) + return ret; platform_set_drvdata(pdev, rproc); - ret = rproc_add(rproc); + ret = devm_rproc_add(&pdev->dev, rproc); if (ret) - goto release_mem; + return ret; return 0; - -release_mem: - of_reserved_mem_device_release(&pdev->dev); -free_rproc: - rproc_free(rproc); - return ret; -} - -static void omap_rproc_remove(struct platform_device *pdev) -{ - struct rproc *rproc = platform_get_drvdata(pdev); - - rproc_del(rproc); - rproc_free(rproc); - of_reserved_mem_device_release(&pdev->dev); } static const struct dev_pm_ops omap_rproc_pm_ops = { @@ -1381,7 +1374,6 @@ static const struct dev_pm_ops omap_rproc_pm_ops = { static struct platform_driver omap_rproc_driver = { .probe = omap_rproc_probe, - .remove_new = omap_rproc_remove, .driver = { .name = "omap-rproc", .pm = &omap_rproc_pm_ops, diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c index 03e5f5d533eb..8c8688f99f0a 100644 --- a/drivers/remoteproc/qcom_common.c +++ b/drivers/remoteproc/qcom_common.c @@ -13,6 +13,7 @@ #include <linux/notifier.h> #include <linux/remoteproc.h> #include <linux/remoteproc/qcom_rproc.h> +#include <linux/auxiliary_bus.h> #include <linux/rpmsg/qcom_glink.h> #include <linux/rpmsg/qcom_smd.h> #include <linux/slab.h> @@ -25,6 +26,7 @@ #define to_glink_subdev(d) container_of(d, struct qcom_rproc_glink, subdev) #define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev) #define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev) +#define to_pdm_subdev(d) container_of(d, struct qcom_rproc_pdm, subdev) #define MAX_NUM_OF_SS 10 #define MAX_REGION_NAME_LENGTH 16 @@ -519,5 +521,90 @@ void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr) } EXPORT_SYMBOL_GPL(qcom_remove_ssr_subdev); +static void pdm_dev_release(struct device *dev) +{ + struct auxiliary_device *adev = to_auxiliary_dev(dev); + + kfree(adev); +} + +static int pdm_notify_prepare(struct rproc_subdev *subdev) +{ + struct qcom_rproc_pdm *pdm = to_pdm_subdev(subdev); + struct auxiliary_device *adev; + int ret; + + adev = kzalloc(sizeof(*adev), GFP_KERNEL); + if (!adev) + return -ENOMEM; + + adev->dev.parent = pdm->dev; + adev->dev.release = pdm_dev_release; + adev->name = "pd-mapper"; + adev->id = pdm->index; + + ret = auxiliary_device_init(adev); + if (ret) { + kfree(adev); + return ret; + } + + ret = auxiliary_device_add(adev); + if (ret) { + auxiliary_device_uninit(adev); + return ret; + } + + pdm->adev = adev; + + return 0; +} + + +static void pdm_notify_unprepare(struct rproc_subdev *subdev) +{ + struct qcom_rproc_pdm *pdm = to_pdm_subdev(subdev); + + if (!pdm->adev) + return; + + auxiliary_device_delete(pdm->adev); + auxiliary_device_uninit(pdm->adev); + pdm->adev = NULL; +} + +/** + * qcom_add_pdm_subdev() - register PD Mapper subdevice + * @rproc: rproc handle + * @pdm: PDM subdevice handle + * + * Register @pdm so that Protection Device mapper service is started when the + * DSP is started too. + */ +void qcom_add_pdm_subdev(struct rproc *rproc, struct qcom_rproc_pdm *pdm) +{ + pdm->dev = &rproc->dev; + pdm->index = rproc->index; + + pdm->subdev.prepare = pdm_notify_prepare; + pdm->subdev.unprepare = pdm_notify_unprepare; + + rproc_add_subdev(rproc, &pdm->subdev); +} +EXPORT_SYMBOL_GPL(qcom_add_pdm_subdev); + +/** + * qcom_remove_pdm_subdev() - remove PD Mapper subdevice + * @rproc: rproc handle + * @pdm: PDM subdevice handle + * + * Remove the PD Mapper subdevice. + */ +void qcom_remove_pdm_subdev(struct rproc *rproc, struct qcom_rproc_pdm *pdm) +{ + rproc_remove_subdev(rproc, &pdm->subdev); +} +EXPORT_SYMBOL_GPL(qcom_remove_pdm_subdev); + MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/remoteproc/qcom_common.h b/drivers/remoteproc/qcom_common.h index 9ef4449052a9..b07fbaa091a0 100644 --- a/drivers/remoteproc/qcom_common.h +++ b/drivers/remoteproc/qcom_common.h @@ -34,6 +34,13 @@ struct qcom_rproc_ssr { struct qcom_ssr_subsystem *info; }; +struct qcom_rproc_pdm { + struct rproc_subdev subdev; + struct device *dev; + int index; + struct auxiliary_device *adev; +}; + void qcom_minidump(struct rproc *rproc, unsigned int minidump_id, void (*rproc_dumpfn_t)(struct rproc *rproc, struct rproc_dump_segment *segment, void *dest, size_t offset, @@ -52,6 +59,9 @@ void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr, const char *ssr_name); void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr); +void qcom_add_pdm_subdev(struct rproc *rproc, struct qcom_rproc_pdm *pdm); +void qcom_remove_pdm_subdev(struct rproc *rproc, struct qcom_rproc_pdm *pdm); + #if IS_ENABLED(CONFIG_QCOM_SYSMON) struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, const char *name, diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index 1d24c9b656a8..572dcb0f055b 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -112,6 +112,7 @@ struct qcom_adsp { struct dev_pm_domain_list *pd_list; struct qcom_rproc_glink glink_subdev; + struct qcom_rproc_pdm pdm_subdev; struct qcom_rproc_ssr ssr_subdev; struct qcom_sysmon *sysmon; @@ -726,6 +727,7 @@ static int adsp_probe(struct platform_device *pdev) goto disable_pm; qcom_add_glink_subdev(rproc, &adsp->glink_subdev, desc->ssr_name); + qcom_add_pdm_subdev(rproc, &adsp->pdm_subdev); qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name); adsp->sysmon = qcom_add_sysmon_subdev(rproc, desc->sysmon_name, @@ -755,6 +757,7 @@ static void adsp_remove(struct platform_device *pdev) qcom_q6v5_deinit(&adsp->q6v5); qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev); + qcom_remove_pdm_subdev(adsp->rproc, &adsp->pdm_subdev); qcom_remove_sysmon_subdev(adsp->sysmon); qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev); qcom_rproc_pds_detach(adsp); diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index 1779fc890e10..2a42215ce8e0 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -228,6 +228,7 @@ struct q6v5 { struct qcom_rproc_glink glink_subdev; struct qcom_rproc_subdev smd_subdev; + struct qcom_rproc_pdm pdm_subdev; struct qcom_rproc_ssr ssr_subdev; struct qcom_sysmon *sysmon; struct platform_device *bam_dmux; @@ -2102,6 +2103,7 @@ static int q6v5_probe(struct platform_device *pdev) qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS); qcom_add_glink_subdev(rproc, &qproc->glink_subdev, "mpss"); qcom_add_smd_subdev(rproc, &qproc->smd_subdev); + qcom_add_pdm_subdev(rproc, &qproc->pdm_subdev); qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss"); qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12); if (IS_ERR(qproc->sysmon)) { @@ -2143,6 +2145,7 @@ static void q6v5_remove(struct platform_device *pdev) qcom_q6v5_deinit(&qproc->q6v5); qcom_remove_sysmon_subdev(qproc->sysmon); qcom_remove_ssr_subdev(rproc, &qproc->ssr_subdev); + qcom_remove_pdm_subdev(rproc, &qproc->pdm_subdev); qcom_remove_smd_subdev(rproc, &qproc->smd_subdev); qcom_remove_glink_subdev(rproc, &qproc->glink_subdev); diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 54d8005d40a3..88e7b84f223c 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -52,6 +52,7 @@ struct adsp_data { const char *ssr_name; const char *sysmon_name; int ssctl_id; + unsigned int smem_host_id; int region_assign_idx; int region_assign_count; @@ -81,6 +82,7 @@ struct qcom_adsp { int lite_pas_id; unsigned int minidump_id; int crash_reason_smem; + unsigned int smem_host_id; bool decrypt_shutdown; const char *info_name; @@ -109,6 +111,7 @@ struct qcom_adsp { struct qcom_rproc_glink glink_subdev; struct qcom_rproc_subdev smd_subdev; + struct qcom_rproc_pdm pdm_subdev; struct qcom_rproc_ssr ssr_subdev; struct qcom_sysmon *sysmon; @@ -399,6 +402,9 @@ static int adsp_stop(struct rproc *rproc) if (handover) qcom_pas_handover(&adsp->q6v5); + if (adsp->smem_host_id) + ret = qcom_smem_bust_hwspin_lock_by_host(adsp->smem_host_id); + return ret; } @@ -727,6 +733,7 @@ static int adsp_probe(struct platform_device *pdev) adsp->pas_id = desc->pas_id; adsp->lite_pas_id = desc->lite_pas_id; adsp->info_name = desc->sysmon_name; + adsp->smem_host_id = desc->smem_host_id; adsp->decrypt_shutdown = desc->decrypt_shutdown; adsp->region_assign_idx = desc->region_assign_idx; adsp->region_assign_count = min_t(int, MAX_ASSIGN_COUNT, desc->region_assign_count); @@ -771,6 +778,7 @@ static int adsp_probe(struct platform_device *pdev) qcom_add_glink_subdev(rproc, &adsp->glink_subdev, desc->ssr_name); qcom_add_smd_subdev(rproc, &adsp->smd_subdev); + qcom_add_pdm_subdev(rproc, &adsp->pdm_subdev); adsp->sysmon = qcom_add_sysmon_subdev(rproc, desc->sysmon_name, desc->ssctl_id); @@ -805,6 +813,7 @@ static void adsp_remove(struct platform_device *pdev) qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev); qcom_remove_sysmon_subdev(adsp->sysmon); qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev); + qcom_remove_pdm_subdev(adsp->rproc, &adsp->pdm_subdev); qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev); adsp_pds_detach(adsp, adsp->proxy_pds, adsp->proxy_pd_count); device_init_wakeup(adsp->dev, false); @@ -1196,6 +1205,7 @@ static const struct adsp_data sm8550_adsp_resource = { .ssr_name = "lpass", .sysmon_name = "adsp", .ssctl_id = 0x14, + .smem_host_id = 2, }; static const struct adsp_data sm8550_cdsp_resource = { @@ -1216,6 +1226,7 @@ static const struct adsp_data sm8550_cdsp_resource = { .ssr_name = "cdsp", .sysmon_name = "cdsp", .ssctl_id = 0x17, + .smem_host_id = 5, }; static const struct adsp_data sm8550_mpss_resource = { @@ -1236,6 +1247,7 @@ static const struct adsp_data sm8550_mpss_resource = { .ssr_name = "mpss", .sysmon_name = "modem", .ssctl_id = 0x12, + .smem_host_id = 1, .region_assign_idx = 2, .region_assign_count = 1, .region_assign_vmid = QCOM_SCM_VMID_MSS_MSA, @@ -1275,6 +1287,7 @@ static const struct adsp_data sm8650_cdsp_resource = { .ssr_name = "cdsp", .sysmon_name = "cdsp", .ssctl_id = 0x17, + .smem_host_id = 5, .region_assign_idx = 2, .region_assign_count = 1, .region_assign_shared = true, @@ -1299,6 +1312,7 @@ static const struct adsp_data sm8650_mpss_resource = { .ssr_name = "mpss", .sysmon_name = "modem", .ssctl_id = 0x12, + .smem_host_id = 1, .region_assign_idx = 2, .region_assign_count = 3, .region_assign_vmid = QCOM_SCM_VMID_MSS_MSA, diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c index 94f68c919ee6..e913dabae992 100644 --- a/drivers/remoteproc/qcom_q6v5_wcss.c +++ b/drivers/remoteproc/qcom_q6v5_wcss.c @@ -148,6 +148,7 @@ struct q6v5_wcss { bool requires_force_stop; struct qcom_rproc_glink glink_subdev; + struct qcom_rproc_pdm pdm_subdev; struct qcom_rproc_ssr ssr_subdev; }; @@ -1052,6 +1053,7 @@ static int q6v5_wcss_probe(struct platform_device *pdev) return ret; qcom_add_glink_subdev(rproc, &wcss->glink_subdev, "q6wcss"); + qcom_add_pdm_subdev(rproc, &wcss->pdm_subdev); qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, "q6wcss"); if (desc->ssctl_id) @@ -1074,6 +1076,7 @@ static void q6v5_wcss_remove(struct platform_device *pdev) struct q6v5_wcss *wcss = rproc->priv; qcom_q6v5_deinit(&wcss->q6v5); + qcom_remove_pdm_subdev(rproc, &wcss->pdm_subdev); rproc_del(rproc); } diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c index 88623df7d0c3..8c7f7950b80e 100644 --- a/drivers/remoteproc/stm32_rproc.c +++ b/drivers/remoteproc/stm32_rproc.c @@ -294,7 +294,7 @@ static void stm32_rproc_mb_vq_work(struct work_struct *work) mutex_lock(&rproc->lock); - if (rproc->state != RPROC_RUNNING) + if (rproc->state != RPROC_RUNNING && rproc->state != RPROC_ATTACHED) goto unlock_mutex; if (rproc_vq_interrupt(rproc, mb->vq_id) == IRQ_NONE) diff --git a/drivers/remoteproc/ti_k3_dsp_remoteproc.c b/drivers/remoteproc/ti_k3_dsp_remoteproc.c index 3555b535b168..a22d41689a7d 100644 --- a/drivers/remoteproc/ti_k3_dsp_remoteproc.c +++ b/drivers/remoteproc/ti_k3_dsp_remoteproc.c @@ -327,7 +327,7 @@ static int k3_dsp_rproc_start(struct rproc *rproc) goto put_mbox; } - dev_err(dev, "booting DSP core using boot addr = 0x%x\n", boot_addr); + dev_dbg(dev, "booting DSP core using boot addr = 0x%x\n", boot_addr); ret = ti_sci_proc_set_config(kproc->tsp, boot_addr, 0, 0); if (ret) goto put_mbox; diff --git a/drivers/remoteproc/ti_k3_r5_remoteproc.c b/drivers/remoteproc/ti_k3_r5_remoteproc.c index 50e486bcfa10..39a47540c590 100644 --- a/drivers/remoteproc/ti_k3_r5_remoteproc.c +++ b/drivers/remoteproc/ti_k3_r5_remoteproc.c @@ -1144,6 +1144,7 @@ static int k3_r5_rproc_configure_mode(struct k3_r5_rproc *kproc) u32 atcm_enable, btcm_enable, loczrama; struct k3_r5_core *core0; enum cluster_mode mode = cluster->mode; + int reset_ctrl_status; int ret; core0 = list_first_entry(&cluster->cores, struct k3_r5_core, elem); @@ -1160,11 +1161,11 @@ static int k3_r5_rproc_configure_mode(struct k3_r5_rproc *kproc) r_state, c_state); } - ret = reset_control_status(core->reset); - if (ret < 0) { + reset_ctrl_status = reset_control_status(core->reset); + if (reset_ctrl_status < 0) { dev_err(cdev, "failed to get initial local reset status, ret = %d\n", - ret); - return ret; + reset_ctrl_status); + return reset_ctrl_status; } /* @@ -1199,7 +1200,7 @@ static int k3_r5_rproc_configure_mode(struct k3_r5_rproc *kproc) * irrelevant if module reset is asserted (POR value has local reset * deasserted), and is deemed as remoteproc mode */ - if (c_state && !ret && !halted) { + if (c_state && !reset_ctrl_status && !halted) { dev_info(cdev, "configured R5F for IPC-only mode\n"); kproc->rproc->state = RPROC_DETACHED; ret = 1; @@ -1217,7 +1218,7 @@ static int k3_r5_rproc_configure_mode(struct k3_r5_rproc *kproc) ret = 0; } else { dev_err(cdev, "mismatched mode: local_reset = %s, module_reset = %s, core_state = %s\n", - !ret ? "deasserted" : "asserted", + !reset_ctrl_status ? "deasserted" : "asserted", c_state ? "deasserted" : "asserted", halted ? "halted" : "unhalted"); ret = -EINVAL; diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c index 84243d1dff9f..596f3ffb8935 100644 --- a/drivers/remoteproc/xlnx_r5_remoteproc.c +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c @@ -25,6 +25,10 @@ /* RX mailbox client buffer max length */ #define MBOX_CLIENT_BUF_MAX (IPI_BUF_LEN_MAX + \ sizeof(struct zynqmp_ipi_message)) + +#define RSC_TBL_XLNX_MAGIC ((uint32_t)'x' << 24 | (uint32_t)'a' << 16 | \ + (uint32_t)'m' << 8 | (uint32_t)'p') + /* * settings for RPU cluster mode which * reflects possible values of xlnx,cluster-mode dt-property @@ -73,6 +77,26 @@ struct mbox_info { struct mbox_chan *rx_chan; }; +/** + * struct rsc_tbl_data + * + * Platform specific data structure used to sync resource table address. + * It's important to maintain order and size of each field on remote side. + * + * @version: version of data structure + * @magic_num: 32-bit magic number. + * @comp_magic_num: complement of above magic number + * @rsc_tbl_size: resource table size + * @rsc_tbl: resource table address + */ +struct rsc_tbl_data { + const int version; + const u32 magic_num; + const u32 comp_magic_num; + const u32 rsc_tbl_size; + const uintptr_t rsc_tbl; +} __packed; + /* * Hardcoded TCM bank values. This will stay in driver to maintain backward * compatibility with device-tree that does not have TCM information. @@ -95,20 +119,24 @@ static const struct mem_bank_data zynqmp_tcm_banks_lockstep[] = { /** * struct zynqmp_r5_core * + * @rsc_tbl_va: resource table virtual address * @dev: device of RPU instance * @np: device node of RPU instance * @tcm_bank_count: number TCM banks accessible to this RPU * @tcm_banks: array of each TCM bank data * @rproc: rproc handle + * @rsc_tbl_size: resource table size retrieved from remote * @pm_domain_id: RPU CPU power domain id * @ipi: pointer to mailbox information */ struct zynqmp_r5_core { + void __iomem *rsc_tbl_va; struct device *dev; struct device_node *np; int tcm_bank_count; struct mem_bank_data **tcm_banks; struct rproc *rproc; + u32 rsc_tbl_size; u32 pm_domain_id; struct mbox_info *ipi; }; @@ -557,6 +585,14 @@ static int add_tcm_banks(struct rproc *rproc) dev_dbg(dev, "TCM carveout %s addr=%llx, da=0x%x, size=0x%lx", bank_name, bank_addr, da, bank_size); + /* + * In DETACHED state firmware is already running so no need to + * request add TCM registers. However, request TCM PD node to let + * platform management firmware know that TCM is in use. + */ + if (rproc->state == RPROC_DETACHED) + continue; + rproc_mem = rproc_mem_entry_init(dev, NULL, bank_addr, bank_size, da, tcm_mem_map, tcm_mem_unmap, @@ -662,6 +698,107 @@ static int zynqmp_r5_rproc_unprepare(struct rproc *rproc) return 0; } +static struct resource_table *zynqmp_r5_get_loaded_rsc_table(struct rproc *rproc, + size_t *size) +{ + struct zynqmp_r5_core *r5_core; + + r5_core = rproc->priv; + + *size = r5_core->rsc_tbl_size; + + return (struct resource_table *)r5_core->rsc_tbl_va; +} + +static int zynqmp_r5_get_rsc_table_va(struct zynqmp_r5_core *r5_core) +{ + struct resource_table *rsc_tbl_addr; + struct device *dev = r5_core->dev; + struct rsc_tbl_data *rsc_data_va; + struct resource res_mem; + struct device_node *np; + int ret; + + /* + * It is expected from remote processor firmware to provide resource + * table address via struct rsc_tbl_data data structure. + * Start address of first entry under "memory-region" property list + * contains that data structure which holds resource table address, size + * and some magic number to validate correct resource table entry. + */ + np = of_parse_phandle(r5_core->np, "memory-region", 0); + if (!np) { + dev_err(dev, "failed to get memory region dev node\n"); + return -EINVAL; + } + + ret = of_address_to_resource(np, 0, &res_mem); + of_node_put(np); + if (ret) { + dev_err(dev, "failed to get memory-region resource addr\n"); + return -EINVAL; + } + + rsc_data_va = (struct rsc_tbl_data *)ioremap_wc(res_mem.start, + sizeof(struct rsc_tbl_data)); + if (!rsc_data_va) { + dev_err(dev, "failed to map resource table data address\n"); + return -EIO; + } + + /* + * If RSC_TBL_XLNX_MAGIC number and its complement isn't found then + * do not consider resource table address valid and don't attach + */ + if (rsc_data_va->magic_num != RSC_TBL_XLNX_MAGIC || + rsc_data_va->comp_magic_num != ~RSC_TBL_XLNX_MAGIC) { + dev_dbg(dev, "invalid magic number, won't attach\n"); + return -EINVAL; + } + + r5_core->rsc_tbl_va = ioremap_wc(rsc_data_va->rsc_tbl, + rsc_data_va->rsc_tbl_size); + if (!r5_core->rsc_tbl_va) { + dev_err(dev, "failed to get resource table va\n"); + return -EINVAL; + } + + rsc_tbl_addr = (struct resource_table *)r5_core->rsc_tbl_va; + + /* + * As of now resource table version 1 is expected. Don't fail to attach + * but warn users about it. + */ + if (rsc_tbl_addr->ver != 1) + dev_warn(dev, "unexpected resource table version %d\n", + rsc_tbl_addr->ver); + + r5_core->rsc_tbl_size = rsc_data_va->rsc_tbl_size; + + iounmap((void __iomem *)rsc_data_va); + + return 0; +} + +static int zynqmp_r5_attach(struct rproc *rproc) +{ + dev_dbg(&rproc->dev, "rproc %d attached\n", rproc->index); + + return 0; +} + +static int zynqmp_r5_detach(struct rproc *rproc) +{ + /* + * Generate last notification to remote after clearing virtio flag. + * Remote can avoid polling on virtio reset flag if kick is generated + * during detach by host and check virtio reset flag on kick interrupt. + */ + zynqmp_r5_rproc_kick(rproc, 0); + + return 0; +} + static const struct rproc_ops zynqmp_r5_rproc_ops = { .prepare = zynqmp_r5_rproc_prepare, .unprepare = zynqmp_r5_rproc_unprepare, @@ -673,6 +810,9 @@ static const struct rproc_ops zynqmp_r5_rproc_ops = { .sanity_check = rproc_elf_sanity_check, .get_boot_addr = rproc_elf_get_boot_addr, .kick = zynqmp_r5_rproc_kick, + .get_loaded_rsc_table = zynqmp_r5_get_loaded_rsc_table, + .attach = zynqmp_r5_attach, + .detach = zynqmp_r5_detach, }; /** @@ -723,6 +863,16 @@ static struct zynqmp_r5_core *zynqmp_r5_add_rproc_core(struct device *cdev) goto free_rproc; } + /* + * If firmware is already available in the memory then move rproc state + * to DETACHED. Firmware can be preloaded via debugger or by any other + * agent (processors) in the system. + * If firmware isn't available in the memory and resource table isn't + * found, then rproc state remains OFFLINE. + */ + if (!zynqmp_r5_get_rsc_table_va(r5_core)) + r5_rproc->state = RPROC_DETACHED; + r5_core->rproc = r5_rproc; return r5_core; @@ -1134,6 +1284,7 @@ static void zynqmp_r5_cluster_exit(void *data) for (i = 0; i < cluster->core_count; i++) { r5_core = cluster->r5_cores[i]; zynqmp_r5_free_mbox(r5_core->ipi); + iounmap(r5_core->rsc_tbl_va); of_reserved_mem_device_release(r5_core->dev); put_device(r5_core->dev); rproc_del(r5_core->rproc); diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index d7a342510902..eec7642d2686 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -52,8 +52,8 @@ static DEFINE_IDA(rpmsg_minor_ida); * @readq: wait object for incoming queue * @default_ept: set to channel default endpoint if the default endpoint should be re-used * on device open to prevent endpoint address update. - * remote_flow_restricted: to indicate if the remote has requested for flow to be limited - * remote_flow_updated: to indicate if the flow control has been requested + * @remote_flow_restricted: to indicate if the remote has requested for flow to be limited + * @remote_flow_updated: to indicate if the flow control has been requested */ struct rpmsg_eptdev { struct device dev; @@ -566,4 +566,5 @@ static void rpmsg_chrdev_exit(void) module_exit(rpmsg_chrdev_exit); MODULE_ALIAS("rpmsg:rpmsg_chrdev"); +MODULE_DESCRIPTION("RPMSG device interface"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 0316c20823ee..6adaeb985dde 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -2248,13 +2248,19 @@ static ssize_t dasd_copy_pair_store(struct device *dev, /* allocate primary devmap if needed */ prim_devmap = dasd_find_busid(prim_busid); - if (IS_ERR(prim_devmap)) + if (IS_ERR(prim_devmap)) { prim_devmap = dasd_add_busid(prim_busid, DASD_FEATURE_DEFAULT); + if (IS_ERR(prim_devmap)) + return PTR_ERR(prim_devmap); + } /* allocate secondary devmap if needed */ sec_devmap = dasd_find_busid(sec_busid); - if (IS_ERR(sec_devmap)) + if (IS_ERR(sec_devmap)) { sec_devmap = dasd_add_busid(sec_busid, DASD_FEATURE_DEFAULT); + if (IS_ERR(sec_devmap)) + return PTR_ERR(sec_devmap); + } /* setting copy relation is only allowed for offline secondary */ if (sec_devmap->device) diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index ea4b1d01bb76..8245b742e4a2 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -29,6 +29,7 @@ #include "dasd_int.h" #include "dasd_diag.h" +MODULE_DESCRIPTION("S/390 Support for DIAG access to DASD Disks"); MODULE_LICENSE("GPL"); /* The maximum number of blocks per request (max_blocks) is dependent on the diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index a76c6af9ea63..9388b5c383ca 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -44,6 +44,7 @@ /* 64k are 128 x 512 byte sectors */ #define DASD_RAW_SECTORS_PER_TRACK 128 +MODULE_DESCRIPTION("S/390 DASD ECKD Disks device driver"); MODULE_LICENSE("GPL"); static struct dasd_discipline dasd_eckd_discipline; diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index 9f2023a077c2..a2216795591d 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -32,6 +32,7 @@ #define DASD_FBA_CCW_LOCATE 0x43 #define DASD_FBA_CCW_DEFINE_EXTENT 0x63 +MODULE_DESCRIPTION("S/390 DASD FBA Disks device driver"); MODULE_LICENSE("GPL"); static struct dasd_discipline dasd_fba_discipline; diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 2e933fd1de70..adeaa8ab9951 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -33,11 +33,12 @@ * than the level indicated above to trigger output. */ +#include <linux/bio-integrity.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/kernel.h> #include <linux/mm.h> -#include <linux/bio.h> +#include <linux/bio-integrity.h> #include <linux/hdreg.h> #include <linux/errno.h> #include <linux/idr.h> diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index e40aac281b06..e4411771f482 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -359,6 +359,32 @@ static struct qcom_smem *__smem; /* Timeout (ms) for the trylock of remote spinlocks */ #define HWSPINLOCK_TIMEOUT 1000 +/* The qcom hwspinlock id is always plus one from the smem host id */ +#define SMEM_HOST_ID_TO_HWSPINLOCK_ID(__x) ((__x) + 1) + +/** + * qcom_smem_bust_hwspin_lock_by_host() - bust the smem hwspinlock for a host + * @host: remote processor id + * + * Busts the hwspin_lock for the given smem host id. This helper is intended + * for remoteproc drivers that manage remoteprocs with an equivalent smem + * driver instance in the remote firmware. Drivers can force a release of the + * smem hwspin_lock if the rproc unexpectedly goes into a bad state. + * + * Context: Process context. + * + * Returns: 0 on success, otherwise negative errno. + */ +int qcom_smem_bust_hwspin_lock_by_host(unsigned int host) +{ + /* This function is for remote procs, so ignore SMEM_HOST_APPS */ + if (host == SMEM_HOST_APPS || host >= SMEM_HOST_COUNT) + return -EINVAL; + + return hwspin_lock_bust(__smem->hwlock, SMEM_HOST_ID_TO_HWSPINLOCK_ID(host)); +} +EXPORT_SYMBOL_GPL(qcom_smem_bust_hwspin_lock_by_host); + /** * qcom_smem_is_available() - Check if SMEM is available * diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 8795187fbc52..f6e700e48aad 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -300,8 +300,6 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz) thermal_zone_device_set_polling(tz, tz->passive_delay_jiffies); else if (tz->polling_delay_jiffies) thermal_zone_device_set_polling(tz, tz->polling_delay_jiffies); - else if (tz->temperature == THERMAL_TEMP_INVALID) - thermal_zone_device_set_polling(tz, msecs_to_jiffies(THERMAL_RECHECK_DELAY_MS)); } static struct thermal_governor *thermal_get_tz_governor(struct thermal_zone_device *tz) @@ -382,7 +380,7 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, td->threshold = trip->temperature; if (tz->last_temperature >= old_threshold && - tz->last_temperature != THERMAL_TEMP_INVALID) { + tz->last_temperature != THERMAL_TEMP_INIT) { /* * Mitigation is under way, so it needs to stop if the zone * temperature falls below the low temperature of the trip. @@ -417,27 +415,6 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, } } -static void update_temperature(struct thermal_zone_device *tz) -{ - int temp, ret; - - ret = __thermal_zone_get_temp(tz, &temp); - if (ret) { - if (ret != -EAGAIN) - dev_warn(&tz->device, - "failed to read out thermal zone (%d)\n", - ret); - return; - } - - tz->last_temperature = tz->temperature; - tz->temperature = temp; - - trace_thermal_temperature(tz); - - thermal_genl_sampling_temp(tz->id, temp); -} - static void thermal_zone_device_check(struct work_struct *work) { struct thermal_zone_device *tz = container_of(work, struct @@ -452,7 +429,7 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz) INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check); - tz->temperature = THERMAL_TEMP_INVALID; + tz->temperature = THERMAL_TEMP_INIT; tz->passive = 0; tz->prev_low_trip = -INT_MAX; tz->prev_high_trip = INT_MAX; @@ -504,6 +481,7 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, struct thermal_trip_desc *td; LIST_HEAD(way_down_list); LIST_HEAD(way_up_list); + int temp, ret; if (tz->suspended) return; @@ -511,10 +489,29 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, if (!thermal_zone_device_is_enabled(tz)) return; - update_temperature(tz); + ret = __thermal_zone_get_temp(tz, &temp); + if (ret) { + if (ret != -EAGAIN) + dev_info(&tz->device, "Temperature check failed (%d)\n", ret); - if (tz->temperature == THERMAL_TEMP_INVALID) + thermal_zone_device_set_polling(tz, msecs_to_jiffies(THERMAL_RECHECK_DELAY_MS)); + return; + } else if (temp <= THERMAL_TEMP_INVALID) { + /* + * Special case: No valid temperature value is available, but + * the zone owner does not want the core to do anything about + * it. Continue regular zone polling if needed, so that this + * function can be called again, but skip everything else. + */ goto monitor; + } + + tz->last_temperature = tz->temperature; + tz->temperature = temp; + + trace_thermal_temperature(tz); + + thermal_genl_sampling_temp(tz->id, temp); tz->notify_event = event; diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 30c0e78859a7..ba8e6fc807ca 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -133,6 +133,9 @@ struct thermal_zone_device { struct thermal_trip_desc trips[] __counted_by(num_trips); }; +/* Initial thermal zone temperature. */ +#define THERMAL_TEMP_INIT INT_MIN + /* * Default delay after a failing thermal zone temperature check before * attempting to check it again. diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c index 81e019493557..aedb8369e2aa 100644 --- a/drivers/thermal/thermal_helpers.c +++ b/drivers/thermal/thermal_helpers.c @@ -163,6 +163,8 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) } ret = __thermal_zone_get_temp(tz, temp); + if (!ret && *temp <= THERMAL_TEMP_INVALID) + ret = -ENODATA; unlock: mutex_unlock(&tz->lock); diff --git a/fs/bcachefs/alloc_foreground.c b/fs/bcachefs/alloc_foreground.c index cabf866c7956..618d2ff0292e 100644 --- a/fs/bcachefs/alloc_foreground.c +++ b/fs/bcachefs/alloc_foreground.c @@ -496,12 +496,6 @@ again: for (alloc_cursor = max(alloc_cursor, bkey_start_offset(k.k)); alloc_cursor < k.k->p.offset; alloc_cursor++) { - ret = btree_trans_too_many_iters(trans); - if (ret) { - ob = ERR_PTR(ret); - break; - } - s->buckets_seen++; u64 bucket = alloc_cursor & ~(~0ULL << 56); diff --git a/fs/bcachefs/fsck.c b/fs/bcachefs/fsck.c index cc4f0963c0c5..9138944c5ae6 100644 --- a/fs/bcachefs/fsck.c +++ b/fs/bcachefs/fsck.c @@ -283,6 +283,7 @@ static int reattach_inode(struct btree_trans *trans, struct bch_inode_unpacked *inode, u32 inode_snapshot) { + struct bch_fs *c = trans->c; struct bch_hash_info dir_hash; struct bch_inode_unpacked lostfound; char name_buf[20]; @@ -317,7 +318,7 @@ static int reattach_inode(struct btree_trans *trans, return ret; } - dir_hash = bch2_hash_info_init(trans->c, &lostfound); + dir_hash = bch2_hash_info_init(c, &lostfound); name = (struct qstr) QSTR(name_buf); @@ -330,8 +331,10 @@ static int reattach_inode(struct btree_trans *trans, inode->bi_subvol ?: inode->bi_inum, &dir_offset, STR_HASH_must_create); - if (ret) + if (ret) { + bch_err_msg(c, ret, "error creating dirent"); return ret; + } inode->bi_dir = lostfound.bi_inum; inode->bi_dir_offset = dir_offset; diff --git a/fs/bcachefs/journal_reclaim.c b/fs/bcachefs/journal_reclaim.c index d8a630742887..70b998d9f19c 100644 --- a/fs/bcachefs/journal_reclaim.c +++ b/fs/bcachefs/journal_reclaim.c @@ -206,6 +206,7 @@ void bch2_journal_space_available(struct journal *j) if (nr_online < metadata_replicas_required(c)) { struct printbuf buf = PRINTBUF; + buf.atomic++; prt_printf(&buf, "insufficient writeable journal devices available: have %u, need %u\n" "rw journal devs:", nr_online, metadata_replicas_required(c)); diff --git a/fs/bcachefs/mean_and_variance.h b/fs/bcachefs/mean_and_variance.h index 4fcf062dd22c..47e4a3c3d26e 100644 --- a/fs/bcachefs/mean_and_variance.h +++ b/fs/bcachefs/mean_and_variance.h @@ -111,11 +111,11 @@ static inline u128_u u128_shl(u128_u i, s8 shift) { u128_u r; - r.lo = i.lo << shift; + r.lo = i.lo << (shift & 63); if (shift < 64) - r.hi = (i.hi << shift) | (i.lo >> (64 - shift)); + r.hi = (i.hi << (shift & 63)) | (i.lo >> (-shift & 63)); else { - r.hi = i.lo << (shift - 64); + r.hi = i.lo << (-shift & 63); r.lo = 0; } return r; diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 5ae8045f4df4..19fa49cd9907 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -2150,5 +2150,5 @@ core_initcall(init_elf_binfmt); module_exit(exit_elf_binfmt); #ifdef CONFIG_BINFMT_ELF_KUNIT_TEST -#include "binfmt_elf_test.c" +#include "tests/binfmt_elf_kunit.c" #endif diff --git a/fs/exec.c b/fs/exec.c index a47d0e4c54f6..37f33212123c 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -2236,5 +2236,5 @@ fs_initcall(init_fs_exec_sysctls); #endif /* CONFIG_SYSCTL */ #ifdef CONFIG_EXEC_KUNIT_TEST -#include "exec_test.c" +#include "tests/exec_kunit.c" #endif diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 55d444bec5c0..bdd96329dddd 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1186,6 +1186,11 @@ static void __prepare_cp_block(struct f2fs_sb_info *sbi) ckpt->valid_node_count = cpu_to_le32(valid_node_count(sbi)); ckpt->valid_inode_count = cpu_to_le32(valid_inode_count(sbi)); ckpt->next_free_nid = cpu_to_le32(last_nid); + + /* update user_block_counts */ + sbi->last_valid_block_count = sbi->total_valid_block_count; + percpu_counter_set(&sbi->alloc_valid_block_count, 0); + percpu_counter_set(&sbi->rf_node_block_count, 0); } static bool __need_flush_quota(struct f2fs_sb_info *sbi) @@ -1575,11 +1580,6 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) start_blk += NR_CURSEG_NODE_TYPE; } - /* update user_block_counts */ - sbi->last_valid_block_count = sbi->total_valid_block_count; - percpu_counter_set(&sbi->alloc_valid_block_count, 0); - percpu_counter_set(&sbi->rf_node_block_count, 0); - /* Here, we have one bio having CP pack except cp pack 2 page */ f2fs_sync_meta_pages(sbi, META, LONG_MAX, FS_CP_META_IO); /* Wait for all dirty meta pages to be submitted for IO */ @@ -1718,6 +1718,7 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) } f2fs_restore_inmem_curseg(sbi); + f2fs_reinit_atgc_curseg(sbi); stat_inc_cp_count(sbi); stop: unblock_operations(sbi); diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c index 1ef82a546391..990b93689b46 100644 --- a/fs/f2fs/compress.c +++ b/fs/f2fs/compress.c @@ -1100,7 +1100,7 @@ retry: struct bio *bio = NULL; ret = f2fs_read_multi_pages(cc, &bio, cc->cluster_size, - &last_block_in_bio, false, true); + &last_block_in_bio, NULL, true); f2fs_put_rpages(cc); f2fs_destroy_compress_ctx(cc, true); if (ret) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index b9b0debc6b3d..6457e5bca9c9 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -925,6 +925,7 @@ alloc_new: #ifdef CONFIG_BLK_DEV_ZONED static bool is_end_zone_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr) { + struct block_device *bdev = sbi->sb->s_bdev; int devi = 0; if (f2fs_is_multi_device(sbi)) { @@ -935,8 +936,9 @@ static bool is_end_zone_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr) return false; } blkaddr -= FDEV(devi).start_blk; + bdev = FDEV(devi).bdev; } - return bdev_is_zoned(FDEV(devi).bdev) && + return bdev_is_zoned(bdev) && f2fs_blkz_is_seq(sbi, devi, blkaddr) && (blkaddr % sbi->blocks_per_blkz == sbi->blocks_per_blkz - 1); } @@ -2067,12 +2069,17 @@ static inline loff_t f2fs_readpage_limit(struct inode *inode) return i_size_read(inode); } +static inline blk_opf_t f2fs_ra_op_flags(struct readahead_control *rac) +{ + return rac ? REQ_RAHEAD : 0; +} + static int f2fs_read_single_page(struct inode *inode, struct folio *folio, unsigned nr_pages, struct f2fs_map_blocks *map, struct bio **bio_ret, sector_t *last_block_in_bio, - bool is_readahead) + struct readahead_control *rac) { struct bio *bio = *bio_ret; const unsigned blocksize = blks_to_bytes(inode, 1); @@ -2148,7 +2155,7 @@ submit_and_realloc: } if (bio == NULL) { bio = f2fs_grab_read_bio(inode, block_nr, nr_pages, - is_readahead ? REQ_RAHEAD : 0, index, + f2fs_ra_op_flags(rac), index, false); if (IS_ERR(bio)) { ret = PTR_ERR(bio); @@ -2178,7 +2185,7 @@ out: #ifdef CONFIG_F2FS_FS_COMPRESSION int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret, unsigned nr_pages, sector_t *last_block_in_bio, - bool is_readahead, bool for_write) + struct readahead_control *rac, bool for_write) { struct dnode_of_data dn; struct inode *inode = cc->inode; @@ -2301,7 +2308,7 @@ submit_and_realloc: if (!bio) { bio = f2fs_grab_read_bio(inode, blkaddr, nr_pages, - is_readahead ? REQ_RAHEAD : 0, + f2fs_ra_op_flags(rac), page->index, for_write); if (IS_ERR(bio)) { ret = PTR_ERR(bio); @@ -2399,7 +2406,7 @@ static int f2fs_mpage_readpages(struct inode *inode, ret = f2fs_read_multi_pages(&cc, &bio, max_nr_pages, &last_block_in_bio, - rac != NULL, false); + rac, false); f2fs_destroy_compress_ctx(&cc, false); if (ret) goto set_error_page; @@ -2449,7 +2456,7 @@ next_page: ret = f2fs_read_multi_pages(&cc, &bio, max_nr_pages, &last_block_in_bio, - rac != NULL, false); + rac, false); f2fs_destroy_compress_ctx(&cc, false); } } @@ -2601,7 +2608,7 @@ bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio) return true; if (IS_NOQUOTA(inode)) return true; - if (f2fs_is_atomic_file(inode)) + if (f2fs_used_in_atomic_write(inode)) return true; /* rewrite low ratio compress data w/ OPU mode to avoid fragmentation */ if (f2fs_compressed_file(inode) && @@ -2688,7 +2695,7 @@ got_it: } /* wait for GCed page writeback via META_MAPPING */ - if (fio->post_read) + if (fio->meta_gc) f2fs_wait_on_block_writeback(inode, fio->old_blkaddr); /* @@ -2783,7 +2790,7 @@ int f2fs_write_single_data_page(struct page *page, int *submitted, .submitted = 0, .compr_blocks = compr_blocks, .need_lock = compr_blocks ? LOCK_DONE : LOCK_RETRY, - .post_read = f2fs_post_read_required(inode) ? 1 : 0, + .meta_gc = f2fs_meta_inode_gc_required(inode) ? 1 : 0, .io_type = io_type, .io_wbc = wbc, .bio = bio, diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 48048fa36427..fd1fc06359ee 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -19,33 +19,23 @@ #include "node.h" #include <trace/events/f2fs.h> -bool sanity_check_extent_cache(struct inode *inode) +bool sanity_check_extent_cache(struct inode *inode, struct page *ipage) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct f2fs_inode_info *fi = F2FS_I(inode); - struct extent_tree *et = fi->extent_tree[EX_READ]; - struct extent_info *ei; - - if (!et) - return true; + struct f2fs_extent *i_ext = &F2FS_INODE(ipage)->i_ext; + struct extent_info ei; - ei = &et->largest; - if (!ei->len) - return true; + get_read_extent_info(&ei, i_ext); - /* Let's drop, if checkpoint got corrupted. */ - if (is_set_ckpt_flags(sbi, CP_ERROR_FLAG)) { - ei->len = 0; - et->largest_updated = true; + if (!ei.len) return true; - } - if (!f2fs_is_valid_blkaddr(sbi, ei->blk, DATA_GENERIC_ENHANCE) || - !f2fs_is_valid_blkaddr(sbi, ei->blk + ei->len - 1, + if (!f2fs_is_valid_blkaddr(sbi, ei.blk, DATA_GENERIC_ENHANCE) || + !f2fs_is_valid_blkaddr(sbi, ei.blk + ei.len - 1, DATA_GENERIC_ENHANCE)) { f2fs_warn(sbi, "%s: inode (ino=%lx) extent info [%u, %u, %u] is incorrect, run fsck to fix", __func__, inode->i_ino, - ei->blk, ei->fofs, ei->len); + ei.blk, ei.fofs, ei.len); return false; } return true; @@ -394,24 +384,22 @@ void f2fs_init_read_extent_tree(struct inode *inode, struct page *ipage) if (!__may_extent_tree(inode, EX_READ)) { /* drop largest read extent */ - if (i_ext && i_ext->len) { + if (i_ext->len) { f2fs_wait_on_page_writeback(ipage, NODE, true, true); i_ext->len = 0; set_page_dirty(ipage); } - goto out; + set_inode_flag(inode, FI_NO_EXTENT); + return; } et = __grab_extent_tree(inode, EX_READ); - if (!i_ext || !i_ext->len) - goto out; - get_read_extent_info(&ei, i_ext); write_lock(&et->lock); - if (atomic_read(&et->node_cnt)) - goto unlock_out; + if (atomic_read(&et->node_cnt) || !ei.len) + goto skip; en = __attach_extent_node(sbi, et, &ei, NULL, &et->root.rb_root.rb_node, true); @@ -423,11 +411,13 @@ void f2fs_init_read_extent_tree(struct inode *inode, struct page *ipage) list_add_tail(&en->list, &eti->extent_list); spin_unlock(&eti->extent_lock); } -unlock_out: +skip: + /* Let's drop, if checkpoint got corrupted. */ + if (f2fs_cp_error(sbi)) { + et->largest.len = 0; + et->largest_updated = true; + } write_unlock(&et->lock); -out: - if (!F2FS_I(inode)->extent_tree[EX_READ]) - set_inode_flag(inode, FI_NO_EXTENT); } void f2fs_init_age_extent_tree(struct inode *inode) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8a9d910aa552..ac19c61f0c3e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -803,6 +803,7 @@ enum { FI_COW_FILE, /* indicate COW file */ FI_ATOMIC_COMMITTED, /* indicate atomic commit completed except disk sync */ FI_ATOMIC_REPLACE, /* indicate atomic replace */ + FI_OPENED_FILE, /* indicate file has been opened */ FI_MAX, /* max flag, never be used */ }; @@ -842,7 +843,11 @@ struct f2fs_inode_info { struct task_struct *atomic_write_task; /* store atomic write task */ struct extent_tree *extent_tree[NR_EXTENT_CACHES]; /* cached extent_tree entry */ - struct inode *cow_inode; /* copy-on-write inode for atomic write */ + union { + struct inode *cow_inode; /* copy-on-write inode for atomic write */ + struct inode *atomic_inode; + /* point to atomic_inode, available only for cow_inode */ + }; /* avoid racing between foreground op and gc */ struct f2fs_rwsem i_gc_rwsem[2]; @@ -1210,7 +1215,7 @@ struct f2fs_io_info { unsigned int in_list:1; /* indicate fio is in io_list */ unsigned int is_por:1; /* indicate IO is from recovery or not */ unsigned int encrypted:1; /* indicate file is encrypted */ - unsigned int post_read:1; /* require post read */ + unsigned int meta_gc:1; /* require meta inode GC */ enum iostat_type io_type; /* io type */ struct writeback_control *io_wbc; /* writeback control */ struct bio **bio; /* bio for ipu */ @@ -3222,21 +3227,15 @@ static inline bool f2fs_need_compress_data(struct inode *inode) return false; } -static inline unsigned int addrs_per_inode(struct inode *inode) +static inline unsigned int addrs_per_page(struct inode *inode, + bool is_inode) { - unsigned int addrs = CUR_ADDRS_PER_INODE(inode) - - get_inline_xattr_addrs(inode); + unsigned int addrs = is_inode ? (CUR_ADDRS_PER_INODE(inode) - + get_inline_xattr_addrs(inode)) : DEF_ADDRS_PER_BLOCK; - if (!f2fs_compressed_file(inode)) - return addrs; - return ALIGN_DOWN(addrs, F2FS_I(inode)->i_cluster_size); -} - -static inline unsigned int addrs_per_block(struct inode *inode) -{ - if (!f2fs_compressed_file(inode)) - return DEF_ADDRS_PER_BLOCK; - return ALIGN_DOWN(DEF_ADDRS_PER_BLOCK, F2FS_I(inode)->i_cluster_size); + if (f2fs_compressed_file(inode)) + return ALIGN_DOWN(addrs, F2FS_I(inode)->i_cluster_size); + return addrs; } static inline void *inline_xattr_addr(struct inode *inode, struct page *page) @@ -3706,6 +3705,7 @@ void f2fs_release_discard_addrs(struct f2fs_sb_info *sbi); int f2fs_npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra); bool f2fs_segment_has_free_slot(struct f2fs_sb_info *sbi, int segno); int f2fs_init_inmem_curseg(struct f2fs_sb_info *sbi); +int f2fs_reinit_atgc_curseg(struct f2fs_sb_info *sbi); void f2fs_save_inmem_curseg(struct f2fs_sb_info *sbi); void f2fs_restore_inmem_curseg(struct f2fs_sb_info *sbi); int f2fs_allocate_segment_for_resize(struct f2fs_sb_info *sbi, int type, @@ -4163,7 +4163,7 @@ extern struct kmem_cache *f2fs_inode_entry_slab; * inline.c */ bool f2fs_may_inline_data(struct inode *inode); -bool f2fs_sanity_check_inline_data(struct inode *inode); +bool f2fs_sanity_check_inline_data(struct inode *inode, struct page *ipage); bool f2fs_may_inline_dentry(struct inode *inode); void f2fs_do_read_inline_data(struct folio *folio, struct page *ipage); void f2fs_truncate_inline_inode(struct inode *inode, @@ -4204,7 +4204,7 @@ void f2fs_leave_shrinker(struct f2fs_sb_info *sbi); /* * extent_cache.c */ -bool sanity_check_extent_cache(struct inode *inode); +bool sanity_check_extent_cache(struct inode *inode, struct page *ipage); void f2fs_init_extent_tree(struct inode *inode); void f2fs_drop_extent_tree(struct inode *inode); void f2fs_destroy_extent_node(struct inode *inode); @@ -4275,6 +4275,16 @@ static inline bool f2fs_post_read_required(struct inode *inode) f2fs_compressed_file(inode); } +static inline bool f2fs_used_in_atomic_write(struct inode *inode) +{ + return f2fs_is_atomic_file(inode) || f2fs_is_cow_file(inode); +} + +static inline bool f2fs_meta_inode_gc_required(struct inode *inode) +{ + return f2fs_post_read_required(inode) || f2fs_used_in_atomic_write(inode); +} + /* * compress.c */ @@ -4310,7 +4320,7 @@ void f2fs_update_read_extent_tree_range_compressed(struct inode *inode, unsigned int llen, unsigned int c_len); int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret, unsigned nr_pages, sector_t *last_block_in_bio, - bool is_readahead, bool for_write); + struct readahead_control *rac, bool for_write); struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc); void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed, bool in_task); @@ -4401,22 +4411,18 @@ static inline int set_compress_context(struct inode *inode) { #ifdef CONFIG_F2FS_FS_COMPRESSION struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct f2fs_inode_info *fi = F2FS_I(inode); - F2FS_I(inode)->i_compress_algorithm = - F2FS_OPTION(sbi).compress_algorithm; - F2FS_I(inode)->i_log_cluster_size = - F2FS_OPTION(sbi).compress_log_size; - F2FS_I(inode)->i_compress_flag = - F2FS_OPTION(sbi).compress_chksum ? - BIT(COMPRESS_CHKSUM) : 0; - F2FS_I(inode)->i_cluster_size = - BIT(F2FS_I(inode)->i_log_cluster_size); - if ((F2FS_I(inode)->i_compress_algorithm == COMPRESS_LZ4 || - F2FS_I(inode)->i_compress_algorithm == COMPRESS_ZSTD) && + fi->i_compress_algorithm = F2FS_OPTION(sbi).compress_algorithm; + fi->i_log_cluster_size = F2FS_OPTION(sbi).compress_log_size; + fi->i_compress_flag = F2FS_OPTION(sbi).compress_chksum ? + BIT(COMPRESS_CHKSUM) : 0; + fi->i_cluster_size = BIT(fi->i_log_cluster_size); + if ((fi->i_compress_algorithm == COMPRESS_LZ4 || + fi->i_compress_algorithm == COMPRESS_ZSTD) && F2FS_OPTION(sbi).compress_level) - F2FS_I(inode)->i_compress_level = - F2FS_OPTION(sbi).compress_level; - F2FS_I(inode)->i_flags |= F2FS_COMPR_FL; + fi->i_compress_level = F2FS_OPTION(sbi).compress_level; + fi->i_flags |= F2FS_COMPR_FL; set_inode_flag(inode, FI_COMPRESSED_FILE); stat_inc_compr_inode(inode); inc_compr_inode_stat(inode); @@ -4431,15 +4437,15 @@ static inline bool f2fs_disable_compressed_file(struct inode *inode) { struct f2fs_inode_info *fi = F2FS_I(inode); - f2fs_down_write(&F2FS_I(inode)->i_sem); + f2fs_down_write(&fi->i_sem); if (!f2fs_compressed_file(inode)) { - f2fs_up_write(&F2FS_I(inode)->i_sem); + f2fs_up_write(&fi->i_sem); return true; } if (f2fs_is_mmap_file(inode) || (S_ISREG(inode->i_mode) && F2FS_HAS_BLOCKS(inode))) { - f2fs_up_write(&F2FS_I(inode)->i_sem); + f2fs_up_write(&fi->i_sem); return false; } @@ -4448,7 +4454,7 @@ static inline bool f2fs_disable_compressed_file(struct inode *inode) clear_inode_flag(inode, FI_COMPRESSED_FILE); f2fs_mark_inode_dirty_sync(inode, true); - f2fs_up_write(&F2FS_I(inode)->i_sem); + f2fs_up_write(&fi->i_sem); return true; } diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index c1ad9b278c47..168f08507004 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -554,6 +554,42 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) return 0; } +static int finish_preallocate_blocks(struct inode *inode) +{ + int ret; + + inode_lock(inode); + if (is_inode_flag_set(inode, FI_OPENED_FILE)) { + inode_unlock(inode); + return 0; + } + + if (!file_should_truncate(inode)) { + set_inode_flag(inode, FI_OPENED_FILE); + inode_unlock(inode); + return 0; + } + + f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); + filemap_invalidate_lock(inode->i_mapping); + + truncate_setsize(inode, i_size_read(inode)); + ret = f2fs_truncate(inode); + + filemap_invalidate_unlock(inode->i_mapping); + f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); + + if (!ret) + set_inode_flag(inode, FI_OPENED_FILE); + + inode_unlock(inode); + if (ret) + return ret; + + file_dont_truncate(inode); + return 0; +} + static int f2fs_file_open(struct inode *inode, struct file *filp) { int err = fscrypt_file_open(inode, filp); @@ -571,7 +607,11 @@ static int f2fs_file_open(struct inode *inode, struct file *filp) filp->f_mode |= FMODE_NOWAIT; filp->f_mode |= FMODE_CAN_ODIRECT; - return dquot_file_open(inode, filp); + err = dquot_file_open(inode, filp); + if (err) + return err; + + return finish_preallocate_blocks(inode); } void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count) @@ -825,6 +865,8 @@ static bool f2fs_force_buffered_io(struct inode *inode, int rw) return true; if (f2fs_compressed_file(inode)) return true; + if (f2fs_has_inline_data(inode)) + return true; /* disallow direct IO if any of devices has unaligned blksize */ if (f2fs_is_multi_device(sbi) && !sbi->aligned_blksize) @@ -937,6 +979,7 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr) { struct inode *inode = d_inode(dentry); + struct f2fs_inode_info *fi = F2FS_I(inode); int err; if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) @@ -955,7 +998,7 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, return -EOPNOTSUPP; if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED) && !IS_ALIGNED(attr->ia_size, - F2FS_BLK_TO_BYTES(F2FS_I(inode)->i_cluster_size))) + F2FS_BLK_TO_BYTES(fi->i_cluster_size))) return -EINVAL; } @@ -1009,7 +1052,7 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, return err; } - f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); + f2fs_down_write(&fi->i_gc_rwsem[WRITE]); filemap_invalidate_lock(inode->i_mapping); truncate_setsize(inode, attr->ia_size); @@ -1021,14 +1064,14 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, * larger than i_size. */ filemap_invalidate_unlock(inode->i_mapping); - f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); + f2fs_up_write(&fi->i_gc_rwsem[WRITE]); if (err) return err; - spin_lock(&F2FS_I(inode)->i_size_lock); + spin_lock(&fi->i_size_lock); inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); - F2FS_I(inode)->last_disk_size = i_size_read(inode); - spin_unlock(&F2FS_I(inode)->i_size_lock); + fi->last_disk_size = i_size_read(inode); + spin_unlock(&fi->i_size_lock); } __setattr_copy(idmap, inode, attr); @@ -1038,7 +1081,7 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, if (is_inode_flag_set(inode, FI_ACL_MODE)) { if (!err) - inode->i_mode = F2FS_I(inode)->i_acl_mode; + inode->i_mode = fi->i_acl_mode; clear_inode_flag(inode, FI_ACL_MODE); } } @@ -1946,15 +1989,15 @@ static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask) if (err) return err; - f2fs_down_write(&F2FS_I(inode)->i_sem); + f2fs_down_write(&fi->i_sem); if (!f2fs_may_compress(inode) || (S_ISREG(inode->i_mode) && F2FS_HAS_BLOCKS(inode))) { - f2fs_up_write(&F2FS_I(inode)->i_sem); + f2fs_up_write(&fi->i_sem); return -EINVAL; } err = set_compress_context(inode); - f2fs_up_write(&F2FS_I(inode)->i_sem); + f2fs_up_write(&fi->i_sem); if (err) return err; @@ -2139,6 +2182,9 @@ static int f2fs_ioc_start_atomic_write(struct file *filp, bool truncate) set_inode_flag(fi->cow_inode, FI_COW_FILE); clear_inode_flag(fi->cow_inode, FI_INLINE_DATA); + + /* Set the COW inode's atomic_inode to the atomic inode */ + F2FS_I(fi->cow_inode)->atomic_inode = inode; } else { /* Reuse the already created COW inode */ ret = f2fs_do_truncate_blocks(fi->cow_inode, 0, true); @@ -3541,6 +3587,7 @@ next: static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg) { struct inode *inode = file_inode(filp); + struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode); pgoff_t page_idx = 0, last_idx; unsigned int released_blocks = 0; @@ -3578,7 +3625,7 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg) if (ret) goto out; - if (!atomic_read(&F2FS_I(inode)->i_compr_blocks)) { + if (!atomic_read(&fi->i_compr_blocks)) { ret = -EPERM; goto out; } @@ -3587,7 +3634,7 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg) inode_set_ctime_current(inode); f2fs_mark_inode_dirty_sync(inode, true); - f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); + f2fs_down_write(&fi->i_gc_rwsem[WRITE]); filemap_invalidate_lock(inode->i_mapping); last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); @@ -3613,7 +3660,7 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg) end_offset = ADDRS_PER_PAGE(dn.node_page, inode); count = min(end_offset - dn.ofs_in_node, last_idx - page_idx); - count = round_up(count, F2FS_I(inode)->i_cluster_size); + count = round_up(count, fi->i_cluster_size); ret = release_compress_blocks(&dn, count); @@ -3629,7 +3676,7 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg) } filemap_invalidate_unlock(inode->i_mapping); - f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); + f2fs_up_write(&fi->i_gc_rwsem[WRITE]); out: if (released_blocks) f2fs_update_time(sbi, REQ_TIME); @@ -3640,14 +3687,14 @@ out: if (ret >= 0) { ret = put_user(released_blocks, (u64 __user *)arg); } else if (released_blocks && - atomic_read(&F2FS_I(inode)->i_compr_blocks)) { + atomic_read(&fi->i_compr_blocks)) { set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_warn(sbi, "%s: partial blocks were released i_ino=%lx " "iblocks=%llu, released=%u, compr_blocks=%u, " "run fsck to fix.", __func__, inode->i_ino, inode->i_blocks, released_blocks, - atomic_read(&F2FS_I(inode)->i_compr_blocks)); + atomic_read(&fi->i_compr_blocks)); } return ret; @@ -3736,6 +3783,7 @@ next: static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg) { struct inode *inode = file_inode(filp); + struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode); pgoff_t page_idx = 0, last_idx; unsigned int reserved_blocks = 0; @@ -3761,10 +3809,10 @@ static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg) goto unlock_inode; } - if (atomic_read(&F2FS_I(inode)->i_compr_blocks)) + if (atomic_read(&fi->i_compr_blocks)) goto unlock_inode; - f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); + f2fs_down_write(&fi->i_gc_rwsem[WRITE]); filemap_invalidate_lock(inode->i_mapping); last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); @@ -3790,7 +3838,7 @@ static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg) end_offset = ADDRS_PER_PAGE(dn.node_page, inode); count = min(end_offset - dn.ofs_in_node, last_idx - page_idx); - count = round_up(count, F2FS_I(inode)->i_cluster_size); + count = round_up(count, fi->i_cluster_size); ret = reserve_compress_blocks(&dn, count, &reserved_blocks); @@ -3805,7 +3853,7 @@ static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg) } filemap_invalidate_unlock(inode->i_mapping); - f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); + f2fs_up_write(&fi->i_gc_rwsem[WRITE]); if (!ret) { clear_inode_flag(inode, FI_COMPRESS_RELEASED); @@ -3821,14 +3869,14 @@ unlock_inode: if (!ret) { ret = put_user(reserved_blocks, (u64 __user *)arg); } else if (reserved_blocks && - atomic_read(&F2FS_I(inode)->i_compr_blocks)) { + atomic_read(&fi->i_compr_blocks)) { set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_warn(sbi, "%s: partial blocks were reserved i_ino=%lx " "iblocks=%llu, reserved=%u, compr_blocks=%u, " "run fsck to fix.", __func__, inode->i_ino, inode->i_blocks, reserved_blocks, - atomic_read(&F2FS_I(inode)->i_compr_blocks)); + atomic_read(&fi->i_compr_blocks)); } return ret; @@ -3891,7 +3939,9 @@ static int f2fs_sec_trim_file(struct file *filp, unsigned long arg) IS_ENCRYPTED(inode) && f2fs_is_multi_device(sbi))) return -EOPNOTSUPP; - file_start_write(filp); + ret = mnt_want_write_file(filp); + if (ret) + return ret; inode_lock(inode); if (f2fs_is_atomic_file(inode) || f2fs_compressed_file(inode) || @@ -4017,7 +4067,7 @@ out: f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); err: inode_unlock(inode); - file_end_write(filp); + mnt_drop_write_file(filp); return ret; } @@ -4052,6 +4102,7 @@ static int f2fs_ioc_get_compress_option(struct file *filp, unsigned long arg) static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg) { struct inode *inode = file_inode(filp); + struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_comp_option option; int ret = 0; @@ -4071,7 +4122,9 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg) option.algorithm >= COMPRESS_MAX) return -EINVAL; - file_start_write(filp); + ret = mnt_want_write_file(filp); + if (ret) + return ret; inode_lock(inode); f2fs_down_write(&F2FS_I(inode)->i_sem); @@ -4090,27 +4143,27 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg) goto out; } - F2FS_I(inode)->i_compress_algorithm = option.algorithm; - F2FS_I(inode)->i_log_cluster_size = option.log_cluster_size; - F2FS_I(inode)->i_cluster_size = BIT(option.log_cluster_size); + fi->i_compress_algorithm = option.algorithm; + fi->i_log_cluster_size = option.log_cluster_size; + fi->i_cluster_size = BIT(option.log_cluster_size); /* Set default level */ - if (F2FS_I(inode)->i_compress_algorithm == COMPRESS_ZSTD) - F2FS_I(inode)->i_compress_level = F2FS_ZSTD_DEFAULT_CLEVEL; + if (fi->i_compress_algorithm == COMPRESS_ZSTD) + fi->i_compress_level = F2FS_ZSTD_DEFAULT_CLEVEL; else - F2FS_I(inode)->i_compress_level = 0; + fi->i_compress_level = 0; /* Adjust mount option level */ if (option.algorithm == F2FS_OPTION(sbi).compress_algorithm && F2FS_OPTION(sbi).compress_level) - F2FS_I(inode)->i_compress_level = F2FS_OPTION(sbi).compress_level; + fi->i_compress_level = F2FS_OPTION(sbi).compress_level; f2fs_mark_inode_dirty_sync(inode, true); if (!f2fs_is_compress_backend_ready(inode)) f2fs_warn(sbi, "compression algorithm is successfully set, " "but current kernel doesn't support this algorithm."); out: - f2fs_up_write(&F2FS_I(inode)->i_sem); + f2fs_up_write(&fi->i_sem); inode_unlock(inode); - file_end_write(filp); + mnt_drop_write_file(filp); return ret; } @@ -4167,7 +4220,9 @@ static int f2fs_ioc_decompress_file(struct file *filp) f2fs_balance_fs(sbi, true); - file_start_write(filp); + ret = mnt_want_write_file(filp); + if (ret) + return ret; inode_lock(inode); if (!f2fs_is_compress_backend_ready(inode)) { @@ -4222,7 +4277,7 @@ static int f2fs_ioc_decompress_file(struct file *filp) f2fs_update_time(sbi, REQ_TIME); out: inode_unlock(inode); - file_end_write(filp); + mnt_drop_write_file(filp); return ret; } @@ -4244,7 +4299,9 @@ static int f2fs_ioc_compress_file(struct file *filp) f2fs_balance_fs(sbi, true); - file_start_write(filp); + ret = mnt_want_write_file(filp); + if (ret) + return ret; inode_lock(inode); if (!f2fs_is_compress_backend_ready(inode)) { @@ -4300,7 +4357,7 @@ static int f2fs_ioc_compress_file(struct file *filp) f2fs_update_time(sbi, REQ_TIME); out: inode_unlock(inode); - file_end_write(filp); + mnt_drop_write_file(filp); return ret; } diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 6066c6eecf41..724bbcb447d3 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1171,7 +1171,8 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, static int ra_data_block(struct inode *inode, pgoff_t index) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct address_space *mapping = inode->i_mapping; + struct address_space *mapping = f2fs_is_cow_file(inode) ? + F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping; struct dnode_of_data dn; struct page *page; struct f2fs_io_info fio = { @@ -1260,6 +1261,8 @@ put_page: static int move_data_block(struct inode *inode, block_t bidx, int gc_type, unsigned int segno, int off) { + struct address_space *mapping = f2fs_is_cow_file(inode) ? + F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping; struct f2fs_io_info fio = { .sbi = F2FS_I_SB(inode), .ino = inode->i_ino, @@ -1282,7 +1285,7 @@ static int move_data_block(struct inode *inode, block_t bidx, CURSEG_ALL_DATA_ATGC : CURSEG_COLD_DATA; /* do not read out */ - page = f2fs_grab_cache_page(inode->i_mapping, bidx, false); + page = f2fs_grab_cache_page(mapping, bidx, false); if (!page) return -ENOMEM; @@ -1563,6 +1566,16 @@ next_step: continue; } + if (f2fs_has_inline_data(inode)) { + iput(inode); + set_sbi_flag(sbi, SBI_NEED_FSCK); + f2fs_err_ratelimited(sbi, + "inode %lx has both inline_data flag and " + "data block, nid=%u, ofs_in_node=%u", + inode->i_ino, dni.nid, ofs_in_node); + continue; + } + err = f2fs_gc_pinned_control(inode, gc_type, segno); if (err == -EAGAIN) { iput(inode); @@ -1579,7 +1592,7 @@ next_step: start_bidx = f2fs_start_bidx_of_node(nofs, inode) + ofs_in_node; - if (f2fs_post_read_required(inode)) { + if (f2fs_meta_inode_gc_required(inode)) { int err = ra_data_block(inode, start_bidx); f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); @@ -1630,7 +1643,7 @@ next_step: start_bidx = f2fs_start_bidx_of_node(nofs, inode) + ofs_in_node; - if (f2fs_post_read_required(inode)) + if (f2fs_meta_inode_gc_required(inode)) err = move_data_block(inode, start_bidx, gc_type, segno, off); else @@ -1638,7 +1651,7 @@ next_step: segno, off); if (!err && (gc_type == FG_GC || - f2fs_post_read_required(inode))) + f2fs_meta_inode_gc_required(inode))) submitted++; if (locked) { @@ -1742,7 +1755,6 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, if (type != GET_SUM_TYPE((&sum->footer))) { f2fs_err(sbi, "Inconsistent segment (%u) type [%d, %d] in SSA and SIT", segno, type, GET_SUM_TYPE((&sum->footer))); - set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_CORRUPTED_SUMMARY); goto skip; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 7638d0d7b7ee..cca7d448e55c 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -16,7 +16,7 @@ static bool support_inline_data(struct inode *inode) { - if (f2fs_is_atomic_file(inode)) + if (f2fs_used_in_atomic_write(inode)) return false; if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) return false; @@ -33,11 +33,29 @@ bool f2fs_may_inline_data(struct inode *inode) return !f2fs_post_read_required(inode); } -bool f2fs_sanity_check_inline_data(struct inode *inode) +static bool inode_has_blocks(struct inode *inode, struct page *ipage) +{ + struct f2fs_inode *ri = F2FS_INODE(ipage); + int i; + + if (F2FS_HAS_BLOCKS(inode)) + return true; + + for (i = 0; i < DEF_NIDS_PER_INODE; i++) { + if (ri->i_nid[i]) + return true; + } + return false; +} + +bool f2fs_sanity_check_inline_data(struct inode *inode, struct page *ipage) { if (!f2fs_has_inline_data(inode)) return false; + if (inode_has_blocks(inode, ipage)) + return false; + if (!support_inline_data(inode)) return true; @@ -203,8 +221,10 @@ int f2fs_convert_inline_inode(struct inode *inode) struct page *ipage, *page; int err = 0; - if (!f2fs_has_inline_data(inode) || - f2fs_hw_is_readonly(sbi) || f2fs_readonly(sbi->sb)) + if (f2fs_hw_is_readonly(sbi) || f2fs_readonly(sbi->sb)) + return -EROFS; + + if (!f2fs_has_inline_data(inode)) return 0; err = f2fs_dquot_initialize(inode); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 005dde72aff3..aef57172014f 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -29,6 +29,9 @@ void f2fs_mark_inode_dirty_sync(struct inode *inode, bool sync) if (is_inode_flag_set(inode, FI_NEW_INODE)) return; + if (f2fs_readonly(F2FS_I_SB(inode)->sb)) + return; + if (f2fs_inode_dirtied(inode, sync)) return; @@ -310,10 +313,6 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page) if (!sanity_check_compress_inode(inode, ri)) return false; } - } else if (f2fs_sb_has_flexible_inline_xattr(sbi)) { - f2fs_warn(sbi, "%s: corrupted inode ino=%lx, run fsck to fix.", - __func__, inode->i_ino); - return false; } if (!f2fs_sb_has_extra_attr(sbi)) { @@ -344,7 +343,7 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page) } } - if (f2fs_sanity_check_inline_data(inode)) { + if (f2fs_sanity_check_inline_data(inode, node_page)) { f2fs_warn(sbi, "%s: inode (ino=%lx, mode=%u) should not have inline_data, run fsck to fix", __func__, inode->i_ino, inode->i_mode); return false; @@ -508,16 +507,16 @@ static int do_read_inode(struct inode *inode) init_idisk_time(inode); - /* Need all the flag bits */ - f2fs_init_read_extent_tree(inode, node_page); - f2fs_init_age_extent_tree(inode); - - if (!sanity_check_extent_cache(inode)) { + if (!sanity_check_extent_cache(inode, node_page)) { f2fs_put_page(node_page, 1); f2fs_handle_error(sbi, ERROR_CORRUPTED_INODE); return -EFSCORRUPTED; } + /* Need all the flag bits */ + f2fs_init_read_extent_tree(inode, node_page); + f2fs_init_age_extent_tree(inode); + f2fs_put_page(node_page, 1); stat_inc_inline_xattr(inode); @@ -610,14 +609,6 @@ make_now: } f2fs_set_inode_flags(inode); - if (file_should_truncate(inode) && - !is_sbi_flag_set(sbi, SBI_POR_DOING)) { - ret = f2fs_truncate(inode); - if (ret) - goto bad_inode; - file_dont_truncate(inode); - } - unlock_new_inode(inode); trace_f2fs_iget(inode); return inode; @@ -645,8 +636,9 @@ retry: void f2fs_update_inode(struct inode *inode, struct page *node_page) { + struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_inode *ri; - struct extent_tree *et = F2FS_I(inode)->extent_tree[EX_READ]; + struct extent_tree *et = fi->extent_tree[EX_READ]; f2fs_wait_on_page_writeback(node_page, NODE, true, true); set_page_dirty(node_page); @@ -656,7 +648,7 @@ void f2fs_update_inode(struct inode *inode, struct page *node_page) ri = F2FS_INODE(node_page); ri->i_mode = cpu_to_le16(inode->i_mode); - ri->i_advise = F2FS_I(inode)->i_advise; + ri->i_advise = fi->i_advise; ri->i_uid = cpu_to_le32(i_uid_read(inode)); ri->i_gid = cpu_to_le32(i_gid_read(inode)); ri->i_links = cpu_to_le32(inode->i_nlink); @@ -682,58 +674,49 @@ void f2fs_update_inode(struct inode *inode, struct page *node_page) ri->i_ctime_nsec = cpu_to_le32(inode_get_ctime_nsec(inode)); ri->i_mtime_nsec = cpu_to_le32(inode_get_mtime_nsec(inode)); if (S_ISDIR(inode->i_mode)) - ri->i_current_depth = - cpu_to_le32(F2FS_I(inode)->i_current_depth); + ri->i_current_depth = cpu_to_le32(fi->i_current_depth); else if (S_ISREG(inode->i_mode)) - ri->i_gc_failures = cpu_to_le16(F2FS_I(inode)->i_gc_failures); - ri->i_xattr_nid = cpu_to_le32(F2FS_I(inode)->i_xattr_nid); - ri->i_flags = cpu_to_le32(F2FS_I(inode)->i_flags); - ri->i_pino = cpu_to_le32(F2FS_I(inode)->i_pino); + ri->i_gc_failures = cpu_to_le16(fi->i_gc_failures); + ri->i_xattr_nid = cpu_to_le32(fi->i_xattr_nid); + ri->i_flags = cpu_to_le32(fi->i_flags); + ri->i_pino = cpu_to_le32(fi->i_pino); ri->i_generation = cpu_to_le32(inode->i_generation); - ri->i_dir_level = F2FS_I(inode)->i_dir_level; + ri->i_dir_level = fi->i_dir_level; if (f2fs_has_extra_attr(inode)) { - ri->i_extra_isize = cpu_to_le16(F2FS_I(inode)->i_extra_isize); + ri->i_extra_isize = cpu_to_le16(fi->i_extra_isize); if (f2fs_sb_has_flexible_inline_xattr(F2FS_I_SB(inode))) ri->i_inline_xattr_size = - cpu_to_le16(F2FS_I(inode)->i_inline_xattr_size); + cpu_to_le16(fi->i_inline_xattr_size); if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)) && - F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize, - i_projid)) { + F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_projid)) { projid_t i_projid; - i_projid = from_kprojid(&init_user_ns, - F2FS_I(inode)->i_projid); + i_projid = from_kprojid(&init_user_ns, fi->i_projid); ri->i_projid = cpu_to_le32(i_projid); } if (f2fs_sb_has_inode_crtime(F2FS_I_SB(inode)) && - F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize, - i_crtime)) { - ri->i_crtime = - cpu_to_le64(F2FS_I(inode)->i_crtime.tv_sec); - ri->i_crtime_nsec = - cpu_to_le32(F2FS_I(inode)->i_crtime.tv_nsec); + F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_crtime)) { + ri->i_crtime = cpu_to_le64(fi->i_crtime.tv_sec); + ri->i_crtime_nsec = cpu_to_le32(fi->i_crtime.tv_nsec); } if (f2fs_sb_has_compression(F2FS_I_SB(inode)) && - F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize, + F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_compress_flag)) { unsigned short compress_flag; - ri->i_compr_blocks = - cpu_to_le64(atomic_read( - &F2FS_I(inode)->i_compr_blocks)); - ri->i_compress_algorithm = - F2FS_I(inode)->i_compress_algorithm; - compress_flag = F2FS_I(inode)->i_compress_flag | - F2FS_I(inode)->i_compress_level << + ri->i_compr_blocks = cpu_to_le64( + atomic_read(&fi->i_compr_blocks)); + ri->i_compress_algorithm = fi->i_compress_algorithm; + compress_flag = fi->i_compress_flag | + fi->i_compress_level << COMPRESS_LEVEL_OFFSET; ri->i_compress_flag = cpu_to_le16(compress_flag); - ri->i_log_cluster_size = - F2FS_I(inode)->i_log_cluster_size; + ri->i_log_cluster_size = fi->i_log_cluster_size; } } @@ -813,8 +796,9 @@ void f2fs_evict_inode(struct inode *inode) f2fs_abort_atomic_write(inode, true); - if (fi->cow_inode) { + if (fi->cow_inode && f2fs_is_cow_file(fi->cow_inode)) { clear_inode_flag(fi->cow_inode, FI_COW_FILE); + F2FS_I(fi->cow_inode)->atomic_inode = NULL; iput(fi->cow_inode); fi->cow_inode = NULL; } diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 1ecde2b45e99..38b4750475db 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -221,6 +221,7 @@ static struct inode *f2fs_new_inode(struct mnt_idmap *idmap, const char *name) { struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct f2fs_inode_info *fi; nid_t ino; struct inode *inode; bool nid_free = false; @@ -241,14 +242,15 @@ static struct inode *f2fs_new_inode(struct mnt_idmap *idmap, inode_init_owner(idmap, inode, dir, mode); + fi = F2FS_I(inode); inode->i_ino = ino; inode->i_blocks = 0; simple_inode_init_ts(inode); - F2FS_I(inode)->i_crtime = inode_get_mtime(inode); + fi->i_crtime = inode_get_mtime(inode); inode->i_generation = get_random_u32(); if (S_ISDIR(inode->i_mode)) - F2FS_I(inode)->i_current_depth = 1; + fi->i_current_depth = 1; err = insert_inode_locked(inode); if (err) { @@ -258,9 +260,9 @@ static struct inode *f2fs_new_inode(struct mnt_idmap *idmap, if (f2fs_sb_has_project_quota(sbi) && (F2FS_I(dir)->i_flags & F2FS_PROJINHERIT_FL)) - F2FS_I(inode)->i_projid = F2FS_I(dir)->i_projid; + fi->i_projid = F2FS_I(dir)->i_projid; else - F2FS_I(inode)->i_projid = make_kprojid(&init_user_ns, + fi->i_projid = make_kprojid(&init_user_ns, F2FS_DEF_PROJID); err = fscrypt_prepare_new_inode(dir, inode, &encrypt); @@ -278,7 +280,7 @@ static struct inode *f2fs_new_inode(struct mnt_idmap *idmap, if (f2fs_sb_has_extra_attr(sbi)) { set_inode_flag(inode, FI_EXTRA_ATTR); - F2FS_I(inode)->i_extra_isize = F2FS_TOTAL_EXTRA_ATTR_SIZE; + fi->i_extra_isize = F2FS_TOTAL_EXTRA_ATTR_SIZE; } if (test_opt(sbi, INLINE_XATTR)) @@ -296,15 +298,15 @@ static struct inode *f2fs_new_inode(struct mnt_idmap *idmap, f2fs_has_inline_dentry(inode)) { xattr_size = DEFAULT_INLINE_XATTR_ADDRS; } - F2FS_I(inode)->i_inline_xattr_size = xattr_size; + fi->i_inline_xattr_size = xattr_size; - F2FS_I(inode)->i_flags = + fi->i_flags = f2fs_mask_flags(mode, F2FS_I(dir)->i_flags & F2FS_FL_INHERITED); if (S_ISDIR(inode->i_mode)) - F2FS_I(inode)->i_flags |= F2FS_INDEX_FL; + fi->i_flags |= F2FS_INDEX_FL; - if (F2FS_I(inode)->i_flags & F2FS_PROJINHERIT_FL) + if (fi->i_flags & F2FS_PROJINHERIT_FL) set_inode_flag(inode, FI_PROJ_INHERIT); /* Check compression first. */ diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 8712e264071f..9756f0f2b7f7 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -280,6 +280,7 @@ static void recover_inline_flags(struct inode *inode, struct f2fs_inode *ri) static int recover_inode(struct inode *inode, struct page *page) { struct f2fs_inode *raw = F2FS_INODE(page); + struct f2fs_inode_info *fi = F2FS_I(inode); char *name; int err; @@ -302,12 +303,12 @@ static int recover_inode(struct inode *inode, struct page *page) i_projid = (projid_t)le32_to_cpu(raw->i_projid); kprojid = make_kprojid(&init_user_ns, i_projid); - if (!projid_eq(kprojid, F2FS_I(inode)->i_projid)) { + if (!projid_eq(kprojid, fi->i_projid)) { err = f2fs_transfer_project_quota(inode, kprojid); if (err) return err; - F2FS_I(inode)->i_projid = kprojid; + fi->i_projid = kprojid; } } } @@ -320,10 +321,10 @@ static int recover_inode(struct inode *inode, struct page *page) inode_set_mtime(inode, le64_to_cpu(raw->i_mtime), le32_to_cpu(raw->i_mtime_nsec)); - F2FS_I(inode)->i_advise = raw->i_advise; - F2FS_I(inode)->i_flags = le32_to_cpu(raw->i_flags); + fi->i_advise = raw->i_advise; + fi->i_flags = le32_to_cpu(raw->i_flags); f2fs_set_inode_flags(inode); - F2FS_I(inode)->i_gc_failures = le16_to_cpu(raw->i_gc_failures); + fi->i_gc_failures = le16_to_cpu(raw->i_gc_failures); recover_inline_flags(inode, raw); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index a0ce3d080f80..78c3198a6308 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2784,11 +2784,19 @@ static unsigned int __get_next_segno(struct f2fs_sb_info *sbi, int type) unsigned short seg_type = curseg->seg_type; sanity_check_seg_type(sbi, seg_type); - if (f2fs_need_rand_seg(sbi)) - return get_random_u32_below(MAIN_SECS(sbi) * SEGS_PER_SEC(sbi)); + if (__is_large_section(sbi)) { + if (f2fs_need_rand_seg(sbi)) { + unsigned int hint = GET_SEC_FROM_SEG(sbi, curseg->segno); - if (__is_large_section(sbi)) + if (GET_SEC_FROM_SEG(sbi, curseg->segno + 1) != hint) + return curseg->segno; + return get_random_u32_inclusive(curseg->segno + 1, + GET_SEG_FROM_SEC(sbi, hint + 1) - 1); + } return curseg->segno; + } else if (f2fs_need_rand_seg(sbi)) { + return get_random_u32_below(MAIN_SECS(sbi) * SEGS_PER_SEC(sbi)); + } /* inmem log may not locate on any segment after mount */ if (!curseg->inited) @@ -2931,12 +2939,12 @@ static int get_atssr_segment(struct f2fs_sb_info *sbi, int type, return ret; } -static int __f2fs_init_atgc_curseg(struct f2fs_sb_info *sbi) +static int __f2fs_init_atgc_curseg(struct f2fs_sb_info *sbi, bool force) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_ALL_DATA_ATGC); int ret = 0; - if (!sbi->am.atgc_enabled) + if (!sbi->am.atgc_enabled && !force) return 0; f2fs_down_read(&SM_I(sbi)->curseg_lock); @@ -2953,9 +2961,30 @@ static int __f2fs_init_atgc_curseg(struct f2fs_sb_info *sbi) f2fs_up_read(&SM_I(sbi)->curseg_lock); return ret; } + int f2fs_init_inmem_curseg(struct f2fs_sb_info *sbi) { - return __f2fs_init_atgc_curseg(sbi); + return __f2fs_init_atgc_curseg(sbi, false); +} + +int f2fs_reinit_atgc_curseg(struct f2fs_sb_info *sbi) +{ + int ret; + + if (!test_opt(sbi, ATGC)) + return 0; + if (sbi->am.atgc_enabled) + return 0; + if (le64_to_cpu(F2FS_CKPT(sbi)->elapsed_time) < + sbi->am.age_threshold) + return 0; + + ret = __f2fs_init_atgc_curseg(sbi, true); + if (!ret) { + sbi->am.atgc_enabled = true; + f2fs_info(sbi, "reenabled age threshold GC"); + } + return ret; } static void __f2fs_save_inmem_curseg(struct f2fs_sb_info *sbi, int type) @@ -3483,7 +3512,9 @@ static int __get_segment_type_6(struct f2fs_io_info *fio) if (page_private_gcing(fio->page)) { if (fio->sbi->am.atgc_enabled && (fio->io_type == FS_DATA_IO) && - (fio->sbi->gc_mode != GC_URGENT_HIGH)) + (fio->sbi->gc_mode != GC_URGENT_HIGH) && + __is_valid_data_blkaddr(fio->old_blkaddr) && + !is_inode_flag_set(inode, FI_OPU_WRITE)) return CURSEG_ALL_DATA_ATGC; else return CURSEG_COLD_DATA; @@ -3828,7 +3859,7 @@ int f2fs_inplace_write_data(struct f2fs_io_info *fio) goto drop_bio; } - if (fio->post_read) + if (fio->meta_gc) f2fs_truncate_meta_inode_pages(sbi, fio->new_blkaddr, 1); stat_inc_inplace_blocks(fio->sbi); @@ -3998,7 +4029,7 @@ void f2fs_wait_on_block_writeback(struct inode *inode, block_t blkaddr) struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct page *cpage; - if (!f2fs_post_read_required(inode)) + if (!f2fs_meta_inode_gc_required(inode)) return; if (!__is_valid_data_blkaddr(blkaddr)) @@ -4017,7 +4048,7 @@ void f2fs_wait_on_block_writeback_range(struct inode *inode, block_t blkaddr, struct f2fs_sb_info *sbi = F2FS_I_SB(inode); block_t i; - if (!f2fs_post_read_required(inode)) + if (!f2fs_meta_inode_gc_required(inode)) return; for (i = 0; i < len; i++) @@ -5187,7 +5218,8 @@ static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type) } /* Allocate a new section if it's not new. */ - if (cs->next_blkoff) { + if (cs->next_blkoff || + cs->segno != GET_SEG_FROM_SEC(sbi, GET_ZONE_FROM_SEC(sbi, cs_section))) { unsigned int old_segno = cs->segno, old_blkoff = cs->next_blkoff; f2fs_allocate_new_section(sbi, type, true); diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index e1c0f418aa11..bfc01a521cb9 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -347,7 +347,8 @@ static inline unsigned int get_ckpt_valid_blocks(struct f2fs_sb_info *sbi, unsigned int segno, bool use_section) { if (use_section && __is_large_section(sbi)) { - unsigned int start_segno = START_SEGNO(segno); + unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); + unsigned int start_segno = GET_SEG_FROM_SEC(sbi, secno); unsigned int blocks = 0; int i; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index df4cf31f93df..3959fd137cc9 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -151,8 +151,6 @@ enum { Opt_mode, Opt_fault_injection, Opt_fault_type, - Opt_lazytime, - Opt_nolazytime, Opt_quota, Opt_noquota, Opt_usrquota, @@ -229,8 +227,6 @@ static match_table_t f2fs_tokens = { {Opt_mode, "mode=%s"}, {Opt_fault_injection, "fault_injection=%u"}, {Opt_fault_type, "fault_type=%u"}, - {Opt_lazytime, "lazytime"}, - {Opt_nolazytime, "nolazytime"}, {Opt_quota, "quota"}, {Opt_noquota, "noquota"}, {Opt_usrquota, "usrquota"}, @@ -918,12 +914,6 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount) f2fs_info(sbi, "fault_type options not supported"); break; #endif - case Opt_lazytime: - sb->s_flags |= SB_LAZYTIME; - break; - case Opt_nolazytime: - sb->s_flags &= ~SB_LAZYTIME; - break; #ifdef CONFIG_QUOTA case Opt_quota: case Opt_usrquota: @@ -4481,6 +4471,7 @@ try_onemore: sb->s_flags = (sb->s_flags & ~SB_POSIXACL) | (test_opt(sbi, POSIX_ACL) ? SB_POSIXACL : 0); super_set_uuid(sb, (void *) raw_super->uuid, sizeof(raw_super->uuid)); + super_set_sysfs_name_bdev(sb); sb->s_iflags |= SB_I_CGROUPWB; /* init f2fs-specific super block info */ diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 09d3ecfaa4f1..fee7ee45ceaa 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -340,13 +340,13 @@ static ssize_t f2fs_sbi_show(struct f2fs_attr *a, if (!strcmp(a->attr.name, "ckpt_thread_ioprio")) { struct ckpt_req_control *cprc = &sbi->cprc_info; int class = IOPRIO_PRIO_CLASS(cprc->ckpt_thread_ioprio); - int data = IOPRIO_PRIO_DATA(cprc->ckpt_thread_ioprio); + int level = IOPRIO_PRIO_LEVEL(cprc->ckpt_thread_ioprio); if (class != IOPRIO_CLASS_RT && class != IOPRIO_CLASS_BE) return -EINVAL; return sysfs_emit(buf, "%s,%d\n", - class == IOPRIO_CLASS_RT ? "rt" : "be", data); + class == IOPRIO_CLASS_RT ? "rt" : "be", level); } #ifdef CONFIG_F2FS_FS_COMPRESSION @@ -450,7 +450,7 @@ out: const char *name = strim((char *)buf); struct ckpt_req_control *cprc = &sbi->cprc_info; int class; - long data; + long level; int ret; if (!strncmp(name, "rt,", 3)) @@ -461,13 +461,13 @@ out: return -EINVAL; name += 3; - ret = kstrtol(name, 10, &data); + ret = kstrtol(name, 10, &level); if (ret) return ret; - if (data >= IOPRIO_NR_LEVELS || data < 0) + if (level >= IOPRIO_NR_LEVELS || level < 0) return -EINVAL; - cprc->ckpt_thread_ioprio = IOPRIO_PRIO_VALUE(class, data); + cprc->ckpt_thread_ioprio = IOPRIO_PRIO_VALUE(class, level); if (test_opt(sbi, MERGE_CHECKPOINT)) { ret = set_task_ioprio(cprc->f2fs_issue_ckpt, cprc->ckpt_thread_ioprio); diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 3eb747d26924..2b532670a561 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -455,7 +455,7 @@ static int hostfs_read_folio(struct file *file, struct folio *folio) if (bytes_read < 0) ret = bytes_read; else - buffer = folio_zero_tail(folio, bytes_read, buffer); + buffer = folio_zero_tail(folio, bytes_read, buffer + bytes_read); kunmap_local(buffer); folio_end_read(folio, ret == 0); diff --git a/fs/jffs2/Kconfig b/fs/jffs2/Kconfig index 7c96bc107218..560187d61562 100644 --- a/fs/jffs2/Kconfig +++ b/fs/jffs2/Kconfig @@ -151,8 +151,9 @@ config JFFS2_RUBIN RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure. choice - prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS + prompt "JFFS2 default compression mode" default JFFS2_CMODE_PRIORITY + depends on JFFS2_COMPRESSION_OPTIONS depends on JFFS2_FS help You can set here the default compression mode of JFFS2 from diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c index cb3cda1390ad..5713994328cb 100644 --- a/fs/jfs/jfs_dmap.c +++ b/fs/jfs/jfs_dmap.c @@ -1626,6 +1626,8 @@ s64 dbDiscardAG(struct inode *ip, int agno, s64 minlen) } else if (rc == -ENOSPC) { /* search for next smaller log2 block */ l2nb = BLKSTOL2(nblocks) - 1; + if (unlikely(l2nb < 0)) + break; nblocks = 1LL << l2nb; } else { /* Trim any already allocated blocks */ diff --git a/fs/jfs/jfs_dtree.c b/fs/jfs/jfs_dtree.c index 031d8f570f58..5d3127ca68a4 100644 --- a/fs/jfs/jfs_dtree.c +++ b/fs/jfs/jfs_dtree.c @@ -834,6 +834,8 @@ int dtInsert(tid_t tid, struct inode *ip, * the full page. */ DT_GETSEARCH(ip, btstack->top, bn, mp, p, index); + if (p->header.freelist == 0) + return -EINVAL; /* * insert entry for new key diff --git a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c index 2ec35889ad24..1407feccbc2d 100644 --- a/fs/jfs/jfs_imap.c +++ b/fs/jfs/jfs_imap.c @@ -290,7 +290,7 @@ int diSync(struct inode *ipimap) int diRead(struct inode *ip) { struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb); - int iagno, ino, extno, rc; + int iagno, ino, extno, rc, agno; struct inode *ipimap; struct dinode *dp; struct iag *iagp; @@ -339,8 +339,11 @@ int diRead(struct inode *ip) /* get the ag for the iag */ agstart = le64_to_cpu(iagp->agstart); + agno = BLKTOAG(agstart, JFS_SBI(ip->i_sb)); release_metapage(mp); + if (agno >= MAXAG || agno < 0) + return -EIO; rel_inode = (ino & (INOSPERPAGE - 1)); pageno = blkno >> sbi->l2nbperpage; diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c index 9609349e92e5..270808b6219b 100644 --- a/fs/jfs/jfs_logmgr.c +++ b/fs/jfs/jfs_logmgr.c @@ -1600,7 +1600,7 @@ void jfs_flush_journal(struct jfs_log *log, int wait) mp, sizeof(struct metapage), 0); print_hex_dump(KERN_ERR, "page: ", DUMP_PREFIX_ADDRESS, 16, - sizeof(long), mp->page, + sizeof(long), mp->folio, sizeof(struct page), 0); } else print_hex_dump(KERN_ERR, "tblock:", diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 961569c11159..df575a873ec6 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -4,6 +4,7 @@ * Portions Copyright (C) Christoph Hellwig, 2001-2002 */ +#include <linux/blkdev.h> #include <linux/fs.h> #include <linux/mm.h> #include <linux/module.h> @@ -46,9 +47,9 @@ static inline void __lock_metapage(struct metapage *mp) do { set_current_state(TASK_UNINTERRUPTIBLE); if (metapage_locked(mp)) { - unlock_page(mp->page); + folio_unlock(mp->folio); io_schedule(); - lock_page(mp->page); + folio_lock(mp->folio); } } while (trylock_metapage(mp)); __set_current_state(TASK_RUNNING); @@ -56,7 +57,7 @@ static inline void __lock_metapage(struct metapage *mp) } /* - * Must have mp->page locked + * Must have mp->folio locked */ static inline void lock_metapage(struct metapage *mp) { @@ -75,36 +76,36 @@ static mempool_t *metapage_mempool; struct meta_anchor { int mp_count; atomic_t io_count; + blk_status_t status; struct metapage *mp[MPS_PER_PAGE]; }; -#define mp_anchor(page) ((struct meta_anchor *)page_private(page)) -static inline struct metapage *page_to_mp(struct page *page, int offset) +static inline struct metapage *folio_to_mp(struct folio *folio, int offset) { - if (!PagePrivate(page)) + struct meta_anchor *anchor = folio->private; + + if (!anchor) return NULL; - return mp_anchor(page)->mp[offset >> L2PSIZE]; + return anchor->mp[offset >> L2PSIZE]; } -static inline int insert_metapage(struct page *page, struct metapage *mp) +static inline int insert_metapage(struct folio *folio, struct metapage *mp) { struct meta_anchor *a; int index; int l2mp_blocks; /* log2 blocks per metapage */ - if (PagePrivate(page)) - a = mp_anchor(page); - else { + a = folio->private; + if (!a) { a = kzalloc(sizeof(struct meta_anchor), GFP_NOFS); if (!a) return -ENOMEM; - set_page_private(page, (unsigned long)a); - SetPagePrivate(page); - kmap(page); + folio_attach_private(folio, a); + kmap(&folio->page); } if (mp) { - l2mp_blocks = L2PSIZE - page->mapping->host->i_blkbits; + l2mp_blocks = L2PSIZE - folio->mapping->host->i_blkbits; index = (mp->index >> l2mp_blocks) & (MPS_PER_PAGE - 1); a->mp_count++; a->mp[index] = mp; @@ -113,10 +114,10 @@ static inline int insert_metapage(struct page *page, struct metapage *mp) return 0; } -static inline void remove_metapage(struct page *page, struct metapage *mp) +static inline void remove_metapage(struct folio *folio, struct metapage *mp) { - struct meta_anchor *a = mp_anchor(page); - int l2mp_blocks = L2PSIZE - page->mapping->host->i_blkbits; + struct meta_anchor *a = folio->private; + int l2mp_blocks = L2PSIZE - folio->mapping->host->i_blkbits; int index; index = (mp->index >> l2mp_blocks) & (MPS_PER_PAGE - 1); @@ -126,48 +127,53 @@ static inline void remove_metapage(struct page *page, struct metapage *mp) a->mp[index] = NULL; if (--a->mp_count == 0) { kfree(a); - set_page_private(page, 0); - ClearPagePrivate(page); - kunmap(page); + folio_detach_private(folio); + kunmap(&folio->page); } } -static inline void inc_io(struct page *page) +static inline void inc_io(struct folio *folio) { - atomic_inc(&mp_anchor(page)->io_count); + struct meta_anchor *anchor = folio->private; + + atomic_inc(&anchor->io_count); } -static inline void dec_io(struct page *page, void (*handler) (struct page *)) +static inline void dec_io(struct folio *folio, blk_status_t status, + void (*handler)(struct folio *, blk_status_t)) { - if (atomic_dec_and_test(&mp_anchor(page)->io_count)) - handler(page); + struct meta_anchor *anchor = folio->private; + + if (anchor->status == BLK_STS_OK) + anchor->status = status; + + if (atomic_dec_and_test(&anchor->io_count)) + handler(folio, anchor->status); } #else -static inline struct metapage *page_to_mp(struct page *page, int offset) +static inline struct metapage *folio_to_mp(struct folio *folio, int offset) { - return PagePrivate(page) ? (struct metapage *)page_private(page) : NULL; + return folio->private; } -static inline int insert_metapage(struct page *page, struct metapage *mp) +static inline int insert_metapage(struct folio *folio, struct metapage *mp) { if (mp) { - set_page_private(page, (unsigned long)mp); - SetPagePrivate(page); - kmap(page); + folio_attach_private(folio, mp); + kmap(&folio->page); } return 0; } -static inline void remove_metapage(struct page *page, struct metapage *mp) +static inline void remove_metapage(struct folio *folio, struct metapage *mp) { - set_page_private(page, 0); - ClearPagePrivate(page); - kunmap(page); + folio_detach_private(folio); + kunmap(&folio->page); } -#define inc_io(page) do {} while(0) -#define dec_io(page, handler) handler(page) +#define inc_io(folio) do {} while(0) +#define dec_io(folio, status, handler) handler(folio, status) #endif @@ -218,12 +224,12 @@ void metapage_exit(void) kmem_cache_destroy(metapage_cache); } -static inline void drop_metapage(struct page *page, struct metapage *mp) +static inline void drop_metapage(struct folio *folio, struct metapage *mp) { if (mp->count || mp->nohomeok || test_bit(META_dirty, &mp->flag) || test_bit(META_io, &mp->flag)) return; - remove_metapage(page, mp); + remove_metapage(folio, mp); INCREMENT(mpStat.pagefree); free_metapage(mp); } @@ -257,23 +263,20 @@ static sector_t metapage_get_blocks(struct inode *inode, sector_t lblock, return lblock; } -static void last_read_complete(struct page *page) +static void last_read_complete(struct folio *folio, blk_status_t status) { - if (!PageError(page)) - SetPageUptodate(page); - unlock_page(page); + if (status) + printk(KERN_ERR "Read error %d at %#llx\n", status, + folio_pos(folio)); + + folio_end_read(folio, status == 0); } static void metapage_read_end_io(struct bio *bio) { - struct page *page = bio->bi_private; - - if (bio->bi_status) { - printk(KERN_ERR "metapage_read_end_io: I/O error\n"); - SetPageError(page); - } + struct folio *folio = bio->bi_private; - dec_io(page, last_read_complete); + dec_io(folio, bio->bi_status, last_read_complete); bio_put(bio); } @@ -299,13 +302,19 @@ static void remove_from_logsync(struct metapage *mp) LOGSYNC_UNLOCK(log, flags); } -static void last_write_complete(struct page *page) +static void last_write_complete(struct folio *folio, blk_status_t status) { struct metapage *mp; unsigned int offset; + if (status) { + int err = blk_status_to_errno(status); + printk(KERN_ERR "metapage_write_end_io: I/O error\n"); + mapping_set_error(folio->mapping, err); + } + for (offset = 0; offset < PAGE_SIZE; offset += PSIZE) { - mp = page_to_mp(page, offset); + mp = folio_to_mp(folio, offset); if (mp && test_bit(META_io, &mp->flag)) { if (mp->lsn) remove_from_logsync(mp); @@ -316,28 +325,25 @@ static void last_write_complete(struct page *page) * safe unless I have the page locked */ } - end_page_writeback(page); + folio_end_writeback(folio); } static void metapage_write_end_io(struct bio *bio) { - struct page *page = bio->bi_private; + struct folio *folio = bio->bi_private; - BUG_ON(!PagePrivate(page)); + BUG_ON(!folio->private); - if (bio->bi_status) { - printk(KERN_ERR "metapage_write_end_io: I/O error\n"); - SetPageError(page); - } - dec_io(page, last_write_complete); + dec_io(folio, bio->bi_status, last_write_complete); bio_put(bio); } -static int metapage_writepage(struct page *page, struct writeback_control *wbc) +static int metapage_write_folio(struct folio *folio, + struct writeback_control *wbc, void *unused) { struct bio *bio = NULL; int block_offset; /* block offset of mp within page */ - struct inode *inode = page->mapping->host; + struct inode *inode = folio->mapping->host; int blocks_per_mp = JFS_SBI(inode->i_sb)->nbperpage; int len; int xlen; @@ -353,14 +359,13 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc) int offset; int bad_blocks = 0; - page_start = (sector_t)page->index << - (PAGE_SHIFT - inode->i_blkbits); - BUG_ON(!PageLocked(page)); - BUG_ON(PageWriteback(page)); - set_page_writeback(page); + page_start = folio_pos(folio) >> inode->i_blkbits; + BUG_ON(!folio_test_locked(folio)); + BUG_ON(folio_test_writeback(folio)); + folio_start_writeback(folio); for (offset = 0; offset < PAGE_SIZE; offset += PSIZE) { - mp = page_to_mp(page, offset); + mp = folio_to_mp(folio, offset); if (!mp || !test_bit(META_dirty, &mp->flag)) continue; @@ -389,22 +394,20 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc) continue; } /* Not contiguous */ - if (bio_add_page(bio, page, bio_bytes, bio_offset) < - bio_bytes) - goto add_failed; + bio_add_folio_nofail(bio, folio, bio_bytes, bio_offset); /* * Increment counter before submitting i/o to keep * count from hitting zero before we're through */ - inc_io(page); + inc_io(folio); if (!bio->bi_iter.bi_size) goto dump_bio; submit_bio(bio); nr_underway++; bio = NULL; } else - inc_io(page); - xlen = (PAGE_SIZE - offset) >> inode->i_blkbits; + inc_io(folio); + xlen = (folio_size(folio) - offset) >> inode->i_blkbits; pblock = metapage_get_blocks(inode, lblock, &xlen); if (!pblock) { printk(KERN_ERR "JFS: metapage_get_blocks failed\n"); @@ -420,7 +423,7 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc) bio = bio_alloc(inode->i_sb->s_bdev, 1, REQ_OP_WRITE, GFP_NOFS); bio->bi_iter.bi_sector = pblock << (inode->i_blkbits - 9); bio->bi_end_io = metapage_write_end_io; - bio->bi_private = page; + bio->bi_private = folio; /* Don't call bio_add_page yet, we may add to this vec */ bio_offset = offset; @@ -430,8 +433,7 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc) next_block = lblock + len; } if (bio) { - if (bio_add_page(bio, page, bio_bytes, bio_offset) < bio_bytes) - goto add_failed; + bio_add_folio_nofail(bio, folio, bio_bytes, bio_offset); if (!bio->bi_iter.bi_size) goto dump_bio; @@ -439,50 +441,56 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc) nr_underway++; } if (redirty) - redirty_page_for_writepage(wbc, page); + folio_redirty_for_writepage(wbc, folio); - unlock_page(page); + folio_unlock(folio); if (bad_blocks) goto err_out; if (nr_underway == 0) - end_page_writeback(page); + folio_end_writeback(folio); return 0; -add_failed: - /* We should never reach here, since we're only adding one vec */ - printk(KERN_ERR "JFS: bio_add_page failed unexpectedly\n"); - goto skip; dump_bio: print_hex_dump(KERN_ERR, "JFS: dump of bio: ", DUMP_PREFIX_ADDRESS, 16, 4, bio, sizeof(*bio), 0); -skip: bio_put(bio); - unlock_page(page); - dec_io(page, last_write_complete); + folio_unlock(folio); + dec_io(folio, BLK_STS_OK, last_write_complete); err_out: while (bad_blocks--) - dec_io(page, last_write_complete); + dec_io(folio, BLK_STS_OK, last_write_complete); return -EIO; } +static int metapage_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + struct blk_plug plug; + int err; + + blk_start_plug(&plug); + err = write_cache_pages(mapping, wbc, metapage_write_folio, NULL); + blk_finish_plug(&plug); + + return err; +} + static int metapage_read_folio(struct file *fp, struct folio *folio) { - struct page *page = &folio->page; - struct inode *inode = page->mapping->host; + struct inode *inode = folio->mapping->host; struct bio *bio = NULL; int block_offset; - int blocks_per_page = i_blocks_per_page(inode, page); + int blocks_per_page = i_blocks_per_folio(inode, folio); sector_t page_start; /* address of page in fs blocks */ sector_t pblock; int xlen; unsigned int len; int offset; - BUG_ON(!PageLocked(page)); - page_start = (sector_t)page->index << - (PAGE_SHIFT - inode->i_blkbits); + BUG_ON(!folio_test_locked(folio)); + page_start = folio_pos(folio) >> inode->i_blkbits; block_offset = 0; while (block_offset < blocks_per_page) { @@ -490,9 +498,9 @@ static int metapage_read_folio(struct file *fp, struct folio *folio) pblock = metapage_get_blocks(inode, page_start + block_offset, &xlen); if (pblock) { - if (!PagePrivate(page)) - insert_metapage(page, NULL); - inc_io(page); + if (!folio->private) + insert_metapage(folio, NULL); + inc_io(folio); if (bio) submit_bio(bio); @@ -501,11 +509,10 @@ static int metapage_read_folio(struct file *fp, struct folio *folio) bio->bi_iter.bi_sector = pblock << (inode->i_blkbits - 9); bio->bi_end_io = metapage_read_end_io; - bio->bi_private = page; + bio->bi_private = folio; len = xlen << inode->i_blkbits; offset = block_offset << inode->i_blkbits; - if (bio_add_page(bio, page, len, offset) < len) - goto add_failed; + bio_add_folio_nofail(bio, folio, len, offset); block_offset += xlen; } else block_offset++; @@ -513,15 +520,9 @@ static int metapage_read_folio(struct file *fp, struct folio *folio) if (bio) submit_bio(bio); else - unlock_page(page); + folio_unlock(folio); return 0; - -add_failed: - printk(KERN_ERR "JFS: bio_add_page failed unexpectedly\n"); - bio_put(bio); - dec_io(page, last_read_complete); - return -EIO; } static bool metapage_release_folio(struct folio *folio, gfp_t gfp_mask) @@ -531,7 +532,7 @@ static bool metapage_release_folio(struct folio *folio, gfp_t gfp_mask) int offset; for (offset = 0; offset < PAGE_SIZE; offset += PSIZE) { - mp = page_to_mp(&folio->page, offset); + mp = folio_to_mp(folio, offset); if (!mp) continue; @@ -546,7 +547,7 @@ static bool metapage_release_folio(struct folio *folio, gfp_t gfp_mask) } if (mp->lsn) remove_from_logsync(mp); - remove_metapage(&folio->page, mp); + remove_metapage(folio, mp); INCREMENT(mpStat.pagefree); free_metapage(mp); } @@ -565,7 +566,7 @@ static void metapage_invalidate_folio(struct folio *folio, size_t offset, const struct address_space_operations jfs_metapage_aops = { .read_folio = metapage_read_folio, - .writepage = metapage_writepage, + .writepages = metapage_writepages, .release_folio = metapage_release_folio, .invalidate_folio = metapage_invalidate_folio, .dirty_folio = filemap_dirty_folio, @@ -579,7 +580,7 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, int l2bsize; struct address_space *mapping; struct metapage *mp = NULL; - struct page *page; + struct folio *folio; unsigned long page_index; unsigned long page_offset; @@ -610,22 +611,22 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, } if (new && (PSIZE == PAGE_SIZE)) { - page = grab_cache_page(mapping, page_index); - if (!page) { - jfs_err("grab_cache_page failed!"); + folio = filemap_grab_folio(mapping, page_index); + if (IS_ERR(folio)) { + jfs_err("filemap_grab_folio failed!"); return NULL; } - SetPageUptodate(page); + folio_mark_uptodate(folio); } else { - page = read_mapping_page(mapping, page_index, NULL); - if (IS_ERR(page)) { + folio = read_mapping_folio(mapping, page_index, NULL); + if (IS_ERR(folio)) { jfs_err("read_mapping_page failed!"); return NULL; } - lock_page(page); + folio_lock(folio); } - mp = page_to_mp(page, page_offset); + mp = folio_to_mp(folio, page_offset); if (mp) { if (mp->logical_size != size) { jfs_error(inode->i_sb, @@ -651,16 +652,16 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, mp = alloc_metapage(GFP_NOFS); if (!mp) goto unlock; - mp->page = page; + mp->folio = folio; mp->sb = inode->i_sb; mp->flag = 0; mp->xflag = COMMIT_PAGE; mp->count = 1; mp->nohomeok = 0; mp->logical_size = size; - mp->data = page_address(page) + page_offset; + mp->data = folio_address(folio) + page_offset; mp->index = lblock; - if (unlikely(insert_metapage(page, mp))) { + if (unlikely(insert_metapage(folio, mp))) { free_metapage(mp); goto unlock; } @@ -672,28 +673,27 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, memset(mp->data, 0, PSIZE); } - unlock_page(page); + folio_unlock(folio); jfs_info("__get_metapage: returning = 0x%p data = 0x%p", mp, mp->data); return mp; unlock: - unlock_page(page); + folio_unlock(folio); return NULL; } void grab_metapage(struct metapage * mp) { jfs_info("grab_metapage: mp = 0x%p", mp); - get_page(mp->page); - lock_page(mp->page); + folio_get(mp->folio); + folio_lock(mp->folio); mp->count++; lock_metapage(mp); - unlock_page(mp->page); + folio_unlock(mp->folio); } -static int metapage_write_one(struct page *page) +static int metapage_write_one(struct folio *folio) { - struct folio *folio = page_folio(page); struct address_space *mapping = folio->mapping; struct writeback_control wbc = { .sync_mode = WB_SYNC_ALL, @@ -707,7 +707,7 @@ static int metapage_write_one(struct page *page) if (folio_clear_dirty_for_io(folio)) { folio_get(folio); - ret = metapage_writepage(page, &wbc); + ret = metapage_write_folio(folio, &wbc, NULL); if (ret == 0) folio_wait_writeback(folio); folio_put(folio); @@ -722,71 +722,69 @@ static int metapage_write_one(struct page *page) void force_metapage(struct metapage *mp) { - struct page *page = mp->page; + struct folio *folio = mp->folio; jfs_info("force_metapage: mp = 0x%p", mp); set_bit(META_forcewrite, &mp->flag); clear_bit(META_sync, &mp->flag); - get_page(page); - lock_page(page); - set_page_dirty(page); - if (metapage_write_one(page)) + folio_get(folio); + folio_lock(folio); + folio_mark_dirty(folio); + if (metapage_write_one(folio)) jfs_error(mp->sb, "metapage_write_one() failed\n"); clear_bit(META_forcewrite, &mp->flag); - put_page(page); + folio_put(folio); } void hold_metapage(struct metapage *mp) { - lock_page(mp->page); + folio_lock(mp->folio); } void put_metapage(struct metapage *mp) { if (mp->count || mp->nohomeok) { /* Someone else will release this */ - unlock_page(mp->page); + folio_unlock(mp->folio); return; } - get_page(mp->page); + folio_get(mp->folio); mp->count++; lock_metapage(mp); - unlock_page(mp->page); + folio_unlock(mp->folio); release_metapage(mp); } void release_metapage(struct metapage * mp) { - struct page *page = mp->page; + struct folio *folio = mp->folio; jfs_info("release_metapage: mp = 0x%p, flag = 0x%lx", mp, mp->flag); - BUG_ON(!page); - - lock_page(page); + folio_lock(folio); unlock_metapage(mp); assert(mp->count); if (--mp->count || mp->nohomeok) { - unlock_page(page); - put_page(page); + folio_unlock(folio); + folio_put(folio); return; } if (test_bit(META_dirty, &mp->flag)) { - set_page_dirty(page); + folio_mark_dirty(folio); if (test_bit(META_sync, &mp->flag)) { clear_bit(META_sync, &mp->flag); - if (metapage_write_one(page)) + if (metapage_write_one(folio)) jfs_error(mp->sb, "metapage_write_one() failed\n"); - lock_page(page); + folio_lock(folio); } } else if (mp->lsn) /* discard_metapage doesn't remove it */ remove_from_logsync(mp); /* Try to keep metapages from using up too much memory */ - drop_metapage(page, mp); + drop_metapage(folio, mp); - unlock_page(page); - put_page(page); + folio_unlock(folio); + folio_put(folio); } void __invalidate_metapages(struct inode *ip, s64 addr, int len) @@ -798,7 +796,6 @@ void __invalidate_metapages(struct inode *ip, s64 addr, int len) struct address_space *mapping = JFS_SBI(ip->i_sb)->direct_inode->i_mapping; struct metapage *mp; - struct page *page; unsigned int offset; /* @@ -807,11 +804,12 @@ void __invalidate_metapages(struct inode *ip, s64 addr, int len) */ for (lblock = addr & ~(BlocksPerPage - 1); lblock < addr + len; lblock += BlocksPerPage) { - page = find_lock_page(mapping, lblock >> l2BlocksPerPage); - if (!page) + struct folio *folio = filemap_lock_folio(mapping, + lblock >> l2BlocksPerPage); + if (IS_ERR(folio)) continue; for (offset = 0; offset < PAGE_SIZE; offset += PSIZE) { - mp = page_to_mp(page, offset); + mp = folio_to_mp(folio, offset); if (!mp) continue; if (mp->index < addr) @@ -824,8 +822,8 @@ void __invalidate_metapages(struct inode *ip, s64 addr, int len) if (mp->lsn) remove_from_logsync(mp); } - unlock_page(page); - put_page(page); + folio_unlock(folio); + folio_put(folio); } } diff --git a/fs/jfs/jfs_metapage.h b/fs/jfs/jfs_metapage.h index 4179f9df4deb..2e5015c2705b 100644 --- a/fs/jfs/jfs_metapage.h +++ b/fs/jfs/jfs_metapage.h @@ -24,7 +24,7 @@ struct metapage { wait_queue_head_t wait; /* implementation */ - struct page *page; + struct folio *folio; struct super_block *sb; unsigned int logical_size; @@ -90,14 +90,14 @@ static inline void discard_metapage(struct metapage *mp) static inline void metapage_nohomeok(struct metapage *mp) { - struct page *page = mp->page; - lock_page(page); + struct folio *folio = mp->folio; + folio_lock(folio); if (!mp->nohomeok++) { mark_metapage_dirty(mp); - get_page(page); - wait_on_page_writeback(page); + folio_get(folio); + folio_wait_writeback(folio); } - unlock_page(page); + folio_unlock(folio); } /* @@ -107,7 +107,7 @@ static inline void metapage_nohomeok(struct metapage *mp) static inline void metapage_wait_for_io(struct metapage *mp) { if (test_bit(META_io, &mp->flag)) - wait_on_page_writeback(mp->page); + folio_wait_writeback(mp->folio); } /* @@ -116,7 +116,7 @@ static inline void metapage_wait_for_io(struct metapage *mp) static inline void _metapage_homeok(struct metapage *mp) { if (!--mp->nohomeok) - put_page(mp->page); + folio_put(mp->folio); } static inline void metapage_homeok(struct metapage *mp) diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c index 9987055293b3..2999ed5d83f5 100644 --- a/fs/jfs/xattr.c +++ b/fs/jfs/xattr.c @@ -797,7 +797,7 @@ ssize_t __jfs_getxattr(struct inode *inode, const char *name, void *data, size_t buf_size) { struct jfs_ea_list *ealist; - struct jfs_ea *ea; + struct jfs_ea *ea, *ealist_end; struct ea_buffer ea_buf; int xattr_size; ssize_t size; @@ -817,9 +817,16 @@ ssize_t __jfs_getxattr(struct inode *inode, const char *name, void *data, goto not_found; ealist = (struct jfs_ea_list *) ea_buf.xattr; + ealist_end = END_EALIST(ealist); /* Find the named attribute */ - for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist); ea = NEXT_EA(ea)) + for (ea = FIRST_EA(ealist); ea < ealist_end; ea = NEXT_EA(ea)) { + if (unlikely(ea + 1 > ealist_end) || + unlikely(NEXT_EA(ea) > ealist_end)) { + size = -EUCLEAN; + goto release; + } + if ((namelen == ea->namelen) && memcmp(name, ea->name, namelen) == 0) { /* Found it */ @@ -834,6 +841,7 @@ ssize_t __jfs_getxattr(struct inode *inode, const char *name, void *data, memcpy(data, value, size); goto release; } + } not_found: size = -ENODATA; release: @@ -861,7 +869,7 @@ ssize_t jfs_listxattr(struct dentry * dentry, char *data, size_t buf_size) ssize_t size = 0; int xattr_size; struct jfs_ea_list *ealist; - struct jfs_ea *ea; + struct jfs_ea *ea, *ealist_end; struct ea_buffer ea_buf; down_read(&JFS_IP(inode)->xattr_sem); @@ -876,9 +884,16 @@ ssize_t jfs_listxattr(struct dentry * dentry, char *data, size_t buf_size) goto release; ealist = (struct jfs_ea_list *) ea_buf.xattr; + ealist_end = END_EALIST(ealist); /* compute required size of list */ - for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist); ea = NEXT_EA(ea)) { + for (ea = FIRST_EA(ealist); ea < ealist_end; ea = NEXT_EA(ea)) { + if (unlikely(ea + 1 > ealist_end) || + unlikely(NEXT_EA(ea) > ealist_end)) { + size = -EUCLEAN; + goto release; + } + if (can_list(ea)) size += name_size(ea) + 1; } diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index 8e6bcdf99770..6ede3e924dec 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -231,7 +231,7 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, struct ntfs_sb_info *sbi; struct ATTRIB *attr_s; struct MFT_REC *rec; - u32 used, asize, rsize, aoff, align; + u32 used, asize, rsize, aoff; bool is_data; CLST len, alen; char *next; @@ -252,10 +252,13 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, rsize = le32_to_cpu(attr->res.data_size); is_data = attr->type == ATTR_DATA && !attr->name_len; - align = sbi->cluster_size; - if (is_attr_compressed(attr)) - align <<= COMPRESSION_UNIT; - len = (rsize + align - 1) >> sbi->cluster_bits; + /* len - how many clusters required to store 'rsize' bytes */ + if (is_attr_compressed(attr)) { + u8 shift = sbi->cluster_bits + NTFS_LZNT_CUNIT; + len = ((rsize + (1u << shift) - 1) >> shift) << NTFS_LZNT_CUNIT; + } else { + len = bytes_to_cluster(sbi, rsize); + } run_init(run); @@ -285,22 +288,21 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, if (err) goto out2; } else if (!page) { - char *kaddr; - - page = grab_cache_page(ni->vfs_inode.i_mapping, 0); - if (!page) { - err = -ENOMEM; + struct address_space *mapping = ni->vfs_inode.i_mapping; + struct folio *folio; + + folio = __filemap_get_folio( + mapping, 0, FGP_LOCK | FGP_ACCESSED | FGP_CREAT, + mapping_gfp_mask(mapping)); + if (IS_ERR(folio)) { + err = PTR_ERR(folio); goto out2; } - kaddr = kmap_atomic(page); - memcpy(kaddr, data, rsize); - memset(kaddr + rsize, 0, PAGE_SIZE - rsize); - kunmap_atomic(kaddr); - flush_dcache_page(page); - SetPageUptodate(page); - set_page_dirty(page); - unlock_page(page); - put_page(page); + folio_fill_tail(folio, 0, data, rsize); + folio_mark_uptodate(folio); + folio_mark_dirty(folio); + folio_unlock(folio); + folio_put(folio); } } @@ -670,7 +672,8 @@ pack_runs: goto undo_2; } - if (!is_mft) + /* keep runs for $MFT::$ATTR_DATA and $MFT::$ATTR_BITMAP. */ + if (ni->mi.rno != MFT_REC_MFT) run_truncate_head(run, evcn + 1); svcn = le64_to_cpu(attr->nres.svcn); @@ -972,6 +975,19 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, if (err) goto out; + /* Check for compressed frame. */ + err = attr_is_frame_compressed(ni, attr, vcn >> NTFS_LZNT_CUNIT, &hint); + if (err) + goto out; + + if (hint) { + /* if frame is compressed - don't touch it. */ + *lcn = COMPRESSED_LCN; + *len = hint; + err = -EOPNOTSUPP; + goto out; + } + if (!*len) { if (run_lookup_entry(run, vcn, lcn, len, NULL)) { if (*lcn != SPARSE_LCN || !new) @@ -1223,11 +1239,12 @@ undo1: goto out; } -int attr_data_read_resident(struct ntfs_inode *ni, struct page *page) +int attr_data_read_resident(struct ntfs_inode *ni, struct folio *folio) { u64 vbo; struct ATTRIB *attr; u32 data_size; + size_t len; attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, NULL); if (!attr) @@ -1236,30 +1253,20 @@ int attr_data_read_resident(struct ntfs_inode *ni, struct page *page) if (attr->non_res) return E_NTFS_NONRESIDENT; - vbo = page->index << PAGE_SHIFT; + vbo = folio->index << PAGE_SHIFT; data_size = le32_to_cpu(attr->res.data_size); - if (vbo < data_size) { - const char *data = resident_data(attr); - char *kaddr = kmap_atomic(page); - u32 use = data_size - vbo; - - if (use > PAGE_SIZE) - use = PAGE_SIZE; + if (vbo > data_size) + len = 0; + else + len = min(data_size - vbo, folio_size(folio)); - memcpy(kaddr, data + vbo, use); - memset(kaddr + use, 0, PAGE_SIZE - use); - kunmap_atomic(kaddr); - flush_dcache_page(page); - SetPageUptodate(page); - } else if (!PageUptodate(page)) { - zero_user_segment(page, 0, PAGE_SIZE); - SetPageUptodate(page); - } + folio_fill_tail(folio, 0, resident_data(attr) + vbo, len); + folio_mark_uptodate(folio); return 0; } -int attr_data_write_resident(struct ntfs_inode *ni, struct page *page) +int attr_data_write_resident(struct ntfs_inode *ni, struct folio *folio) { u64 vbo; struct mft_inode *mi; @@ -1275,17 +1282,13 @@ int attr_data_write_resident(struct ntfs_inode *ni, struct page *page) return E_NTFS_NONRESIDENT; } - vbo = page->index << PAGE_SHIFT; + vbo = folio->index << PAGE_SHIFT; data_size = le32_to_cpu(attr->res.data_size); if (vbo < data_size) { char *data = resident_data(attr); - char *kaddr = kmap_atomic(page); - u32 use = data_size - vbo; + size_t len = min(data_size - vbo, folio_size(folio)); - if (use > PAGE_SIZE) - use = PAGE_SIZE; - memcpy(data + vbo, kaddr, use); - kunmap_atomic(kaddr); + memcpy_from_folio(data + vbo, folio, 0, len); mi->dirty = true; } ni->i_valid = data_size; @@ -1378,7 +1381,7 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr, u32 voff; u8 bytes_per_off; char *addr; - struct page *page; + struct folio *folio; int i, err; __le32 *off32; __le64 *off64; @@ -1423,18 +1426,18 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr, wof_size = le64_to_cpu(attr->nres.data_size); down_write(&ni->file.run_lock); - page = ni->file.offs_page; - if (!page) { - page = alloc_page(GFP_KERNEL); - if (!page) { + folio = ni->file.offs_folio; + if (!folio) { + folio = folio_alloc(GFP_KERNEL, 0); + if (!folio) { err = -ENOMEM; goto out; } - page->index = -1; - ni->file.offs_page = page; + folio->index = -1; + ni->file.offs_folio = folio; } - lock_page(page); - addr = page_address(page); + folio_lock(folio); + addr = folio_address(folio); if (vbo[1]) { voff = vbo[1] & (PAGE_SIZE - 1); @@ -1450,7 +1453,8 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr, do { pgoff_t index = vbo[i] >> PAGE_SHIFT; - if (index != page->index) { + if (index != folio->index) { + struct page *page = &folio->page; u64 from = vbo[i] & ~(u64)(PAGE_SIZE - 1); u64 to = min(from + PAGE_SIZE, wof_size); @@ -1463,10 +1467,10 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr, err = ntfs_bio_pages(sbi, run, &page, 1, from, to - from, REQ_OP_READ); if (err) { - page->index = -1; + folio->index = -1; goto out1; } - page->index = index; + folio->index = index; } if (i) { @@ -1504,7 +1508,7 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr, *ondisk_size = off[1] - off[0]; out1: - unlock_page(page); + folio_unlock(folio); out: up_write(&ni->file.run_lock); return err; @@ -1722,6 +1726,7 @@ repack: attr_b->nres.total_size = cpu_to_le64(total_size); inode_set_bytes(&ni->vfs_inode, total_size); + ni->ni_flags |= NI_FLAG_UPDATE_PARENT; mi_b->dirty = true; mark_inode_dirty(&ni->vfs_inode); @@ -2356,8 +2361,13 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) mask = (sbi->cluster_size << attr_b->nres.c_unit) - 1; } - if (vbo > data_size) { - /* Insert range after the file size is not allowed. */ + if (vbo >= data_size) { + /* + * Insert range after the file size is not allowed. + * If the offset is equal to or greater than the end of + * file, an error is returned. For such operations (i.e., inserting + * a hole at the end of file), ftruncate(2) should be used. + */ return -EINVAL; } diff --git a/fs/ntfs3/bitmap.c b/fs/ntfs3/bitmap.c index c9eb01ccee51..cf4fe21a5039 100644 --- a/fs/ntfs3/bitmap.c +++ b/fs/ntfs3/bitmap.c @@ -1382,7 +1382,7 @@ int wnd_extend(struct wnd_bitmap *wnd, size_t new_bits) err = ntfs_vbo_to_lbo(sbi, &wnd->run, vbo, &lbo, &bytes); if (err) - break; + return err; bh = ntfs_bread(sb, lbo >> sb->s_blocksize_bits); if (!bh) diff --git a/fs/ntfs3/dir.c b/fs/ntfs3/dir.c index 1937e8e612f8..fc6a8aa29e3a 100644 --- a/fs/ntfs3/dir.c +++ b/fs/ntfs3/dir.c @@ -272,9 +272,12 @@ out: return err == -ENOENT ? NULL : err ? ERR_PTR(err) : inode; } -static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni, - const struct NTFS_DE *e, u8 *name, - struct dir_context *ctx) +/* + * returns false if 'ctx' if full + */ +static inline bool ntfs_dir_emit(struct ntfs_sb_info *sbi, + struct ntfs_inode *ni, const struct NTFS_DE *e, + u8 *name, struct dir_context *ctx) { const struct ATTR_FILE_NAME *fname; unsigned long ino; @@ -284,29 +287,29 @@ static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni, fname = Add2Ptr(e, sizeof(struct NTFS_DE)); if (fname->type == FILE_NAME_DOS) - return 0; + return true; if (!mi_is_ref(&ni->mi, &fname->home)) - return 0; + return true; ino = ino_get(&e->ref); if (ino == MFT_REC_ROOT) - return 0; + return true; /* Skip meta files. Unless option to show metafiles is set. */ if (!sbi->options->showmeta && ntfs_is_meta_file(sbi, ino)) - return 0; + return true; if (sbi->options->nohidden && (fname->dup.fa & FILE_ATTRIBUTE_HIDDEN)) - return 0; + return true; name_len = ntfs_utf16_to_nls(sbi, fname->name, fname->name_len, name, PATH_MAX); if (name_len <= 0) { ntfs_warn(sbi->sb, "failed to convert name for inode %lx.", ino); - return 0; + return true; } /* @@ -326,7 +329,8 @@ static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni, * It does additional locks/reads just to get the type of name. * Should we use additional mount option to enable branch below? */ - if ((fname->dup.fa & FILE_ATTRIBUTE_REPARSE_POINT) && + if (((fname->dup.fa & FILE_ATTRIBUTE_REPARSE_POINT) || + fname->dup.ea_size) && ino != ni->mi.rno) { struct inode *inode = ntfs_iget5(sbi->sb, &e->ref, NULL); if (!IS_ERR_OR_NULL(inode)) { @@ -335,17 +339,20 @@ static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni, } } - return !dir_emit(ctx, (s8 *)name, name_len, ino, dt_type); + return dir_emit(ctx, (s8 *)name, name_len, ino, dt_type); } /* * ntfs_read_hdr - Helper function for ntfs_readdir(). + * + * returns 0 if ok. + * returns -EINVAL if directory is corrupted. + * returns +1 if 'ctx' is full. */ static int ntfs_read_hdr(struct ntfs_sb_info *sbi, struct ntfs_inode *ni, const struct INDEX_HDR *hdr, u64 vbo, u64 pos, u8 *name, struct dir_context *ctx) { - int err; const struct NTFS_DE *e; u32 e_size; u32 end = le32_to_cpu(hdr->used); @@ -353,12 +360,12 @@ static int ntfs_read_hdr(struct ntfs_sb_info *sbi, struct ntfs_inode *ni, for (;; off += e_size) { if (off + sizeof(struct NTFS_DE) > end) - return -1; + return -EINVAL; e = Add2Ptr(hdr, off); e_size = le16_to_cpu(e->size); if (e_size < sizeof(struct NTFS_DE) || off + e_size > end) - return -1; + return -EINVAL; if (de_is_last(e)) return 0; @@ -368,14 +375,15 @@ static int ntfs_read_hdr(struct ntfs_sb_info *sbi, struct ntfs_inode *ni, continue; if (le16_to_cpu(e->key_size) < SIZEOF_ATTRIBUTE_FILENAME) - return -1; + return -EINVAL; ctx->pos = vbo + off; /* Submit the name to the filldir callback. */ - err = ntfs_filldir(sbi, ni, e, name, ctx); - if (err) - return err; + if (!ntfs_dir_emit(sbi, ni, e, name, ctx)) { + /* ctx is full. */ + return +1; + } } } @@ -474,8 +482,6 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx) vbo = (u64)bit << index_bits; if (vbo >= i_size) { - ntfs_inode_err(dir, "Looks like your dir is corrupt"); - ctx->pos = eod; err = -EINVAL; goto out; } @@ -498,9 +504,16 @@ out: __putname(name); put_indx_node(node); - if (err == -ENOENT) { + if (err == 1) { + /* 'ctx' is full. */ + err = 0; + } else if (err == -ENOENT) { err = 0; ctx->pos = pos; + } else if (err < 0) { + if (err == -EINVAL) + ntfs_inode_err(dir, "directory corrupted"); + ctx->pos = eod; } return err; @@ -618,10 +631,12 @@ const struct file_operations ntfs_dir_operations = { #endif }; +#if IS_ENABLED(CONFIG_NTFS_FS) const struct file_operations ntfs_legacy_dir_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, .iterate_shared = ntfs_readdir, .open = ntfs_file_open, }; +#endif // clang-format on diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c index 2f903b6ce157..ca1ddc46bd86 100644 --- a/fs/ntfs3/file.c +++ b/fs/ntfs3/file.c @@ -13,6 +13,7 @@ #include <linux/compat.h> #include <linux/falloc.h> #include <linux/fiemap.h> +#include <linux/fileattr.h> #include "debug.h" #include "ntfs.h" @@ -48,6 +49,65 @@ static int ntfs_ioctl_fitrim(struct ntfs_sb_info *sbi, unsigned long arg) return 0; } +/* + * ntfs_fileattr_get - inode_operations::fileattr_get + */ +int ntfs_fileattr_get(struct dentry *dentry, struct fileattr *fa) +{ + struct inode *inode = d_inode(dentry); + struct ntfs_inode *ni = ntfs_i(inode); + u32 flags = 0; + + if (inode->i_flags & S_IMMUTABLE) + flags |= FS_IMMUTABLE_FL; + + if (inode->i_flags & S_APPEND) + flags |= FS_APPEND_FL; + + if (is_compressed(ni)) + flags |= FS_COMPR_FL; + + if (is_encrypted(ni)) + flags |= FS_ENCRYPT_FL; + + fileattr_fill_flags(fa, flags); + + return 0; +} + +/* + * ntfs_fileattr_set - inode_operations::fileattr_set + */ +int ntfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry, + struct fileattr *fa) +{ + struct inode *inode = d_inode(dentry); + u32 flags = fa->flags; + unsigned int new_fl = 0; + + if (fileattr_has_fsx(fa)) + return -EOPNOTSUPP; + + if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL)) + return -EOPNOTSUPP; + + if (flags & FS_IMMUTABLE_FL) + new_fl |= S_IMMUTABLE; + + if (flags & FS_APPEND_FL) + new_fl |= S_APPEND; + + inode_set_flags(inode, new_fl, S_IMMUTABLE | S_APPEND); + + inode_set_ctime_current(inode); + mark_inode_dirty(inode); + + return 0; +} + +/* + * ntfs_ioctl - file_operations::unlocked_ioctl + */ long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg) { struct inode *inode = file_inode(filp); @@ -77,20 +137,27 @@ int ntfs_getattr(struct mnt_idmap *idmap, const struct path *path, struct inode *inode = d_inode(path->dentry); struct ntfs_inode *ni = ntfs_i(inode); + stat->result_mask |= STATX_BTIME; + stat->btime = ni->i_crtime; + stat->blksize = ni->mi.sbi->cluster_size; /* 512, 1K, ..., 2M */ + + if (inode->i_flags & S_IMMUTABLE) + stat->attributes |= STATX_ATTR_IMMUTABLE; + + if (inode->i_flags & S_APPEND) + stat->attributes |= STATX_ATTR_APPEND; + if (is_compressed(ni)) stat->attributes |= STATX_ATTR_COMPRESSED; if (is_encrypted(ni)) stat->attributes |= STATX_ATTR_ENCRYPTED; - stat->attributes_mask |= STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED; + stat->attributes_mask |= STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED | + STATX_ATTR_IMMUTABLE | STATX_ATTR_APPEND; generic_fillattr(idmap, request_mask, inode, stat); - stat->result_mask |= STATX_BTIME; - stat->btime = ni->i_crtime; - stat->blksize = ni->mi.sbi->cluster_size; /* 512, 1K, ..., 2M */ - return 0; } @@ -196,9 +263,9 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to) PAGE_SIZE; iblock = page_off >> inode->i_blkbits; - folio = __filemap_get_folio(mapping, idx, - FGP_LOCK | FGP_ACCESSED | FGP_CREAT, - mapping_gfp_constraint(mapping, ~__GFP_FS)); + folio = __filemap_get_folio( + mapping, idx, FGP_LOCK | FGP_ACCESSED | FGP_CREAT, + mapping_gfp_constraint(mapping, ~__GFP_FS)); if (IS_ERR(folio)) return PTR_ERR(folio); @@ -253,8 +320,7 @@ out: */ static int ntfs_file_mmap(struct file *file, struct vm_area_struct *vma) { - struct address_space *mapping = file->f_mapping; - struct inode *inode = mapping->host; + struct inode *inode = file_inode(file); struct ntfs_inode *ni = ntfs_i(inode); u64 from = ((u64)vma->vm_pgoff << PAGE_SHIFT); bool rw = vma->vm_flags & VM_WRITE; @@ -299,10 +365,7 @@ static int ntfs_file_mmap(struct file *file, struct vm_area_struct *vma) } if (ni->i_valid < to) { - if (!inode_trylock(inode)) { - err = -EAGAIN; - goto out; - } + inode_lock(inode); err = ntfs_extend_initialized_size(file, ni, ni->i_valid, to); inode_unlock(inode); @@ -431,7 +494,7 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size) */ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) { - struct inode *inode = file->f_mapping->host; + struct inode *inode = file_inode(file); struct address_space *mapping = inode->i_mapping; struct super_block *sb = inode->i_sb; struct ntfs_sb_info *sbi = sb->s_fs_info; @@ -744,7 +807,7 @@ out: static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) { struct file *file = iocb->ki_filp; - struct inode *inode = file->f_mapping->host; + struct inode *inode = file_inode(file); struct ntfs_inode *ni = ntfs_i(inode); if (unlikely(ntfs3_forced_shutdown(inode->i_sb))) @@ -781,7 +844,7 @@ static ssize_t ntfs_file_splice_read(struct file *in, loff_t *ppos, struct pipe_inode_info *pipe, size_t len, unsigned int flags) { - struct inode *inode = in->f_mapping->host; + struct inode *inode = file_inode(in); struct ntfs_inode *ni = ntfs_i(inode); if (unlikely(ntfs3_forced_shutdown(inode->i_sb))) @@ -824,23 +887,25 @@ static int ntfs_get_frame_pages(struct address_space *mapping, pgoff_t index, *frame_uptodate = true; for (npages = 0; npages < pages_per_frame; npages++, index++) { - struct page *page; + struct folio *folio; - page = find_or_create_page(mapping, index, gfp_mask); - if (!page) { + folio = __filemap_get_folio(mapping, index, + FGP_LOCK | FGP_ACCESSED | FGP_CREAT, + gfp_mask); + if (IS_ERR(folio)) { while (npages--) { - page = pages[npages]; - unlock_page(page); - put_page(page); + folio = page_folio(pages[npages]); + folio_unlock(folio); + folio_put(folio); } return -ENOMEM; } - if (!PageUptodate(page)) + if (!folio_test_uptodate(folio)) *frame_uptodate = false; - pages[npages] = page; + pages[npages] = &folio->page; } return 0; @@ -1075,8 +1140,7 @@ out: static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; - struct address_space *mapping = file->f_mapping; - struct inode *inode = mapping->host; + struct inode *inode = file_inode(file); ssize_t ret; int err; struct ntfs_inode *ni = ntfs_i(inode); @@ -1198,7 +1262,7 @@ static int ntfs_file_release(struct inode *inode, struct file *file) } /* - * ntfs_fiemap - file_operations::fiemap + * ntfs_fiemap - inode_operations::fiemap */ int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, __u64 start, __u64 len) @@ -1227,6 +1291,8 @@ const struct inode_operations ntfs_file_inode_operations = { .get_acl = ntfs_get_acl, .set_acl = ntfs_set_acl, .fiemap = ntfs_fiemap, + .fileattr_get = ntfs_fileattr_get, + .fileattr_set = ntfs_fileattr_set, }; const struct file_operations ntfs_file_operations = { @@ -1246,6 +1312,7 @@ const struct file_operations ntfs_file_operations = { .release = ntfs_file_release, }; +#if IS_ENABLED(CONFIG_NTFS_FS) const struct file_operations ntfs_legacy_file_operations = { .llseek = generic_file_llseek, .read_iter = ntfs_file_read_iter, @@ -1253,4 +1320,5 @@ const struct file_operations ntfs_legacy_file_operations = { .open = ntfs_file_open, .release = ntfs_file_release, }; +#endif // clang-format on diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 0008670939a4..a469c608a394 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -122,10 +122,10 @@ void ni_clear(struct ntfs_inode *ni) else { run_close(&ni->file.run); #ifdef CONFIG_NTFS3_LZX_XPRESS - if (ni->file.offs_page) { + if (ni->file.offs_folio) { /* On-demand allocated page for offsets. */ - put_page(ni->file.offs_page); - ni->file.offs_page = NULL; + folio_put(ni->file.offs_folio); + ni->file.offs_folio = NULL; } #endif } @@ -1501,7 +1501,7 @@ int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type, if (is_ext) { if (flags & ATTR_FLAG_COMPRESSED) - attr->nres.c_unit = COMPRESSION_UNIT; + attr->nres.c_unit = NTFS_LZNT_CUNIT; attr->nres.total_size = attr->nres.alloc_size; } @@ -1601,8 +1601,10 @@ int ni_delete_all(struct ntfs_inode *ni) asize = le32_to_cpu(attr->size); roff = le16_to_cpu(attr->nres.run_off); - if (roff > asize) + if (roff > asize) { + _ntfs_bad_inode(&ni->vfs_inode); return -EINVAL; + } /* run==1 means unpack and deallocate. */ run_unpack_ex(RUN_DEALLOCATE, sbi, ni->mi.rno, svcn, evcn, svcn, @@ -1897,6 +1899,47 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, } /* + * fiemap_fill_next_extent_k - a copy of fiemap_fill_next_extent + * but it accepts kernel address for fi_extents_start + */ +static int fiemap_fill_next_extent_k(struct fiemap_extent_info *fieinfo, + u64 logical, u64 phys, u64 len, u32 flags) +{ + struct fiemap_extent extent; + struct fiemap_extent __user *dest = fieinfo->fi_extents_start; + + /* only count the extents */ + if (fieinfo->fi_extents_max == 0) { + fieinfo->fi_extents_mapped++; + return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0; + } + + if (fieinfo->fi_extents_mapped >= fieinfo->fi_extents_max) + return 1; + + if (flags & FIEMAP_EXTENT_DELALLOC) + flags |= FIEMAP_EXTENT_UNKNOWN; + if (flags & FIEMAP_EXTENT_DATA_ENCRYPTED) + flags |= FIEMAP_EXTENT_ENCODED; + if (flags & (FIEMAP_EXTENT_DATA_TAIL | FIEMAP_EXTENT_DATA_INLINE)) + flags |= FIEMAP_EXTENT_NOT_ALIGNED; + + memset(&extent, 0, sizeof(extent)); + extent.fe_logical = logical; + extent.fe_physical = phys; + extent.fe_length = len; + extent.fe_flags = flags; + + dest += fieinfo->fi_extents_mapped; + memcpy(dest, &extent, sizeof(extent)); + + fieinfo->fi_extents_mapped++; + if (fieinfo->fi_extents_mapped == fieinfo->fi_extents_max) + return 1; + return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0; +} + +/* * ni_fiemap - Helper for file_fiemap(). * * Assumed ni_lock. @@ -1906,6 +1949,8 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, __u64 vbo, __u64 len) { int err = 0; + struct fiemap_extent __user *fe_u = fieinfo->fi_extents_start; + struct fiemap_extent *fe_k = NULL; struct ntfs_sb_info *sbi = ni->mi.sbi; u8 cluster_bits = sbi->cluster_bits; struct runs_tree *run; @@ -1953,6 +1998,18 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, goto out; } + /* + * To avoid lock problems replace pointer to user memory by pointer to kernel memory. + */ + fe_k = kmalloc_array(fieinfo->fi_extents_max, + sizeof(struct fiemap_extent), + GFP_NOFS | __GFP_ZERO); + if (!fe_k) { + err = -ENOMEM; + goto out; + } + fieinfo->fi_extents_start = fe_k; + end = vbo + len; alloc_size = le64_to_cpu(attr->nres.alloc_size); if (end > alloc_size) @@ -2041,8 +2098,9 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, if (vbo + dlen >= end) flags |= FIEMAP_EXTENT_LAST; - err = fiemap_fill_next_extent(fieinfo, vbo, lbo, dlen, - flags); + err = fiemap_fill_next_extent_k(fieinfo, vbo, lbo, dlen, + flags); + if (err < 0) break; if (err == 1) { @@ -2062,7 +2120,8 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, if (vbo + bytes >= end) flags |= FIEMAP_EXTENT_LAST; - err = fiemap_fill_next_extent(fieinfo, vbo, lbo, bytes, flags); + err = fiemap_fill_next_extent_k(fieinfo, vbo, lbo, bytes, + flags); if (err < 0) break; if (err == 1) { @@ -2075,7 +2134,19 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, up_read(run_lock); + /* + * Copy to user memory out of lock + */ + if (copy_to_user(fe_u, fe_k, + fieinfo->fi_extents_max * + sizeof(struct fiemap_extent))) { + err = -EFAULT; + } + out: + /* Restore original pointer. */ + fieinfo->fi_extents_start = fe_u; + kfree(fe_k); return err; } @@ -2085,12 +2156,12 @@ out: * When decompressing, we typically obtain more than one page per reference. * We inject the additional pages into the page cache. */ -int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page) +int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio) { int err; struct ntfs_sb_info *sbi = ni->mi.sbi; - struct address_space *mapping = page->mapping; - pgoff_t index = page->index; + struct address_space *mapping = folio->mapping; + pgoff_t index = folio->index; u64 frame_vbo, vbo = (u64)index << PAGE_SHIFT; struct page **pages = NULL; /* Array of at most 16 pages. stack? */ u8 frame_bits; @@ -2100,7 +2171,8 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page) struct page *pg; if (vbo >= i_size_read(&ni->vfs_inode)) { - SetPageUptodate(page); + folio_zero_range(folio, 0, folio_size(folio)); + folio_mark_uptodate(folio); err = 0; goto out; } @@ -2124,7 +2196,7 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page) goto out; } - pages[idx] = page; + pages[idx] = &folio->page; index = frame_vbo >> PAGE_SHIFT; gfp_mask = mapping_gfp_mask(mapping); @@ -2143,9 +2215,6 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page) err = ni_read_frame(ni, frame_vbo, pages, pages_per_frame); out1: - if (err) - SetPageError(page); - for (i = 0; i < pages_per_frame; i++) { pg = pages[i]; if (i == idx || !pg) @@ -2157,7 +2226,7 @@ out1: out: /* At this point, err contains 0 or -EIO depending on the "critical" page. */ kfree(pages); - unlock_page(page); + folio_unlock(folio); return err; } @@ -2362,9 +2431,9 @@ remove_wof: /* Clear cached flag. */ ni->ni_flags &= ~NI_FLAG_COMPRESSED_MASK; - if (ni->file.offs_page) { - put_page(ni->file.offs_page); - ni->file.offs_page = NULL; + if (ni->file.offs_folio) { + folio_put(ni->file.offs_folio); + ni->file.offs_folio = NULL; } mapping->a_ops = &ntfs_aops; @@ -2718,7 +2787,6 @@ out: for (i = 0; i < pages_per_frame; i++) { pg = pages[i]; kunmap(pg); - ClearPageError(pg); SetPageUptodate(pg); } diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index d7807d255dfe..c64dd114ac65 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -724,7 +724,8 @@ static bool check_rstbl(const struct RESTART_TABLE *rt, size_t bytes) if (!rsize || rsize > bytes || rsize + sizeof(struct RESTART_TABLE) > bytes || bytes < ts || - le16_to_cpu(rt->total) > ne || ff > ts || lf > ts || + le16_to_cpu(rt->total) > ne || ff > ts - sizeof(__le32) || + lf > ts - sizeof(__le32) || (ff && ff < sizeof(struct RESTART_TABLE)) || (lf && lf < sizeof(struct RESTART_TABLE))) { return false; @@ -754,6 +755,9 @@ static bool check_rstbl(const struct RESTART_TABLE *rt, size_t bytes) return false; off = le32_to_cpu(*(__le32 *)Add2Ptr(rt, off)); + + if (off > ts - sizeof(__le32)) + return false; } return true; @@ -2992,7 +2996,7 @@ static struct ATTRIB *attr_create_nonres_log(struct ntfs_sb_info *sbi, if (is_ext) { attr->name_off = SIZEOF_NONRESIDENT_EX_LE; if (is_attr_compressed(attr)) - attr->nres.c_unit = COMPRESSION_UNIT; + attr->nres.c_unit = NTFS_LZNT_CUNIT; attr->nres.run_off = cpu_to_le16(SIZEOF_NONRESIDENT_EX + name_size); @@ -3722,6 +3726,8 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) u64 rec_lsn, checkpt_lsn = 0, rlsn = 0; struct ATTR_NAME_ENTRY *attr_names = NULL; + u32 attr_names_bytes = 0; + u32 oatbl_bytes = 0; struct RESTART_TABLE *dptbl = NULL; struct RESTART_TABLE *trtbl = NULL; const struct RESTART_TABLE *rt; @@ -3736,6 +3742,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) struct NTFS_RESTART *rst = NULL; struct lcb *lcb = NULL; struct OPEN_ATTR_ENRTY *oe; + struct ATTR_NAME_ENTRY *ane; struct TRANSACTION_ENTRY *tr; struct DIR_PAGE_ENTRY *dp; u32 i, bytes_per_attr_entry; @@ -3915,6 +3922,9 @@ check_restart_area: goto out; } + log->page_mask = log->page_size - 1; + log->page_bits = blksize_bits(log->page_size); + /* If the file size has shrunk then we won't mount it. */ if (log->l_size < le64_to_cpu(ra2->l_size)) { err = -EINVAL; @@ -4104,7 +4114,7 @@ process_log: /* Allocate and Read the Transaction Table. */ if (!rst->transact_table_len) - goto check_dirty_page_table; + goto check_dirty_page_table; /* reduce tab pressure. */ t64 = le64_to_cpu(rst->transact_table_lsn); err = read_log_rec_lcb(log, t64, lcb_ctx_prev, &lcb); @@ -4144,7 +4154,7 @@ process_log: check_dirty_page_table: /* The next record back should be the Dirty Pages Table. */ if (!rst->dirty_pages_len) - goto check_attribute_names; + goto check_attribute_names; /* reduce tab pressure. */ t64 = le64_to_cpu(rst->dirty_pages_table_lsn); err = read_log_rec_lcb(log, t64, lcb_ctx_prev, &lcb); @@ -4180,7 +4190,7 @@ check_dirty_page_table: /* Convert Ra version '0' into version '1'. */ if (rst->major_ver) - goto end_conv_1; + goto end_conv_1; /* reduce tab pressure. */ dp = NULL; while ((dp = enum_rstbl(dptbl, dp))) { @@ -4200,8 +4210,7 @@ end_conv_1: * remembering the oldest lsn values. */ if (sbi->cluster_size <= log->page_size) - goto trace_dp_table; - + goto trace_dp_table; /* reduce tab pressure. */ dp = NULL; while ((dp = enum_rstbl(dptbl, dp))) { struct DIR_PAGE_ENTRY *next = dp; @@ -4222,7 +4231,7 @@ trace_dp_table: check_attribute_names: /* The next record should be the Attribute Names. */ if (!rst->attr_names_len) - goto check_attr_table; + goto check_attr_table; /* reduce tab pressure. */ t64 = le64_to_cpu(rst->attr_names_lsn); err = read_log_rec_lcb(log, t64, lcb_ctx_prev, &lcb); @@ -4240,9 +4249,9 @@ check_attribute_names: } t32 = lrh_length(lrh); - rec_len -= t32; + attr_names_bytes = rec_len - t32; - attr_names = kmemdup(Add2Ptr(lrh, t32), rec_len, GFP_NOFS); + attr_names = kmemdup(Add2Ptr(lrh, t32), attr_names_bytes, GFP_NOFS); if (!attr_names) { err = -ENOMEM; goto out; @@ -4254,7 +4263,7 @@ check_attribute_names: check_attr_table: /* The next record should be the attribute Table. */ if (!rst->open_attr_len) - goto check_attribute_names2; + goto check_attribute_names2; /* reduce tab pressure. */ t64 = le64_to_cpu(rst->open_attr_table_lsn); err = read_log_rec_lcb(log, t64, lcb_ctx_prev, &lcb); @@ -4274,14 +4283,14 @@ check_attr_table: t16 = le16_to_cpu(lrh->redo_off); rt = Add2Ptr(lrh, t16); - t32 = rec_len - t16; + oatbl_bytes = rec_len - t16; - if (!check_rstbl(rt, t32)) { + if (!check_rstbl(rt, oatbl_bytes)) { err = -EINVAL; goto out; } - oatbl = kmemdup(rt, t32, GFP_NOFS); + oatbl = kmemdup(rt, oatbl_bytes, GFP_NOFS); if (!oatbl) { err = -ENOMEM; goto out; @@ -4314,17 +4323,40 @@ check_attr_table: lcb = NULL; check_attribute_names2: - if (rst->attr_names_len && oatbl) { - struct ATTR_NAME_ENTRY *ane = attr_names; - while (ane->off) { + if (attr_names && oatbl) { + off = 0; + for (;;) { + /* Check we can use attribute name entry 'ane'. */ + static_assert(sizeof(*ane) == 4); + if (off + sizeof(*ane) > attr_names_bytes) { + /* just ignore the rest. */ + break; + } + + ane = Add2Ptr(attr_names, off); + t16 = le16_to_cpu(ane->off); + if (!t16) { + /* this is the only valid exit. */ + break; + } + + /* Check we can use open attribute entry 'oe'. */ + if (t16 + sizeof(*oe) > oatbl_bytes) { + /* just ignore the rest. */ + break; + } + /* TODO: Clear table on exit! */ - oe = Add2Ptr(oatbl, le16_to_cpu(ane->off)); + oe = Add2Ptr(oatbl, t16); t16 = le16_to_cpu(ane->name_bytes); + off += t16 + sizeof(*ane); + if (off > attr_names_bytes) { + /* just ignore the rest. */ + break; + } oe->name_len = t16 / sizeof(short); oe->ptr = ane->name; oe->is_attr_name = 2; - ane = Add2Ptr(ane, - sizeof(struct ATTR_NAME_ENTRY) + t16); } } @@ -4520,7 +4552,6 @@ copy_lcns: } } goto next_log_record_analyze; - ; } case OpenNonresidentAttribute: @@ -4659,7 +4690,7 @@ end_log_records_enumerate: * table are not empty. */ if ((!dptbl || !dptbl->total) && (!trtbl || !trtbl->total)) - goto end_reply; + goto end_replay; sbi->flags |= NTFS_FLAGS_NEED_REPLAY; if (is_ro) @@ -5088,7 +5119,7 @@ undo_action_done: sbi->flags &= ~NTFS_FLAGS_NEED_REPLAY; -end_reply: +end_replay: err = 0; if (is_ro) diff --git a/fs/ntfs3/fsntfs.c b/fs/ntfs3/fsntfs.c index 626d3f2c7e2d..0fa636038b4e 100644 --- a/fs/ntfs3/fsntfs.c +++ b/fs/ntfs3/fsntfs.c @@ -2650,8 +2650,8 @@ int ntfs_set_label(struct ntfs_sb_info *sbi, u8 *label, int len) { int err; struct ATTRIB *attr; + u32 uni_bytes; struct ntfs_inode *ni = sbi->volume.ni; - const u8 max_ulen = 0x80; /* TODO: use attrdef to get maximum length */ /* Allocate PATH_MAX bytes. */ struct cpu_str *uni = __getname(); @@ -2663,7 +2663,8 @@ int ntfs_set_label(struct ntfs_sb_info *sbi, u8 *label, int len) if (err < 0) goto out; - if (uni->len > max_ulen) { + uni_bytes = uni->len * sizeof(u16); + if (uni_bytes > NTFS_LABEL_MAX_LENGTH * sizeof(u16)) { ntfs_warn(sbi->sb, "new label is too long"); err = -EFBIG; goto out; @@ -2674,13 +2675,13 @@ int ntfs_set_label(struct ntfs_sb_info *sbi, u8 *label, int len) /* Ignore any errors. */ ni_remove_attr(ni, ATTR_LABEL, NULL, 0, false, NULL); - err = ni_insert_resident(ni, uni->len * sizeof(u16), ATTR_LABEL, NULL, - 0, &attr, NULL, NULL); + err = ni_insert_resident(ni, uni_bytes, ATTR_LABEL, NULL, 0, &attr, + NULL, NULL); if (err < 0) goto unlock_out; /* write new label in on-disk struct. */ - memcpy(resident_data(attr), uni->name, uni->len * sizeof(u16)); + memcpy(resident_data(attr), uni->name, uni_bytes); /* update cached value of current label. */ if (len >= ARRAY_SIZE(sbi->volume.label)) diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index d0f15bbf78f6..9089c58a005c 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -978,7 +978,7 @@ static struct indx_node *indx_new(struct ntfs_index *indx, hdr->used = cpu_to_le32(eo + sizeof(struct NTFS_DE) + sizeof(u64)); de_set_vbn_le(e, *sub_vbn); - hdr->flags = 1; + hdr->flags = NTFS_INDEX_HDR_HAS_SUBNODES; } else { e->size = cpu_to_le16(sizeof(struct NTFS_DE)); hdr->used = cpu_to_le32(eo + sizeof(struct NTFS_DE)); @@ -1683,7 +1683,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, e->size = cpu_to_le16(sizeof(struct NTFS_DE) + sizeof(u64)); e->flags = NTFS_IE_HAS_SUBNODES | NTFS_IE_LAST; - hdr->flags = 1; + hdr->flags = NTFS_INDEX_HDR_HAS_SUBNODES; hdr->used = hdr->total = cpu_to_le32(new_root_size - offsetof(struct INDEX_ROOT, ihdr)); diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 0f1664db94ad..6b0bdc474e76 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -18,7 +18,7 @@ #include "ntfs_fs.h" /* - * ntfs_read_mft - Read record and parses MFT. + * ntfs_read_mft - Read record and parse MFT. */ static struct inode *ntfs_read_mft(struct inode *inode, const struct cpu_str *name, @@ -441,10 +441,9 @@ end_enum: * Usually a hard links to directories are disabled. */ inode->i_op = &ntfs_dir_inode_operations; - if (is_legacy_ntfs(inode->i_sb)) - inode->i_fop = &ntfs_legacy_dir_operations; - else - inode->i_fop = &ntfs_dir_operations; + inode->i_fop = unlikely(is_legacy_ntfs(sb)) ? + &ntfs_legacy_dir_operations : + &ntfs_dir_operations; ni->i_valid = 0; } else if (S_ISLNK(mode)) { ni->std_fa &= ~FILE_ATTRIBUTE_DIRECTORY; @@ -454,10 +453,9 @@ end_enum: } else if (S_ISREG(mode)) { ni->std_fa &= ~FILE_ATTRIBUTE_DIRECTORY; inode->i_op = &ntfs_file_inode_operations; - if (is_legacy_ntfs(inode->i_sb)) - inode->i_fop = &ntfs_legacy_file_operations; - else - inode->i_fop = &ntfs_file_operations; + inode->i_fop = unlikely(is_legacy_ntfs(sb)) ? + &ntfs_legacy_file_operations : + &ntfs_file_operations; inode->i_mapping->a_ops = is_compressed(ni) ? &ntfs_aops_cmpr : &ntfs_aops; if (ino != MFT_REC_MFT) @@ -580,10 +578,11 @@ static noinline int ntfs_get_block_vbo(struct inode *inode, u64 vbo, bh->b_blocknr = RESIDENT_LCN; bh->b_size = block_size; if (!folio) { + /* direct io (read) or bmap call */ err = 0; } else { ni_lock(ni); - err = attr_data_read_resident(ni, &folio->page); + err = attr_data_read_resident(ni, folio); ni_unlock(ni); if (!err) @@ -710,25 +709,24 @@ static sector_t ntfs_bmap(struct address_space *mapping, sector_t block) static int ntfs_read_folio(struct file *file, struct folio *folio) { - struct page *page = &folio->page; int err; - struct address_space *mapping = page->mapping; + struct address_space *mapping = folio->mapping; struct inode *inode = mapping->host; struct ntfs_inode *ni = ntfs_i(inode); if (is_resident(ni)) { ni_lock(ni); - err = attr_data_read_resident(ni, page); + err = attr_data_read_resident(ni, folio); ni_unlock(ni); if (err != E_NTFS_NONRESIDENT) { - unlock_page(page); + folio_unlock(folio); return err; } } if (is_compressed(ni)) { ni_lock(ni); - err = ni_readpage_cmpr(ni, page); + err = ni_readpage_cmpr(ni, folio); ni_unlock(ni); return err; } @@ -872,7 +870,7 @@ static int ntfs_resident_writepage(struct folio *folio, return -EIO; ni_lock(ni); - ret = attr_data_write_resident(ni, &folio->page); + ret = attr_data_write_resident(ni, folio); ni_unlock(ni); if (ret != E_NTFS_NONRESIDENT) @@ -914,24 +912,25 @@ int ntfs_write_begin(struct file *file, struct address_space *mapping, *pagep = NULL; if (is_resident(ni)) { - struct page *page = - grab_cache_page_write_begin(mapping, pos >> PAGE_SHIFT); + struct folio *folio = __filemap_get_folio( + mapping, pos >> PAGE_SHIFT, FGP_WRITEBEGIN, + mapping_gfp_mask(mapping)); - if (!page) { - err = -ENOMEM; + if (IS_ERR(folio)) { + err = PTR_ERR(folio); goto out; } ni_lock(ni); - err = attr_data_read_resident(ni, page); + err = attr_data_read_resident(ni, folio); ni_unlock(ni); if (!err) { - *pagep = page; + *pagep = &folio->page; goto out; } - unlock_page(page); - put_page(page); + folio_unlock(folio); + folio_put(folio); if (err != E_NTFS_NONRESIDENT) goto out; @@ -950,6 +949,7 @@ out: int ntfs_write_end(struct file *file, struct address_space *mapping, loff_t pos, u32 len, u32 copied, struct page *page, void *fsdata) { + struct folio *folio = page_folio(page); struct inode *inode = mapping->host; struct ntfs_inode *ni = ntfs_i(inode); u64 valid = ni->i_valid; @@ -958,26 +958,26 @@ int ntfs_write_end(struct file *file, struct address_space *mapping, loff_t pos, if (is_resident(ni)) { ni_lock(ni); - err = attr_data_write_resident(ni, page); + err = attr_data_write_resident(ni, folio); ni_unlock(ni); if (!err) { + struct buffer_head *head = folio_buffers(folio); dirty = true; - /* Clear any buffers in page. */ - if (page_has_buffers(page)) { - struct buffer_head *head, *bh; + /* Clear any buffers in folio. */ + if (head) { + struct buffer_head *bh = head; - bh = head = page_buffers(page); do { clear_buffer_dirty(bh); clear_buffer_mapped(bh); set_buffer_uptodate(bh); } while (head != (bh = bh->b_this_page)); } - SetPageUptodate(page); + folio_mark_uptodate(folio); err = copied; } - unlock_page(page); - put_page(page); + folio_unlock(folio); + folio_put(folio); } else { err = generic_write_end(file, mapping, pos, len, copied, page, fsdata); @@ -1093,33 +1093,31 @@ int ntfs_flush_inodes(struct super_block *sb, struct inode *i1, if (!ret && i2) ret = writeback_inode(i2); if (!ret) - ret = sync_blockdev_nowait(sb->s_bdev); + ret = filemap_flush(sb->s_bdev_file->f_mapping); return ret; } -int inode_write_data(struct inode *inode, const void *data, size_t bytes) +/* + * Helper function to read file. + */ +int inode_read_data(struct inode *inode, void *data, size_t bytes) { pgoff_t idx; + struct address_space *mapping = inode->i_mapping; - /* Write non resident data. */ for (idx = 0; bytes; idx++) { size_t op = bytes > PAGE_SIZE ? PAGE_SIZE : bytes; - struct page *page = ntfs_map_page(inode->i_mapping, idx); + struct page *page = read_mapping_page(mapping, idx, NULL); + void *kaddr; if (IS_ERR(page)) return PTR_ERR(page); - lock_page(page); - WARN_ON(!PageUptodate(page)); - ClearPageUptodate(page); - - memcpy(page_address(page), data, op); - - flush_dcache_page(page); - SetPageUptodate(page); - unlock_page(page); + kaddr = kmap_atomic(page); + memcpy(data, kaddr, op); + kunmap_atomic(kaddr); - ntfs_unmap_page(page); + put_page(page); bytes -= op; data = Add2Ptr(data, PAGE_SIZE); @@ -1508,7 +1506,7 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir, attr->size = cpu_to_le32(SIZEOF_NONRESIDENT_EX + 8); attr->name_off = SIZEOF_NONRESIDENT_EX_LE; attr->flags = ATTR_FLAG_COMPRESSED; - attr->nres.c_unit = COMPRESSION_UNIT; + attr->nres.c_unit = NTFS_LZNT_CUNIT; asize = SIZEOF_NONRESIDENT_EX + 8; } else { attr->size = cpu_to_le32(SIZEOF_NONRESIDENT + 8); @@ -1559,7 +1557,7 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir, /* * Below function 'ntfs_save_wsl_perm' requires 0x78 bytes. - * It is good idea to keep extened attributes resident. + * It is good idea to keep extended attributes resident. */ if (asize + t16 + 0x78 + 8 > sbi->record_size) { CLST alen; @@ -1628,10 +1626,9 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir, if (S_ISDIR(mode)) { inode->i_op = &ntfs_dir_inode_operations; - if (is_legacy_ntfs(inode->i_sb)) - inode->i_fop = &ntfs_legacy_dir_operations; - else - inode->i_fop = &ntfs_dir_operations; + inode->i_fop = unlikely(is_legacy_ntfs(sb)) ? + &ntfs_legacy_dir_operations : + &ntfs_dir_operations; } else if (S_ISLNK(mode)) { inode->i_op = &ntfs_link_inode_operations; inode->i_fop = NULL; @@ -1640,10 +1637,9 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir, inode_nohighmem(inode); } else if (S_ISREG(mode)) { inode->i_op = &ntfs_file_inode_operations; - if (is_legacy_ntfs(inode->i_sb)) - inode->i_fop = &ntfs_legacy_file_operations; - else - inode->i_fop = &ntfs_file_operations; + inode->i_fop = unlikely(is_legacy_ntfs(sb)) ? + &ntfs_legacy_file_operations : + &ntfs_file_operations; inode->i_mapping->a_ops = is_compressed(ni) ? &ntfs_aops_cmpr : &ntfs_aops; init_rwsem(&ni->file.run_lock); @@ -1668,7 +1664,9 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir, * The packed size of extended attribute is stored in direntry too. * 'fname' here points to inside new_de. */ - ntfs_save_wsl_perm(inode, &fname->dup.ea_size); + err = ntfs_save_wsl_perm(inode, &fname->dup.ea_size); + if (err) + goto out6; /* * update ea_size in file_name attribute too. @@ -1712,6 +1710,12 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir, goto out2; out6: + attr = ni_find_attr(ni, NULL, NULL, ATTR_EA, NULL, 0, NULL, NULL); + if (attr && attr->non_res) { + /* Delete ATTR_EA, if non-resident. */ + attr_set_size(ni, ATTR_EA, NULL, 0, NULL, 0, NULL, false, NULL); + } + if (rp_inserted) ntfs_remove_reparse(sbi, IO_REPARSE_TAG_SYMLINK, &new_de->ref); @@ -2133,5 +2137,6 @@ const struct address_space_operations ntfs_aops = { const struct address_space_operations ntfs_aops_cmpr = { .read_folio = ntfs_read_folio, .readahead = ntfs_readahead, + .dirty_folio = block_dirty_folio, }; // clang-format on diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c index 71498421ce60..f16d318c4372 100644 --- a/fs/ntfs3/namei.c +++ b/fs/ntfs3/namei.c @@ -112,9 +112,7 @@ static int ntfs_create(struct mnt_idmap *idmap, struct inode *dir, } /* - * ntfs_mknod - * - * inode_operations::mknod + * ntfs_mknod - inode_operations::mknod */ static int ntfs_mknod(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev) @@ -509,6 +507,8 @@ const struct inode_operations ntfs_dir_inode_operations = { .getattr = ntfs_getattr, .listxattr = ntfs_listxattr, .fiemap = ntfs_fiemap, + .fileattr_get = ntfs_fileattr_get, + .fileattr_set = ntfs_fileattr_set, }; const struct inode_operations ntfs_special_inode_operations = { diff --git a/fs/ntfs3/ntfs.h b/fs/ntfs3/ntfs.h index 3d6143c7abc0..241f2ffdd920 100644 --- a/fs/ntfs3/ntfs.h +++ b/fs/ntfs3/ntfs.h @@ -82,9 +82,6 @@ typedef u32 CLST; #define RESIDENT_LCN ((CLST)-2) #define COMPRESSED_LCN ((CLST)-3) -#define COMPRESSION_UNIT 4 -#define COMPRESS_MAX_CLUSTER 0x1000 - enum RECORD_NUM { MFT_REC_MFT = 0, MFT_REC_MIRR = 1, @@ -696,14 +693,15 @@ static inline bool de_has_vcn_ex(const struct NTFS_DE *e) offsetof(struct ATTR_FILE_NAME, name) + \ NTFS_NAME_LEN * sizeof(short), 8) +#define NTFS_INDEX_HDR_HAS_SUBNODES cpu_to_le32(1) + struct INDEX_HDR { __le32 de_off; // 0x00: The offset from the start of this structure // to the first NTFS_DE. __le32 used; // 0x04: The size of this structure plus all // entries (quad-word aligned). __le32 total; // 0x08: The allocated size of for this structure plus all entries. - u8 flags; // 0x0C: 0x00 = Small directory, 0x01 = Large directory. - u8 res[3]; + __le32 flags; // 0x0C: 0x00 = Small directory, 0x01 = Large directory. // // de_off + used <= total @@ -751,7 +749,7 @@ static inline struct NTFS_DE *hdr_next_de(const struct INDEX_HDR *hdr, static inline bool hdr_has_subnode(const struct INDEX_HDR *hdr) { - return hdr->flags & 1; + return hdr->flags & NTFS_INDEX_HDR_HAS_SUBNODES; } struct INDEX_BUFFER { @@ -771,7 +769,7 @@ static inline bool ib_is_empty(const struct INDEX_BUFFER *ib) static inline bool ib_is_leaf(const struct INDEX_BUFFER *ib) { - return !(ib->ihdr.flags & 1); + return !(ib->ihdr.flags & NTFS_INDEX_HDR_HAS_SUBNODES); } /* Index root structure ( 0x90 ). */ @@ -1002,9 +1000,6 @@ struct REPARSE_POINT { static_assert(sizeof(struct REPARSE_POINT) == 0x18); -/* Maximum allowed size of the reparse data. */ -#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16 * 1024) - /* * The value of the following constant needs to satisfy the following * conditions: diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index f9ed6d2b065d..e5255a251929 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -383,7 +383,7 @@ struct ntfs_inode { struct rw_semaphore run_lock; struct runs_tree run; #ifdef CONFIG_NTFS3_LZX_XPRESS - struct page *offs_page; + struct folio *offs_folio; #endif } file; }; @@ -434,8 +434,8 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, struct ATTRIB **ret); int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, CLST *len, bool *new, bool zero); -int attr_data_read_resident(struct ntfs_inode *ni, struct page *page); -int attr_data_write_resident(struct ntfs_inode *ni, struct page *page); +int attr_data_read_resident(struct ntfs_inode *ni, struct folio *folio); +int attr_data_write_resident(struct ntfs_inode *ni, struct folio *folio); int attr_load_runs_vcn(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, u8 name_len, struct runs_tree *run, CLST vcn); @@ -497,6 +497,9 @@ extern const struct file_operations ntfs_dir_operations; extern const struct file_operations ntfs_legacy_dir_operations; /* Globals from file.c */ +int ntfs_fileattr_get(struct dentry *dentry, struct fileattr *fa); +int ntfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry, + struct fileattr *fa); int ntfs_getattr(struct mnt_idmap *idmap, const struct path *path, struct kstat *stat, u32 request_mask, u32 flags); int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry, @@ -564,7 +567,7 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint); #define _ni_write_inode(i, w) ni_write_inode(i, w, __func__) int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, __u64 vbo, __u64 len); -int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page); +int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio); int ni_decompress_file(struct ntfs_inode *ni); int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, u32 pages_per_frame); @@ -716,7 +719,7 @@ int ntfs3_write_inode(struct inode *inode, struct writeback_control *wbc); int ntfs_sync_inode(struct inode *inode); int ntfs_flush_inodes(struct super_block *sb, struct inode *i1, struct inode *i2); -int inode_write_data(struct inode *inode, const void *data, size_t bytes); +int inode_read_data(struct inode *inode, void *data, size_t bytes); int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, const struct cpu_str *uni, umode_t mode, dev_t dev, const char *symname, u32 size, @@ -910,22 +913,6 @@ static inline bool ntfs_is_meta_file(struct ntfs_sb_info *sbi, CLST rno) rno == sbi->usn_jrnl_no; } -static inline void ntfs_unmap_page(struct page *page) -{ - kunmap(page); - put_page(page); -} - -static inline struct page *ntfs_map_page(struct address_space *mapping, - unsigned long index) -{ - struct page *page = read_mapping_page(mapping, index, NULL); - - if (!IS_ERR(page)) - kmap(page); - return page; -} - static inline size_t wnd_zone_bit(const struct wnd_bitmap *wnd) { return wnd->zone_bit; @@ -1156,6 +1143,13 @@ static inline void le64_sub_cpu(__le64 *var, u64 val) *var = cpu_to_le64(le64_to_cpu(*var) - val); } +#if IS_ENABLED(CONFIG_NTFS_FS) bool is_legacy_ntfs(struct super_block *sb); +#else +static inline bool is_legacy_ntfs(struct super_block *sb) +{ + return false; +} +#endif #endif /* _LINUX_NTFS3_NTFS_FS_H */ diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c index c5b688c5f984..a8758b85803f 100644 --- a/fs/ntfs3/super.c +++ b/fs/ntfs3/super.c @@ -275,7 +275,7 @@ static const struct fs_parameter_spec ntfs_fs_parameters[] = { fsparam_flag_no("acl", Opt_acl), fsparam_string("iocharset", Opt_iocharset), fsparam_flag_no("prealloc", Opt_prealloc), - fsparam_flag_no("nocase", Opt_nocase), + fsparam_flag_no("case", Opt_nocase), {} }; // clang-format on @@ -464,7 +464,7 @@ static int ntfs3_volinfo(struct seq_file *m, void *o) struct super_block *sb = m->private; struct ntfs_sb_info *sbi = sb->s_fs_info; - seq_printf(m, "ntfs%d.%d\n%u\n%zu\n\%zu\n%zu\n%s\n%s\n", + seq_printf(m, "ntfs%d.%d\n%u\n%zu\n%zu\n%zu\n%s\n%s\n", sbi->volume.major_ver, sbi->volume.minor_ver, sbi->cluster_size, sbi->used.bitmap.nbits, sbi->mft.bitmap.nbits, @@ -1159,7 +1159,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) CLST vcn, lcn, len; struct ATTRIB *attr; const struct VOLUME_INFO *info; - u32 idx, done, bytes; + u32 done, bytes; struct ATTR_DEF_ENTRY *t; u16 *shared; struct MFT_REF ref; @@ -1201,7 +1201,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) /* * Load $Volume. This should be done before $LogFile - * 'cause 'sbi->volume.ni' is used 'ntfs_set_state'. + * 'cause 'sbi->volume.ni' is used in 'ntfs_set_state'. */ ref.low = cpu_to_le32(MFT_REC_VOL); ref.seq = cpu_to_le16(MFT_REC_VOL); @@ -1431,31 +1431,22 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) goto put_inode_out; } - for (done = idx = 0; done < bytes; done += PAGE_SIZE, idx++) { - unsigned long tail = bytes - done; - struct page *page = ntfs_map_page(inode->i_mapping, idx); + /* Read the entire file. */ + err = inode_read_data(inode, sbi->def_table, bytes); + if (err) { + ntfs_err(sb, "Failed to read $AttrDef (%d).", err); + goto put_inode_out; + } - if (IS_ERR(page)) { - err = PTR_ERR(page); - ntfs_err(sb, "Failed to read $AttrDef (%d).", err); - goto put_inode_out; - } - memcpy(Add2Ptr(t, done), page_address(page), - min(PAGE_SIZE, tail)); - ntfs_unmap_page(page); - - if (!idx && ATTR_STD != t->type) { - ntfs_err(sb, "$AttrDef is corrupted."); - err = -EINVAL; - goto put_inode_out; - } + if (ATTR_STD != t->type) { + ntfs_err(sb, "$AttrDef is corrupted."); + err = -EINVAL; + goto put_inode_out; } t += 1; sbi->def_entries = 1; done = sizeof(struct ATTR_DEF_ENTRY); - sbi->reparse.max_size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE; - sbi->ea_max_size = 0x10000; /* default formatter value */ while (done + sizeof(struct ATTR_DEF_ENTRY) <= bytes) { u32 t32 = le32_to_cpu(t->type); @@ -1491,27 +1482,22 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) goto put_inode_out; } - for (idx = 0; idx < (0x10000 * sizeof(short) >> PAGE_SHIFT); idx++) { - const __le16 *src; - u16 *dst = Add2Ptr(sbi->upcase, idx << PAGE_SHIFT); - struct page *page = ntfs_map_page(inode->i_mapping, idx); - - if (IS_ERR(page)) { - err = PTR_ERR(page); - ntfs_err(sb, "Failed to read $UpCase (%d).", err); - goto put_inode_out; - } - - src = page_address(page); + /* Read the entire file. */ + err = inode_read_data(inode, sbi->upcase, 0x10000 * sizeof(short)); + if (err) { + ntfs_err(sb, "Failed to read $UpCase (%d).", err); + goto put_inode_out; + } #ifdef __BIG_ENDIAN - for (i = 0; i < PAGE_SIZE / sizeof(u16); i++) + { + const __le16 *src = sbi->upcase; + u16 *dst = sbi->upcase; + + for (i = 0; i < 0x10000; i++) *dst++ = le16_to_cpu(*src++); -#else - memcpy(dst, src, PAGE_SIZE); -#endif - ntfs_unmap_page(page); } +#endif shared = ntfs_set_shared(sbi->upcase, 0x10000 * sizeof(short)); if (shared && sbi->upcase != shared) { @@ -1847,10 +1833,8 @@ bool is_legacy_ntfs(struct super_block *sb) #else static inline void register_as_ntfs_legacy(void) {} static inline void unregister_as_ntfs_legacy(void) {} -bool is_legacy_ntfs(struct super_block *sb) { return false; } #endif - // clang-format on static int __init init_ntfs_fs(void) @@ -1876,8 +1860,7 @@ static int __init init_ntfs_fs(void) ntfs_inode_cachep = kmem_cache_create( "ntfs_inode_cache", sizeof(struct ntfs_inode), 0, - (SLAB_RECLAIM_ACCOUNT | SLAB_ACCOUNT), - init_once); + (SLAB_RECLAIM_ACCOUNT | SLAB_ACCOUNT), init_once); if (!ntfs_inode_cachep) { err = -ENOMEM; goto out1; diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c index 73785dece7a7..0703e1ae32b2 100644 --- a/fs/ntfs3/xattr.c +++ b/fs/ntfs3/xattr.c @@ -195,10 +195,8 @@ static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer, { const struct EA_INFO *info; struct EA_FULL *ea_all = NULL; - const struct EA_FULL *ea; u32 off, size; int err; - int ea_size; size_t ret; err = ntfs_read_ea(ni, &ea_all, 0, &info); @@ -212,16 +210,18 @@ static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer, /* Enumerate all xattrs. */ ret = 0; - for (off = 0; off + sizeof(struct EA_FULL) < size; off += ea_size) { - ea = Add2Ptr(ea_all, off); - ea_size = unpacked_ea_size(ea); + off = 0; + while (off + sizeof(struct EA_FULL) < size) { + const struct EA_FULL *ea = Add2Ptr(ea_all, off); + int ea_size = unpacked_ea_size(ea); + u8 name_len = ea->name_len; - if (!ea->name_len) + if (!name_len) break; - if (ea->name_len > ea_size) { + if (name_len > ea_size) { ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_ERROR); - err = -EINVAL; /* corrupted fs */ + err = -EINVAL; /* corrupted fs. */ break; } @@ -230,16 +230,17 @@ static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer, if (off + ea_size > size) break; - if (ret + ea->name_len + 1 > bytes_per_buffer) { + if (ret + name_len + 1 > bytes_per_buffer) { err = -ERANGE; goto out; } - memcpy(buffer + ret, ea->name, ea->name_len); - buffer[ret + ea->name_len] = 0; + memcpy(buffer + ret, ea->name, name_len); + buffer[ret + name_len] = 0; } - ret += ea->name_len + 1; + ret += name_len + 1; + off += ea_size; } out: diff --git a/fs/binfmt_elf_test.c b/fs/tests/binfmt_elf_kunit.c index 11d734fec366..11d734fec366 100644 --- a/fs/binfmt_elf_test.c +++ b/fs/tests/binfmt_elf_kunit.c diff --git a/fs/exec_test.c b/fs/tests/exec_kunit.c index 7c77d039680b..7c77d039680b 100644 --- a/fs/exec_test.c +++ b/fs/tests/exec_kunit.c diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 677315e51e54..ad6afc5c4918 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -443,29 +443,10 @@ #endif /* - * Some symbol definitions will not exist yet during the first pass of the - * link, but are guaranteed to exist in the final link. Provide preliminary - * definitions that will be superseded in the final link to avoid having to - * rely on weak external linkage, which requires a GOT when used in position - * independent code. - */ -#define PRELIMINARY_SYMBOL_DEFINITIONS \ - PROVIDE(kallsyms_addresses = .); \ - PROVIDE(kallsyms_offsets = .); \ - PROVIDE(kallsyms_names = .); \ - PROVIDE(kallsyms_num_syms = .); \ - PROVIDE(kallsyms_relative_base = .); \ - PROVIDE(kallsyms_token_table = .); \ - PROVIDE(kallsyms_token_index = .); \ - PROVIDE(kallsyms_markers = .); \ - PROVIDE(kallsyms_seqs_of_names = .); - -/* * Read only Data */ #define RO_DATA(align) \ . = ALIGN((align)); \ - PRELIMINARY_SYMBOL_DEFINITIONS \ .rodata : AT(ADDR(.rodata) - LOAD_OFFSET) { \ __start_rodata = .; \ *(.rodata) *(.rodata.*) \ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index e93059f71c71..f0b95c76c707 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -274,6 +274,9 @@ static inline bool invalid_phys_cpuid(phys_cpuid_t phys_id) return phys_id == PHYS_CPUID_INVALID; } + +int __init acpi_get_madt_revision(void); + /* Validate the processor object's proc_id */ bool acpi_duplicate_processor_id(int proc_id); /* Processor _CTS control */ diff --git a/include/linux/bio-integrity.h b/include/linux/bio-integrity.h new file mode 100644 index 000000000000..dd831c269e99 --- /dev/null +++ b/include/linux/bio-integrity.h @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_BIO_INTEGRITY_H +#define _LINUX_BIO_INTEGRITY_H + +#include <linux/bio.h> + +enum bip_flags { + BIP_BLOCK_INTEGRITY = 1 << 0, /* block layer owns integrity data */ + BIP_MAPPED_INTEGRITY = 1 << 1, /* ref tag has been remapped */ + BIP_CTRL_NOCHECK = 1 << 2, /* disable HBA integrity checking */ + BIP_DISK_NOCHECK = 1 << 3, /* disable disk integrity checking */ + BIP_IP_CHECKSUM = 1 << 4, /* IP checksum */ + BIP_COPY_USER = 1 << 5, /* Kernel bounce buffer in use */ +}; + +struct bio_integrity_payload { + struct bio *bip_bio; /* parent bio */ + + struct bvec_iter bip_iter; + + unsigned short bip_vcnt; /* # of integrity bio_vecs */ + unsigned short bip_max_vcnt; /* integrity bio_vec slots */ + unsigned short bip_flags; /* control flags */ + + struct bvec_iter bio_iter; /* for rewinding parent bio */ + + struct work_struct bip_work; /* I/O completion */ + + struct bio_vec *bip_vec; + struct bio_vec bip_inline_vecs[];/* embedded bvec array */ +}; + +#ifdef CONFIG_BLK_DEV_INTEGRITY + +#define bip_for_each_vec(bvl, bip, iter) \ + for_each_bvec(bvl, (bip)->bip_vec, iter, (bip)->bip_iter) + +#define bio_for_each_integrity_vec(_bvl, _bio, _iter) \ + for_each_bio(_bio) \ + bip_for_each_vec(_bvl, _bio->bi_integrity, _iter) + +static inline struct bio_integrity_payload *bio_integrity(struct bio *bio) +{ + if (bio->bi_opf & REQ_INTEGRITY) + return bio->bi_integrity; + + return NULL; +} + +static inline bool bio_integrity_flagged(struct bio *bio, enum bip_flags flag) +{ + struct bio_integrity_payload *bip = bio_integrity(bio); + + if (bip) + return bip->bip_flags & flag; + + return false; +} + +static inline sector_t bip_get_seed(struct bio_integrity_payload *bip) +{ + return bip->bip_iter.bi_sector; +} + +static inline void bip_set_seed(struct bio_integrity_payload *bip, + sector_t seed) +{ + bip->bip_iter.bi_sector = seed; +} + +struct bio_integrity_payload *bio_integrity_alloc(struct bio *bio, gfp_t gfp, + unsigned int nr); +int bio_integrity_add_page(struct bio *bio, struct page *page, unsigned int len, + unsigned int offset); +int bio_integrity_map_user(struct bio *bio, void __user *ubuf, ssize_t len, u32 seed); +void bio_integrity_unmap_user(struct bio *bio); +bool bio_integrity_prep(struct bio *bio); +void bio_integrity_advance(struct bio *bio, unsigned int bytes_done); +void bio_integrity_trim(struct bio *bio); +int bio_integrity_clone(struct bio *bio, struct bio *bio_src, gfp_t gfp_mask); +int bioset_integrity_create(struct bio_set *bs, int pool_size); +void bioset_integrity_free(struct bio_set *bs); +void bio_integrity_init(void); + +#else /* CONFIG_BLK_DEV_INTEGRITY */ + +static inline struct bio_integrity_payload *bio_integrity(struct bio *bio) +{ + return NULL; +} + +static inline int bioset_integrity_create(struct bio_set *bs, int pool_size) +{ + return 0; +} + +static inline void bioset_integrity_free(struct bio_set *bs) +{ +} + +static inline int bio_integrity_map_user(struct bio *bio, void __user *ubuf, + ssize_t len, u32 seed) +{ + return -EINVAL; +} + +static inline void bio_integrity_unmap_user(struct bio *bio) +{ +} + +static inline bool bio_integrity_prep(struct bio *bio) +{ + return true; +} + +static inline int bio_integrity_clone(struct bio *bio, struct bio *bio_src, + gfp_t gfp_mask) +{ + return 0; +} + +static inline void bio_integrity_advance(struct bio *bio, + unsigned int bytes_done) +{ +} + +static inline void bio_integrity_trim(struct bio *bio) +{ +} + +static inline void bio_integrity_init(void) +{ +} + +static inline bool bio_integrity_flagged(struct bio *bio, enum bip_flags flag) +{ + return false; +} + +static inline struct bio_integrity_payload * +bio_integrity_alloc(struct bio *bio, gfp_t gfp, unsigned int nr) +{ + return ERR_PTR(-EINVAL); +} + +static inline int bio_integrity_add_page(struct bio *bio, struct page *page, + unsigned int len, unsigned int offset) +{ + return 0; +} +#endif /* CONFIG_BLK_DEV_INTEGRITY */ +#endif /* _LINUX_BIO_INTEGRITY_H */ diff --git a/include/linux/bio.h b/include/linux/bio.h index 818e93612947..a46e2047bea4 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -321,69 +321,6 @@ static inline void bio_next_folio(struct folio_iter *fi, struct bio *bio) #define bio_for_each_folio_all(fi, bio) \ for (bio_first_folio(&fi, bio, 0); fi.folio; bio_next_folio(&fi, bio)) -enum bip_flags { - BIP_BLOCK_INTEGRITY = 1 << 0, /* block layer owns integrity data */ - BIP_MAPPED_INTEGRITY = 1 << 1, /* ref tag has been remapped */ - BIP_CTRL_NOCHECK = 1 << 2, /* disable HBA integrity checking */ - BIP_DISK_NOCHECK = 1 << 3, /* disable disk integrity checking */ - BIP_IP_CHECKSUM = 1 << 4, /* IP checksum */ - BIP_INTEGRITY_USER = 1 << 5, /* Integrity payload is user address */ - BIP_COPY_USER = 1 << 6, /* Kernel bounce buffer in use */ -}; - -/* - * bio integrity payload - */ -struct bio_integrity_payload { - struct bio *bip_bio; /* parent bio */ - - struct bvec_iter bip_iter; - - unsigned short bip_vcnt; /* # of integrity bio_vecs */ - unsigned short bip_max_vcnt; /* integrity bio_vec slots */ - unsigned short bip_flags; /* control flags */ - - struct bvec_iter bio_iter; /* for rewinding parent bio */ - - struct work_struct bip_work; /* I/O completion */ - - struct bio_vec *bip_vec; - struct bio_vec bip_inline_vecs[];/* embedded bvec array */ -}; - -#if defined(CONFIG_BLK_DEV_INTEGRITY) - -static inline struct bio_integrity_payload *bio_integrity(struct bio *bio) -{ - if (bio->bi_opf & REQ_INTEGRITY) - return bio->bi_integrity; - - return NULL; -} - -static inline bool bio_integrity_flagged(struct bio *bio, enum bip_flags flag) -{ - struct bio_integrity_payload *bip = bio_integrity(bio); - - if (bip) - return bip->bip_flags & flag; - - return false; -} - -static inline sector_t bip_get_seed(struct bio_integrity_payload *bip) -{ - return bip->bip_iter.bi_sector; -} - -static inline void bip_set_seed(struct bio_integrity_payload *bip, - sector_t seed) -{ - bip->bip_iter.bi_sector = seed; -} - -#endif /* CONFIG_BLK_DEV_INTEGRITY */ - void bio_trim(struct bio *bio, sector_t offset, sector_t size); extern struct bio *bio_split(struct bio *bio, int sectors, gfp_t gfp, struct bio_set *bs); @@ -721,99 +658,6 @@ static inline bool bioset_initialized(struct bio_set *bs) return bs->bio_slab != NULL; } -#if defined(CONFIG_BLK_DEV_INTEGRITY) - -#define bip_for_each_vec(bvl, bip, iter) \ - for_each_bvec(bvl, (bip)->bip_vec, iter, (bip)->bip_iter) - -#define bio_for_each_integrity_vec(_bvl, _bio, _iter) \ - for_each_bio(_bio) \ - bip_for_each_vec(_bvl, _bio->bi_integrity, _iter) - -int bio_integrity_map_user(struct bio *bio, void __user *ubuf, ssize_t len, u32 seed); -void bio_integrity_unmap_free_user(struct bio *bio); -extern struct bio_integrity_payload *bio_integrity_alloc(struct bio *, gfp_t, unsigned int); -extern int bio_integrity_add_page(struct bio *, struct page *, unsigned int, unsigned int); -extern bool bio_integrity_prep(struct bio *); -extern void bio_integrity_advance(struct bio *, unsigned int); -extern void bio_integrity_trim(struct bio *); -extern int bio_integrity_clone(struct bio *, struct bio *, gfp_t); -extern int bioset_integrity_create(struct bio_set *, int); -extern void bioset_integrity_free(struct bio_set *); -extern void bio_integrity_init(void); - -#else /* CONFIG_BLK_DEV_INTEGRITY */ - -static inline void *bio_integrity(struct bio *bio) -{ - return NULL; -} - -static inline int bioset_integrity_create(struct bio_set *bs, int pool_size) -{ - return 0; -} - -static inline void bioset_integrity_free (struct bio_set *bs) -{ - return; -} - -static inline bool bio_integrity_prep(struct bio *bio) -{ - return true; -} - -static inline int bio_integrity_clone(struct bio *bio, struct bio *bio_src, - gfp_t gfp_mask) -{ - return 0; -} - -static inline void bio_integrity_advance(struct bio *bio, - unsigned int bytes_done) -{ - return; -} - -static inline void bio_integrity_trim(struct bio *bio) -{ - return; -} - -static inline void bio_integrity_init(void) -{ - return; -} - -static inline bool bio_integrity_flagged(struct bio *bio, enum bip_flags flag) -{ - return false; -} - -static inline void *bio_integrity_alloc(struct bio * bio, gfp_t gfp, - unsigned int nr) -{ - return ERR_PTR(-EINVAL); -} - -static inline int bio_integrity_add_page(struct bio *bio, struct page *page, - unsigned int len, unsigned int offset) -{ - return 0; -} - -static inline int bio_integrity_map_user(struct bio *bio, void __user *ubuf, - ssize_t len, u32 seed) -{ - return -EINVAL; -} -static inline void bio_integrity_unmap_free_user(struct bio *bio) -{ -} - -#endif /* CONFIG_BLK_DEV_INTEGRITY */ - /* * Mark a bio as polled. Note that for async polled IO, the caller must * expect -EWOULDBLOCK if we cannot allocate a request (or other resources). diff --git a/include/linux/blk-integrity.h b/include/linux/blk-integrity.h index 804f856ed3e5..de98049b7ded 100644 --- a/include/linux/blk-integrity.h +++ b/include/linux/blk-integrity.h @@ -3,6 +3,7 @@ #define _LINUX_BLK_INTEGRITY_H #include <linux/blk-mq.h> +#include <linux/bio-integrity.h> struct request; diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index 89ba6b16fe8b..8d304b1d16b1 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -27,38 +27,61 @@ typedef enum rq_end_io_ret (rq_end_io_fn)(struct request *, blk_status_t); * request flags */ typedef __u32 __bitwise req_flags_t; -/* drive already may have started this one */ -#define RQF_STARTED ((__force req_flags_t)(1 << 1)) -/* request for flush sequence */ -#define RQF_FLUSH_SEQ ((__force req_flags_t)(1 << 4)) -/* merge of different types, fail separately */ -#define RQF_MIXED_MERGE ((__force req_flags_t)(1 << 5)) -/* don't call prep for this one */ -#define RQF_DONTPREP ((__force req_flags_t)(1 << 7)) -/* use hctx->sched_tags */ -#define RQF_SCHED_TAGS ((__force req_flags_t)(1 << 8)) -/* use an I/O scheduler for this request */ -#define RQF_USE_SCHED ((__force req_flags_t)(1 << 9)) -/* vaguely specified driver internal error. Ignored by the block layer */ -#define RQF_FAILED ((__force req_flags_t)(1 << 10)) -/* don't warn about errors */ -#define RQF_QUIET ((__force req_flags_t)(1 << 11)) -/* account into disk and partition IO statistics */ -#define RQF_IO_STAT ((__force req_flags_t)(1 << 13)) -/* runtime pm request */ -#define RQF_PM ((__force req_flags_t)(1 << 15)) -/* on IO scheduler merge hash */ -#define RQF_HASHED ((__force req_flags_t)(1 << 16)) -/* track IO completion time */ -#define RQF_STATS ((__force req_flags_t)(1 << 17)) -/* Look at ->special_vec for the actual data payload instead of the - bio chain. */ -#define RQF_SPECIAL_PAYLOAD ((__force req_flags_t)(1 << 18)) -/* The request completion needs to be signaled to zone write pluging. */ -#define RQF_ZONE_WRITE_PLUGGING ((__force req_flags_t)(1 << 20)) -/* ->timeout has been called, don't expire again */ -#define RQF_TIMED_OUT ((__force req_flags_t)(1 << 21)) -#define RQF_RESV ((__force req_flags_t)(1 << 23)) +/* Keep rqf_name[] in sync with the definitions below */ +enum { + /* drive already may have started this one */ + __RQF_STARTED, + /* request for flush sequence */ + __RQF_FLUSH_SEQ, + /* merge of different types, fail separately */ + __RQF_MIXED_MERGE, + /* don't call prep for this one */ + __RQF_DONTPREP, + /* use hctx->sched_tags */ + __RQF_SCHED_TAGS, + /* use an I/O scheduler for this request */ + __RQF_USE_SCHED, + /* vaguely specified driver internal error. Ignored by block layer */ + __RQF_FAILED, + /* don't warn about errors */ + __RQF_QUIET, + /* account into disk and partition IO statistics */ + __RQF_IO_STAT, + /* runtime pm request */ + __RQF_PM, + /* on IO scheduler merge hash */ + __RQF_HASHED, + /* track IO completion time */ + __RQF_STATS, + /* Look at ->special_vec for the actual data payload instead of the + bio chain. */ + __RQF_SPECIAL_PAYLOAD, + /* request completion needs to be signaled to zone write plugging. */ + __RQF_ZONE_WRITE_PLUGGING, + /* ->timeout has been called, don't expire again */ + __RQF_TIMED_OUT, + __RQF_RESV, + __RQF_BITS +}; + +#define RQF_STARTED ((__force req_flags_t)(1 << __RQF_STARTED)) +#define RQF_FLUSH_SEQ ((__force req_flags_t)(1 << __RQF_FLUSH_SEQ)) +#define RQF_MIXED_MERGE ((__force req_flags_t)(1 << __RQF_MIXED_MERGE)) +#define RQF_DONTPREP ((__force req_flags_t)(1 << __RQF_DONTPREP)) +#define RQF_SCHED_TAGS ((__force req_flags_t)(1 << __RQF_SCHED_TAGS)) +#define RQF_USE_SCHED ((__force req_flags_t)(1 << __RQF_USE_SCHED)) +#define RQF_FAILED ((__force req_flags_t)(1 << __RQF_FAILED)) +#define RQF_QUIET ((__force req_flags_t)(1 << __RQF_QUIET)) +#define RQF_IO_STAT ((__force req_flags_t)(1 << __RQF_IO_STAT)) +#define RQF_PM ((__force req_flags_t)(1 << __RQF_PM)) +#define RQF_HASHED ((__force req_flags_t)(1 << __RQF_HASHED)) +#define RQF_STATS ((__force req_flags_t)(1 << __RQF_STATS)) +#define RQF_SPECIAL_PAYLOAD \ + ((__force req_flags_t)(1 << __RQF_SPECIAL_PAYLOAD)) +#define RQF_ZONE_WRITE_PLUGGING \ + ((__force req_flags_t)(1 << __RQF_ZONE_WRITE_PLUGGING)) +#define RQF_TIMED_OUT ((__force req_flags_t)(1 << __RQF_TIMED_OUT)) +#define RQF_RESV ((__force req_flags_t)(1 << __RQF_RESV)) /* flags that prevent us from merging requests: */ #define RQF_NOMERGE_FLAGS \ @@ -278,8 +301,12 @@ enum blk_eh_timer_return { BLK_EH_RESET_TIMER, }; -#define BLK_TAG_ALLOC_FIFO 0 /* allocate starting from 0 */ -#define BLK_TAG_ALLOC_RR 1 /* allocate starting from last allocated tag */ +/* Keep alloc_policy_name[] in sync with the definitions below */ +enum { + BLK_TAG_ALLOC_FIFO, /* allocate starting from 0 */ + BLK_TAG_ALLOC_RR, /* allocate starting from last allocated tag */ + BLK_TAG_ALLOC_MAX +}; /** * struct blk_mq_hw_ctx - State for a hardware queue facing the hardware @@ -644,6 +671,7 @@ struct blk_mq_ops { #endif }; +/* Keep hctx_flag_name[] in sync with the definitions below */ enum { BLK_MQ_F_SHOULD_MERGE = 1 << 0, BLK_MQ_F_TAG_QUEUE_SHARED = 1 << 1, @@ -653,27 +681,17 @@ enum { */ BLK_MQ_F_STACKING = 1 << 2, BLK_MQ_F_TAG_HCTX_SHARED = 1 << 3, - BLK_MQ_F_BLOCKING = 1 << 5, + BLK_MQ_F_BLOCKING = 1 << 4, /* Do not allow an I/O scheduler to be configured. */ - BLK_MQ_F_NO_SCHED = 1 << 6, + BLK_MQ_F_NO_SCHED = 1 << 5, + /* * Select 'none' during queue registration in case of a single hwq * or shared hwqs instead of 'mq-deadline'. */ - BLK_MQ_F_NO_SCHED_BY_DEFAULT = 1 << 7, - BLK_MQ_F_ALLOC_POLICY_START_BIT = 8, + BLK_MQ_F_NO_SCHED_BY_DEFAULT = 1 << 6, + BLK_MQ_F_ALLOC_POLICY_START_BIT = 7, BLK_MQ_F_ALLOC_POLICY_BITS = 1, - - BLK_MQ_S_STOPPED = 0, - BLK_MQ_S_TAG_ACTIVE = 1, - BLK_MQ_S_SCHED_RESTART = 2, - - /* hw queue is inactive after all its CPUs become offline */ - BLK_MQ_S_INACTIVE = 3, - - BLK_MQ_MAX_DEPTH = 10240, - - BLK_MQ_CPU_WORK_BATCH = 8, }; #define BLK_MQ_FLAG_TO_ALLOC_POLICY(flags) \ ((flags >> BLK_MQ_F_ALLOC_POLICY_START_BIT) & \ @@ -682,8 +700,19 @@ enum { ((policy & ((1 << BLK_MQ_F_ALLOC_POLICY_BITS) - 1)) \ << BLK_MQ_F_ALLOC_POLICY_START_BIT) +#define BLK_MQ_MAX_DEPTH (10240) #define BLK_MQ_NO_HCTX_IDX (-1U) +enum { + /* Keep hctx_state_name[] in sync with the definitions below */ + BLK_MQ_S_STOPPED, + BLK_MQ_S_TAG_ACTIVE, + BLK_MQ_S_SCHED_RESTART, + /* hw queue is inactive after all its CPUs become offline */ + BLK_MQ_S_INACTIVE, + BLK_MQ_S_MAX +}; + struct gendisk *__blk_mq_alloc_disk(struct blk_mq_tag_set *set, struct queue_limits *lim, void *queuedata, struct lock_class_key *lkclass); diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index 632edd71f8c6..36ed96133217 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -354,6 +354,7 @@ enum req_op { REQ_OP_LAST = (__force blk_opf_t)36, }; +/* Keep cmd_flag_name[] in sync with the definitions below */ enum req_flag_bits { __REQ_FAILFAST_DEV = /* no driver retries of device errors */ REQ_OP_BITS, diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index b8196e219ac2..e85ec73a07d5 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -588,27 +588,28 @@ struct request_queue { }; /* Keep blk_queue_flag_name[] in sync with the definitions below */ -#define QUEUE_FLAG_STOPPED 0 /* queue is stopped */ -#define QUEUE_FLAG_DYING 1 /* queue being torn down */ -#define QUEUE_FLAG_NOMERGES 3 /* disable merge attempts */ -#define QUEUE_FLAG_SAME_COMP 4 /* complete on same CPU-group */ -#define QUEUE_FLAG_FAIL_IO 5 /* fake timeout */ -#define QUEUE_FLAG_NOXMERGES 9 /* No extended merges */ -#define QUEUE_FLAG_SAME_FORCE 12 /* force complete on same CPU */ -#define QUEUE_FLAG_INIT_DONE 14 /* queue is initialized */ -#define QUEUE_FLAG_STATS 20 /* track IO start and completion times */ -#define QUEUE_FLAG_REGISTERED 22 /* queue has been registered to a disk */ -#define QUEUE_FLAG_QUIESCED 24 /* queue has been quiesced */ -#define QUEUE_FLAG_RQ_ALLOC_TIME 27 /* record rq->alloc_time_ns */ -#define QUEUE_FLAG_HCTX_ACTIVE 28 /* at least one blk-mq hctx is active */ -#define QUEUE_FLAG_SQ_SCHED 30 /* single queue style io dispatch */ +enum { + QUEUE_FLAG_DYING, /* queue being torn down */ + QUEUE_FLAG_NOMERGES, /* disable merge attempts */ + QUEUE_FLAG_SAME_COMP, /* complete on same CPU-group */ + QUEUE_FLAG_FAIL_IO, /* fake timeout */ + QUEUE_FLAG_NOXMERGES, /* No extended merges */ + QUEUE_FLAG_SAME_FORCE, /* force complete on same CPU */ + QUEUE_FLAG_INIT_DONE, /* queue is initialized */ + QUEUE_FLAG_STATS, /* track IO start and completion times */ + QUEUE_FLAG_REGISTERED, /* queue has been registered to a disk */ + QUEUE_FLAG_QUIESCED, /* queue has been quiesced */ + QUEUE_FLAG_RQ_ALLOC_TIME, /* record rq->alloc_time_ns */ + QUEUE_FLAG_HCTX_ACTIVE, /* at least one blk-mq hctx is active */ + QUEUE_FLAG_SQ_SCHED, /* single queue style io dispatch */ + QUEUE_FLAG_MAX +}; #define QUEUE_FLAG_MQ_DEFAULT (1UL << QUEUE_FLAG_SAME_COMP) void blk_queue_flag_set(unsigned int flag, struct request_queue *q); void blk_queue_flag_clear(unsigned int flag, struct request_queue *q); -#define blk_queue_stopped(q) test_bit(QUEUE_FLAG_STOPPED, &(q)->queue_flags) #define blk_queue_dying(q) test_bit(QUEUE_FLAG_DYING, &(q)->queue_flags) #define blk_queue_init_done(q) test_bit(QUEUE_FLAG_INIT_DONE, &(q)->queue_flags) #define blk_queue_nomerges(q) test_bit(QUEUE_FLAG_NOMERGES, &(q)->queue_flags) diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h index 293af7f8a694..ae04035b6cbe 100644 --- a/include/linux/cgroup-defs.h +++ b/include/linux/cgroup-defs.h @@ -539,9 +539,6 @@ struct cgroup { /* used to store eBPF programs */ struct cgroup_bpf bpf; - /* If there is block congestion on this cgroup. */ - atomic_t congestion_count; - /* Used to store internal freezer state */ struct cgroup_freezer_state freezer; diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 41d1d71c36ff..01bee2b289c2 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -259,15 +259,14 @@ struct node_footer { #define CUR_ADDRS_PER_INODE(inode) (DEF_ADDRS_PER_INODE - \ get_extra_isize(inode)) #define DEF_NIDS_PER_INODE 5 /* Node IDs in an Inode */ -#define ADDRS_PER_INODE(inode) addrs_per_inode(inode) +#define ADDRS_PER_INODE(inode) addrs_per_page(inode, true) /* Address Pointers in a Direct Block */ #define DEF_ADDRS_PER_BLOCK ((F2FS_BLKSIZE - sizeof(struct node_footer)) / sizeof(__le32)) -#define ADDRS_PER_BLOCK(inode) addrs_per_block(inode) +#define ADDRS_PER_BLOCK(inode) addrs_per_page(inode, false) /* Node IDs in an Indirect Block */ #define NIDS_PER_BLOCK ((F2FS_BLKSIZE - sizeof(struct node_footer)) / sizeof(__le32)) -#define ADDRS_PER_PAGE(page, inode) \ - (IS_INODE(page) ? ADDRS_PER_INODE(inode) : ADDRS_PER_BLOCK(inode)) +#define ADDRS_PER_PAGE(page, inode) (addrs_per_page(inode, IS_INODE(page))) #define NODE_DIR1_BLOCK (DEF_ADDRS_PER_INODE + 1) #define NODE_DIR2_BLOCK (DEF_ADDRS_PER_INODE + 2) diff --git a/include/linux/hwspinlock.h b/include/linux/hwspinlock.h index bfe7c1f1ac6d..f0231dbc4777 100644 --- a/include/linux/hwspinlock.h +++ b/include/linux/hwspinlock.h @@ -68,6 +68,7 @@ int __hwspin_lock_timeout(struct hwspinlock *, unsigned int, int, int __hwspin_trylock(struct hwspinlock *, int, unsigned long *); void __hwspin_unlock(struct hwspinlock *, int, unsigned long *); int of_hwspin_lock_get_id_byname(struct device_node *np, const char *name); +int hwspin_lock_bust(struct hwspinlock *hwlock, unsigned int id); int devm_hwspin_lock_free(struct device *dev, struct hwspinlock *hwlock); struct hwspinlock *devm_hwspin_lock_request(struct device *dev); struct hwspinlock *devm_hwspin_lock_request_specific(struct device *dev, @@ -127,6 +128,11 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags) { } +static inline int hwspin_lock_bust(struct hwspinlock *hwlock, unsigned int id) +{ + return 0; +} + static inline int of_hwspin_lock_get_id(struct device_node *np, int index) { return 0; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index e9cc14b1f9a1..8caaa13834bf 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -30,7 +30,6 @@ extern const struct device_type i2c_client_type; /* --- General options ------------------------------------------------ */ struct i2c_msg; -struct i2c_algorithm; struct i2c_adapter; struct i2c_client; struct i2c_driver; @@ -512,16 +511,15 @@ i2c_register_board_info(int busnum, struct i2c_board_info const *info, #endif /* I2C_BOARDINFO */ /** - * struct i2c_algorithm - represent I2C transfer method - * @xfer: Issue a set of i2c transactions to the given I2C adapter - * defined by the msgs array, with num messages available to transfer via - * the adapter specified by adap. - * @xfer_atomic: same as @xfer. Yet, only using atomic context - * so e.g. PMICs can be accessed very late before shutdown. Optional. - * @smbus_xfer: Issue smbus transactions to the given I2C adapter. If this + * struct i2c_algorithm - represent I2C transfer methods + * @xfer: Transfer a given number of messages defined by the msgs array via + * the specified adapter. + * @xfer_atomic: Same as @xfer. Yet, only using atomic context so e.g. PMICs + * can be accessed very late before shutdown. Optional. + * @smbus_xfer: Issue SMBus transactions to the given I2C adapter. If this * is not present, then the bus layer will try and convert the SMBus calls * into I2C transfers instead. - * @smbus_xfer_atomic: same as @smbus_xfer. Yet, only using atomic context + * @smbus_xfer_atomic: Same as @smbus_xfer. Yet, only using atomic context * so e.g. PMICs can be accessed very late before shutdown. Optional. * @functionality: Return the flags that this algorithm/adapter pair supports * from the ``I2C_FUNC_*`` flags. @@ -533,8 +531,6 @@ i2c_register_board_info(int busnum, struct i2c_board_info const *info, * @reg_slave: deprecated, use @reg_target * @unreg_slave: deprecated, use @unreg_target * - * - * The following structs are for those who like to implement new bus drivers: * i2c_algorithm is the interface to a class of hardware solutions which can * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584 * to name two of the most common. @@ -550,9 +546,6 @@ struct i2c_algorithm { * to NULL. If an adapter algorithm can do SMBus access, set * smbus_xfer. If set to NULL, the SMBus protocol is simulated * using common I2C messages. - * - * xfer should return the number of messages successfully - * processed, or a negative value on error */ union { int (*xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index bea39a0292eb..3f30c88e0b4c 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -169,7 +169,7 @@ static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev) { - return request_threaded_irq(irq, handler, NULL, flags, name, dev); + return request_threaded_irq(irq, handler, NULL, flags | IRQF_COND_ONESHOT, name, dev); } extern int __must_check diff --git a/include/linux/irq.h b/include/linux/irq.h index a217e1029c1d..1f5dbf1f92c9 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -1106,6 +1106,7 @@ enum irq_gc_flags { * @irq_flags_to_set: IRQ* flags to set on irq setup * @irq_flags_to_clear: IRQ* flags to clear on irq setup * @gc_flags: Generic chip specific setup flags + * @exit: Function called on each chip when they are destroyed. * @gc: Array of pointers to generic interrupt chips */ struct irq_domain_chip_generic { @@ -1114,9 +1115,37 @@ struct irq_domain_chip_generic { unsigned int irq_flags_to_clear; unsigned int irq_flags_to_set; enum irq_gc_flags gc_flags; + void (*exit)(struct irq_chip_generic *gc); struct irq_chip_generic *gc[]; }; +/** + * struct irq_domain_chip_generic_info - Generic chip information structure + * @name: Name of the generic interrupt chip + * @handler: Interrupt handler used by the generic interrupt chip + * @irqs_per_chip: Number of interrupts each chip handles (max 32) + * @num_ct: Number of irq_chip_type instances associated with each + * chip + * @irq_flags_to_clear: IRQ_* bits to clear in the mapping function + * @irq_flags_to_set: IRQ_* bits to set in the mapping function + * @gc_flags: Generic chip specific setup flags + * @init: Function called on each chip when they are created. + * Allow to do some additional chip initialisation. + * @exit: Function called on each chip when they are destroyed. + * Allow to do some chip cleanup operation. + */ +struct irq_domain_chip_generic_info { + const char *name; + irq_flow_handler_t handler; + unsigned int irqs_per_chip; + unsigned int num_ct; + unsigned int irq_flags_to_clear; + unsigned int irq_flags_to_set; + enum irq_gc_flags gc_flags; + int (*init)(struct irq_chip_generic *gc); + void (*exit)(struct irq_chip_generic *gc); +}; + /* Generic chip callback functions */ void irq_gc_noop(struct irq_data *d); void irq_gc_mask_disable_reg(struct irq_data *d); @@ -1153,6 +1182,20 @@ int devm_irq_setup_generic_chip(struct device *dev, struct irq_chip_generic *gc, struct irq_chip_generic *irq_get_domain_generic_chip(struct irq_domain *d, unsigned int hw_irq); +#ifdef CONFIG_GENERIC_IRQ_CHIP +int irq_domain_alloc_generic_chips(struct irq_domain *d, + const struct irq_domain_chip_generic_info *info); +void irq_domain_remove_generic_chips(struct irq_domain *d); +#else +static inline int +irq_domain_alloc_generic_chips(struct irq_domain *d, + const struct irq_domain_chip_generic_info *info) +{ + return -EINVAL; +} +static inline void irq_domain_remove_generic_chips(struct irq_domain *d) { } +#endif /* CONFIG_GENERIC_IRQ_CHIP */ + int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip, int num_ct, const char *name, irq_flow_handler_t handler, diff --git a/include/linux/irqchip/arm-gic-v4.h b/include/linux/irqchip/arm-gic-v4.h index 2c63375bbd43..ecabed6d3307 100644 --- a/include/linux/irqchip/arm-gic-v4.h +++ b/include/linux/irqchip/arm-gic-v4.h @@ -25,6 +25,14 @@ struct its_vm { irq_hw_number_t db_lpi_base; unsigned long *db_bitmap; int nr_db_lpis; + /* + * Ensures mutual exclusion between updates to vlpi_count[] + * and map/unmap when using the ITSList mechanism. + * + * The lock order for any sequence involving the ITSList is + * vmapp_lock -> vpe_lock ->vmovp_lock. + */ + raw_spinlock_t vmapp_lock; u32 vlpi_count[GICv4_ITS_LIST_MAX]; }; diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 21ecf582a0fe..de6105f68fec 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -74,11 +74,24 @@ void of_phandle_args_to_fwspec(struct device_node *np, const u32 *args, * struct irq_domain_ops - Methods for irq_domain objects * @match: Match an interrupt controller device node to a host, returns * 1 on a match + * @select: Match an interrupt controller fw specification. It is more generic + * than @match as it receives a complete struct irq_fwspec. Therefore, + * @select is preferred if provided. Returns 1 on a match. * @map: Create or update a mapping between a virtual irq number and a hw * irq number. This is called only once for a given mapping. * @unmap: Dispose of such a mapping * @xlate: Given a device tree node and interrupt specifier, decode * the hardware irq number and linux irq type value. + * @alloc: Allocate @nr_irqs interrupts starting from @virq. + * @free: Free @nr_irqs interrupts starting from @virq. + * @activate: Activate one interrupt in HW (@irqd). If @reserve is set, only + * reserve the vector. If unset, assign the vector (called from + * request_irq()). + * @deactivate: Disarm one interrupt (@irqd). + * @translate: Given @fwspec, decode the hardware irq number (@out_hwirq) and + * linux irq type value (@out_type). This is a generalised @xlate + * (over struct irq_fwspec) and is preferred if provided. + * @debug_show: For domains to show specific data for an interrupt in debugfs. * * Functions below are provided by the driver and called whenever a new mapping * is created or an old mapping is disposed. The driver can then proceed to @@ -131,6 +144,9 @@ struct irq_domain_chip_generic; * Optional elements: * @fwnode: Pointer to firmware node associated with the irq_domain. Pretty easy * to swap it for the of_node via the irq_domain_get_of_node accessor + * @bus_token: @fwnode's device_node might be used for several irq domains. But + * in connection with @bus_token, the pair shall be unique in a + * system. * @gc: Pointer to a list of generic chips. There is a helper function for * setting up one or more generic chips for interrupt controllers * drivers using the generic chip library which uses this pointer. @@ -141,9 +157,12 @@ struct irq_domain_chip_generic; * purposes related to the irq domain. * @parent: Pointer to parent irq_domain to support hierarchy irq_domains * @msi_parent_ops: Pointer to MSI parent domain methods for per device domain init + * @exit: Function called when the domain is destroyed * * Revmap data, used internally by the irq domain code: - * @revmap_size: Size of the linear map table @revmap[] + * @hwirq_max: Top limit for the HW irq number. Especially to avoid + * conflicts/failures with reserved HW irqs. Can be ~0. + * @revmap_size: Size of the linear map table @revmap * @revmap_tree: Radix map tree for hwirqs that don't fit in the linear map * @revmap: Linear table of irq_data pointers */ @@ -169,6 +188,7 @@ struct irq_domain { #ifdef CONFIG_GENERIC_MSI_IRQ const struct msi_parent_ops *msi_parent_ops; #endif + void (*exit)(struct irq_domain *d); /* reverse map data. The linear map gets appended to the irq_domain */ irq_hw_number_t hwirq_max; @@ -182,7 +202,7 @@ enum { /* Irq domain is hierarchical */ IRQ_DOMAIN_FLAG_HIERARCHY = (1 << 0), - /* Irq domain name was allocated in __irq_domain_add() */ + /* Irq domain name was allocated internally */ IRQ_DOMAIN_NAME_ALLOCATED = (1 << 1), /* Irq domain is an IPI domain with virq per cpu */ @@ -208,6 +228,9 @@ enum { /* Irq domain is a MSI device domain */ IRQ_DOMAIN_FLAG_MSI_DEVICE = (1 << 9), + /* Irq domain must destroy generic chips when removed */ + IRQ_DOMAIN_FLAG_DESTROY_GC = (1 << 10), + /* * Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved * for implementation specific purposes and ignored by the @@ -257,10 +280,51 @@ static inline struct fwnode_handle *irq_domain_alloc_fwnode(phys_addr_t *pa) } void irq_domain_free_fwnode(struct fwnode_handle *fwnode); -struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int size, - irq_hw_number_t hwirq_max, int direct_max, - const struct irq_domain_ops *ops, - void *host_data); + +struct irq_domain_chip_generic_info; + +/** + * struct irq_domain_info - Domain information structure + * @fwnode: firmware node for the interrupt controller + * @domain_flags: Additional flags to add to the domain flags + * @size: Size of linear map; 0 for radix mapping only + * @hwirq_max: Maximum number of interrupts supported by controller + * @direct_max: Maximum value of direct maps; + * Use ~0 for no limit; 0 for no direct mapping + * @bus_token: Domain bus token + * @ops: Domain operation callbacks + * @host_data: Controller private data pointer + * @dgc_info: Geneneric chip information structure pointer used to + * create generic chips for the domain if not NULL. + * @init: Function called when the domain is created. + * Allow to do some additional domain initialisation. + * @exit: Function called when the domain is destroyed. + * Allow to do some additional cleanup operation. + */ +struct irq_domain_info { + struct fwnode_handle *fwnode; + unsigned int domain_flags; + unsigned int size; + irq_hw_number_t hwirq_max; + int direct_max; + enum irq_domain_bus_token bus_token; + const struct irq_domain_ops *ops; + void *host_data; +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY + /** + * @parent: Pointer to the parent irq domain used in a hierarchy domain + */ + struct irq_domain *parent; +#endif + struct irq_domain_chip_generic_info *dgc_info; + int (*init)(struct irq_domain *d); + void (*exit)(struct irq_domain *d); +}; + +struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info); +struct irq_domain *devm_irq_domain_instantiate(struct device *dev, + const struct irq_domain_info *info); + struct irq_domain *irq_domain_create_simple(struct fwnode_handle *fwnode, unsigned int size, unsigned int first_irq, @@ -293,7 +357,7 @@ static inline struct fwnode_handle *of_node_to_fwnode(struct device_node *node) extern const struct fwnode_operations irqchip_fwnode_ops; -static inline bool is_fwnode_irqchip(struct fwnode_handle *fwnode) +static inline bool is_fwnode_irqchip(const struct fwnode_handle *fwnode) { return fwnode && fwnode->ops == &irqchip_fwnode_ops; } @@ -350,7 +414,17 @@ static inline struct irq_domain *irq_domain_add_linear(struct device_node *of_no const struct irq_domain_ops *ops, void *host_data) { - return __irq_domain_add(of_node_to_fwnode(of_node), size, size, 0, ops, host_data); + struct irq_domain_info info = { + .fwnode = of_node_to_fwnode(of_node), + .size = size, + .hwirq_max = size, + .ops = ops, + .host_data = host_data, + }; + struct irq_domain *d; + + d = irq_domain_instantiate(&info); + return IS_ERR(d) ? NULL : d; } #ifdef CONFIG_IRQ_DOMAIN_NOMAP @@ -359,7 +433,17 @@ static inline struct irq_domain *irq_domain_add_nomap(struct device_node *of_nod const struct irq_domain_ops *ops, void *host_data) { - return __irq_domain_add(of_node_to_fwnode(of_node), 0, max_irq, max_irq, ops, host_data); + struct irq_domain_info info = { + .fwnode = of_node_to_fwnode(of_node), + .hwirq_max = max_irq, + .direct_max = max_irq, + .ops = ops, + .host_data = host_data, + }; + struct irq_domain *d; + + d = irq_domain_instantiate(&info); + return IS_ERR(d) ? NULL : d; } extern unsigned int irq_create_direct_mapping(struct irq_domain *host); @@ -369,7 +453,16 @@ static inline struct irq_domain *irq_domain_add_tree(struct device_node *of_node const struct irq_domain_ops *ops, void *host_data) { - return __irq_domain_add(of_node_to_fwnode(of_node), 0, ~0, 0, ops, host_data); + struct irq_domain_info info = { + .fwnode = of_node_to_fwnode(of_node), + .hwirq_max = ~0U, + .ops = ops, + .host_data = host_data, + }; + struct irq_domain *d; + + d = irq_domain_instantiate(&info); + return IS_ERR(d) ? NULL : d; } static inline struct irq_domain *irq_domain_create_linear(struct fwnode_handle *fwnode, @@ -377,14 +470,33 @@ static inline struct irq_domain *irq_domain_create_linear(struct fwnode_handle * const struct irq_domain_ops *ops, void *host_data) { - return __irq_domain_add(fwnode, size, size, 0, ops, host_data); + struct irq_domain_info info = { + .fwnode = fwnode, + .size = size, + .hwirq_max = size, + .ops = ops, + .host_data = host_data, + }; + struct irq_domain *d; + + d = irq_domain_instantiate(&info); + return IS_ERR(d) ? NULL : d; } static inline struct irq_domain *irq_domain_create_tree(struct fwnode_handle *fwnode, const struct irq_domain_ops *ops, void *host_data) { - return __irq_domain_add(fwnode, 0, ~0, 0, ops, host_data); + struct irq_domain_info info = { + .fwnode = fwnode, + .hwirq_max = ~0, + .ops = ops, + .host_data = host_data, + }; + struct irq_domain *d; + + d = irq_domain_instantiate(&info); + return IS_ERR(d) ? NULL : d; } extern void irq_domain_remove(struct irq_domain *host); diff --git a/include/linux/module.h b/include/linux/module.h index 4213d8993cd8..88ecc5e9f523 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -174,6 +174,12 @@ extern void cleanup_module(void); #define MODULE_SOFTDEP(_softdep) MODULE_INFO(softdep, _softdep) /* + * Weak module dependencies. See man modprobe.d for details. + * Example: MODULE_WEAKDEP("module-foo") + */ +#define MODULE_WEAKDEP(_weakdep) MODULE_INFO(weakdep, _weakdep) + +/* * MODULE_FILE is used for generating modules.builtin * So, make it no-op when this is being built as a module */ diff --git a/include/linux/msi.h b/include/linux/msi.h index 26588da88bdd..944979763825 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -21,11 +21,7 @@ #include <linux/irqdomain_defs.h> #include <linux/cpumask_types.h> #include <linux/msi_api.h> -#include <linux/xarray.h> -#include <linux/mutex.h> -#include <linux/list.h> #include <linux/irq.h> -#include <linux/bits.h> #include <asm/msi.h> @@ -81,7 +77,6 @@ extern int pci_msi_ignore_mask; /* Helper functions */ struct msi_desc; struct pci_dev; -struct platform_msi_priv_data; struct device_attribute; struct irq_domain; struct irq_affinity_desc; @@ -228,22 +223,6 @@ struct msi_dev_domain { struct irq_domain *domain; }; -/** - * msi_device_data - MSI per device data - * @properties: MSI properties which are interesting to drivers - * @platform_data: Platform-MSI specific data - * @mutex: Mutex protecting the MSI descriptor store - * @__domains: Internal data for per device MSI domains - * @__iter_idx: Index to search the next entry for iterators - */ -struct msi_device_data { - unsigned long properties; - struct platform_msi_priv_data *platform_data; - struct mutex mutex; - struct msi_dev_domain __domains[MSI_MAX_DEVICE_IRQDOMAINS]; - unsigned long __iter_idx; -}; - int msi_setup_device_data(struct device *dev); void msi_lock_descs(struct device *dev); @@ -556,6 +535,8 @@ enum { MSI_FLAG_USE_DEV_FWNODE = (1 << 7), /* Set parent->dev into domain->pm_dev on device domain creation */ MSI_FLAG_PARENT_PM_DEV = (1 << 8), + /* Support for parent mask/unmask */ + MSI_FLAG_PCI_MSI_MASK_PARENT = (1 << 9), /* Mask for the generic functionality */ MSI_GENERIC_FLAGS_MASK = GENMASK(15, 0), @@ -639,35 +620,6 @@ void msi_domain_free_irqs_all(struct device *dev, unsigned int domid); struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain); -struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode, - struct msi_domain_info *info, - struct irq_domain *parent); - -/* When an MSI domain is used as an intermediate domain */ -int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev, - int nvec, msi_alloc_info_t *args); -int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev, - int virq, int nvec, msi_alloc_info_t *args); -void msi_domain_depopulate_descs(struct device *dev, int virq, int nvec); - -struct irq_domain * -__platform_msi_create_device_domain(struct device *dev, - unsigned int nvec, - bool is_tree, - irq_write_msi_msg_t write_msi_msg, - const struct irq_domain_ops *ops, - void *host_data); - -#define platform_msi_create_device_domain(dev, nvec, write, ops, data) \ - __platform_msi_create_device_domain(dev, nvec, false, write, ops, data) -#define platform_msi_create_device_tree_domain(dev, nvec, write, ops, data) \ - __platform_msi_create_device_domain(dev, nvec, true, write, ops, data) - -int platform_msi_device_domain_alloc(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs); -void platform_msi_device_domain_free(struct irq_domain *domain, unsigned int virq, - unsigned int nvec); -void *platform_msi_get_host_data(struct irq_domain *domain); /* Per device platform MSI */ int platform_device_msi_init_and_alloc_irqs(struct device *dev, unsigned int nvec, irq_write_msi_msg_t write_msi_msg); diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 483a191bb4df..d9c7edb6422b 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -1537,10 +1537,4 @@ unsigned int i_blocks_per_folio(struct inode *inode, struct folio *folio) { return folio_size(folio) >> inode->i_blkbits; } - -static inline -unsigned int i_blocks_per_page(struct inode *inode, struct page *page) -{ - return i_blocks_per_folio(inode, page_folio(page)); -} #endif /* _LINUX_PAGEMAP_H */ diff --git a/include/linux/platform_data/i2c-mux-gpio.h b/include/linux/platform_data/i2c-mux-gpio.h index 816a4cd3ccb5..96843aab4d1e 100644 --- a/include/linux/platform_data/i2c-mux-gpio.h +++ b/include/linux/platform_data/i2c-mux-gpio.h @@ -19,6 +19,7 @@ * position * @n_values: Number of multiplexer positions (busses to instantiate) * @idle: Bitmask to write to MUX when idle or GPIO_I2CMUX_NO_IDLE if not used + * @settle_time: Delay to wait when a new bus is selected */ struct i2c_mux_gpio_platform_data { int parent; @@ -26,6 +27,7 @@ struct i2c_mux_gpio_platform_data { const unsigned *values; int n_values; unsigned idle; + u32 settle_time; }; #endif /* _LINUX_I2C_MUX_GPIO_H */ diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index c852cc882501..72dc7e45c90c 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -309,18 +309,11 @@ struct power_supply { #endif #ifdef CONFIG_LEDS_TRIGGERS - struct led_trigger *charging_full_trig; - char *charging_full_trig_name; + struct led_trigger *trig; struct led_trigger *charging_trig; - char *charging_trig_name; struct led_trigger *full_trig; - char *full_trig_name; - struct led_trigger *online_trig; - char *online_trig_name; struct led_trigger *charging_blink_full_solid_trig; - char *charging_blink_full_solid_trig_name; struct led_trigger *charging_orange_full_green_trig; - char *charging_orange_full_green_trig_name; #endif }; @@ -743,7 +736,7 @@ struct power_supply_battery_info { int overvoltage_limit_uv; int constant_charge_current_max_ua; int constant_charge_voltage_max_uv; - struct power_supply_maintenance_charge_table *maintenance_charge; + const struct power_supply_maintenance_charge_table *maintenance_charge; int maintenance_charge_size; int alert_low_temp_charge_current_ua; int alert_low_temp_charge_voltage_uv; @@ -762,9 +755,9 @@ struct power_supply_battery_info { int ocv_table_size[POWER_SUPPLY_OCV_TEMP_MAX]; struct power_supply_resistance_temp_table *resist_table; int resist_table_size; - struct power_supply_vbat_ri_table *vbat2ri_discharging; + const struct power_supply_vbat_ri_table *vbat2ri_discharging; int vbat2ri_discharging_size; - struct power_supply_vbat_ri_table *vbat2ri_charging; + const struct power_supply_vbat_ri_table *vbat2ri_charging; int vbat2ri_charging_size; int bti_resistance_ohm; int bti_resistance_tolerance; @@ -817,7 +810,7 @@ power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *table int table_len, int temp); extern int power_supply_vbat2ri(struct power_supply_battery_info *info, int vbat_uv, bool charging); -extern struct power_supply_maintenance_charge_table * +extern const struct power_supply_maintenance_charge_table * power_supply_get_maintenance_charging_setting(struct power_supply_battery_info *info, int index); extern bool power_supply_battery_bti_in_range(struct power_supply_battery_info *info, int resistance); @@ -831,7 +824,7 @@ extern int power_supply_set_battery_charged(struct power_supply *psy); static inline bool power_supply_supports_maintenance_charging(struct power_supply_battery_info *info) { - struct power_supply_maintenance_charge_table *mt; + const struct power_supply_maintenance_charge_table *mt; mt = power_supply_get_maintenance_charging_setting(info, 0); diff --git a/include/linux/sbitmap.h b/include/linux/sbitmap.h index d662cf136021..c09cdcc99471 100644 --- a/include/linux/sbitmap.h +++ b/include/linux/sbitmap.h @@ -36,6 +36,11 @@ struct sbitmap_word { * @cleared: word holding cleared bits */ unsigned long cleared ____cacheline_aligned_in_smp; + + /** + * @swap_lock: serializes simultaneous updates of ->word and ->cleared + */ + spinlock_t swap_lock; } ____cacheline_aligned_in_smp; /** diff --git a/include/linux/soc/qcom/smem.h b/include/linux/soc/qcom/smem.h index 0943bf419e11..f946e3beca21 100644 --- a/include/linux/soc/qcom/smem.h +++ b/include/linux/soc/qcom/smem.h @@ -15,4 +15,6 @@ phys_addr_t qcom_smem_virt_to_phys(void *p); int qcom_smem_get_soc_id(u32 *id); int qcom_smem_get_feature_code(u32 *code); +int qcom_smem_bust_hwspin_lock_by_host(unsigned int host); + #endif diff --git a/init/Kconfig b/init/Kconfig index 4b81a49a25c4..c41260ffe99a 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1803,24 +1803,6 @@ config KALLSYMS_ABSOLUTE_PERCPU depends on KALLSYMS default X86_64 && SMP -config KALLSYMS_BASE_RELATIVE - bool - depends on KALLSYMS - default y - help - Instead of emitting them as absolute values in the native word size, - emit the symbol references in the kallsyms table as 32-bit entries, - each containing a relative value in the range [base, base + U32_MAX] - or, when KALLSYMS_ABSOLUTE_PERCPU is in effect, each containing either - an absolute value in the range [0, S32_MAX] or a relative value in the - range [base, base + S32_MAX], where base is the lowest relative symbol - address encountered in the image. - - On 64-bit builds, this reduces the size of the address table by 50%, - but more importantly, it results in entries whose values are build - time constants, and no relocation pass is required at runtime to fix - up the entries based on the runtime load address of the kernel. - # end of the "standard kernel features (expert users)" menu config ARCH_HAS_MEMBARRIER_CALLBACKS diff --git a/init/Makefile b/init/Makefile index ab71cedc5fd6..10b652d33e87 100644 --- a/init/Makefile +++ b/init/Makefile @@ -52,7 +52,7 @@ CFLAGS_version.o := -include $(obj)/utsversion-tmp.h # Build version-timestamp.c with final UTS_VERSION # -include/generated/utsversion.h: build-version-auto = $(shell $(src)/build-version) +include/generated/utsversion.h: build-version-auto = $(shell $(srctree)/scripts/build-version) include/generated/utsversion.h: build-timestamp-auto = $(shell LC_ALL=C date) include/generated/utsversion.h: FORCE $(call filechk,uts_version) diff --git a/io_uring/kbuf.c b/io_uring/kbuf.c index d2945c9c812b..c95dc1736dd9 100644 --- a/io_uring/kbuf.c +++ b/io_uring/kbuf.c @@ -657,8 +657,10 @@ static int io_alloc_pbuf_ring(struct io_ring_ctx *ctx, ring_size = reg->ring_entries * sizeof(struct io_uring_buf_ring); bl->buf_ring = io_pages_map(&bl->buf_pages, &bl->buf_nr_pages, ring_size); - if (!bl->buf_ring) + if (IS_ERR(bl->buf_ring)) { + bl->buf_ring = NULL; return -ENOMEM; + } bl->is_buf_ring = 1; bl->is_mmap = 1; diff --git a/io_uring/uring_cmd.c b/io_uring/uring_cmd.c index 21ac5fb2d5f0..a54163a83968 100644 --- a/io_uring/uring_cmd.c +++ b/io_uring/uring_cmd.c @@ -265,7 +265,7 @@ int io_uring_cmd(struct io_kiocb *req, unsigned int issue_flags) req_set_fail(req); io_req_uring_cleanup(req, issue_flags); io_req_set_res(req, ret, 0); - return ret; + return ret < 0 ? ret : IOU_OK; } int io_uring_cmd_import_fixed(u64 ubuf, unsigned long len, int rw, diff --git a/kernel/irq/debugfs.c b/kernel/irq/debugfs.c index aae0402507ed..c6ffb97966be 100644 --- a/kernel/irq/debugfs.c +++ b/kernel/irq/debugfs.c @@ -9,14 +9,8 @@ static struct dentry *irq_dir; -struct irq_bit_descr { - unsigned int mask; - char *name; -}; -#define BIT_MASK_DESCR(m) { .mask = m, .name = #m } - -static void irq_debug_show_bits(struct seq_file *m, int ind, unsigned int state, - const struct irq_bit_descr *sd, int size) +void irq_debug_show_bits(struct seq_file *m, int ind, unsigned int state, + const struct irq_bit_descr *sd, int size) { int i; diff --git a/kernel/irq/devres.c b/kernel/irq/devres.c index f6e5515ee077..b3e98668f4dd 100644 --- a/kernel/irq/devres.c +++ b/kernel/irq/devres.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/module.h> #include <linux/interrupt.h> +#include <linux/irqdomain.h> #include <linux/device.h> #include <linux/gfp.h> #include <linux/irq.h> @@ -282,3 +283,43 @@ int devm_irq_setup_generic_chip(struct device *dev, struct irq_chip_generic *gc, } EXPORT_SYMBOL_GPL(devm_irq_setup_generic_chip); #endif /* CONFIG_GENERIC_IRQ_CHIP */ + +#ifdef CONFIG_IRQ_DOMAIN +static void devm_irq_domain_remove(struct device *dev, void *res) +{ + struct irq_domain **domain = res; + + irq_domain_remove(*domain); +} + +/** + * devm_irq_domain_instantiate() - Instantiate a new irq domain data for a + * managed device. + * @dev: Device to instantiate the domain for + * @info: Domain information pointer pointing to the information for this + * domain + * + * Return: A pointer to the instantiated irq domain or an ERR_PTR value. + */ +struct irq_domain *devm_irq_domain_instantiate(struct device *dev, + const struct irq_domain_info *info) +{ + struct irq_domain *domain; + struct irq_domain **dr; + + dr = devres_alloc(devm_irq_domain_remove, sizeof(*dr), GFP_KERNEL); + if (!dr) + return ERR_PTR(-ENOMEM); + + domain = irq_domain_instantiate(info); + if (!IS_ERR(domain)) { + *dr = domain; + devres_add(dev, dr); + } else { + devres_free(dr); + } + + return domain; +} +EXPORT_SYMBOL_GPL(devm_irq_domain_instantiate); +#endif /* CONFIG_IRQ_DOMAIN */ diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c index d39a40bc542b..32ffcbb87fa1 100644 --- a/kernel/irq/generic-chip.c +++ b/kernel/irq/generic-chip.c @@ -276,21 +276,14 @@ irq_gc_init_mask_cache(struct irq_chip_generic *gc, enum irq_gc_flags flags) } /** - * __irq_alloc_domain_generic_chips - Allocate generic chips for an irq domain - * @d: irq domain for which to allocate chips - * @irqs_per_chip: Number of interrupts each chip handles (max 32) - * @num_ct: Number of irq_chip_type instances associated with this - * @name: Name of the irq chip - * @handler: Default flow handler associated with these chips - * @clr: IRQ_* bits to clear in the mapping function - * @set: IRQ_* bits to set in the mapping function - * @gcflags: Generic chip specific setup flags + * irq_domain_alloc_generic_chips - Allocate generic chips for an irq domain + * @d: irq domain for which to allocate chips + * @info: Generic chip information + * + * Return: 0 on success, negative error code on failure */ -int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip, - int num_ct, const char *name, - irq_flow_handler_t handler, - unsigned int clr, unsigned int set, - enum irq_gc_flags gcflags) +int irq_domain_alloc_generic_chips(struct irq_domain *d, + const struct irq_domain_chip_generic_info *info) { struct irq_domain_chip_generic *dgc; struct irq_chip_generic *gc; @@ -300,27 +293,29 @@ int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip, size_t gc_sz; size_t sz; void *tmp; + int ret; if (d->gc) return -EBUSY; - numchips = DIV_ROUND_UP(d->revmap_size, irqs_per_chip); + numchips = DIV_ROUND_UP(d->revmap_size, info->irqs_per_chip); if (!numchips) return -EINVAL; /* Allocate a pointer, generic chip and chiptypes for each chip */ - gc_sz = struct_size(gc, chip_types, num_ct); + gc_sz = struct_size(gc, chip_types, info->num_ct); dgc_sz = struct_size(dgc, gc, numchips); sz = dgc_sz + numchips * gc_sz; tmp = dgc = kzalloc(sz, GFP_KERNEL); if (!dgc) return -ENOMEM; - dgc->irqs_per_chip = irqs_per_chip; + dgc->irqs_per_chip = info->irqs_per_chip; dgc->num_chips = numchips; - dgc->irq_flags_to_set = set; - dgc->irq_flags_to_clear = clr; - dgc->gc_flags = gcflags; + dgc->irq_flags_to_set = info->irq_flags_to_set; + dgc->irq_flags_to_clear = info->irq_flags_to_clear; + dgc->gc_flags = info->gc_flags; + dgc->exit = info->exit; d->gc = dgc; /* Calc pointer to the first generic chip */ @@ -328,15 +323,22 @@ int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip, for (i = 0; i < numchips; i++) { /* Store the pointer to the generic chip */ dgc->gc[i] = gc = tmp; - irq_init_generic_chip(gc, name, num_ct, i * irqs_per_chip, - NULL, handler); + irq_init_generic_chip(gc, info->name, info->num_ct, + i * dgc->irqs_per_chip, NULL, + info->handler); gc->domain = d; - if (gcflags & IRQ_GC_BE_IO) { + if (dgc->gc_flags & IRQ_GC_BE_IO) { gc->reg_readl = &irq_readl_be; gc->reg_writel = &irq_writel_be; } + if (info->init) { + ret = info->init(gc); + if (ret) + goto err; + } + raw_spin_lock_irqsave(&gc_lock, flags); list_add_tail(&gc->list, &gc_list); raw_spin_unlock_irqrestore(&gc_lock, flags); @@ -344,6 +346,69 @@ int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip, tmp += gc_sz; } return 0; + +err: + while (i--) { + if (dgc->exit) + dgc->exit(dgc->gc[i]); + irq_remove_generic_chip(dgc->gc[i], ~0U, 0, 0); + } + d->gc = NULL; + kfree(dgc); + return ret; +} +EXPORT_SYMBOL_GPL(irq_domain_alloc_generic_chips); + +/** + * irq_domain_remove_generic_chips - Remove generic chips from an irq domain + * @d: irq domain for which generic chips are to be removed + */ +void irq_domain_remove_generic_chips(struct irq_domain *d) +{ + struct irq_domain_chip_generic *dgc = d->gc; + unsigned int i; + + if (!dgc) + return; + + for (i = 0; i < dgc->num_chips; i++) { + if (dgc->exit) + dgc->exit(dgc->gc[i]); + irq_remove_generic_chip(dgc->gc[i], ~0U, 0, 0); + } + d->gc = NULL; + kfree(dgc); +} +EXPORT_SYMBOL_GPL(irq_domain_remove_generic_chips); + +/** + * __irq_alloc_domain_generic_chips - Allocate generic chips for an irq domain + * @d: irq domain for which to allocate chips + * @irqs_per_chip: Number of interrupts each chip handles (max 32) + * @num_ct: Number of irq_chip_type instances associated with this + * @name: Name of the irq chip + * @handler: Default flow handler associated with these chips + * @clr: IRQ_* bits to clear in the mapping function + * @set: IRQ_* bits to set in the mapping function + * @gcflags: Generic chip specific setup flags + */ +int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip, + int num_ct, const char *name, + irq_flow_handler_t handler, + unsigned int clr, unsigned int set, + enum irq_gc_flags gcflags) +{ + struct irq_domain_chip_generic_info info = { + .irqs_per_chip = irqs_per_chip, + .num_ct = num_ct, + .name = name, + .handler = handler, + .irq_flags_to_clear = clr, + .irq_flags_to_set = set, + .gc_flags = gcflags, + }; + + return irq_domain_alloc_generic_chips(d, &info); } EXPORT_SYMBOL_GPL(__irq_alloc_domain_generic_chips); diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index ed28059e9849..fe0272cd84a5 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -501,6 +501,16 @@ static inline struct irq_data *irqd_get_parent_data(struct irq_data *irqd) #ifdef CONFIG_GENERIC_IRQ_DEBUGFS #include <linux/debugfs.h> +struct irq_bit_descr { + unsigned int mask; + char *name; +}; + +#define BIT_MASK_DESCR(m) { .mask = m, .name = #m } + +void irq_debug_show_bits(struct seq_file *m, int ind, unsigned int state, + const struct irq_bit_descr *sd, int size); + void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *desc); static inline void irq_remove_debugfs_entry(struct irq_desc *desc) { diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index aadc8891cc16..cea8f6874b1f 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -111,6 +111,7 @@ EXPORT_SYMBOL_GPL(__irq_domain_alloc_fwnode); /** * irq_domain_free_fwnode - Free a non-OF-backed fwnode_handle + * @fwnode: fwnode_handle to free * * Free a fwnode_handle allocated with irq_domain_alloc_fwnode. */ @@ -127,27 +128,12 @@ void irq_domain_free_fwnode(struct fwnode_handle *fwnode) } EXPORT_SYMBOL_GPL(irq_domain_free_fwnode); -static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode, - unsigned int size, - irq_hw_number_t hwirq_max, - int direct_max, - const struct irq_domain_ops *ops, - void *host_data) +static int irq_domain_set_name(struct irq_domain *domain, + const struct fwnode_handle *fwnode, + enum irq_domain_bus_token bus_token) { - struct irqchip_fwid *fwid; - struct irq_domain *domain; - static atomic_t unknown_domains; - - if (WARN_ON((size && direct_max) || - (!IS_ENABLED(CONFIG_IRQ_DOMAIN_NOMAP) && direct_max) || - (direct_max && (direct_max != hwirq_max)))) - return NULL; - - domain = kzalloc_node(struct_size(domain, revmap, size), - GFP_KERNEL, of_node_to_nid(to_of_node(fwnode))); - if (!domain) - return NULL; + struct irqchip_fwid *fwid; if (is_fwnode_irqchip(fwnode)) { fwid = container_of(fwnode, struct irqchip_fwid, fwnode); @@ -155,17 +141,23 @@ static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode, switch (fwid->type) { case IRQCHIP_FWNODE_NAMED: case IRQCHIP_FWNODE_NAMED_ID: - domain->fwnode = fwnode; - domain->name = kstrdup(fwid->name, GFP_KERNEL); - if (!domain->name) { - kfree(domain); - return NULL; - } + domain->name = bus_token ? + kasprintf(GFP_KERNEL, "%s-%d", + fwid->name, bus_token) : + kstrdup(fwid->name, GFP_KERNEL); + if (!domain->name) + return -ENOMEM; domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED; break; default: - domain->fwnode = fwnode; domain->name = fwid->name; + if (bus_token) { + domain->name = kasprintf(GFP_KERNEL, "%s-%d", + fwid->name, bus_token); + if (!domain->name) + return -ENOMEM; + domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED; + } break; } } else if (is_of_node(fwnode) || is_acpi_device_node(fwnode) || @@ -177,42 +169,68 @@ static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode, * unhappy about. Replace them with ':', which does * the trick and is not as offensive as '\'... */ - name = kasprintf(GFP_KERNEL, "%pfw", fwnode); - if (!name) { - kfree(domain); - return NULL; - } + name = bus_token ? + kasprintf(GFP_KERNEL, "%pfw-%d", fwnode, bus_token) : + kasprintf(GFP_KERNEL, "%pfw", fwnode); + if (!name) + return -ENOMEM; domain->name = strreplace(name, '/', ':'); - domain->fwnode = fwnode; domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED; } if (!domain->name) { if (fwnode) pr_err("Invalid fwnode type for irqdomain\n"); - domain->name = kasprintf(GFP_KERNEL, "unknown-%d", - atomic_inc_return(&unknown_domains)); - if (!domain->name) { - kfree(domain); - return NULL; - } + domain->name = bus_token ? + kasprintf(GFP_KERNEL, "unknown-%d-%d", + atomic_inc_return(&unknown_domains), + bus_token) : + kasprintf(GFP_KERNEL, "unknown-%d", + atomic_inc_return(&unknown_domains)); + if (!domain->name) + return -ENOMEM; domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED; } - fwnode_handle_get(fwnode); - fwnode_dev_initialized(fwnode, true); + return 0; +} + +static struct irq_domain *__irq_domain_create(const struct irq_domain_info *info) +{ + struct irq_domain *domain; + int err; + + if (WARN_ON((info->size && info->direct_max) || + (!IS_ENABLED(CONFIG_IRQ_DOMAIN_NOMAP) && info->direct_max) || + (info->direct_max && info->direct_max != info->hwirq_max))) + return ERR_PTR(-EINVAL); + + domain = kzalloc_node(struct_size(domain, revmap, info->size), + GFP_KERNEL, of_node_to_nid(to_of_node(info->fwnode))); + if (!domain) + return ERR_PTR(-ENOMEM); + + err = irq_domain_set_name(domain, info->fwnode, info->bus_token); + if (err) { + kfree(domain); + return ERR_PTR(err); + } + + domain->fwnode = fwnode_handle_get(info->fwnode); + fwnode_dev_initialized(domain->fwnode, true); /* Fill structure */ INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL); - domain->ops = ops; - domain->host_data = host_data; - domain->hwirq_max = hwirq_max; + domain->ops = info->ops; + domain->host_data = info->host_data; + domain->bus_token = info->bus_token; + domain->hwirq_max = info->hwirq_max; - if (direct_max) + if (info->direct_max) domain->flags |= IRQ_DOMAIN_FLAG_NO_MAP; - domain->revmap_size = size; + domain->revmap_size = info->size; /* * Hierarchical domains use the domain lock of the root domain @@ -240,34 +258,64 @@ static void __irq_domain_publish(struct irq_domain *domain) pr_debug("Added domain %s\n", domain->name); } +static void irq_domain_free(struct irq_domain *domain) +{ + fwnode_dev_initialized(domain->fwnode, false); + fwnode_handle_put(domain->fwnode); + if (domain->flags & IRQ_DOMAIN_NAME_ALLOCATED) + kfree(domain->name); + kfree(domain); +} + /** - * __irq_domain_add() - Allocate a new irq_domain data structure - * @fwnode: firmware node for the interrupt controller - * @size: Size of linear map; 0 for radix mapping only - * @hwirq_max: Maximum number of interrupts supported by controller - * @direct_max: Maximum value of direct maps; Use ~0 for no limit; 0 for no - * direct mapping - * @ops: domain callbacks - * @host_data: Controller private data pointer + * irq_domain_instantiate() - Instantiate a new irq domain data structure + * @info: Domain information pointer pointing to the information for this domain * - * Allocates and initializes an irq_domain structure. - * Returns pointer to IRQ domain, or NULL on failure. + * Return: A pointer to the instantiated irq domain or an ERR_PTR value. */ -struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int size, - irq_hw_number_t hwirq_max, int direct_max, - const struct irq_domain_ops *ops, - void *host_data) +struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info) { struct irq_domain *domain; + int err; + + domain = __irq_domain_create(info); + if (IS_ERR(domain)) + return domain; - domain = __irq_domain_create(fwnode, size, hwirq_max, direct_max, - ops, host_data); - if (domain) - __irq_domain_publish(domain); + domain->flags |= info->domain_flags; + domain->exit = info->exit; + +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY + if (info->parent) { + domain->root = info->parent->root; + domain->parent = info->parent; + } +#endif + + if (info->dgc_info) { + err = irq_domain_alloc_generic_chips(domain, info->dgc_info); + if (err) + goto err_domain_free; + } + + if (info->init) { + err = info->init(domain); + if (err) + goto err_domain_gc_remove; + } + + __irq_domain_publish(domain); return domain; + +err_domain_gc_remove: + if (info->dgc_info) + irq_domain_remove_generic_chips(domain); +err_domain_free: + irq_domain_free(domain); + return ERR_PTR(err); } -EXPORT_SYMBOL_GPL(__irq_domain_add); +EXPORT_SYMBOL_GPL(irq_domain_instantiate); /** * irq_domain_remove() - Remove an irq domain. @@ -279,6 +327,9 @@ EXPORT_SYMBOL_GPL(__irq_domain_add); */ void irq_domain_remove(struct irq_domain *domain) { + if (domain->exit) + domain->exit(domain); + mutex_lock(&irq_domain_mutex); debugfs_remove_domain_dir(domain); @@ -294,13 +345,11 @@ void irq_domain_remove(struct irq_domain *domain) mutex_unlock(&irq_domain_mutex); - pr_debug("Removed domain %s\n", domain->name); + if (domain->flags & IRQ_DOMAIN_FLAG_DESTROY_GC) + irq_domain_remove_generic_chips(domain); - fwnode_dev_initialized(domain->fwnode, false); - fwnode_handle_put(domain->fwnode); - if (domain->flags & IRQ_DOMAIN_NAME_ALLOCATED) - kfree(domain->name); - kfree(domain); + pr_debug("Removed domain %s\n", domain->name); + irq_domain_free(domain); } EXPORT_SYMBOL_GPL(irq_domain_remove); @@ -360,10 +409,17 @@ struct irq_domain *irq_domain_create_simple(struct fwnode_handle *fwnode, const struct irq_domain_ops *ops, void *host_data) { + struct irq_domain_info info = { + .fwnode = fwnode, + .size = size, + .hwirq_max = size, + .ops = ops, + .host_data = host_data, + }; struct irq_domain *domain; - domain = __irq_domain_add(fwnode, size, size, 0, ops, host_data); - if (!domain) + domain = irq_domain_instantiate(&info); + if (IS_ERR(domain)) return NULL; if (first_irq > 0) { @@ -416,11 +472,20 @@ struct irq_domain *irq_domain_create_legacy(struct fwnode_handle *fwnode, const struct irq_domain_ops *ops, void *host_data) { + struct irq_domain_info info = { + .fwnode = fwnode, + .size = first_hwirq + size, + .hwirq_max = first_hwirq + size, + .ops = ops, + .host_data = host_data, + }; struct irq_domain *domain; - domain = __irq_domain_add(fwnode, first_hwirq + size, first_hwirq + size, 0, ops, host_data); - if (domain) - irq_domain_associate_many(domain, first_irq, first_hwirq, size); + domain = irq_domain_instantiate(&info); + if (IS_ERR(domain)) + return NULL; + + irq_domain_associate_many(domain, first_irq, first_hwirq, size); return domain; } @@ -438,7 +503,8 @@ struct irq_domain *irq_find_matching_fwspec(struct irq_fwspec *fwspec, struct fwnode_handle *fwnode = fwspec->fwnode; int rc; - /* We might want to match the legacy controller last since + /* + * We might want to match the legacy controller last since * it might potentially be set to match all interrupts in * the absence of a device node. This isn't a problem so far * yet though... @@ -982,6 +1048,12 @@ EXPORT_SYMBOL_GPL(__irq_resolve_mapping); /** * irq_domain_xlate_onecell() - Generic xlate for direct one cell bindings + * @d: Interrupt domain involved in the translation + * @ctrlr: The device tree node for the device whose interrupt is translated + * @intspec: The interrupt specifier data from the device tree + * @intsize: The number of entries in @intspec + * @out_hwirq: Pointer to storage for the hardware interrupt number + * @out_type: Pointer to storage for the interrupt type * * Device Tree IRQ specifier translation function which works with one cell * bindings where the cell value maps directly to the hwirq number. @@ -1000,6 +1072,12 @@ EXPORT_SYMBOL_GPL(irq_domain_xlate_onecell); /** * irq_domain_xlate_twocell() - Generic xlate for direct two cell bindings + * @d: Interrupt domain involved in the translation + * @ctrlr: The device tree node for the device whose interrupt is translated + * @intspec: The interrupt specifier data from the device tree + * @intsize: The number of entries in @intspec + * @out_hwirq: Pointer to storage for the hardware interrupt number + * @out_type: Pointer to storage for the interrupt type * * Device Tree IRQ specifier translation function which works with two cell * bindings where the cell values map directly to the hwirq number @@ -1018,6 +1096,12 @@ EXPORT_SYMBOL_GPL(irq_domain_xlate_twocell); /** * irq_domain_xlate_onetwocell() - Generic xlate for one or two cell bindings + * @d: Interrupt domain involved in the translation + * @ctrlr: The device tree node for the device whose interrupt is translated + * @intspec: The interrupt specifier data from the device tree + * @intsize: The number of entries in @intspec + * @out_hwirq: Pointer to storage for the hardware interrupt number + * @out_type: Pointer to storage for the interrupt type * * Device Tree IRQ specifier translation function which works with either one * or two cell bindings where the cell values map directly to the hwirq number @@ -1051,6 +1135,10 @@ EXPORT_SYMBOL_GPL(irq_domain_simple_ops); /** * irq_domain_translate_onecell() - Generic translate for direct one cell * bindings + * @d: Interrupt domain involved in the translation + * @fwspec: The firmware interrupt specifier to translate + * @out_hwirq: Pointer to storage for the hardware interrupt number + * @out_type: Pointer to storage for the interrupt type */ int irq_domain_translate_onecell(struct irq_domain *d, struct irq_fwspec *fwspec, @@ -1068,6 +1156,10 @@ EXPORT_SYMBOL_GPL(irq_domain_translate_onecell); /** * irq_domain_translate_twocell() - Generic translate for direct two cell * bindings + * @d: Interrupt domain involved in the translation + * @fwspec: The firmware interrupt specifier to translate + * @out_hwirq: Pointer to storage for the hardware interrupt number + * @out_type: Pointer to storage for the interrupt type * * Device Tree IRQ specifier translation function which works with two cell * bindings where the cell values map directly to the hwirq number @@ -1144,23 +1236,22 @@ struct irq_domain *irq_domain_create_hierarchy(struct irq_domain *parent, const struct irq_domain_ops *ops, void *host_data) { - struct irq_domain *domain; - - if (size) - domain = __irq_domain_create(fwnode, size, size, 0, ops, host_data); - else - domain = __irq_domain_create(fwnode, 0, ~0, 0, ops, host_data); - - if (domain) { - if (parent) - domain->root = parent->root; - domain->parent = parent; - domain->flags |= flags; + struct irq_domain_info info = { + .fwnode = fwnode, + .size = size, + .hwirq_max = size, + .ops = ops, + .host_data = host_data, + .domain_flags = flags, + .parent = parent, + }; + struct irq_domain *d; - __irq_domain_publish(domain); - } + if (!info.size) + info.hwirq_max = ~0U; - return domain; + d = irq_domain_instantiate(&info); + return IS_ERR(d) ? NULL : d; } EXPORT_SYMBOL_GPL(irq_domain_create_hierarchy); @@ -1932,13 +2023,26 @@ static void irq_domain_free_one_irq(struct irq_domain *domain, unsigned int virq static struct dentry *domain_dir; -static void -irq_domain_debug_show_one(struct seq_file *m, struct irq_domain *d, int ind) +static const struct irq_bit_descr irqdomain_flags[] = { + BIT_MASK_DESCR(IRQ_DOMAIN_FLAG_HIERARCHY), + BIT_MASK_DESCR(IRQ_DOMAIN_NAME_ALLOCATED), + BIT_MASK_DESCR(IRQ_DOMAIN_FLAG_IPI_PER_CPU), + BIT_MASK_DESCR(IRQ_DOMAIN_FLAG_IPI_SINGLE), + BIT_MASK_DESCR(IRQ_DOMAIN_FLAG_MSI), + BIT_MASK_DESCR(IRQ_DOMAIN_FLAG_ISOLATED_MSI), + BIT_MASK_DESCR(IRQ_DOMAIN_FLAG_NO_MAP), + BIT_MASK_DESCR(IRQ_DOMAIN_FLAG_MSI_PARENT), + BIT_MASK_DESCR(IRQ_DOMAIN_FLAG_MSI_DEVICE), + BIT_MASK_DESCR(IRQ_DOMAIN_FLAG_NONCORE), +}; + +static void irq_domain_debug_show_one(struct seq_file *m, struct irq_domain *d, int ind) { seq_printf(m, "%*sname: %s\n", ind, "", d->name); seq_printf(m, "%*ssize: %u\n", ind + 1, "", d->revmap_size); seq_printf(m, "%*smapped: %u\n", ind + 1, "", d->mapcount); seq_printf(m, "%*sflags: 0x%08x\n", ind +1 , "", d->flags); + irq_debug_show_bits(m, ind, d->flags, irqdomain_flags, ARRAY_SIZE(irqdomain_flags)); if (d->ops && d->ops->debug_show) d->ops->debug_show(m, d, NULL, ind + 1); #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 2024f89baea4..5fa0547ece0c 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -8,18 +8,34 @@ * This file contains common code to support Message Signaled Interrupts for * PCI compatible and non PCI compatible devices. */ -#include <linux/types.h> #include <linux/device.h> #include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/msi.h> +#include <linux/mutex.h> +#include <linux/pci.h> #include <linux/slab.h> #include <linux/sysfs.h> -#include <linux/pci.h> +#include <linux/types.h> +#include <linux/xarray.h> #include "internals.h" /** + * struct msi_device_data - MSI per device data + * @properties: MSI properties which are interesting to drivers + * @mutex: Mutex protecting the MSI descriptor store + * @__domains: Internal data for per device MSI domains + * @__iter_idx: Index to search the next entry for iterators + */ +struct msi_device_data { + unsigned long properties; + struct mutex mutex; + struct msi_dev_domain __domains[MSI_MAX_DEVICE_IRQDOMAINS]; + unsigned long __iter_idx; +}; + +/** * struct msi_ctrl - MSI internal management control structure * @domid: ID of the domain on which management operations should be done * @first: First (hardware) slot index to operate on @@ -1088,8 +1104,8 @@ bool msi_match_device_irq_domain(struct device *dev, unsigned int domid, return ret; } -int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev, - int nvec, msi_alloc_info_t *arg) +static int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev, + int nvec, msi_alloc_info_t *arg) { struct msi_domain_info *info = domain->host_data; struct msi_domain_ops *ops = info->ops; @@ -1097,77 +1113,6 @@ int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev, return ops->msi_prepare(domain, dev, nvec, arg); } -int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev, - int virq_base, int nvec, msi_alloc_info_t *arg) -{ - struct msi_domain_info *info = domain->host_data; - struct msi_domain_ops *ops = info->ops; - struct msi_ctrl ctrl = { - .domid = MSI_DEFAULT_DOMAIN, - .first = virq_base, - .last = virq_base + nvec - 1, - }; - struct msi_desc *desc; - struct xarray *xa; - int ret, virq; - - msi_lock_descs(dev); - - if (!msi_ctrl_valid(dev, &ctrl)) { - ret = -EINVAL; - goto unlock; - } - - ret = msi_domain_add_simple_msi_descs(dev, &ctrl); - if (ret) - goto unlock; - - xa = &dev->msi.data->__domains[ctrl.domid].store; - - for (virq = virq_base; virq < virq_base + nvec; virq++) { - desc = xa_load(xa, virq); - desc->irq = virq; - - ops->set_desc(arg, desc); - ret = irq_domain_alloc_irqs_hierarchy(domain, virq, 1, arg); - if (ret) - goto fail; - - irq_set_msi_desc(virq, desc); - } - msi_unlock_descs(dev); - return 0; - -fail: - for (--virq; virq >= virq_base; virq--) { - msi_domain_depopulate_descs(dev, virq, 1); - irq_domain_free_irqs_common(domain, virq, 1); - } - msi_domain_free_descs(dev, &ctrl); -unlock: - msi_unlock_descs(dev); - return ret; -} - -void msi_domain_depopulate_descs(struct device *dev, int virq_base, int nvec) -{ - struct msi_ctrl ctrl = { - .domid = MSI_DEFAULT_DOMAIN, - .first = virq_base, - .last = virq_base + nvec - 1, - }; - struct msi_desc *desc; - struct xarray *xa; - unsigned long idx; - - if (!msi_ctrl_valid(dev, &ctrl)) - return; - - xa = &dev->msi.data->__domains[ctrl.domid].store; - xa_for_each_range(xa, idx, desc, ctrl.first, ctrl.last) - desc->irq = 0; -} - /* * Carefully check whether the device can use reservation mode. If * reservation mode is enabled then the early activation will assign a diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c index 5c320c3f10a7..8cccdf40725a 100644 --- a/kernel/irq/proc.c +++ b/kernel/irq/proc.c @@ -461,10 +461,10 @@ int show_interrupts(struct seq_file *p, void *v) { static int prec; - unsigned long flags, any_count = 0; int i = *(loff_t *) v, j; struct irqaction *action; struct irq_desc *desc; + unsigned long flags; if (i > ACTUAL_NR_IRQS) return 0; @@ -488,10 +488,7 @@ int show_interrupts(struct seq_file *p, void *v) if (!desc || irq_settings_is_hidden(desc)) goto outsparse; - if (desc->kstat_irqs) - any_count = kstat_irqs_desc(desc, cpu_online_mask); - - if ((!desc->action || irq_desc_is_chained(desc)) && !any_count) + if (!desc->action || irq_desc_is_chained(desc) || !desc->kstat_irqs) goto outsparse; seq_printf(p, "%*d: ", prec, i); diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index 98b9622d372e..fb2c77368d18 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -148,9 +148,6 @@ static unsigned int get_symbol_offset(unsigned long pos) unsigned long kallsyms_sym_address(int idx) { - if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE)) - return kallsyms_addresses[idx]; - /* values are unsigned offsets if --absolute-percpu is not in effect */ if (!IS_ENABLED(CONFIG_KALLSYMS_ABSOLUTE_PERCPU)) return kallsyms_relative_base + (u32)kallsyms_offsets[idx]; @@ -325,7 +322,7 @@ static unsigned long get_symbol_pos(unsigned long addr, unsigned long symbol_start = 0, symbol_end = 0; unsigned long i, low, high, mid; - /* Do a binary search on the sorted kallsyms_addresses array. */ + /* Do a binary search on the sorted kallsyms_offsets array. */ low = 0; high = kallsyms_num_syms; diff --git a/kernel/kallsyms_internal.h b/kernel/kallsyms_internal.h index 85480274fc8f..9633782f8250 100644 --- a/kernel/kallsyms_internal.h +++ b/kernel/kallsyms_internal.h @@ -4,12 +4,6 @@ #include <linux/types.h> -/* - * These will be re-linked against their real values during the second link - * stage. Preliminary values must be provided in the linker script using the - * PROVIDE() directive so that the first link stage can complete successfully. - */ -extern const unsigned long kallsyms_addresses[]; extern const int kallsyms_offsets[]; extern const u8 kallsyms_names[]; diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index 52426665eecc..3c21c31796db 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c @@ -346,6 +346,7 @@ int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs, * /sys/kernel/livepatch/<patch>/enabled * /sys/kernel/livepatch/<patch>/transition * /sys/kernel/livepatch/<patch>/force + * /sys/kernel/livepatch/<patch>/replace * /sys/kernel/livepatch/<patch>/<object> * /sys/kernel/livepatch/<patch>/<object>/patched * /sys/kernel/livepatch/<patch>/<object>/<function,sympos> @@ -401,7 +402,7 @@ static ssize_t enabled_show(struct kobject *kobj, struct klp_patch *patch; patch = container_of(kobj, struct klp_patch, kobj); - return snprintf(buf, PAGE_SIZE-1, "%d\n", patch->enabled); + return sysfs_emit(buf, "%d\n", patch->enabled); } static ssize_t transition_show(struct kobject *kobj, @@ -410,8 +411,7 @@ static ssize_t transition_show(struct kobject *kobj, struct klp_patch *patch; patch = container_of(kobj, struct klp_patch, kobj); - return snprintf(buf, PAGE_SIZE-1, "%d\n", - patch == klp_transition_patch); + return sysfs_emit(buf, "%d\n", patch == klp_transition_patch); } static ssize_t force_store(struct kobject *kobj, struct kobj_attribute *attr, @@ -443,13 +443,24 @@ static ssize_t force_store(struct kobject *kobj, struct kobj_attribute *attr, return count; } +static ssize_t replace_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct klp_patch *patch; + + patch = container_of(kobj, struct klp_patch, kobj); + return sysfs_emit(buf, "%d\n", patch->replace); +} + static struct kobj_attribute enabled_kobj_attr = __ATTR_RW(enabled); static struct kobj_attribute transition_kobj_attr = __ATTR_RO(transition); static struct kobj_attribute force_kobj_attr = __ATTR_WO(force); +static struct kobj_attribute replace_kobj_attr = __ATTR_RO(replace); static struct attribute *klp_patch_attrs[] = { &enabled_kobj_attr.attr, &transition_kobj_attr.attr, &force_kobj_attr.attr, + &replace_kobj_attr.attr, NULL }; ATTRIBUTE_GROUPS(klp_patch); diff --git a/kernel/vmcore_info.c b/kernel/vmcore_info.c index 1d5eadd9dd61..8b4f8cc2e0ec 100644 --- a/kernel/vmcore_info.c +++ b/kernel/vmcore_info.c @@ -216,12 +216,8 @@ static int __init crash_save_vmcoreinfo_init(void) VMCOREINFO_SYMBOL(kallsyms_num_syms); VMCOREINFO_SYMBOL(kallsyms_token_table); VMCOREINFO_SYMBOL(kallsyms_token_index); -#ifdef CONFIG_KALLSYMS_BASE_RELATIVE VMCOREINFO_SYMBOL(kallsyms_offsets); VMCOREINFO_SYMBOL(kallsyms_relative_base); -#else - VMCOREINFO_SYMBOL(kallsyms_addresses); -#endif /* CONFIG_KALLSYMS_BASE_RELATIVE */ #endif /* CONFIG_KALLSYMS */ arch_crash_save_vmcoreinfo(); diff --git a/lib/sbitmap.c b/lib/sbitmap.c index 1e453f825c05..5e2e93307f0d 100644 --- a/lib/sbitmap.c +++ b/lib/sbitmap.c @@ -60,12 +60,30 @@ static inline void update_alloc_hint_after_get(struct sbitmap *sb, /* * See if we have deferred clears that we can batch move */ -static inline bool sbitmap_deferred_clear(struct sbitmap_word *map) +static inline bool sbitmap_deferred_clear(struct sbitmap_word *map, + unsigned int depth, unsigned int alloc_hint, bool wrap) { - unsigned long mask; + unsigned long mask, word_mask; - if (!READ_ONCE(map->cleared)) - return false; + guard(spinlock_irqsave)(&map->swap_lock); + + if (!map->cleared) { + if (depth == 0) + return false; + + word_mask = (~0UL) >> (BITS_PER_LONG - depth); + /* + * The current behavior is to always retry after moving + * ->cleared to word, and we change it to retry in case + * of any free bits. To avoid an infinite loop, we need + * to take wrap & alloc_hint into account, otherwise a + * soft lockup may occur. + */ + if (!wrap && alloc_hint) + word_mask &= ~((1UL << alloc_hint) - 1); + + return (READ_ONCE(map->word) & word_mask) != word_mask; + } /* * First get a stable cleared mask, setting the old mask to 0. @@ -85,6 +103,7 @@ int sbitmap_init_node(struct sbitmap *sb, unsigned int depth, int shift, bool alloc_hint) { unsigned int bits_per_word; + int i; if (shift < 0) shift = sbitmap_calculate_shift(depth); @@ -116,6 +135,9 @@ int sbitmap_init_node(struct sbitmap *sb, unsigned int depth, int shift, return -ENOMEM; } + for (i = 0; i < sb->map_nr; i++) + spin_lock_init(&sb->map[i].swap_lock); + return 0; } EXPORT_SYMBOL_GPL(sbitmap_init_node); @@ -126,7 +148,7 @@ void sbitmap_resize(struct sbitmap *sb, unsigned int depth) unsigned int i; for (i = 0; i < sb->map_nr; i++) - sbitmap_deferred_clear(&sb->map[i]); + sbitmap_deferred_clear(&sb->map[i], 0, 0, 0); sb->depth = depth; sb->map_nr = DIV_ROUND_UP(sb->depth, bits_per_word); @@ -179,7 +201,7 @@ static int sbitmap_find_bit_in_word(struct sbitmap_word *map, alloc_hint, wrap); if (nr != -1) break; - if (!sbitmap_deferred_clear(map)) + if (!sbitmap_deferred_clear(map, depth, alloc_hint, wrap)) break; } while (1); @@ -496,7 +518,7 @@ unsigned long __sbitmap_queue_get_batch(struct sbitmap_queue *sbq, int nr_tags, unsigned int map_depth = __map_depth(sb, index); unsigned long val; - sbitmap_deferred_clear(map); + sbitmap_deferred_clear(map, 0, 0, 0); val = READ_ONCE(map->word); if (val == (1UL << (map_depth - 1)) - 1) goto next; diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index faf37bafa3f8..ed8a7493524b 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -68,7 +68,7 @@ kbuild-file = $(or $(wildcard $(src)/Kbuild),$(src)/Makefile) # Read a file, replacing newlines with spaces # # Make 4.2 or later can read a file by using its builtin function. -ifneq ($(filter-out 3.% 4.0 4.1, $(MAKE_VERSION)),) +ifneq ($(filter-out 4.0 4.1, $(MAKE_VERSION)),) read-file = $(subst $(newline),$(space),$(file < $1)) else read-file = $(shell cat $1 2>/dev/null) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 29da0dc9776d..fe3668dc4954 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -409,12 +409,16 @@ cmd_dtc = $(HOSTCC) -E $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; DT_CHECK_CMD = $(DT_CHECKER) $(DT_CHECKER_FLAGS) -u $(srctree)/$(DT_BINDING_DIR) -p $(DT_TMP_SCHEMA) +# NOTE: +# Do not replace $(filter %.dtb %.dtbo, $^) with $(real-prereqs). When a single +# DTB is turned into a multi-blob DTB, $^ will contain header file dependencies +# recorded in the .*.cmd file. ifneq ($(CHECK_DTBS),) quiet_cmd_fdtoverlay = DTOVLCH $@ - cmd_fdtoverlay = $(objtree)/scripts/dtc/fdtoverlay -o $@ -i $(real-prereqs) ; $(DT_CHECK_CMD) $@ || true + cmd_fdtoverlay = $(objtree)/scripts/dtc/fdtoverlay -o $@ -i $(filter %.dtb %.dtbo, $^) ; $(DT_CHECK_CMD) $@ || true else quiet_cmd_fdtoverlay = DTOVL $@ - cmd_fdtoverlay = $(objtree)/scripts/dtc/fdtoverlay -o $@ -i $(real-prereqs) + cmd_fdtoverlay = $(objtree)/scripts/dtc/fdtoverlay -o $@ -i $(filter %.dtb %.dtbo, $^) endif $(multi-dtb-y): FORCE @@ -529,6 +533,7 @@ quiet_cmd_fit = FIT $@ cmd_fit = $(MAKE_FIT) -o $@ --arch $(UIMAGE_ARCH) --os linux \ --name '$(UIMAGE_NAME)' \ $(if $(findstring 1,$(KBUILD_VERBOSE)),-v) \ + $(if $(FIT_DECOMPOSE_DTBS),--decompose-dtbs) \ --compress $(FIT_COMPRESSION) -k $< @$(word 2,$^) # XZ diff --git a/scripts/Makefile.package b/scripts/Makefile.package index bf016af8bf8a..4a80584ec771 100644 --- a/scripts/Makefile.package +++ b/scripts/Makefile.package @@ -141,6 +141,19 @@ snap-pkg: cd $(objtree)/snap && \ snapcraft --target-arch=$(UTS_MACHINE) +# pacman-pkg +# --------------------------------------------------------------------------- + +PHONY += pacman-pkg +pacman-pkg: + @ln -srf $(srctree)/scripts/package/PKGBUILD $(objtree)/PKGBUILD + +objtree="$(realpath $(objtree))" \ + BUILDDIR="$(realpath $(objtree))/pacman" \ + CARCH="$(UTS_MACHINE)" \ + KBUILD_MAKEFLAGS="$(MAKEFLAGS)" \ + KBUILD_REVISION="$(shell $(srctree)/scripts/build-version)" \ + makepkg $(MAKEPKGOPTS) + # dir-pkg tar*-pkg - tarball targets # --------------------------------------------------------------------------- @@ -221,6 +234,7 @@ help: @echo ' bindeb-pkg - Build only the binary kernel deb package' @echo ' snap-pkg - Build only the binary kernel snap package' @echo ' (will connect to external hosts)' + @echo ' pacman-pkg - Build only the binary kernel pacman package' @echo ' dir-pkg - Build the kernel as a plain directory structure' @echo ' tar-pkg - Build the kernel as an uncompressed tarball' @echo ' targz-pkg - Build the kernel as a gzip compressed tarball' diff --git a/init/build-version b/scripts/build-version index 537d45815083..537d45815083 100755 --- a/init/build-version +++ b/scripts/build-version diff --git a/scripts/kconfig/array_size.h b/scripts/include/array_size.h index 26ba78d867d1..26ba78d867d1 100644 --- a/scripts/kconfig/array_size.h +++ b/scripts/include/array_size.h diff --git a/scripts/kconfig/hashtable.h b/scripts/include/hashtable.h index a0a2c8f5f639..a0a2c8f5f639 100644 --- a/scripts/kconfig/hashtable.h +++ b/scripts/include/hashtable.h diff --git a/scripts/kconfig/list.h b/scripts/include/list.h index 882859ddf9f4..409201cd495b 100644 --- a/scripts/kconfig/list.h +++ b/scripts/include/list.h @@ -128,6 +128,29 @@ static inline void list_del(struct list_head *entry) } /** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del_entry(list); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del_entry(list); + list_add_tail(list, head); +} + +/** * list_is_head - tests whether @list is the list @head * @list: the entry to test * @head: the head of the list @@ -167,6 +190,17 @@ static inline int list_empty(const struct list_head *head) list_entry((ptr)->next, type, member) /** + * list_last_entry - get the last element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_last_entry(ptr, type, member) \ + list_entry((ptr)->prev, type, member) + +/** * list_next_entry - get the next element in list * @pos: the type * to cursor * @member: the name of the list_head within the struct. @@ -175,6 +209,14 @@ static inline int list_empty(const struct list_head *head) list_entry((pos)->member.next, typeof(*(pos)), member) /** + * list_prev_entry - get the prev element in list + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. + */ +#define list_prev_entry(pos, member) \ + list_entry((pos)->member.prev, typeof(*(pos)), member) + +/** * list_entry_is_head - test if the entry points to the head of the list * @pos: the type * to cursor * @head: the head for your list. @@ -195,6 +237,17 @@ static inline int list_empty(const struct list_head *head) pos = list_next_entry(pos, member)) /** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_last_entry(head, typeof(*pos), member); \ + !list_entry_is_head(pos, head, member); \ + pos = list_prev_entry(pos, member)) + +/** * list_for_each_entry_safe - iterate over list of given type. Safe against removal of list entry * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage diff --git a/scripts/kconfig/list_types.h b/scripts/include/list_types.h index d935b7c5aa81..d935b7c5aa81 100644 --- a/scripts/kconfig/list_types.h +++ b/scripts/include/list_types.h diff --git a/scripts/install.sh b/scripts/install.sh index 9bb0fb44f04a..05d62ac513ee 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -20,6 +20,10 @@ do fi done +if [ -n "${INSTALL_PATH}" ] && ! [ -e "${INSTALL_PATH}" ]; then + mkdir -p "${INSTALL_PATH}" +fi + # User/arch may have a custom install script for file in "${HOME}/bin/${INSTALLKERNEL}" \ "/sbin/${INSTALLKERNEL}" \ diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index 47978efe4797..0ed873491bf5 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -6,7 +6,7 @@ * of the GNU General Public License, incorporated herein by reference. * * Usage: kallsyms [--all-symbols] [--absolute-percpu] - * [--base-relative] [--lto-clang] in.map > out.S + * [--lto-clang] in.map > out.S * * Table compression uses all the unused char codes on the symbols and * maps these to the most used substrings (tokens). For instance, it might @@ -36,8 +36,7 @@ struct sym_entry { unsigned long long addr; unsigned int len; unsigned int seq; - unsigned int start_pos; - unsigned int percpu_absolute; + bool percpu_absolute; unsigned char sym[]; }; @@ -63,7 +62,6 @@ static struct sym_entry **table; static unsigned int table_size, table_cnt; static int all_symbols; static int absolute_percpu; -static int base_relative; static int lto_clang; static int token_profit[0x10000]; @@ -76,7 +74,7 @@ static unsigned char best_table_len[256]; static void usage(void) { fprintf(stderr, "Usage: kallsyms [--all-symbols] [--absolute-percpu] " - "[--base-relative] [--lto-clang] in.map > out.S\n"); + "[--lto-clang] in.map > out.S\n"); exit(1); } @@ -183,7 +181,7 @@ static struct sym_entry *read_symbol(FILE *in, char **buf, size_t *buf_len) sym->len = len; sym->sym[0] = type; strcpy(sym_name(sym), name); - sym->percpu_absolute = 0; + sym->percpu_absolute = false; return sym; } @@ -259,12 +257,6 @@ static void shrink_table(void) } } table_cnt = pos; - - /* When valid symbol is not registered, exit to error */ - if (!table_cnt) { - fprintf(stderr, "No valid symbol.\n"); - exit(1); - } } static void read_map(const char *in) @@ -285,7 +277,7 @@ static void read_map(const char *in) if (!sym) continue; - sym->start_pos = table_cnt; + sym->seq = table_cnt; if (table_cnt >= table_size) { table_size += 10000; @@ -347,7 +339,7 @@ static int expand_symbol(const unsigned char *data, int len, char *result) return total; } -static int symbol_absolute(const struct sym_entry *s) +static bool symbol_absolute(const struct sym_entry *s) { return s->percpu_absolute; } @@ -400,7 +392,7 @@ static void write_src(void) { unsigned int i, k, off; unsigned int best_idx[256]; - unsigned int *markers; + unsigned int *markers, markers_cnt; char buf[KSYM_NAME_LEN]; printf("#include <asm/bitsperlong.h>\n"); @@ -420,7 +412,8 @@ static void write_src(void) /* table of offset markers, that give the offset in the compressed stream * every 256 symbols */ - markers = malloc(sizeof(unsigned int) * ((table_cnt + 255) / 256)); + markers_cnt = (table_cnt + 255) / 256; + markers = malloc(sizeof(*markers) * markers_cnt); if (!markers) { fprintf(stderr, "kallsyms failure: " "unable to allocate required memory\n"); @@ -462,21 +455,19 @@ static void write_src(void) } for (k = 0; k < table[i]->len; k++) printf(", 0x%02x", table[i]->sym[k]); - printf("\n"); - } - printf("\n"); - /* - * Now that we wrote out the compressed symbol names, restore the - * original names, which are needed in some of the later steps. - */ - for (i = 0; i < table_cnt; i++) { + /* + * Now that we wrote out the compressed symbol name, restore the + * original name and print it in the comment. + */ expand_symbol(table[i]->sym, table[i]->len, buf); strcpy((char *)table[i]->sym, buf); + printf("\t/* %s */\n", table[i]->sym); } + printf("\n"); output_label("kallsyms_markers"); - for (i = 0; i < ((table_cnt + 255) >> 8); i++) + for (i = 0; i < markers_cnt; i++) printf("\t.long\t%u\n", markers[i]); printf("\n"); @@ -497,54 +488,43 @@ static void write_src(void) printf("\t.short\t%d\n", best_idx[i]); printf("\n"); - if (!base_relative) - output_label("kallsyms_addresses"); - else - output_label("kallsyms_offsets"); + output_label("kallsyms_offsets"); for (i = 0; i < table_cnt; i++) { - if (base_relative) { - /* - * Use the offset relative to the lowest value - * encountered of all relative symbols, and emit - * non-relocatable fixed offsets that will be fixed - * up at runtime. - */ + /* + * Use the offset relative to the lowest value + * encountered of all relative symbols, and emit + * non-relocatable fixed offsets that will be fixed + * up at runtime. + */ - long long offset; - int overflow; - - if (!absolute_percpu) { - offset = table[i]->addr - relative_base; - overflow = (offset < 0 || offset > UINT_MAX); - } else if (symbol_absolute(table[i])) { - offset = table[i]->addr; - overflow = (offset < 0 || offset > INT_MAX); - } else { - offset = relative_base - table[i]->addr - 1; - overflow = (offset < INT_MIN || offset >= 0); - } - if (overflow) { - fprintf(stderr, "kallsyms failure: " - "%s symbol value %#llx out of range in relative mode\n", - symbol_absolute(table[i]) ? "absolute" : "relative", - table[i]->addr); - exit(EXIT_FAILURE); - } - printf("\t.long\t%#x /* %s */\n", (int)offset, table[i]->sym); - } else if (!symbol_absolute(table[i])) { - output_address(table[i]->addr); + long long offset; + int overflow; + + if (!absolute_percpu) { + offset = table[i]->addr - relative_base; + overflow = (offset < 0 || offset > UINT_MAX); + } else if (symbol_absolute(table[i])) { + offset = table[i]->addr; + overflow = (offset < 0 || offset > INT_MAX); } else { - printf("\tPTR\t%#llx\n", table[i]->addr); + offset = relative_base - table[i]->addr - 1; + overflow = (offset < INT_MIN || offset >= 0); + } + if (overflow) { + fprintf(stderr, "kallsyms failure: " + "%s symbol value %#llx out of range in relative mode\n", + symbol_absolute(table[i]) ? "absolute" : "relative", + table[i]->addr); + exit(EXIT_FAILURE); } + printf("\t.long\t%#x\t/* %s */\n", (int)offset, table[i]->sym); } printf("\n"); - if (base_relative) { - output_label("kallsyms_relative_base"); - output_address(relative_base); - printf("\n"); - } + output_label("kallsyms_relative_base"); + output_address(relative_base); + printf("\n"); if (lto_clang) for (i = 0; i < table_cnt; i++) @@ -553,10 +533,11 @@ static void write_src(void) sort_symbols_by_name(); output_label("kallsyms_seqs_of_names"); for (i = 0; i < table_cnt; i++) - printf("\t.byte 0x%02x, 0x%02x, 0x%02x\n", + printf("\t.byte 0x%02x, 0x%02x, 0x%02x\t/* %s */\n", (unsigned char)(table[i]->seq >> 16), (unsigned char)(table[i]->seq >> 8), - (unsigned char)(table[i]->seq >> 0)); + (unsigned char)(table[i]->seq >> 0), + table[i]->sym); printf("\n"); } @@ -780,7 +761,7 @@ static int compare_symbols(const void *a, const void *b) return wa - wb; /* sort by initial order, so that other symbols are left undisturbed */ - return sa->start_pos - sb->start_pos; + return sa->seq - sb->seq; } static void sort_symbols(void) @@ -800,7 +781,7 @@ static void make_percpus_absolute(void) * versions of this tool. */ table[i]->sym[0] = 'A'; - table[i]->percpu_absolute = 1; + table[i]->percpu_absolute = true; } } @@ -826,7 +807,6 @@ int main(int argc, char **argv) static const struct option long_options[] = { {"all-symbols", no_argument, &all_symbols, 1}, {"absolute-percpu", no_argument, &absolute_percpu, 1}, - {"base-relative", no_argument, &base_relative, 1}, {"lto-clang", no_argument, <o_clang, 1}, {}, }; @@ -847,8 +827,7 @@ int main(int argc, char **argv) if (absolute_percpu) make_percpus_absolute(); sort_symbols(); - if (base_relative) - record_relative_base(); + record_relative_base(); optimize_token_table(); write_src(); diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 8ad2c52d9b1f..3d7d454c54da 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -114,51 +114,54 @@ static void set_randconfig_seed(void) srand(seed); } -static bool randomize_choice_values(struct symbol *csym) +/** + * randomize_choice_values - randomize choice block + * + * @choice: menu entry for the choice + */ +static void randomize_choice_values(struct menu *choice) { - struct property *prop; - struct symbol *sym; - struct expr *e; - int cnt, def; + struct menu *menu; + int x; + int cnt = 0; /* - * If choice is mod then we may have more items selected - * and if no then no-one. - * In both cases stop. + * First, count the number of symbols to randomize. If sym_has_value() + * is true, it was specified by KCONFIG_ALLCONFIG. It needs to be + * respected. */ - if (csym->curr.tri != yes) - return false; + menu_for_each_sub_entry(menu, choice) { + struct symbol *sym = menu->sym; - prop = sym_get_choice_prop(csym); + if (sym && !sym_has_value(sym)) + cnt++; + } - /* count entries in choice block */ - cnt = 0; - expr_list_for_each_sym(prop->expr, e, sym) - cnt++; + while (cnt > 0) { + x = rand() % cnt; - /* - * find a random value and set it to yes, - * set the rest to no so we have only one set - */ - def = rand() % cnt; - - cnt = 0; - expr_list_for_each_sym(prop->expr, e, sym) { - if (def == cnt++) { - sym->def[S_DEF_USER].tri = yes; - csym->def[S_DEF_USER].val = sym; - } else { - sym->def[S_DEF_USER].tri = no; + menu_for_each_sub_entry(menu, choice) { + struct symbol *sym = menu->sym; + + if (sym && !sym_has_value(sym)) + x--; + + if (x < 0) { + sym->def[S_DEF_USER].tri = yes; + sym->flags |= SYMBOL_DEF_USER; + /* + * Move the selected item to the _tail_ because + * this needs to have a lower priority than the + * user input from KCONFIG_ALLCONFIG. + */ + list_move_tail(&sym->choice_link, + &choice->choice_members); + + break; + } } - sym->flags |= SYMBOL_DEF_USER; - /* clear VALID to get value calculated */ - sym->flags &= ~SYMBOL_VALID; + cnt--; } - csym->flags |= SYMBOL_DEF_USER; - /* clear VALID to get value calculated */ - csym->flags &= ~SYMBOL_VALID; - - return true; } enum conf_def_mode { @@ -169,9 +172,9 @@ enum conf_def_mode { def_random }; -static bool conf_set_all_new_symbols(enum conf_def_mode mode) +static void conf_set_all_new_symbols(enum conf_def_mode mode) { - struct symbol *sym, *csym; + struct menu *menu; int cnt; /* * can't go as the default in switch-case below, otherwise gcc whines @@ -180,7 +183,6 @@ static bool conf_set_all_new_symbols(enum conf_def_mode mode) int pby = 50; /* probability of bool = y */ int pty = 33; /* probability of tristate = y */ int ptm = 33; /* probability of tristate = m */ - bool has_changed = false; if (mode == def_random) { int n, p[3]; @@ -227,79 +229,51 @@ static bool conf_set_all_new_symbols(enum conf_def_mode mode) } } - for_all_symbols(sym) { - if (sym_has_value(sym) || sym->flags & SYMBOL_VALID) - continue; - switch (sym_get_type(sym)) { - case S_BOOLEAN: - case S_TRISTATE: - has_changed = true; - switch (mode) { - case def_yes: - sym->def[S_DEF_USER].tri = yes; - break; - case def_mod: - sym->def[S_DEF_USER].tri = mod; - break; - case def_no: - sym->def[S_DEF_USER].tri = no; - break; - case def_random: - sym->def[S_DEF_USER].tri = no; - cnt = rand() % 100; - if (sym->type == S_TRISTATE) { - if (cnt < pty) - sym->def[S_DEF_USER].tri = yes; - else if (cnt < pty + ptm) - sym->def[S_DEF_USER].tri = mod; - } else if (cnt < pby) - sym->def[S_DEF_USER].tri = yes; - break; - default: - continue; - } - if (!(sym_is_choice(sym) && mode == def_random)) - sym->flags |= SYMBOL_DEF_USER; - break; - default: - break; - } - - } + menu_for_each_entry(menu) { + struct symbol *sym = menu->sym; + tristate val; - sym_clear_all_valid(); + if (!sym || !menu->prompt || sym_has_value(sym) || + (sym->type != S_BOOLEAN && sym->type != S_TRISTATE) || + sym_is_choice_value(sym)) + continue; - /* - * We have different type of choice blocks. - * If curr.tri equals to mod then we can select several - * choice symbols in one block. - * In this case we do nothing. - * If curr.tri equals yes then only one symbol can be - * selected in a choice block and we set it to yes, - * and the rest to no. - */ - if (mode != def_random) { - for_all_symbols(csym) { - if ((sym_is_choice(csym) && !sym_has_value(csym)) || - sym_is_choice_value(csym)) - csym->flags |= SYMBOL_NEED_SET_CHOICE_VALUES; + if (sym_is_choice(sym)) { + if (mode == def_random) + randomize_choice_values(menu); + continue; } - } - for_all_symbols(csym) { - if (sym_has_value(csym) || !sym_is_choice(csym)) + switch (mode) { + case def_yes: + val = yes; + break; + case def_mod: + val = mod; + break; + case def_no: + val = no; + break; + case def_random: + val = no; + cnt = rand() % 100; + if (sym->type == S_TRISTATE) { + if (cnt < pty) + val = yes; + else if (cnt < pty + ptm) + val = mod; + } else if (cnt < pby) { + val = yes; + } + break; + default: continue; - - sym_calc_value(csym); - if (mode == def_random) - has_changed |= randomize_choice_values(csym); - else { - set_all_choice_values(csym); - has_changed = true; } + sym->def[S_DEF_USER].tri = val; + sym->flags |= SYMBOL_DEF_USER; } - return has_changed; + sym_clear_all_valid(); } static void conf_rewrite_tristates(tristate old_val, tristate new_val) @@ -448,39 +422,15 @@ help: static void conf_choice(struct menu *menu) { - struct symbol *sym, *def_sym; + struct symbol *def_sym; struct menu *child; - bool is_new; - - sym = menu->sym; - is_new = !sym_has_value(sym); - if (sym_is_changeable(sym)) { - conf_sym(menu); - sym_calc_value(sym); - switch (sym_get_tristate_value(sym)) { - case no: - case mod: - return; - case yes: - break; - } - } else { - switch (sym_get_tristate_value(sym)) { - case no: - return; - case mod: - printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu)); - return; - case yes: - break; - } - } + bool is_new = false; while (1) { int cnt, def; printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu)); - def_sym = sym_get_choice_value(sym); + def_sym = sym_calc_choice(menu); cnt = def = 0; line[0] = 0; for (child = menu->list; child; child = child->next) { @@ -498,8 +448,10 @@ static void conf_choice(struct menu *menu) printf("%*c", indent, ' '); printf(" %d. %s (%s)", cnt, menu_get_prompt(child), child->sym->name); - if (!sym_has_value(child->sym)) + if (!sym_has_value(child->sym)) { + is_new = true; printf(" (NEW)"); + } printf("\n"); } printf("%*schoice", indent - 1, ""); @@ -549,7 +501,7 @@ static void conf_choice(struct menu *menu) print_help(child); continue; } - sym_set_tristate_value(child->sym, yes); + choice_set_value(menu, child->sym); return; } } @@ -596,9 +548,7 @@ static void conf(struct menu *menu) if (sym_is_choice(sym)) { conf_choice(menu); - if (sym->curr.tri != mod) - return; - goto conf_childs; + return; } switch (sym->type) { @@ -630,10 +580,7 @@ static void check_conf(struct menu *menu) return; sym = menu->sym; - if (sym && !sym_has_value(sym) && - (sym_is_changeable(sym) || - (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes))) { - + if (sym && !sym_has_value(sym) && sym_is_changeable(sym)) { switch (input_mode) { case listnewconfig: if (sym->name) @@ -849,8 +796,7 @@ int main(int ac, char **av) conf_set_all_new_symbols(def_default); break; case randconfig: - /* Really nothing to do in this loop */ - while (conf_set_all_new_symbols(def_random)) ; + conf_set_all_new_symbols(def_random); break; case defconfig: conf_set_all_new_symbols(def_default); diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 85b53069ba7a..76193ce5a792 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -382,10 +382,7 @@ load: def_flags = SYMBOL_DEF << def; for_all_symbols(sym) { - sym->flags |= SYMBOL_CHANGED; sym->flags &= ~(def_flags|SYMBOL_VALID); - if (sym_is_choice(sym)) - sym->flags |= def_flags; switch (sym->type) { case S_INT: case S_HEX: @@ -399,6 +396,8 @@ load: } while (getline_stripped(&line, &line_asize, in) != -1) { + struct menu *choice; + conf_lineno++; if (!line[0]) /* blank line */ @@ -460,25 +459,14 @@ load: if (conf_set_sym_val(sym, def, def_flags, val)) continue; - if (sym && sym_is_choice_value(sym)) { - struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); - switch (sym->def[def].tri) { - case no: - break; - case mod: - if (cs->def[def].tri == yes) { - conf_warning("%s creates inconsistent choice state", sym->name); - cs->flags &= ~def_flags; - } - break; - case yes: - if (cs->def[def].tri != no) - conf_warning("override: %s changes choice state", sym->name); - cs->def[def].val = sym; - break; - } - cs->def[def].tri = EXPR_OR(cs->def[def].tri, sym->def[def].tri); - } + /* + * If this is a choice member, give it the highest priority. + * If conflicting CONFIG options are given from an input file, + * the last one wins. + */ + choice = sym_get_choice_menu(sym); + if (choice) + list_move(&sym->choice_link, &choice->choice_members); } free(line); fclose(in); @@ -489,7 +477,6 @@ load: int conf_read(const char *name) { struct symbol *sym; - int conf_unsaved = 0; conf_set_changed(false); @@ -520,23 +507,11 @@ int conf_read(const char *name) } else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE)) /* no previous value and not saved */ continue; - conf_unsaved++; + conf_set_changed(true); /* maybe print value in verbose mode... */ } - for_all_symbols(sym) { - if (sym_has_value(sym) && !sym_is_choice_value(sym)) { - /* Reset values of generates values, so they'll appear - * as new, if they should become visible, but that - * doesn't quite work if the Kconfig and the saved - * configuration disagree. - */ - if (sym->visible == no && !conf_unsaved) - sym->flags &= ~SYMBOL_DEF_USER; - } - } - - if (conf_warnings || conf_unsaved) + if (conf_warnings) conf_set_changed(true); return 0; @@ -784,35 +759,31 @@ int conf_write_defconfig(const char *filename) struct menu *choice; sym = menu->sym; - if (sym && !sym_is_choice(sym)) { - sym_calc_value(sym); - if (!(sym->flags & SYMBOL_WRITE)) - continue; - sym->flags &= ~SYMBOL_WRITE; - /* If we cannot change the symbol - skip */ - if (!sym_is_changeable(sym)) - continue; - /* If symbol equals to default value - skip */ - if (strcmp(sym_get_string_value(sym), sym_get_string_default(sym)) == 0) - continue; - /* - * If symbol is a choice value and equals to the - * default for a choice - skip. - */ - choice = sym_get_choice_menu(sym); - if (choice) { - struct symbol *ds; - - ds = sym_choice_default(choice->sym); - if (sym == ds) { - if ((sym->type == S_BOOLEAN) && - sym_get_tristate_value(sym) == yes) - continue; - } - } - print_symbol_for_dotconfig(out, sym); + if (!sym || sym_is_choice(sym)) + continue; + + sym_calc_value(sym); + if (!(sym->flags & SYMBOL_WRITE)) + continue; + sym->flags &= ~SYMBOL_WRITE; + /* Skip unchangeable symbols */ + if (!sym_is_changeable(sym)) + continue; + /* Skip symbols that are equal to the default */ + if (!strcmp(sym_get_string_value(sym), sym_get_string_default(sym))) + continue; + + /* Skip choice values that are equal to the default */ + choice = sym_get_choice_menu(sym); + if (choice) { + struct symbol *ds; + + ds = sym_choice_default(choice); + if (sym == ds && sym_get_tristate_value(sym) == yes) + continue; } + print_symbol_for_dotconfig(out, sym); } fclose(out); return 0; @@ -1141,16 +1112,14 @@ int conf_write_autoconf(int overwrite) } static bool conf_changed; -static void (*conf_changed_callback)(void); +static void (*conf_changed_callback)(bool); void conf_set_changed(bool val) { - bool changed = conf_changed != val; + if (conf_changed_callback && conf_changed != val) + conf_changed_callback(val); conf_changed = val; - - if (conf_changed_callback && changed) - conf_changed_callback(); } bool conf_get_changed(void) @@ -1158,27 +1127,7 @@ bool conf_get_changed(void) return conf_changed; } -void conf_set_changed_callback(void (*fn)(void)) +void conf_set_changed_callback(void (*fn)(bool)) { conf_changed_callback = fn; } - -void set_all_choice_values(struct symbol *csym) -{ - struct property *prop; - struct symbol *sym; - struct expr *e; - - prop = sym_get_choice_prop(csym); - - /* - * Set all non-assinged choice values to no - */ - expr_list_for_each_sym(prop->expr, e, sym) { - if (!sym_has_value(sym)) - sym->def[S_DEF_USER].tri = no; - } - csym->flags |= SYMBOL_DEF_USER; - /* clear VALID to get value calculated */ - csym->flags &= ~(SYMBOL_VALID | SYMBOL_NEED_SET_CHOICE_VALUES); -} diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c index fcc190b67b6f..c349da7fe3f8 100644 --- a/scripts/kconfig/expr.c +++ b/scripts/kconfig/expr.c @@ -90,7 +90,6 @@ struct expr *expr_copy(const struct expr *org) break; case E_AND: case E_OR: - case E_LIST: e->left.expr = expr_copy(org->left.expr); e->right.expr = expr_copy(org->right.expr); break; @@ -136,9 +135,6 @@ void expr_free(struct expr *e) static int trans_count; -#define e1 (*ep1) -#define e2 (*ep2) - /* * expr_eliminate_eq() helper. * @@ -151,38 +147,38 @@ static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct e { /* Recurse down to leaves */ - if (e1->type == type) { - __expr_eliminate_eq(type, &e1->left.expr, &e2); - __expr_eliminate_eq(type, &e1->right.expr, &e2); + if ((*ep1)->type == type) { + __expr_eliminate_eq(type, &(*ep1)->left.expr, ep2); + __expr_eliminate_eq(type, &(*ep1)->right.expr, ep2); return; } - if (e2->type == type) { - __expr_eliminate_eq(type, &e1, &e2->left.expr); - __expr_eliminate_eq(type, &e1, &e2->right.expr); + if ((*ep2)->type == type) { + __expr_eliminate_eq(type, ep1, &(*ep2)->left.expr); + __expr_eliminate_eq(type, ep1, &(*ep2)->right.expr); return; } - /* e1 and e2 are leaves. Compare them. */ + /* *ep1 and *ep2 are leaves. Compare them. */ - if (e1->type == E_SYMBOL && e2->type == E_SYMBOL && - e1->left.sym == e2->left.sym && - (e1->left.sym == &symbol_yes || e1->left.sym == &symbol_no)) + if ((*ep1)->type == E_SYMBOL && (*ep2)->type == E_SYMBOL && + (*ep1)->left.sym == (*ep2)->left.sym && + ((*ep1)->left.sym == &symbol_yes || (*ep1)->left.sym == &symbol_no)) return; - if (!expr_eq(e1, e2)) + if (!expr_eq(*ep1, *ep2)) return; - /* e1 and e2 are equal leaves. Prepare them for elimination. */ + /* *ep1 and *ep2 are equal leaves. Prepare them for elimination. */ trans_count++; - expr_free(e1); expr_free(e2); + expr_free(*ep1); expr_free(*ep2); switch (type) { case E_OR: - e1 = expr_alloc_symbol(&symbol_no); - e2 = expr_alloc_symbol(&symbol_no); + *ep1 = expr_alloc_symbol(&symbol_no); + *ep2 = expr_alloc_symbol(&symbol_no); break; case E_AND: - e1 = expr_alloc_symbol(&symbol_yes); - e2 = expr_alloc_symbol(&symbol_yes); + *ep1 = expr_alloc_symbol(&symbol_yes); + *ep2 = expr_alloc_symbol(&symbol_yes); break; default: ; @@ -220,29 +216,26 @@ static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct e */ void expr_eliminate_eq(struct expr **ep1, struct expr **ep2) { - if (!e1 || !e2) + if (!*ep1 || !*ep2) return; - switch (e1->type) { + switch ((*ep1)->type) { case E_OR: case E_AND: - __expr_eliminate_eq(e1->type, ep1, ep2); + __expr_eliminate_eq((*ep1)->type, ep1, ep2); default: ; } - if (e1->type != e2->type) switch (e2->type) { + if ((*ep1)->type != (*ep2)->type) switch ((*ep2)->type) { case E_OR: case E_AND: - __expr_eliminate_eq(e2->type, ep1, ep2); + __expr_eliminate_eq((*ep2)->type, ep1, ep2); default: ; } - e1 = expr_eliminate_yn(e1); - e2 = expr_eliminate_yn(e2); + *ep1 = expr_eliminate_yn(*ep1); + *ep2 = expr_eliminate_yn(*ep2); } -#undef e1 -#undef e2 - /* * Returns true if 'e1' and 'e2' are equal, after minor simplification. Two * &&/|| expressions are considered equal if every operand in one expression @@ -286,7 +279,6 @@ int expr_eq(struct expr *e1, struct expr *e2) expr_free(e2); trans_count = old_count; return res; - case E_LIST: case E_RANGE: case E_NONE: /* panic */; @@ -566,59 +558,55 @@ static struct expr *expr_join_and(struct expr *e1, struct expr *e2) */ static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2) { -#define e1 (*ep1) -#define e2 (*ep2) struct expr *tmp; /* Recurse down to leaves */ - if (e1->type == type) { - expr_eliminate_dups1(type, &e1->left.expr, &e2); - expr_eliminate_dups1(type, &e1->right.expr, &e2); + if ((*ep1)->type == type) { + expr_eliminate_dups1(type, &(*ep1)->left.expr, ep2); + expr_eliminate_dups1(type, &(*ep1)->right.expr, ep2); return; } - if (e2->type == type) { - expr_eliminate_dups1(type, &e1, &e2->left.expr); - expr_eliminate_dups1(type, &e1, &e2->right.expr); + if ((*ep2)->type == type) { + expr_eliminate_dups1(type, ep1, &(*ep2)->left.expr); + expr_eliminate_dups1(type, ep1, &(*ep2)->right.expr); return; } - /* e1 and e2 are leaves. Compare and process them. */ + /* *ep1 and *ep2 are leaves. Compare and process them. */ - if (e1 == e2) + if (*ep1 == *ep2) return; - switch (e1->type) { + switch ((*ep1)->type) { case E_OR: case E_AND: - expr_eliminate_dups1(e1->type, &e1, &e1); + expr_eliminate_dups1((*ep1)->type, ep1, ep1); default: ; } switch (type) { case E_OR: - tmp = expr_join_or(e1, e2); + tmp = expr_join_or(*ep1, *ep2); if (tmp) { - expr_free(e1); expr_free(e2); - e1 = expr_alloc_symbol(&symbol_no); - e2 = tmp; + expr_free(*ep1); expr_free(*ep2); + *ep1 = expr_alloc_symbol(&symbol_no); + *ep2 = tmp; trans_count++; } break; case E_AND: - tmp = expr_join_and(e1, e2); + tmp = expr_join_and(*ep1, *ep2); if (tmp) { - expr_free(e1); expr_free(e2); - e1 = expr_alloc_symbol(&symbol_yes); - e2 = tmp; + expr_free(*ep1); expr_free(*ep2); + *ep1 = expr_alloc_symbol(&symbol_yes); + *ep2 = tmp; trans_count++; } break; default: ; } -#undef e1 -#undef e2 } /* @@ -639,7 +627,7 @@ struct expr *expr_eliminate_dups(struct expr *e) return e; oldcount = trans_count; - while (1) { + do { trans_count = 0; switch (e->type) { case E_OR: case E_AND: @@ -647,11 +635,8 @@ struct expr *expr_eliminate_dups(struct expr *e) default: ; } - if (!trans_count) - /* No simplifications done in this pass. We're done */ - break; e = expr_eliminate_yn(e); - } + } while (trans_count); /* repeat until we get no more simplifications */ trans_count = oldcount; return e; } @@ -676,7 +661,6 @@ struct expr *expr_transform(struct expr *e) case E_LTH: case E_UNEQUAL: case E_SYMBOL: - case E_LIST: break; default: e->left.expr = expr_transform(e->left.expr); @@ -947,7 +931,6 @@ struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symb break; case E_SYMBOL: return expr_alloc_comp(type, e->left.sym, sym); - case E_LIST: case E_RANGE: case E_NONE: /* panic */; @@ -1083,29 +1066,27 @@ static int expr_compare_type(enum expr_type t1, enum expr_type t2) case E_GTH: if (t2 == E_EQUAL || t2 == E_UNEQUAL) return 1; + /* fallthrough */ case E_EQUAL: case E_UNEQUAL: if (t2 == E_NOT) return 1; + /* fallthrough */ case E_NOT: if (t2 == E_AND) return 1; + /* fallthrough */ case E_AND: if (t2 == E_OR) return 1; - case E_OR: - if (t2 == E_LIST) - return 1; - case E_LIST: - if (t2 == 0) - return 1; + /* fallthrough */ default: - return -1; + break; } return 0; } -void expr_print(struct expr *e, +void expr_print(const struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken) { @@ -1171,13 +1152,6 @@ void expr_print(struct expr *e, fn(data, NULL, " && "); expr_print(e->right.expr, fn, data, E_AND); break; - case E_LIST: - fn(data, e->right.sym, e->right.sym->name); - if (e->left.expr) { - fn(data, NULL, " ^ "); - expr_print(e->left.expr, fn, data, E_LIST); - } - break; case E_RANGE: fn(data, NULL, "["); fn(data, e->left.sym, e->left.sym->name); @@ -1237,7 +1211,7 @@ static void expr_print_gstr_helper(void *data, struct symbol *sym, const char *s str_printf(gs, " [=%s]", sym_str); } -void expr_gstr_print(struct expr *e, struct gstr *gs) +void expr_gstr_print(const struct expr *e, struct gstr *gs) { expr_print(e, expr_print_gstr_helper, gs, E_NONE); } diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index 7c0c242318bc..2bc96cd28253 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -12,12 +12,11 @@ extern "C" { #include <assert.h> #include <stdio.h> -#include "list_types.h" #ifndef __cplusplus #include <stdbool.h> #endif -#include "list_types.h" +#include <list_types.h> typedef enum tristate { no, mod, yes @@ -26,7 +25,7 @@ typedef enum tristate { enum expr_type { E_NONE, E_OR, E_AND, E_NOT, E_EQUAL, E_UNEQUAL, E_LTH, E_LEQ, E_GTH, E_GEQ, - E_LIST, E_SYMBOL, E_RANGE + E_SYMBOL, E_RANGE }; union expr_data { @@ -43,9 +42,6 @@ struct expr { #define EXPR_AND(dep1, dep2) (((dep1)<(dep2))?(dep1):(dep2)) #define EXPR_NOT(dep) (2-(dep)) -#define expr_list_for_each_sym(l, e, s) \ - for (e = (l); e && (s = e->right.sym); e = e->left.expr) - struct expr_value { struct expr *expr; tristate tri; @@ -73,6 +69,8 @@ enum { * Represents a configuration symbol. * * Choices are represented as a special kind of symbol with null name. + * + * @choice_link: linked to menu::choice_members */ struct symbol { /* link node for the hash table */ @@ -110,6 +108,8 @@ struct symbol { /* config entries associated with this symbol */ struct list_head menus; + struct list_head choice_link; + /* SYMBOL_* flags */ int flags; @@ -130,10 +130,8 @@ struct symbol { #define SYMBOL_CONST 0x0001 /* symbol is const */ #define SYMBOL_CHECK 0x0008 /* used during dependency checking */ -#define SYMBOL_CHOICEVAL 0x0020 /* used as a value in a choice block */ #define SYMBOL_VALID 0x0080 /* set when symbol.curr is calculated */ #define SYMBOL_WRITE 0x0200 /* write symbol to file (KCONFIG_CONFIG) */ -#define SYMBOL_CHANGED 0x0400 /* ? */ #define SYMBOL_WRITTEN 0x0800 /* track info to avoid double-write to .config */ #define SYMBOL_CHECKED 0x2000 /* used during dependency checking */ #define SYMBOL_WARNED 0x8000 /* warning has been issued */ @@ -145,9 +143,6 @@ struct symbol { #define SYMBOL_DEF3 0x40000 /* symbol.def[S_DEF_3] is valid */ #define SYMBOL_DEF4 0x80000 /* symbol.def[S_DEF_4] is valid */ -/* choice values need to be set before calculating this symbol value */ -#define SYMBOL_NEED_SET_CHOICE_VALUES 0x100000 - #define SYMBOL_MAXLENGTH 256 /* A property represent the config options that can be associated @@ -170,7 +165,6 @@ enum prop_type { P_COMMENT, /* text associated with a comment */ P_MENU, /* prompt associated with a menu or menuconfig symbol */ P_DEFAULT, /* default y */ - P_CHOICE, /* choice value */ P_SELECT, /* select BAR */ P_IMPLY, /* imply BAR */ P_RANGE, /* range 7..100 (for a symbol) */ @@ -184,7 +178,7 @@ struct property { struct expr_value visible; struct expr *expr; /* the optional conditional part of the property */ struct menu *menu; /* the menu the property are associated with - * valid for: P_SELECT, P_RANGE, P_CHOICE, + * valid for: P_SELECT, P_RANGE, * P_PROMPT, P_DEFAULT, P_MENU, P_COMMENT */ const char *filename; /* what file was this property defined */ int lineno; /* what lineno was this property defined */ @@ -194,7 +188,6 @@ struct property { for (st = sym->prop; st; st = st->next) \ if (st->type == (tok)) #define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT) -#define for_all_choices(sym, st) for_all_properties(sym, st, P_CHOICE) #define for_all_prompts(sym, st) \ for (st = sym->prop; st; st = st->next) \ if (st->text) @@ -204,6 +197,8 @@ struct property { * for all front ends). Each symbol, menu, etc. defined in the Kconfig files * gets a node. A symbol defined in multiple locations gets one node at each * location. + * + * @choice_members: list of choice members with priority. */ struct menu { /* The next menu node at the same level */ @@ -223,6 +218,8 @@ struct menu { struct list_head link; /* link to symbol::menus */ + struct list_head choice_members; + /* * The prompt associated with the node. This holds the prompt for a * symbol as well as the text for a menu or comment, along with the @@ -292,11 +289,11 @@ struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symb void expr_fprint(struct expr *e, FILE *out); struct gstr; /* forward */ -void expr_gstr_print(struct expr *e, struct gstr *gs); +void expr_gstr_print(const struct expr *e, struct gstr *gs); void expr_gstr_print_revdep(struct expr *e, struct gstr *gs, tristate pr_type, const char *title); -static inline int expr_is_yes(struct expr *e) +static inline int expr_is_yes(const struct expr *e) { return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes); } diff --git a/scripts/kconfig/gconf-cfg.sh b/scripts/kconfig/gconf-cfg.sh index 040d8f338820..fc954c0538fa 100755 --- a/scripts/kconfig/gconf-cfg.sh +++ b/scripts/kconfig/gconf-cfg.sh @@ -1,6 +1,8 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 +set -eu + cflags=$1 libs=$2 diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index e04dbafd3add..c0f46f189060 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -65,9 +65,6 @@ static void display_list(void); static void display_tree(struct menu *menu); static void display_tree_part(void); static void update_tree(struct menu *src, GtkTreeIter * dst); -static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row); -static gchar **fill_row(struct menu *menu); -static void conf_changed(void); static void replace_button_icon(GladeXML *xml, GdkDrawable *window, GtkStyle *style, gchar *btn_name, gchar **xpm) @@ -87,6 +84,12 @@ static void replace_button_icon(GladeXML *xml, GdkDrawable *window, gtk_tool_button_set_icon_widget(button, image); } +static void conf_changed(bool dirty) +{ + gtk_widget_set_sensitive(save_btn, dirty); + gtk_widget_set_sensitive(save_menu_item, dirty); +} + /* Main Window Initialization */ static void init_main_window(const gchar *glade_file) { @@ -1051,7 +1054,7 @@ static gchar **fill_row(struct menu *menu) if (sym_is_choice(sym)) { // parse childs for getting final value struct menu *child; - struct symbol *def_sym = sym_get_choice_value(sym); + struct symbol *def_sym = sym_calc_choice(menu); struct menu *def_menu = NULL; for (child = menu->list; child; child = child->next) { @@ -1064,12 +1067,10 @@ static gchar **fill_row(struct menu *menu) row[COL_VALUE] = g_strdup(menu_get_prompt(def_menu)); - if (sym_get_type(sym) == S_BOOLEAN) { - row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); - return row; - } + row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); + return row; } - if (sym->flags & SYMBOL_CHOICEVAL) + if (sym_is_choice_value(sym)) row[COL_BTNRAD] = GINT_TO_POINTER(TRUE); stype = sym_get_type(sym); @@ -1447,10 +1448,3 @@ int main(int ac, char *av[]) return 0; } - -static void conf_changed(void) -{ - bool changed = conf_get_changed(); - gtk_widget_set_sensitive(save_btn, changed); - gtk_widget_set_sensitive(save_menu_item, changed); -} diff --git a/scripts/kconfig/internal.h b/scripts/kconfig/internal.h index 6c721c4cfd72..02106eb7815e 100644 --- a/scripts/kconfig/internal.h +++ b/scripts/kconfig/internal.h @@ -2,7 +2,7 @@ #ifndef INTERNAL_H #define INTERNAL_H -#include "hashtable.h" +#include <hashtable.h> #define SYMBOL_HASHSIZE (1U << 14) diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index 64dfc354dd5c..401bdf36323a 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -40,7 +40,6 @@ void zconf_nextfile(const char *name); /* confdata.c */ extern struct gstr autoconf_cmd; const char *conf_get_configname(void); -void set_all_choice_values(struct symbol *csym); /* confdata.c and expr.c */ static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out) @@ -76,7 +75,7 @@ struct gstr str_new(void); void str_free(struct gstr *gs); void str_append(struct gstr *gs, const char *s); void str_printf(struct gstr *gs, const char *fmt, ...); -char *str_get(struct gstr *gs); +char *str_get(const struct gstr *gs); /* menu.c */ struct menu *menu_next(struct menu *menu, struct menu *root); @@ -85,13 +84,14 @@ struct menu *menu_next(struct menu *menu, struct menu *root); #define menu_for_each_entry(menu) \ menu_for_each_sub_entry(menu, &rootmenu) void _menu_init(void); -void menu_warn(struct menu *menu, const char *fmt, ...); +void menu_warn(const struct menu *menu, const char *fmt, ...); struct menu *menu_add_menu(void); void menu_end_menu(void); void menu_add_entry(struct symbol *sym); void menu_add_dep(struct expr *dep); void menu_add_visibility(struct expr *dep); -struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep); +struct property *menu_add_prompt(enum prop_type type, const char *prompt, + struct expr *dep); void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep); void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep); void menu_finalize(void); @@ -101,8 +101,8 @@ extern struct menu rootmenu; bool menu_is_empty(struct menu *menu); bool menu_is_visible(struct menu *menu); -bool menu_has_prompt(struct menu *menu); -const char *menu_get_prompt(struct menu *menu); +bool menu_has_prompt(const struct menu *menu); +const char *menu_get_prompt(const struct menu *menu); struct menu *menu_get_parent_menu(struct menu *menu); int get_jump_key_char(void); struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head); @@ -110,35 +110,27 @@ void menu_get_ext_help(struct menu *menu, struct gstr *help); /* symbol.c */ void sym_clear_all_valid(void); -struct symbol *sym_choice_default(struct symbol *sym); +struct symbol *sym_choice_default(struct menu *choice); +struct symbol *sym_calc_choice(struct menu *choice); struct property *sym_get_range_prop(struct symbol *sym); const char *sym_get_string_default(struct symbol *sym); struct symbol *sym_check_deps(struct symbol *sym); -struct symbol *prop_get_symbol(struct property *prop); +struct symbol *prop_get_symbol(const struct property *prop); -static inline tristate sym_get_tristate_value(struct symbol *sym) +static inline tristate sym_get_tristate_value(const struct symbol *sym) { return sym->curr.tri; } - -static inline struct symbol *sym_get_choice_value(struct symbol *sym) -{ - return (struct symbol *)sym->curr.val; -} - -static inline bool sym_is_choice(struct symbol *sym) +static inline bool sym_is_choice(const struct symbol *sym) { /* A choice is a symbol with no name */ return sym->name == NULL; } -static inline bool sym_is_choice_value(struct symbol *sym) -{ - return sym->flags & SYMBOL_CHOICEVAL ? true : false; -} +bool sym_is_choice_value(const struct symbol *sym); -static inline bool sym_has_value(struct symbol *sym) +static inline bool sym_has_value(const struct symbol *sym) { return sym->flags & SYMBOL_DEF_USER ? true : false; } diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h index d76aaf4ea117..63519cd24bc7 100644 --- a/scripts/kconfig/lkc_proto.h +++ b/scripts/kconfig/lkc_proto.h @@ -13,7 +13,7 @@ int conf_write(const char *name); int conf_write_autoconf(int overwrite); void conf_set_changed(bool val); bool conf_get_changed(void); -void conf_set_changed_callback(void (*fn)(void)); +void conf_set_changed_callback(void (*fn)(bool)); void conf_set_message_callback(void (*fn)(const char *s)); bool conf_errors(void); @@ -25,21 +25,23 @@ struct symbol ** sym_re_search(const char *pattern); const char * sym_type_name(enum symbol_type type); void sym_calc_value(struct symbol *sym); bool sym_dep_errors(void); -enum symbol_type sym_get_type(struct symbol *sym); -bool sym_tristate_within_range(struct symbol *sym,tristate tri); +enum symbol_type sym_get_type(const struct symbol *sym); +bool sym_tristate_within_range(const struct symbol *sym, tristate tri); bool sym_set_tristate_value(struct symbol *sym,tristate tri); +void choice_set_value(struct menu *choice, struct symbol *sym); tristate sym_toggle_tristate_value(struct symbol *sym); bool sym_string_valid(struct symbol *sym, const char *newval); bool sym_string_within_range(struct symbol *sym, const char *str); bool sym_set_string_value(struct symbol *sym, const char *newval); -bool sym_is_changeable(struct symbol *sym); -struct property * sym_get_choice_prop(struct symbol *sym); -struct menu *sym_get_choice_menu(struct symbol *sym); +bool sym_is_changeable(const struct symbol *sym); +struct menu *sym_get_choice_menu(const struct symbol *sym); const char * sym_get_string_value(struct symbol *sym); const char * prop_get_type_name(enum prop_type type); /* expr.c */ -void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken); +void expr_print(const struct expr *e, + void (*fn)(void *, struct symbol *, const char *), + void *data, int prevtoken); #endif /* LKC_PROTO_H */ diff --git a/scripts/kconfig/mconf-cfg.sh b/scripts/kconfig/mconf-cfg.sh index 1e61f50a5905..1bc304dc2f7d 100755 --- a/scripts/kconfig/mconf-cfg.sh +++ b/scripts/kconfig/mconf-cfg.sh @@ -1,6 +1,8 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 +set -eu + cflags=$1 libs=$2 diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c index d6a61ca1a984..3887eac75289 100644 --- a/scripts/kconfig/mconf.c +++ b/scripts/kconfig/mconf.c @@ -19,7 +19,7 @@ #include <signal.h> #include <unistd.h> -#include "list.h" +#include <list.h> #include "lkc.h" #include "lxdialog/dialog.h" #include "mnconf-common.h" @@ -514,7 +514,7 @@ static void build_conf(struct menu *menu) type = sym_get_type(sym); if (sym_is_choice(sym)) { - struct symbol *def_sym = sym_get_choice_value(sym); + struct symbol *def_sym = sym_calc_choice(menu); struct menu *def_menu = NULL; child_count++; @@ -523,28 +523,14 @@ static void build_conf(struct menu *menu) def_menu = child; } - val = sym_get_tristate_value(sym); - if (sym_is_changeable(sym)) { - switch (val) { - case yes: ch = '*'; break; - case mod: ch = 'M'; break; - default: ch = ' '; break; - } - item_make("<%c>", ch); - item_set_tag('t'); - item_set_data(menu); - } else { - item_make(" "); - item_set_tag(def_menu ? 't' : ':'); - item_set_data(menu); - } + item_make(" "); + item_set_tag(def_menu ? 't' : ':'); + item_set_data(menu); item_add_str("%*c%s", indent + 1, ' ', menu_get_prompt(menu)); - if (val == yes) { - if (def_menu) - item_add_str(" (%s) --->", menu_get_prompt(def_menu)); - return; - } + if (def_menu) + item_add_str(" (%s) --->", menu_get_prompt(def_menu)); + return; } else { if (menu == current_menu) { item_make("---%*c%s", indent + 1, ' ', menu_get_prompt(menu)); @@ -614,7 +600,7 @@ static void conf_choice(struct menu *menu) struct menu *child; struct symbol *active; - active = sym_get_choice_value(menu->sym); + active = sym_calc_choice(menu); while (1) { int res; int selected; @@ -633,7 +619,7 @@ static void conf_choice(struct menu *menu) item_set_data(child); if (child->sym == active) item_set_selected(1); - if (child->sym == sym_get_choice_value(menu->sym)) + if (child->sym == sym_calc_choice(menu)) item_set_tag('X'); } dialog_clear(); @@ -650,7 +636,7 @@ static void conf_choice(struct menu *menu) if (!child->sym) break; - sym_set_tristate_value(child->sym, yes); + choice_set_value(menu, child->sym); } return; case 1: @@ -814,7 +800,7 @@ static void conf(struct menu *menu, struct menu *active_menu) conf(submenu, NULL); break; case 't': - if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes) + if (sym_is_choice(sym)) conf_choice(submenu); else if (submenu->prompt->type == P_MENU) conf(submenu, NULL); diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index eef9b63cdf11..323cc0b62be6 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -8,9 +8,9 @@ #include <stdlib.h> #include <string.h> +#include <list.h> #include "lkc.h" #include "internal.h" -#include "list.h" static const char nohelp_text[] = "There is no help available for this option."; @@ -38,7 +38,7 @@ struct menu *menu_next(struct menu *menu, struct menu *root) return menu->next; } -void menu_warn(struct menu *menu, const char *fmt, ...) +void menu_warn(const struct menu *menu, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -48,7 +48,7 @@ void menu_warn(struct menu *menu, const char *fmt, ...) va_end(ap); } -static void prop_warn(struct property *prop, const char *fmt, ...) +static void prop_warn(const struct property *prop, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -175,7 +175,7 @@ static struct property *menu_add_prop(enum prop_type type, struct expr *expr, return prop; } -struct property *menu_add_prompt(enum prop_type type, char *prompt, +struct property *menu_add_prompt(enum prop_type type, const char *prompt, struct expr *dep) { struct property *prop = menu_add_prop(type, NULL, dep); @@ -306,7 +306,7 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) struct menu *menu, *last_menu; struct symbol *sym; struct property *prop; - struct expr *parentdep, *basedep, *dep, *dep2, **ep; + struct expr *basedep, *dep, *dep2; sym = parent->sym; if (parent->list) { @@ -315,35 +315,6 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) * and propagate parent dependencies before moving on. */ - bool is_choice = false; - - if (sym && sym_is_choice(sym)) - is_choice = true; - - if (is_choice) { - if (sym->type == S_UNKNOWN) { - /* find the first choice value to find out choice type */ - current_entry = parent; - for (menu = parent->list; menu; menu = menu->next) { - if (menu->sym && menu->sym->type != S_UNKNOWN) { - menu_set_type(menu->sym->type); - break; - } - } - } - - /* - * Use the choice itself as the parent dependency of - * the contained items. This turns the mode of the - * choice into an upper bound on the visibility of the - * choice value symbols. - */ - parentdep = expr_alloc_symbol(sym); - } else { - /* Menu node for 'menu', 'if' */ - parentdep = parent->dep; - } - /* For each child menu node... */ for (menu = parent->list; menu; menu = menu->next) { /* @@ -352,7 +323,7 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) */ basedep = rewrite_m(menu->dep); basedep = expr_transform(basedep); - basedep = expr_alloc_and(expr_copy(parentdep), basedep); + basedep = expr_alloc_and(expr_copy(parent->dep), basedep); basedep = expr_eliminate_dups(basedep); menu->dep = basedep; @@ -416,15 +387,12 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) } } - if (is_choice) - expr_free(parentdep); - /* * Recursively process children in the same fashion before * moving on */ for (menu = parent->list; menu; menu = menu->next) - _menu_finalize(menu, is_choice); + _menu_finalize(menu, sym && sym_is_choice(sym)); } else if (!inside_choice && sym) { /* * Automatic submenu creation. If sym is a symbol and A, B, C, @@ -499,34 +467,6 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) sym->dir_dep.expr = expr_alloc_or(sym->dir_dep.expr, parent->dep); } for (menu = parent->list; menu; menu = menu->next) { - if (sym && sym_is_choice(sym) && - menu->sym && !sym_is_choice_value(menu->sym)) { - current_entry = menu; - menu->sym->flags |= SYMBOL_CHOICEVAL; - /* Non-tristate choice values of tristate choices must - * depend on the choice being set to Y. The choice - * values' dependencies were propagated to their - * properties above, so the change here must be re- - * propagated. - */ - if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) { - basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes); - menu->dep = expr_alloc_and(basedep, menu->dep); - for (prop = menu->sym->prop; prop; prop = prop->next) { - if (prop->menu != menu) - continue; - prop->visible.expr = expr_alloc_and(expr_copy(basedep), - prop->visible.expr); - } - } - menu_add_symbol(P_CHOICE, sym, NULL); - prop = sym_get_choice_prop(sym); - for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr) - ; - *ep = expr_alloc_one(E_LIST, NULL); - (*ep)->right.sym = menu->sym; - } - /* * This code serves two purposes: * @@ -575,17 +515,6 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) sym_check_prop(sym); sym->flags |= SYMBOL_WARNED; } - - /* - * For choices, add a reverse dependency (corresponding to a select) of - * '<visibility> && m'. This prevents the user from setting the choice - * mode to 'n' when the choice is visible. - */ - if (sym && sym_is_choice(sym) && parent->prompt) { - sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr, - expr_alloc_and(parent->prompt->visible.expr, - expr_alloc_symbol(&symbol_mod))); - } } void menu_finalize(void) @@ -593,7 +522,7 @@ void menu_finalize(void) _menu_finalize(&rootmenu, false); } -bool menu_has_prompt(struct menu *menu) +bool menu_has_prompt(const struct menu *menu) { if (!menu->prompt) return false; @@ -618,7 +547,6 @@ bool menu_is_empty(struct menu *menu) bool menu_is_visible(struct menu *menu) { - struct menu *child; struct symbol *sym; tristate visible; @@ -637,24 +565,10 @@ bool menu_is_visible(struct menu *menu) } else visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr); - if (visible != no) - return true; - - if (!sym || sym_get_tristate_value(menu->sym) == no) - return false; - - for (child = menu->list; child; child = child->next) { - if (menu_is_visible(child)) { - if (sym) - sym->flags |= SYMBOL_DEF_USER; - return true; - } - } - - return false; + return visible != no; } -const char *menu_get_prompt(struct menu *menu) +const char *menu_get_prompt(const struct menu *menu) { if (menu->prompt) return menu->prompt->text; @@ -675,13 +589,14 @@ struct menu *menu_get_parent_menu(struct menu *menu) return menu; } -static void get_def_str(struct gstr *r, struct menu *menu) +static void get_def_str(struct gstr *r, const struct menu *menu) { str_printf(r, "Defined at %s:%d\n", menu->filename, menu->lineno); } -static void get_dep_str(struct gstr *r, struct expr *expr, const char *prefix) +static void get_dep_str(struct gstr *r, const struct expr *expr, + const char *prefix) { if (!expr_is_yes(expr)) { str_append(r, prefix); diff --git a/scripts/kconfig/mnconf-common.c b/scripts/kconfig/mnconf-common.c index 18cb9a6c5aaa..8e24b07121df 100644 --- a/scripts/kconfig/mnconf-common.c +++ b/scripts/kconfig/mnconf-common.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only +#include <list.h> #include "expr.h" -#include "list.h" #include "mnconf-common.h" int jump_key_char; diff --git a/scripts/kconfig/mnconf-common.h b/scripts/kconfig/mnconf-common.h index ab6292cc4bf2..53bd7292e931 100644 --- a/scripts/kconfig/mnconf-common.h +++ b/scripts/kconfig/mnconf-common.h @@ -4,6 +4,8 @@ #include <stddef.h> +#include <list_types.h> + struct search_data { struct list_head *head; struct menu *target; diff --git a/scripts/kconfig/nconf-cfg.sh b/scripts/kconfig/nconf-cfg.sh index f871a2160e36..a20290b1a37d 100755 --- a/scripts/kconfig/nconf-cfg.sh +++ b/scripts/kconfig/nconf-cfg.sh @@ -1,6 +1,8 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 +set -eu + cflags=$1 libs=$2 diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index e1cb09418cbe..b91ca47e9e9a 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -11,7 +11,7 @@ #include <strings.h> #include <stdlib.h> -#include "list.h" +#include <list.h> #include "lkc.h" #include "mnconf-common.h" #include "nconf.h" @@ -815,7 +815,7 @@ static void build_conf(struct menu *menu) type = sym_get_type(sym); if (sym_is_choice(sym)) { - struct symbol *def_sym = sym_get_choice_value(sym); + struct symbol *def_sym = sym_calc_choice(menu); struct menu *def_menu = NULL; child_count++; @@ -825,30 +825,13 @@ static void build_conf(struct menu *menu) } val = sym_get_tristate_value(sym); - if (sym_is_changeable(sym)) { - switch (val) { - case yes: - ch = '*'; - break; - case mod: - ch = 'M'; - break; - default: - ch = ' '; - break; - } - item_make(menu, 't', "<%c>", ch); - } else { - item_make(menu, def_menu ? 't' : ':', " "); - } + item_make(menu, def_menu ? 't' : ':', " "); item_add_str("%*c%s", indent + 1, ' ', menu_get_prompt(menu)); - if (val == yes) { - if (def_menu) - item_add_str(" (%s) --->", menu_get_prompt(def_menu)); - return; - } + if (def_menu) + item_add_str(" (%s) --->", menu_get_prompt(def_menu)); + return; } else { if (menu == current_menu) { item_make(menu, ':', @@ -1191,8 +1174,7 @@ static void selected_conf(struct menu *menu, struct menu *active_menu) conf(submenu); break; case 't': - if (sym_is_choice(sym) && - sym_get_tristate_value(sym) == yes) + if (sym_is_choice(sym)) conf_choice(submenu); else if (submenu->prompt && submenu->prompt->type == P_MENU) @@ -1257,7 +1239,7 @@ static void conf_choice(struct menu *menu) .pattern = "", }; - active = sym_get_choice_value(menu->sym); + active = sym_calc_choice(menu); /* this is mostly duplicated from the conf() function. */ while (!global_exit) { reset_menu(); @@ -1266,7 +1248,7 @@ static void conf_choice(struct menu *menu) if (!show_all_items && !menu_is_visible(child)) continue; - if (child->sym == sym_get_choice_value(menu->sym)) + if (child->sym == sym_calc_choice(menu)) item_make(child, ':', "<X> %s", menu_get_prompt(child)); else if (child->sym) @@ -1349,7 +1331,7 @@ static void conf_choice(struct menu *menu) case ' ': case 10: case KEY_RIGHT: - sym_set_tristate_value(child->sym, yes); + choice_set_value(menu, child->sym); return; case 'h': case '?': diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y index ff709001b1f0..61900feb4254 100644 --- a/scripts/kconfig/parser.y +++ b/scripts/kconfig/parser.y @@ -28,9 +28,7 @@ static void zconf_error(const char *err, ...); static bool zconf_endtoken(const char *tokenname, const char *expected_tokenname); -struct menu *current_menu, *current_entry; - -static bool inside_choice = false; +struct menu *current_menu, *current_entry, *current_choice; %} @@ -90,7 +88,7 @@ static bool inside_choice = false; %type <symbol> nonconst_symbol %type <symbol> symbol -%type <type> type logic_type default +%type <type> type default %type <expr> expr %type <expr> if_expr %type <string> end @@ -147,12 +145,21 @@ config_entry_start: T_CONFIG nonconst_symbol T_EOL config_stmt: config_entry_start config_option_list { - if (inside_choice) { + if (current_choice) { if (!current_entry->prompt) { fprintf(stderr, "%s:%d: error: choice member must have a prompt\n", current_entry->filename, current_entry->lineno); yynerrs++; } + + if (current_entry->sym->type != S_BOOLEAN) { + fprintf(stderr, "%s:%d: error: choice member must be bool\n", + current_entry->filename, current_entry->lineno); + yynerrs++; + } + + list_add_tail(¤t_entry->sym->choice_link, + ¤t_choice->choice_members); } printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno); @@ -234,7 +241,9 @@ choice: T_CHOICE T_EOL struct symbol *sym = sym_lookup(NULL, 0); menu_add_entry(sym); - menu_add_expr(P_CHOICE, NULL, NULL); + menu_set_type(S_BOOLEAN); + INIT_LIST_HEAD(¤t_entry->choice_members); + printd(DEBUG_PARSE, "%s:%d:choice\n", cur_filename, cur_lineno); }; @@ -248,12 +257,12 @@ choice_entry: choice choice_option_list $$ = menu_add_menu(); - inside_choice = true; + current_choice = current_entry; }; choice_end: end { - inside_choice = false; + current_choice = NULL; if (zconf_endtoken($1, "choice")) { menu_end_menu(); @@ -277,10 +286,10 @@ choice_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL printd(DEBUG_PARSE, "%s:%d:prompt\n", cur_filename, cur_lineno); }; -choice_option: logic_type prompt_stmt_opt T_EOL +choice_option: T_BOOL T_WORD_QUOTE if_expr T_EOL { - menu_set_type($1); - printd(DEBUG_PARSE, "%s:%d:type(%u)\n", cur_filename, cur_lineno, $1); + menu_add_prompt(P_PROMPT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:bool\n", cur_filename, cur_lineno); }; choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL @@ -290,15 +299,12 @@ choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL }; type: - logic_type + T_BOOL { $$ = S_BOOLEAN; } + | T_TRISTATE { $$ = S_TRISTATE; } | T_INT { $$ = S_INT; } | T_HEX { $$ = S_HEX; } | T_STRING { $$ = S_STRING; } -logic_type: - T_BOOL { $$ = S_BOOLEAN; } - | T_TRISTATE { $$ = S_TRISTATE; } - default: T_DEFAULT { $$ = S_UNKNOWN; } | T_DEF_BOOL { $$ = S_BOOLEAN; } @@ -483,7 +489,7 @@ assign_val: * * Return: -1 if an error is found, 0 otherwise. */ -static int choice_check_sanity(struct menu *menu) +static int choice_check_sanity(const struct menu *menu) { struct property *prop; int ret = 0; @@ -638,7 +644,7 @@ static void print_quoted_string(FILE *out, const char *str) putc('"', out); } -static void print_symbol(FILE *out, struct menu *menu) +static void print_symbol(FILE *out, const struct menu *menu) { struct symbol *sym = menu->sym; struct property *prop; @@ -689,9 +695,6 @@ static void print_symbol(FILE *out, struct menu *menu) } fputc('\n', out); break; - case P_CHOICE: - fputs(" #choice value\n", out); - break; case P_SELECT: fputs( " select ", out); expr_fprint(prop->expr, out); diff --git a/scripts/kconfig/preprocess.c b/scripts/kconfig/preprocess.c index f0a4a218c4a5..67d1fb95c491 100644 --- a/scripts/kconfig/preprocess.c +++ b/scripts/kconfig/preprocess.c @@ -9,9 +9,9 @@ #include <stdlib.h> #include <string.h> -#include "array_size.h" +#include <array_size.h> +#include <list.h> #include "internal.h" -#include "list.h" #include "lkc.h" #include "preprocess.h" diff --git a/scripts/kconfig/qconf-cfg.sh b/scripts/kconfig/qconf-cfg.sh index 0e113b0f2455..bb2df66363a8 100755 --- a/scripts/kconfig/qconf-cfg.sh +++ b/scripts/kconfig/qconf-cfg.sh @@ -1,6 +1,8 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 +set -eu + cflags=$1 libs=$2 bin=$3 diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index c6c42c0f4e5d..7d239c032b3d 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -147,7 +147,7 @@ void ConfigItem::updateMenu(void) expr = sym_get_tristate_value(sym); switch (expr) { case yes: - if (sym_is_choice_value(sym) && type == S_BOOLEAN) + if (sym_is_choice_value(sym)) setIcon(promptColIdx, choiceYesIcon); else setIcon(promptColIdx, symbolYesIcon); @@ -1101,14 +1101,6 @@ QString ConfigInfoView::debug_info(struct symbol *sym) &stream, E_NONE); stream << "<br>"; break; - case P_CHOICE: - if (sym_is_choice(sym)) { - stream << "choice: "; - expr_print(prop->expr, expr_print_help, - &stream, E_NONE); - stream << "<br>"; - } - break; default: stream << "unknown property: "; stream << prop_get_type_name(prop->type); @@ -1397,8 +1389,6 @@ ConfigMainWindow::ConfigMainWindow(void) conf_set_changed_callback(conf_changed); - // Set saveAction's initial state - conf_changed(); configname = xstrdup(conf_get_configname()); QAction *saveAsAction = new QAction("Save &As...", this); @@ -1851,10 +1841,10 @@ void ConfigMainWindow::saveSettings(void) configSettings->writeSizes("/split2", split2->sizes()); } -void ConfigMainWindow::conf_changed(void) +void ConfigMainWindow::conf_changed(bool dirty) { if (saveAction) - saveAction->setEnabled(conf_get_changed()); + saveAction->setEnabled(dirty); } void fixup_rootmenu(struct menu *menu) @@ -1904,7 +1894,6 @@ int main(int ac, char** av) conf_parse(name); fixup_rootmenu(&rootmenu); - conf_read(NULL); //zconfdump(stdout); configApp = new QApplication(ac, av); @@ -1916,6 +1905,9 @@ int main(int ac, char** av) //zconfdump(stdout); configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit())); configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings())); + + conf_read(NULL); + v->show(); configApp->exec(); diff --git a/scripts/kconfig/qconf.h b/scripts/kconfig/qconf.h index 78b0a1dfcd53..53373064d90a 100644 --- a/scripts/kconfig/qconf.h +++ b/scripts/kconfig/qconf.h @@ -239,7 +239,7 @@ class ConfigMainWindow : public QMainWindow { char *configname; static QAction *saveAction; - static void conf_changed(void); + static void conf_changed(bool); public: ConfigMainWindow(void); public slots: diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 0e439d3d48d1..71502abd3b12 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -40,16 +40,12 @@ struct symbol *modules_sym; static tristate modules_val; static int sym_warnings; -enum symbol_type sym_get_type(struct symbol *sym) +enum symbol_type sym_get_type(const struct symbol *sym) { enum symbol_type type = sym->type; - if (type == S_TRISTATE) { - if (sym_is_choice_value(sym) && sym->visible == yes) - type = S_BOOLEAN; - else if (modules_val == no) - type = S_BOOLEAN; - } + if (type == S_TRISTATE && modules_val == no) + type = S_BOOLEAN; return type; } @@ -72,15 +68,6 @@ const char *sym_type_name(enum symbol_type type) return "???"; } -struct property *sym_get_choice_prop(struct symbol *sym) -{ - struct property *prop; - - for_all_choices(sym, prop) - return prop; - return NULL; -} - /** * sym_get_choice_menu - get the parent choice menu if present * @@ -88,7 +75,7 @@ struct property *sym_get_choice_prop(struct symbol *sym) * * Return: a choice menu if this function is called against a choice member. */ -struct menu *sym_get_choice_menu(struct symbol *sym) +struct menu *sym_get_choice_menu(const struct symbol *sym) { struct menu *menu = NULL; struct menu *m; @@ -192,7 +179,6 @@ static void sym_set_changed(struct symbol *sym) { struct menu *menu; - sym->flags |= SYMBOL_CHANGED; list_for_each_entry(menu, &sym->menus, link) menu->flags |= MENU_CHANGED; } @@ -208,26 +194,12 @@ static void sym_set_all_changed(void) static void sym_calc_visibility(struct symbol *sym) { struct property *prop; - struct symbol *choice_sym = NULL; tristate tri; /* any prompt visible? */ tri = no; - - if (sym_is_choice_value(sym)) - choice_sym = prop_get_symbol(sym_get_choice_prop(sym)); - for_all_prompts(sym, prop) { prop->visible.tri = expr_calc_value(prop->visible.expr); - /* - * Tristate choice_values with visibility 'mod' are - * not visible if the corresponding choice's value is - * 'yes'. - */ - if (choice_sym && sym->type == S_TRISTATE && - prop->visible.tri == mod && choice_sym->curr.tri == yes) - prop->visible.tri = no; - tri = EXPR_OR(tri, prop->visible.tri); } if (tri == mod && (sym->type != S_TRISTATE || modules_val == no)) @@ -274,14 +246,14 @@ static void sym_calc_visibility(struct symbol *sym) * Next locate the first visible choice value * Return NULL if none was found */ -struct symbol *sym_choice_default(struct symbol *sym) +struct symbol *sym_choice_default(struct menu *choice) { + struct menu *menu; struct symbol *def_sym; struct property *prop; - struct expr *e; /* any of the defaults visible? */ - for_all_defaults(sym, prop) { + for_all_defaults(choice->sym, prop) { prop->visible.tri = expr_calc_value(prop->visible.expr); if (prop->visible.tri == no) continue; @@ -291,48 +263,99 @@ struct symbol *sym_choice_default(struct symbol *sym) } /* just get the first visible value */ - prop = sym_get_choice_prop(sym); - expr_list_for_each_sym(prop->expr, e, def_sym) - if (def_sym->visible != no) - return def_sym; + menu_for_each_sub_entry(menu, choice) + if (menu->sym && menu->sym->visible != no) + return menu->sym; /* failed to locate any defaults */ return NULL; } -static struct symbol *sym_calc_choice(struct symbol *sym) +/* + * sym_calc_choice - calculate symbol values in a choice + * + * @choice: a menu of the choice + * + * Return: a chosen symbol + */ +struct symbol *sym_calc_choice(struct menu *choice) { - struct symbol *def_sym; - struct property *prop; - struct expr *e; - int flags; - - /* first calculate all choice values' visibilities */ - flags = sym->flags; - prop = sym_get_choice_prop(sym); - expr_list_for_each_sym(prop->expr, e, def_sym) { - sym_calc_visibility(def_sym); - if (def_sym->visible != no) - flags &= def_sym->flags; + struct symbol *res = NULL; + struct symbol *sym; + struct menu *menu; + + /* Traverse the list of choice members in the priority order. */ + list_for_each_entry(sym, &choice->choice_members, choice_link) { + sym_calc_visibility(sym); + if (sym->visible == no) + continue; + + /* The first visible symble with the user value 'y'. */ + if (sym_has_value(sym) && sym->def[S_DEF_USER].tri == yes) { + res = sym; + break; + } } - sym->flags &= flags | ~SYMBOL_DEF_USER; + /* + * If 'y' is not found in the user input, use the default, unless it is + * explicitly set to 'n'. + */ + if (!res) { + res = sym_choice_default(choice); + if (res && sym_has_value(res) && res->def[S_DEF_USER].tri == no) + res = NULL; + } - /* is the user choice visible? */ - def_sym = sym->def[S_DEF_USER].val; - if (def_sym && def_sym->visible != no) - return def_sym; + /* Still not found. Pick up the first visible, user-unspecified symbol. */ + if (!res) { + menu_for_each_sub_entry(menu, choice) { + sym = menu->sym; - def_sym = sym_choice_default(sym); + if (!sym || sym->visible == no || sym_has_value(sym)) + continue; - if (def_sym == NULL) - /* no choice? reset tristate value */ - sym->curr.tri = no; + res = sym; + break; + } + } + + /* + * Still not found. Traverse the linked list in the _reverse_ order to + * pick up the least prioritized 'n'. + */ + if (!res) { + list_for_each_entry_reverse(sym, &choice->choice_members, + choice_link) { + if (sym->visible == no) + continue; + + res = sym; + break; + } + } + + menu_for_each_sub_entry(menu, choice) { + tristate val; + + sym = menu->sym; + + if (!sym || sym->visible == no) + continue; + + val = sym == res ? yes : no; + + if (sym->curr.tri != val) + sym_set_changed(sym); + + sym->curr.tri = val; + sym->flags |= SYMBOL_VALID | SYMBOL_WRITE; + } - return def_sym; + return res; } -static void sym_warn_unmet_dep(struct symbol *sym) +static void sym_warn_unmet_dep(const struct symbol *sym) { struct gstr gs = str_new(); @@ -365,7 +388,7 @@ void sym_calc_value(struct symbol *sym) { struct symbol_value newval, oldval; struct property *prop; - struct expr *e; + struct menu *choice_menu; if (!sym) return; @@ -373,13 +396,6 @@ void sym_calc_value(struct symbol *sym) if (sym->flags & SYMBOL_VALID) return; - if (sym_is_choice_value(sym) && - sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES) { - sym->flags &= ~SYMBOL_NEED_SET_CHOICE_VALUES; - prop = sym_get_choice_prop(sym); - sym_calc_value(prop_get_symbol(prop)); - } - sym->flags |= SYMBOL_VALID; oldval = sym->curr; @@ -418,9 +434,11 @@ void sym_calc_value(struct symbol *sym) switch (sym_get_type(sym)) { case S_BOOLEAN: case S_TRISTATE: - if (sym_is_choice_value(sym) && sym->visible == yes) { - prop = sym_get_choice_prop(sym); - newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no; + choice_menu = sym_get_choice_menu(sym); + + if (choice_menu) { + sym_calc_choice(choice_menu); + newval.tri = sym->curr.tri; } else { if (sym->visible != no) { /* if the symbol is visible use the user value @@ -479,8 +497,6 @@ void sym_calc_value(struct symbol *sym) } sym->curr = newval; - if (sym_is_choice(sym) && newval.tri == yes) - sym->curr.val = sym_calc_choice(sym); sym_validate_range(sym); if (memcmp(&oldval, &sym->curr, sizeof(oldval))) { @@ -491,23 +507,8 @@ void sym_calc_value(struct symbol *sym) } } - if (sym_is_choice(sym)) { - struct symbol *choice_sym; - - prop = sym_get_choice_prop(sym); - expr_list_for_each_sym(prop->expr, e, choice_sym) { - if ((sym->flags & SYMBOL_WRITE) && - choice_sym->visible != no) - choice_sym->flags |= SYMBOL_WRITE; - if (sym->flags & SYMBOL_CHANGED) - sym_set_changed(choice_sym); - } - + if (sym_is_choice(sym)) sym->flags &= ~SYMBOL_WRITE; - } - - if (sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES) - set_all_choice_values(sym); } void sym_clear_all_valid(void) @@ -520,7 +521,7 @@ void sym_clear_all_valid(void) sym_calc_value(modules_sym); } -bool sym_tristate_within_range(struct symbol *sym, tristate val) +bool sym_tristate_within_range(const struct symbol *sym, tristate val) { int type = sym_get_type(sym); @@ -534,8 +535,6 @@ bool sym_tristate_within_range(struct symbol *sym, tristate val) return false; if (sym->visible <= sym->rev_dep.tri) return false; - if (sym_is_choice_value(sym) && sym->visible == yes) - return val == yes; return val >= sym->rev_dep.tri && val <= sym->visible; } @@ -543,42 +542,75 @@ bool sym_set_tristate_value(struct symbol *sym, tristate val) { tristate oldval = sym_get_tristate_value(sym); - if (oldval != val && !sym_tristate_within_range(sym, val)) + if (!sym_tristate_within_range(sym, val)) return false; - if (!(sym->flags & SYMBOL_DEF_USER)) { + if (!(sym->flags & SYMBOL_DEF_USER) || sym->def[S_DEF_USER].tri != val) { + sym->def[S_DEF_USER].tri = val; sym->flags |= SYMBOL_DEF_USER; sym_set_changed(sym); } - /* - * setting a choice value also resets the new flag of the choice - * symbol and all other choice values. - */ - if (sym_is_choice_value(sym) && val == yes) { - struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); - struct property *prop; - struct expr *e; - - cs->def[S_DEF_USER].val = sym; - cs->flags |= SYMBOL_DEF_USER; - prop = sym_get_choice_prop(cs); - for (e = prop->expr; e; e = e->left.expr) { - if (e->right.sym->visible != no) - e->right.sym->flags |= SYMBOL_DEF_USER; - } - } - sym->def[S_DEF_USER].tri = val; if (oldval != val) sym_clear_all_valid(); return true; } +/** + * choice_set_value - set the user input to a choice + * + * @choice: menu entry for the choice + * @sym: selected symbol + */ +void choice_set_value(struct menu *choice, struct symbol *sym) +{ + struct menu *menu; + bool changed = false; + + menu_for_each_sub_entry(menu, choice) { + tristate val; + + if (!menu->sym) + continue; + + if (menu->sym->visible == no) + continue; + + val = menu->sym == sym ? yes : no; + + if (menu->sym->curr.tri != val) + changed = true; + + menu->sym->def[S_DEF_USER].tri = val; + menu->sym->flags |= SYMBOL_DEF_USER; + + /* + * Now, the user has explicitly enabled or disabled this symbol, + * it should be given the highest priority. We are possibly + * setting multiple symbols to 'n', where the first symbol is + * given the least prioritized 'n'. This works well when the + * choice block ends up with selecting 'n' symbol. + * (see sym_calc_choice()) + */ + list_move(&menu->sym->choice_link, &choice->choice_members); + } + + if (changed) + sym_clear_all_valid(); +} + tristate sym_toggle_tristate_value(struct symbol *sym) { + struct menu *choice; tristate oldval, newval; + choice = sym_get_choice_menu(sym); + if (choice) { + choice_set_value(choice, sym); + return yes; + } + oldval = newval = sym_get_tristate_value(sym); do { switch (newval) { @@ -834,9 +866,14 @@ const char *sym_get_string_value(struct symbol *sym) return (const char *)sym->curr.val; } -bool sym_is_changeable(struct symbol *sym) +bool sym_is_changeable(const struct symbol *sym) +{ + return !sym_is_choice(sym) && sym->visible > sym->rev_dep.tri; +} + +bool sym_is_choice_value(const struct symbol *sym) { - return sym->visible > sym->rev_dep.tri; + return !list_empty(&sym->choice_link); } HASHTABLE_DEFINE(sym_hashtable, SYMBOL_HASHSIZE); @@ -876,6 +913,7 @@ struct symbol *sym_lookup(const char *name, int flags) symbol->type = S_UNKNOWN; symbol->flags = flags; INIT_LIST_HEAD(&symbol->menus); + INIT_LIST_HEAD(&symbol->choice_link); hash_add(sym_hashtable, &symbol->node, hash); @@ -1036,13 +1074,14 @@ static void sym_check_print_recursive(struct symbol *last_sym) { struct dep_stack *stack; struct symbol *sym, *next_sym; - struct menu *menu = NULL; - struct property *prop; + struct menu *choice; struct dep_stack cv_stack; + enum prop_type type; - if (sym_is_choice_value(last_sym)) { + choice = sym_get_choice_menu(last_sym); + if (choice) { dep_stack_insert(&cv_stack, last_sym); - last_sym = prop_get_symbol(sym_get_choice_prop(last_sym)); + last_sym = choice->sym; } for (stack = check_top; stack != NULL; stack = stack->prev) @@ -1056,59 +1095,37 @@ static void sym_check_print_recursive(struct symbol *last_sym) for (; stack; stack = stack->next) { sym = stack->sym; next_sym = stack->next ? stack->next->sym : last_sym; - prop = stack->prop; - if (prop == NULL) - prop = stack->sym->prop; - - /* for choice values find the menu entry (used below) */ - if (sym_is_choice(sym) || sym_is_choice_value(sym)) { - for (prop = sym->prop; prop; prop = prop->next) { - menu = prop->menu; - if (prop->menu) - break; - } - } + type = stack->prop ? stack->prop->type : P_UNKNOWN; + if (stack->sym == last_sym) - fprintf(stderr, "%s:%d:error: recursive dependency detected!\n", - prop->filename, prop->lineno); + fprintf(stderr, "error: recursive dependency detected!\n"); - if (sym_is_choice(sym)) { - fprintf(stderr, "%s:%d:\tchoice %s contains symbol %s\n", - menu->filename, menu->lineno, - sym->name ? sym->name : "<choice>", - next_sym->name ? next_sym->name : "<choice>"); - } else if (sym_is_choice_value(sym)) { - fprintf(stderr, "%s:%d:\tsymbol %s is part of choice %s\n", - menu->filename, menu->lineno, + if (sym_is_choice(next_sym)) { + choice = list_first_entry(&next_sym->menus, struct menu, link); + + fprintf(stderr, "\tsymbol %s is part of choice block at %s:%d\n", sym->name ? sym->name : "<choice>", - next_sym->name ? next_sym->name : "<choice>"); + choice->filename, choice->lineno); } else if (stack->expr == &sym->dir_dep.expr) { - fprintf(stderr, "%s:%d:\tsymbol %s depends on %s\n", - prop->filename, prop->lineno, + fprintf(stderr, "\tsymbol %s depends on %s\n", sym->name ? sym->name : "<choice>", - next_sym->name ? next_sym->name : "<choice>"); + next_sym->name); } else if (stack->expr == &sym->rev_dep.expr) { - fprintf(stderr, "%s:%d:\tsymbol %s is selected by %s\n", - prop->filename, prop->lineno, - sym->name ? sym->name : "<choice>", - next_sym->name ? next_sym->name : "<choice>"); + fprintf(stderr, "\tsymbol %s is selected by %s\n", + sym->name, next_sym->name); } else if (stack->expr == &sym->implied.expr) { - fprintf(stderr, "%s:%d:\tsymbol %s is implied by %s\n", - prop->filename, prop->lineno, - sym->name ? sym->name : "<choice>", - next_sym->name ? next_sym->name : "<choice>"); + fprintf(stderr, "\tsymbol %s is implied by %s\n", + sym->name, next_sym->name); } else if (stack->expr) { - fprintf(stderr, "%s:%d:\tsymbol %s %s value contains %s\n", - prop->filename, prop->lineno, + fprintf(stderr, "\tsymbol %s %s value contains %s\n", sym->name ? sym->name : "<choice>", - prop_get_type_name(prop->type), - next_sym->name ? next_sym->name : "<choice>"); + prop_get_type_name(type), + next_sym->name); } else { - fprintf(stderr, "%s:%d:\tsymbol %s %s is visible depending on %s\n", - prop->filename, prop->lineno, + fprintf(stderr, "\tsymbol %s %s is visible depending on %s\n", sym->name ? sym->name : "<choice>", - prop_get_type_name(prop->type), - next_sym->name ? next_sym->name : "<choice>"); + prop_get_type_name(type), + next_sym->name); } } @@ -1121,7 +1138,7 @@ static void sym_check_print_recursive(struct symbol *last_sym) dep_stack_remove(); } -static struct symbol *sym_check_expr_deps(struct expr *e) +static struct symbol *sym_check_expr_deps(const struct expr *e) { struct symbol *sym; @@ -1182,8 +1199,7 @@ static struct symbol *sym_check_sym_deps(struct symbol *sym) stack.expr = NULL; for (prop = sym->prop; prop; prop = prop->next) { - if (prop->type == P_CHOICE || prop->type == P_SELECT || - prop->type == P_IMPLY) + if (prop->type == P_SELECT || prop->type == P_IMPLY) continue; stack.prop = prop; sym2 = sym_check_expr_deps(prop->visible.expr); @@ -1237,9 +1253,13 @@ out: if (menu->sym) menu->sym->flags &= ~SYMBOL_CHECK; - if (sym2 && sym_is_choice_value(sym2) && - prop_get_symbol(sym_get_choice_prop(sym2)) == choice) - sym2 = choice; + if (sym2) { + struct menu *choice_menu2; + + choice_menu2 = sym_get_choice_menu(sym2); + if (choice_menu2 == choice_menu) + sym2 = choice; + } dep_stack_remove(); @@ -1248,8 +1268,8 @@ out: struct symbol *sym_check_deps(struct symbol *sym) { + struct menu *choice; struct symbol *sym2; - struct property *prop; if (sym->flags & SYMBOL_CHECK) { sym_check_print_recursive(sym); @@ -1258,13 +1278,13 @@ struct symbol *sym_check_deps(struct symbol *sym) if (sym->flags & SYMBOL_CHECKED) return NULL; - if (sym_is_choice_value(sym)) { + choice = sym_get_choice_menu(sym); + if (choice) { struct dep_stack stack; /* for choice groups start the check with main choice symbol */ dep_stack_insert(&stack, sym); - prop = sym_get_choice_prop(sym); - sym2 = sym_check_deps(prop_get_symbol(prop)); + sym2 = sym_check_deps(choice->sym); dep_stack_remove(); } else if (sym_is_choice(sym)) { sym2 = sym_check_choice_deps(sym); @@ -1277,10 +1297,9 @@ struct symbol *sym_check_deps(struct symbol *sym) return sym2; } -struct symbol *prop_get_symbol(struct property *prop) +struct symbol *prop_get_symbol(const struct property *prop) { - if (prop->expr && (prop->expr->type == E_SYMBOL || - prop->expr->type == E_LIST)) + if (prop->expr && prop->expr->type == E_SYMBOL) return prop->expr->left.sym; return NULL; } @@ -1296,8 +1315,6 @@ const char *prop_get_type_name(enum prop_type type) return "menu"; case P_DEFAULT: return "default"; - case P_CHOICE: - return "choice"; case P_SELECT: return "select"; case P_IMPLY: diff --git a/scripts/kconfig/tests/choice/Kconfig b/scripts/kconfig/tests/choice/Kconfig index 8cdda40868a1..cd252579a623 100644 --- a/scripts/kconfig/tests/choice/Kconfig +++ b/scripts/kconfig/tests/choice/Kconfig @@ -1,10 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -config MODULES - bool "Enable loadable module support" - modules - default y - choice prompt "boolean choice" default BOOL_CHOICE1 @@ -16,15 +11,3 @@ config BOOL_CHOICE1 bool "choice 1" endchoice - -choice - prompt "tristate choice" - default TRI_CHOICE1 - -config TRI_CHOICE0 - tristate "choice 0" - -config TRI_CHOICE1 - tristate "choice 1" - -endchoice diff --git a/scripts/kconfig/tests/choice/__init__.py b/scripts/kconfig/tests/choice/__init__.py index 05e162220085..0fc7bf9b5c78 100644 --- a/scripts/kconfig/tests/choice/__init__.py +++ b/scripts/kconfig/tests/choice/__init__.py @@ -1,11 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 """ Basic choice tests. - -The handling of 'choice' is a bit complicated part in Kconfig. - -The behavior of 'y' choice is intuitive. If choice values are tristate, -the choice can be 'm' where each value can be enabled independently. """ @@ -14,11 +9,6 @@ def test_oldask0(conf): assert conf.stdout_contains('oldask0_expected_stdout') -def test_oldask1(conf): - assert conf.oldaskconfig('oldask1_config') == 0 - assert conf.stdout_contains('oldask1_expected_stdout') - - def test_allyes(conf): assert conf.allyesconfig() == 0 assert conf.config_contains('allyes_expected_config') diff --git a/scripts/kconfig/tests/choice/alldef_expected_config b/scripts/kconfig/tests/choice/alldef_expected_config index 7a754bf4be94..b359a2e6493e 100644 --- a/scripts/kconfig/tests/choice/alldef_expected_config +++ b/scripts/kconfig/tests/choice/alldef_expected_config @@ -1,5 +1,2 @@ -CONFIG_MODULES=y # CONFIG_BOOL_CHOICE0 is not set CONFIG_BOOL_CHOICE1=y -# CONFIG_TRI_CHOICE0 is not set -# CONFIG_TRI_CHOICE1 is not set diff --git a/scripts/kconfig/tests/choice/allmod_expected_config b/scripts/kconfig/tests/choice/allmod_expected_config index d1f51651740c..b359a2e6493e 100644 --- a/scripts/kconfig/tests/choice/allmod_expected_config +++ b/scripts/kconfig/tests/choice/allmod_expected_config @@ -1,5 +1,2 @@ -CONFIG_MODULES=y # CONFIG_BOOL_CHOICE0 is not set CONFIG_BOOL_CHOICE1=y -CONFIG_TRI_CHOICE0=m -CONFIG_TRI_CHOICE1=m diff --git a/scripts/kconfig/tests/choice/allno_expected_config b/scripts/kconfig/tests/choice/allno_expected_config index b88ee7a43136..b359a2e6493e 100644 --- a/scripts/kconfig/tests/choice/allno_expected_config +++ b/scripts/kconfig/tests/choice/allno_expected_config @@ -1,5 +1,2 @@ -# CONFIG_MODULES is not set # CONFIG_BOOL_CHOICE0 is not set CONFIG_BOOL_CHOICE1=y -# CONFIG_TRI_CHOICE0 is not set -CONFIG_TRI_CHOICE1=y diff --git a/scripts/kconfig/tests/choice/allyes_expected_config b/scripts/kconfig/tests/choice/allyes_expected_config index 8a76c1816893..b359a2e6493e 100644 --- a/scripts/kconfig/tests/choice/allyes_expected_config +++ b/scripts/kconfig/tests/choice/allyes_expected_config @@ -1,5 +1,2 @@ -CONFIG_MODULES=y # CONFIG_BOOL_CHOICE0 is not set CONFIG_BOOL_CHOICE1=y -# CONFIG_TRI_CHOICE0 is not set -CONFIG_TRI_CHOICE1=y diff --git a/scripts/kconfig/tests/choice/oldask0_expected_stdout b/scripts/kconfig/tests/choice/oldask0_expected_stdout index d2257db46423..80ec34c61ebc 100644 --- a/scripts/kconfig/tests/choice/oldask0_expected_stdout +++ b/scripts/kconfig/tests/choice/oldask0_expected_stdout @@ -1,8 +1,4 @@ -Enable loadable module support (MODULES) [Y/n/?] (NEW) boolean choice 1. choice 0 (BOOL_CHOICE0) (NEW) > 2. choice 1 (BOOL_CHOICE1) (NEW) choice[1-2?]: -tristate choice [M/y/?] (NEW) - choice 0 (TRI_CHOICE0) [N/m/?] (NEW) - choice 1 (TRI_CHOICE1) [N/m/?] (NEW) diff --git a/scripts/kconfig/tests/choice/oldask1_config b/scripts/kconfig/tests/choice/oldask1_config deleted file mode 100644 index 0f417856c81c..000000000000 --- a/scripts/kconfig/tests/choice/oldask1_config +++ /dev/null @@ -1 +0,0 @@ -# CONFIG_MODULES is not set diff --git a/scripts/kconfig/tests/choice/oldask1_expected_stdout b/scripts/kconfig/tests/choice/oldask1_expected_stdout deleted file mode 100644 index ffa20ad7f38e..000000000000 --- a/scripts/kconfig/tests/choice/oldask1_expected_stdout +++ /dev/null @@ -1,9 +0,0 @@ -Enable loadable module support (MODULES) [N/y/?] -boolean choice - 1. choice 0 (BOOL_CHOICE0) (NEW) -> 2. choice 1 (BOOL_CHOICE1) (NEW) -choice[1-2?]: -tristate choice - 1. choice 0 (TRI_CHOICE0) (NEW) -> 2. choice 1 (TRI_CHOICE1) (NEW) -choice[1-2?]: diff --git a/scripts/kconfig/tests/choice_value_with_m_dep/Kconfig b/scripts/kconfig/tests/choice_value_with_m_dep/Kconfig deleted file mode 100644 index bd970cec07d6..000000000000 --- a/scripts/kconfig/tests/choice_value_with_m_dep/Kconfig +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -config MODULES - def_bool y - modules - -config DEP - tristate - default m - -choice - prompt "Tristate Choice" - -config CHOICE0 - tristate "Choice 0" - -config CHOICE1 - tristate "Choice 1" - depends on DEP - -endchoice diff --git a/scripts/kconfig/tests/choice_value_with_m_dep/__init__.py b/scripts/kconfig/tests/choice_value_with_m_dep/__init__.py deleted file mode 100644 index 075b4e08696e..000000000000 --- a/scripts/kconfig/tests/choice_value_with_m_dep/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -""" -Hide tristate choice values with mod dependency in y choice. - -If tristate choice values depend on symbols set to 'm', they should be -hidden when the choice containing them is changed from 'm' to 'y' -(i.e. exclusive choice). - -Related Linux commit: fa64e5f6a35efd5e77d639125d973077ca506074 -""" - - -def test(conf): - assert conf.oldaskconfig('config', 'y') == 0 - assert conf.config_contains('expected_config') - assert conf.stdout_contains('expected_stdout') diff --git a/scripts/kconfig/tests/choice_value_with_m_dep/config b/scripts/kconfig/tests/choice_value_with_m_dep/config deleted file mode 100644 index 3a126b7a2546..000000000000 --- a/scripts/kconfig/tests/choice_value_with_m_dep/config +++ /dev/null @@ -1,2 +0,0 @@ -CONFIG_CHOICE0=m -CONFIG_CHOICE1=m diff --git a/scripts/kconfig/tests/choice_value_with_m_dep/expected_config b/scripts/kconfig/tests/choice_value_with_m_dep/expected_config deleted file mode 100644 index 4d07b449540e..000000000000 --- a/scripts/kconfig/tests/choice_value_with_m_dep/expected_config +++ /dev/null @@ -1,3 +0,0 @@ -CONFIG_MODULES=y -CONFIG_DEP=m -CONFIG_CHOICE0=y diff --git a/scripts/kconfig/tests/choice_value_with_m_dep/expected_stdout b/scripts/kconfig/tests/choice_value_with_m_dep/expected_stdout deleted file mode 100644 index 2b50ab65c86a..000000000000 --- a/scripts/kconfig/tests/choice_value_with_m_dep/expected_stdout +++ /dev/null @@ -1,4 +0,0 @@ -Tristate Choice [M/y/?] y -Tristate Choice -> 1. Choice 0 (CHOICE0) -choice[1]: 1 diff --git a/scripts/kconfig/tests/err_recursive_dep/expected_stderr b/scripts/kconfig/tests/err_recursive_dep/expected_stderr index 05d4ced70320..fc2e860af082 100644 --- a/scripts/kconfig/tests/err_recursive_dep/expected_stderr +++ b/scripts/kconfig/tests/err_recursive_dep/expected_stderr @@ -1,38 +1,38 @@ -Kconfig:5:error: recursive dependency detected! -Kconfig:5: symbol A depends on A +error: recursive dependency detected! + symbol A depends on A For a resolution refer to Documentation/kbuild/kconfig-language.rst subsection "Kconfig recursive dependency limitations" -Kconfig:11:error: recursive dependency detected! -Kconfig:11: symbol B is selected by B +error: recursive dependency detected! + symbol B is selected by B For a resolution refer to Documentation/kbuild/kconfig-language.rst subsection "Kconfig recursive dependency limitations" -Kconfig:17:error: recursive dependency detected! -Kconfig:17: symbol C1 depends on C2 -Kconfig:21: symbol C2 depends on C1 +error: recursive dependency detected! + symbol C1 depends on C2 + symbol C2 depends on C1 For a resolution refer to Documentation/kbuild/kconfig-language.rst subsection "Kconfig recursive dependency limitations" -Kconfig:27:error: recursive dependency detected! -Kconfig:27: symbol D1 depends on D2 -Kconfig:32: symbol D2 is selected by D1 +error: recursive dependency detected! + symbol D1 depends on D2 + symbol D2 is selected by D1 For a resolution refer to Documentation/kbuild/kconfig-language.rst subsection "Kconfig recursive dependency limitations" -Kconfig:37:error: recursive dependency detected! -Kconfig:37: symbol E1 depends on E2 -Kconfig:42: symbol E2 is implied by E1 +error: recursive dependency detected! + symbol E1 depends on E2 + symbol E2 is implied by E1 For a resolution refer to Documentation/kbuild/kconfig-language.rst subsection "Kconfig recursive dependency limitations" -Kconfig:49:error: recursive dependency detected! -Kconfig:49: symbol F1 default value contains F2 -Kconfig:51: symbol F2 depends on F1 +error: recursive dependency detected! + symbol F1 default value contains F2 + symbol F2 depends on F1 For a resolution refer to Documentation/kbuild/kconfig-language.rst subsection "Kconfig recursive dependency limitations" -Kconfig:60:error: recursive dependency detected! -Kconfig:60: symbol G depends on G +error: recursive dependency detected! + symbol G depends on G For a resolution refer to Documentation/kbuild/kconfig-language.rst subsection "Kconfig recursive dependency limitations" diff --git a/scripts/kconfig/tests/inter_choice/Kconfig b/scripts/kconfig/tests/inter_choice/Kconfig deleted file mode 100644 index 26c25f68695b..000000000000 --- a/scripts/kconfig/tests/inter_choice/Kconfig +++ /dev/null @@ -1,25 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -config MODULES - def_bool y - modules - -choice - prompt "Choice" - -config CHOICE_VAL0 - tristate "Choice 0" - -config CHOIVE_VAL1 - tristate "Choice 1" - -endchoice - -choice - prompt "Another choice" - depends on CHOICE_VAL0 - -config DUMMY - bool "dummy" - -endchoice diff --git a/scripts/kconfig/tests/inter_choice/__init__.py b/scripts/kconfig/tests/inter_choice/__init__.py deleted file mode 100644 index ffea6b1148a6..000000000000 --- a/scripts/kconfig/tests/inter_choice/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -""" -Do not affect user-assigned choice value by another choice. - -Handling of state flags for choices is complecated. In old days, -the defconfig result of a choice could be affected by another choice -if those choices interact by 'depends on', 'select', etc. - -Related Linux commit: fbe98bb9ed3dae23e320c6b113e35f129538d14a -""" - - -def test(conf): - assert conf.defconfig('defconfig') == 0 - assert conf.config_contains('expected_config') diff --git a/scripts/kconfig/tests/inter_choice/defconfig b/scripts/kconfig/tests/inter_choice/defconfig deleted file mode 100644 index 162c4148e2a5..000000000000 --- a/scripts/kconfig/tests/inter_choice/defconfig +++ /dev/null @@ -1 +0,0 @@ -CONFIG_CHOICE_VAL0=y diff --git a/scripts/kconfig/tests/inter_choice/expected_config b/scripts/kconfig/tests/inter_choice/expected_config deleted file mode 100644 index 5dceefb054e3..000000000000 --- a/scripts/kconfig/tests/inter_choice/expected_config +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG_MODULES=y -CONFIG_CHOICE_VAL0=y -# CONFIG_CHOIVE_VAL1 is not set -CONFIG_DUMMY=y diff --git a/scripts/kconfig/util.c b/scripts/kconfig/util.c index 439c131b424e..696ff477671e 100644 --- a/scripts/kconfig/util.c +++ b/scripts/kconfig/util.c @@ -8,7 +8,7 @@ #include <stdlib.h> #include <string.h> -#include "hashtable.h" +#include <hashtable.h> #include "lkc.h" unsigned int strhash(const char *s) @@ -98,7 +98,7 @@ void str_printf(struct gstr *gs, const char *fmt, ...) } /* Retrieve value of growable string */ -char *str_get(struct gstr *gs) +char *str_get(const struct gstr *gs) { return gs->s; } diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index 518c70b8db50..f7b2503cdba9 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -45,7 +45,6 @@ info() # Link of vmlinux # ${1} - output file -# ${2}, ${3}, ... - optional extra .o files vmlinux_link() { local output=${1} @@ -90,7 +89,7 @@ vmlinux_link() ldflags="${ldflags} ${wl}--script=${objtree}/${KBUILD_LDS}" # The kallsyms linking does not need debug symbols included. - if [ "$output" != "${output#.tmp_vmlinux.kallsyms}" ] ; then + if [ -n "${strip_debug}" ] ; then ldflags="${ldflags} ${wl}--strip-debug" fi @@ -101,15 +100,15 @@ vmlinux_link() ${ld} ${ldflags} -o ${output} \ ${wl}--whole-archive ${objs} ${wl}--no-whole-archive \ ${wl}--start-group ${libs} ${wl}--end-group \ - $@ ${ldlibs} + ${kallsymso} ${btf_vmlinux_bin_o} ${ldlibs} } # generate .BTF typeinfo from DWARF debuginfo # ${1} - vmlinux image -# ${2} - file to dump raw BTF data into gen_btf() { local pahole_ver + local btf_data=${1}.btf.o if ! [ -x "$(command -v ${PAHOLE})" ]; then echo >&2 "BTF: ${1}: pahole (${PAHOLE}) is not available" @@ -122,18 +121,16 @@ gen_btf() return 1 fi - vmlinux_link ${1} - - info "BTF" ${2} + info BTF "${btf_data}" LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${PAHOLE_FLAGS} ${1} - # Create ${2} which contains just .BTF section but no symbols. Add + # Create ${btf_data} which contains just .BTF section but no symbols. Add # SHF_ALLOC because .BTF will be part of the vmlinux image. --strip-all # deletes all symbols including __start_BTF and __stop_BTF, which will # be redefined in the linker script. Add 2>/dev/null to suppress GNU # objcopy warnings: "empty loadable segment detected at ..." ${OBJCOPY} --only-section=.BTF --set-section-flags .BTF=alloc,readonly \ - --strip-all ${1} ${2} 2>/dev/null + --strip-all ${1} "${btf_data}" 2>/dev/null # Change e_type to ET_REL so that it can be used to link final vmlinux. # GNU ld 2.35+ and lld do not allow an ET_EXEC input. if is_enabled CONFIG_CPU_BIG_ENDIAN; then @@ -141,10 +138,12 @@ gen_btf() else et_rel='\1\0' fi - printf "${et_rel}" | dd of=${2} conv=notrunc bs=1 seek=16 status=none + printf "${et_rel}" | dd of="${btf_data}" conv=notrunc bs=1 seek=16 status=none + + btf_vmlinux_bin_o=${btf_data} } -# Create ${2} .S file with all symbols from the ${1} object file +# Create ${2}.o file with all symbols from the ${1} object file kallsyms() { local kallsymopt; @@ -157,35 +156,27 @@ kallsyms() kallsymopt="${kallsymopt} --absolute-percpu" fi - if is_enabled CONFIG_KALLSYMS_BASE_RELATIVE; then - kallsymopt="${kallsymopt} --base-relative" - fi - if is_enabled CONFIG_LTO_CLANG; then kallsymopt="${kallsymopt} --lto-clang" fi - info KSYMS ${2} - scripts/kallsyms ${kallsymopt} ${1} > ${2} + info KSYMS "${2}.S" + scripts/kallsyms ${kallsymopt} "${1}" > "${2}.S" + + info AS "${2}.o" + ${CC} ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS} \ + ${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL} -c -o "${2}.o" "${2}.S" + + kallsymso=${2}.o } -# Perform one step in kallsyms generation, including temporary linking of -# vmlinux. -kallsyms_step() +# Perform kallsyms for the given temporary vmlinux. +sysmap_and_kallsyms() { - kallsymso_prev=${kallsymso} - kallsyms_vmlinux=.tmp_vmlinux.kallsyms${1} - kallsymso=${kallsyms_vmlinux}.o - kallsyms_S=${kallsyms_vmlinux}.S - - vmlinux_link ${kallsyms_vmlinux} "${kallsymso_prev}" ${btf_vmlinux_bin_o} - mksysmap ${kallsyms_vmlinux} ${kallsyms_vmlinux}.syms - kallsyms ${kallsyms_vmlinux}.syms ${kallsyms_S} + mksysmap "${1}" "${1}.syms" + kallsyms "${1}.syms" "${1}.kallsyms" - info AS ${kallsymso} - ${CC} ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS} \ - ${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL} \ - -c -o ${kallsymso} ${kallsyms_S} + kallsyms_sysmap=${1}.syms } # Create map file with all symbols from ${1} @@ -223,26 +214,40 @@ fi ${MAKE} -f "${srctree}/scripts/Makefile.build" obj=init init/version-timestamp.o -btf_vmlinux_bin_o="" +btf_vmlinux_bin_o= +kallsymso= +strip_debug= + +if is_enabled CONFIG_KALLSYMS; then + kallsyms /dev/null .tmp_vmlinux0.kallsyms +fi + +if is_enabled CONFIG_KALLSYMS || is_enabled CONFIG_DEBUG_INFO_BTF; then + + # The kallsyms linking does not need debug symbols, but the BTF does. + if ! is_enabled CONFIG_DEBUG_INFO_BTF; then + strip_debug=1 + fi + + vmlinux_link .tmp_vmlinux1 +fi + if is_enabled CONFIG_DEBUG_INFO_BTF; then - btf_vmlinux_bin_o=.btf.vmlinux.bin.o - if ! gen_btf .tmp_vmlinux.btf $btf_vmlinux_bin_o ; then + if ! gen_btf .tmp_vmlinux1; then echo >&2 "Failed to generate BTF for vmlinux" echo >&2 "Try to disable CONFIG_DEBUG_INFO_BTF" exit 1 fi fi -kallsymso="" -kallsymso_prev="" -kallsyms_vmlinux="" if is_enabled CONFIG_KALLSYMS; then # kallsyms support # Generate section listing all symbols and add it into vmlinux - # It's a three step process: + # It's a four step process: + # 0) Generate a dummy __kallsyms with empty symbol list. # 1) Link .tmp_vmlinux.kallsyms1 so it has all symbols and sections, - # but __kallsyms is empty. + # with a dummy __kallsyms. # Running kallsyms on that gives us .tmp_kallsyms1.o with # the right size # 2) Link .tmp_vmlinux.kallsyms2 so it now has a __kallsyms section of @@ -261,19 +266,25 @@ if is_enabled CONFIG_KALLSYMS; then # a) Verify that the System.map from vmlinux matches the map from # ${kallsymso}. - kallsyms_step 1 - kallsyms_step 2 + # The kallsyms linking does not need debug symbols included. + strip_debug=1 - # step 3 - size1=$(${CONFIG_SHELL} "${srctree}/scripts/file-size.sh" ${kallsymso_prev}) + sysmap_and_kallsyms .tmp_vmlinux1 + size1=$(${CONFIG_SHELL} "${srctree}/scripts/file-size.sh" ${kallsymso}) + + vmlinux_link .tmp_vmlinux2 + sysmap_and_kallsyms .tmp_vmlinux2 size2=$(${CONFIG_SHELL} "${srctree}/scripts/file-size.sh" ${kallsymso}) if [ $size1 -ne $size2 ] || [ -n "${KALLSYMS_EXTRA_PASS}" ]; then - kallsyms_step 3 + vmlinux_link .tmp_vmlinux3 + sysmap_and_kallsyms .tmp_vmlinux3 fi fi -vmlinux_link vmlinux "${kallsymso}" ${btf_vmlinux_bin_o} +strip_debug= + +vmlinux_link vmlinux # fill in BTF IDs if is_enabled CONFIG_DEBUG_INFO_BTF && is_enabled CONFIG_BPF; then @@ -293,7 +304,7 @@ fi # step a (see comment above) if is_enabled CONFIG_KALLSYMS; then - if ! cmp -s System.map ${kallsyms_vmlinux}.syms; then + if ! cmp -s System.map "${kallsyms_sysmap}"; then echo >&2 Inconsistent kallsyms data echo >&2 'Try "make KALLSYMS_EXTRA_PASS=1" as a workaround' exit 1 diff --git a/scripts/make_fit.py b/scripts/make_fit.py index 263147df80a4..4a1bb2f55861 100755 --- a/scripts/make_fit.py +++ b/scripts/make_fit.py @@ -22,6 +22,11 @@ the entire FIT. Use -c to compress the data, using bzip2, gzip, lz4, lzma, lzo and zstd algorithms. +Use -D to decompose "composite" DTBs into their base components and +deduplicate the resulting base DTBs and DTB overlays. This requires the +DTBs to be sourced from the kernel build directory, as the implementation +looks at the .cmd files produced by the kernel build. + The resulting FIT can be booted by bootloaders which support FIT, such as U-Boot, Linuxboot, Tianocore, etc. @@ -64,6 +69,8 @@ def parse_args(): help='Specifies the architecture') parser.add_argument('-c', '--compress', type=str, default='none', help='Specifies the compression') + parser.add_argument('-D', '--decompose-dtbs', action='store_true', + help='Decompose composite DTBs into base DTB and overlays') parser.add_argument('-E', '--external', action='store_true', help='Convert the FIT to use external data') parser.add_argument('-n', '--name', type=str, required=True, @@ -140,12 +147,12 @@ def finish_fit(fsw, entries): fsw.end_node() seq = 0 with fsw.add_node('configurations'): - for model, compat in entries: + for model, compat, files in entries: seq += 1 with fsw.add_node(f'conf-{seq}'): fsw.property('compatible', bytes(compat)) fsw.property_string('description', model) - fsw.property_string('fdt', f'fdt-{seq}') + fsw.property('fdt', bytes(''.join(f'fdt-{x}\x00' for x in files), "ascii")) fsw.property_string('kernel', 'kernel') fsw.end_node() @@ -193,21 +200,9 @@ def output_dtb(fsw, seq, fname, arch, compress): fname (str): Filename containing the DTB arch: FIT architecture, e.g. 'arm64' compress (str): Compressed algorithm, e.g. 'gzip' - - Returns: - tuple: - str: Model name - bytes: Compatible stringlist """ with fsw.add_node(f'fdt-{seq}'): - # Get the compatible / model information - with open(fname, 'rb') as inf: - data = inf.read() - fdt = libfdt.FdtRo(data) - model = fdt.getprop(0, 'model').as_str() - compat = fdt.getprop(0, 'compatible') - - fsw.property_string('description', model) + fsw.property_string('description', os.path.basename(fname)) fsw.property_string('type', 'flat_dt') fsw.property_string('arch', arch) fsw.property_string('compression', compress) @@ -215,9 +210,45 @@ def output_dtb(fsw, seq, fname, arch, compress): with open(fname, 'rb') as inf: compressed = compress_data(inf, compress) fsw.property('data', compressed) - return model, compat +def process_dtb(fname, args): + """Process an input DTB, decomposing it if requested and is possible + + Args: + fname (str): Filename containing the DTB + args (Namespace): Program arguments + Returns: + tuple: + str: Model name string + str: Root compatible string + files: list of filenames corresponding to the DTB + """ + # Get the compatible / model information + with open(fname, 'rb') as inf: + data = inf.read() + fdt = libfdt.FdtRo(data) + model = fdt.getprop(0, 'model').as_str() + compat = fdt.getprop(0, 'compatible') + + if args.decompose_dtbs: + # Check if the DTB needs to be decomposed + path, basename = os.path.split(fname) + cmd_fname = os.path.join(path, f'.{basename}.cmd') + with open(cmd_fname, 'r', encoding='ascii') as inf: + cmd = inf.read() + + if 'scripts/dtc/fdtoverlay' in cmd: + # This depends on the structure of the composite DTB command + files = cmd.split() + files = files[files.index('-i') + 1:] + else: + files = [fname] + else: + files = [fname] + + return (model, compat, files) + def build_fit(args): """Build the FIT from the provided files and arguments @@ -235,6 +266,7 @@ def build_fit(args): fsw = libfdt.FdtSw() setup_fit(fsw, args.name) entries = [] + fdts = {} # Handle the kernel with open(args.kernel, 'rb') as inf: @@ -243,12 +275,22 @@ def build_fit(args): write_kernel(fsw, comp_data, args) for fname in args.dtbs: - # Ignore overlay (.dtbo) files - if os.path.splitext(fname)[1] == '.dtb': - seq += 1 - size += os.path.getsize(fname) - model, compat = output_dtb(fsw, seq, fname, args.arch, args.compress) - entries.append([model, compat]) + # Ignore non-DTB (*.dtb) files + if os.path.splitext(fname)[1] != '.dtb': + continue + + (model, compat, files) = process_dtb(fname, args) + + for fn in files: + if fn not in fdts: + seq += 1 + size += os.path.getsize(fn) + output_dtb(fsw, seq, fn, args.arch, args.compress) + fdts[fn] = seq + + files_seq = [fdts[fn] for fn in files] + + entries.append([model, compat, files_seq]) finish_fit(fsw, entries) diff --git a/scripts/mod/list.h b/scripts/mod/list.h deleted file mode 100644 index a924a6c4aa4d..000000000000 --- a/scripts/mod/list.h +++ /dev/null @@ -1,213 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef LIST_H -#define LIST_H - -#include <stdbool.h> -#include <stddef.h> - -/* Are two types/vars the same type (ignoring qualifiers)? */ -#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) - -/** - * container_of - cast a member of a structure out to the containing structure - * @ptr: the pointer to the member. - * @type: the type of the container struct this is embedded in. - * @member: the name of the member within the struct. - * - */ -#define container_of(ptr, type, member) ({ \ - void *__mptr = (void *)(ptr); \ - _Static_assert(__same_type(*(ptr), ((type *)0)->member) || \ - __same_type(*(ptr), void), \ - "pointer type mismatch in container_of()"); \ - ((type *)(__mptr - offsetof(type, member))); }) - -#define LIST_POISON1 ((void *) 0x100) -#define LIST_POISON2 ((void *) 0x122) - -/* - * Circular doubly linked list implementation. - * - * Some of the internal functions ("__xxx") are useful when - * manipulating whole lists rather than single entries, as - * sometimes we already know the next/prev entries and we can - * generate better code by using them directly rather than - * using the generic single-entry routines. - */ - -struct list_head { - struct list_head *next, *prev; -}; - -#define LIST_HEAD_INIT(name) { &(name), &(name) } - -#define LIST_HEAD(name) \ - struct list_head name = LIST_HEAD_INIT(name) - -/** - * INIT_LIST_HEAD - Initialize a list_head structure - * @list: list_head structure to be initialized. - * - * Initializes the list_head to point to itself. If it is a list header, - * the result is an empty list. - */ -static inline void INIT_LIST_HEAD(struct list_head *list) -{ - list->next = list; - list->prev = list; -} - -/* - * Insert a new entry between two known consecutive entries. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -static inline void __list_add(struct list_head *new, - struct list_head *prev, - struct list_head *next) -{ - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; -} - -/** - * list_add - add a new entry - * @new: new entry to be added - * @head: list head to add it after - * - * Insert a new entry after the specified head. - * This is good for implementing stacks. - */ -static inline void list_add(struct list_head *new, struct list_head *head) -{ - __list_add(new, head, head->next); -} - -/** - * list_add_tail - add a new entry - * @new: new entry to be added - * @head: list head to add it before - * - * Insert a new entry before the specified head. - * This is useful for implementing queues. - */ -static inline void list_add_tail(struct list_head *new, struct list_head *head) -{ - __list_add(new, head->prev, head); -} - -/* - * Delete a list entry by making the prev/next entries - * point to each other. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -static inline void __list_del(struct list_head *prev, struct list_head *next) -{ - next->prev = prev; - prev->next = next; -} - -static inline void __list_del_entry(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); -} - -/** - * list_del - deletes entry from list. - * @entry: the element to delete from the list. - * Note: list_empty() on entry does not return true after this, the entry is - * in an undefined state. - */ -static inline void list_del(struct list_head *entry) -{ - __list_del_entry(entry); - entry->next = LIST_POISON1; - entry->prev = LIST_POISON2; -} - -/** - * list_is_head - tests whether @list is the list @head - * @list: the entry to test - * @head: the head of the list - */ -static inline int list_is_head(const struct list_head *list, const struct list_head *head) -{ - return list == head; -} - -/** - * list_empty - tests whether a list is empty - * @head: the list to test. - */ -static inline int list_empty(const struct list_head *head) -{ - return head->next == head; -} - -/** - * list_entry - get the struct for this entry - * @ptr: the &struct list_head pointer. - * @type: the type of the struct this is embedded in. - * @member: the name of the list_head within the struct. - */ -#define list_entry(ptr, type, member) \ - container_of(ptr, type, member) - -/** - * list_first_entry - get the first element from a list - * @ptr: the list head to take the element from. - * @type: the type of the struct this is embedded in. - * @member: the name of the list_head within the struct. - * - * Note, that list is expected to be not empty. - */ -#define list_first_entry(ptr, type, member) \ - list_entry((ptr)->next, type, member) - -/** - * list_next_entry - get the next element in list - * @pos: the type * to cursor - * @member: the name of the list_head within the struct. - */ -#define list_next_entry(pos, member) \ - list_entry((pos)->member.next, typeof(*(pos)), member) - -/** - * list_entry_is_head - test if the entry points to the head of the list - * @pos: the type * to cursor - * @head: the head for your list. - * @member: the name of the list_head within the struct. - */ -#define list_entry_is_head(pos, head, member) \ - (&pos->member == (head)) - -/** - * list_for_each_entry - iterate over list of given type - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_head within the struct. - */ -#define list_for_each_entry(pos, head, member) \ - for (pos = list_first_entry(head, typeof(*pos), member); \ - !list_entry_is_head(pos, head, member); \ - pos = list_next_entry(pos, member)) - -/** - * list_for_each_entry_safe - iterate over list of given type. Safe against removal of list entry - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_head within the struct. - */ -#define list_for_each_entry_safe(pos, n, head, member) \ - for (pos = list_first_entry(head, typeof(*pos), member), \ - n = list_next_entry(pos, member); \ - !list_entry_is_head(pos, head, member); \ - pos = n, n = list_next_entry(n, member)) - -#endif /* LIST_H */ diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 4b1edb257618..d16d0ace2775 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -20,6 +20,9 @@ #include <limits.h> #include <stdbool.h> #include <errno.h> + +#include <hashtable.h> +#include <list.h> #include "modpost.h" #include "../../include/linux/license.h" @@ -199,13 +202,8 @@ static struct module *new_module(const char *name, size_t namelen) return mod; } -/* A hash of all exported symbols, - * struct symbol is also used for lists of unresolved symbols */ - -#define SYMBOL_HASH_SIZE 1024 - struct symbol { - struct symbol *next; + struct hlist_node hnode;/* link to hash table */ struct list_head list; /* link to module::exported_symbols or module::unresolved_symbols */ struct module *module; char *namespace; @@ -218,7 +216,7 @@ struct symbol { char name[]; }; -static struct symbol *symbolhash[SYMBOL_HASH_SIZE]; +static HASHTABLE_DEFINE(symbol_hashtable, 1U << 10); /* This is based on the hash algorithm from gdbm, via tdb */ static inline unsigned int tdb_hash(const char *name) @@ -250,11 +248,7 @@ static struct symbol *alloc_symbol(const char *name) /* For the hash of exported symbols */ static void hash_add_symbol(struct symbol *sym) { - unsigned int hash; - - hash = tdb_hash(sym->name) % SYMBOL_HASH_SIZE; - sym->next = symbolhash[hash]; - symbolhash[hash] = sym; + hash_add(symbol_hashtable, &sym->hnode, tdb_hash(sym->name)); } static void sym_add_unresolved(const char *name, struct module *mod, bool weak) @@ -275,7 +269,7 @@ static struct symbol *sym_find_with_module(const char *name, struct module *mod) if (name[0] == '.') name++; - for (s = symbolhash[tdb_hash(name) % SYMBOL_HASH_SIZE]; s; s = s->next) { + hash_for_each_possible(symbol_hashtable, s, hnode, tdb_hash(name)) { if (strcmp(s->name, name) == 0 && (!mod || s->module == mod)) return s; } @@ -954,17 +948,6 @@ static int secref_whitelist(const char *fromsec, const char *fromsym, match(fromsym, PATTERNS("*_ops", "*_probe", "*_console"))) return 0; - /* - * symbols in data sections must not refer to .exit.*, but there are - * quite a few offenders, so hide these unless for W=1 builds until - * these are fixed. - */ - if (!extra_warn && - match(fromsec, PATTERNS(DATA_SECTIONS)) && - match(tosec, PATTERNS(ALL_EXIT_SECTIONS)) && - match(fromsym, PATTERNS("*driver"))) - return 0; - /* Check for pattern 3 */ if (strstarts(fromsec, ".head.text") && match(tosec, PATTERNS(ALL_INIT_SECTIONS))) @@ -1168,40 +1151,6 @@ static Elf_Addr addend_386_rel(uint32_t *location, unsigned int r_type) return (Elf_Addr)(-1); } -#ifndef R_ARM_CALL -#define R_ARM_CALL 28 -#endif -#ifndef R_ARM_JUMP24 -#define R_ARM_JUMP24 29 -#endif - -#ifndef R_ARM_THM_CALL -#define R_ARM_THM_CALL 10 -#endif -#ifndef R_ARM_THM_JUMP24 -#define R_ARM_THM_JUMP24 30 -#endif - -#ifndef R_ARM_MOVW_ABS_NC -#define R_ARM_MOVW_ABS_NC 43 -#endif - -#ifndef R_ARM_MOVT_ABS -#define R_ARM_MOVT_ABS 44 -#endif - -#ifndef R_ARM_THM_MOVW_ABS_NC -#define R_ARM_THM_MOVW_ABS_NC 47 -#endif - -#ifndef R_ARM_THM_MOVT_ABS -#define R_ARM_THM_MOVT_ABS 48 -#endif - -#ifndef R_ARM_THM_JUMP19 -#define R_ARM_THM_JUMP19 51 -#endif - static int32_t sign_extend32(int32_t value, int index) { uint8_t shift = 31 - index; @@ -1262,7 +1211,7 @@ static Elf_Addr addend_arm_rel(void *loc, Elf_Sym *sym, unsigned int r_type) ((lower & 0x07ff) << 1), 20); return offset + sym->st_value + 4; - case R_ARM_THM_CALL: + case R_ARM_THM_PC22: case R_ARM_THM_JUMP24: /* * Encoding T4: diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h index ee43c7950636..58197b34a3c8 100644 --- a/scripts/mod/modpost.h +++ b/scripts/mod/modpost.h @@ -13,7 +13,7 @@ #include <elf.h> #include "../../include/linux/module_symbol.h" -#include "list.h" +#include <list_types.h> #include "elfconfig.h" /* On BSD-alike OSes elf.h defines these according to host's word size */ diff --git a/scripts/package/PKGBUILD b/scripts/package/PKGBUILD new file mode 100644 index 000000000000..663ce300dd06 --- /dev/null +++ b/scripts/package/PKGBUILD @@ -0,0 +1,108 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Maintainer: Thomas Weißschuh <linux@weissschuh.net> +# Contributor: Jan Alexander Steffens (heftig) <heftig@archlinux.org> + +pkgbase=${PACMAN_PKGBASE:-linux-upstream} +pkgname=("${pkgbase}" "${pkgbase}-api-headers") +if grep -q CONFIG_MODULES=y include/config/auto.conf; then + pkgname+=("${pkgbase}-headers") +fi +pkgver="${KERNELRELEASE//-/_}" +# The PKGBUILD is evaluated multiple times. +# Running scripts/build-version from here would introduce inconsistencies. +pkgrel="${KBUILD_REVISION}" +pkgdesc='Upstream Linux' +url='https://www.kernel.org/' +# Enable flexible cross-compilation +arch=(${CARCH}) +license=(GPL-2.0-only) +makedepends=( + bc + bison + cpio + flex + gettext + kmod + libelf + openssl + pahole + perl + python + rsync + tar +) +options=(!debug !strip !buildflags !makeflags) + +build() { + # MAKEFLAGS from makepkg.conf override the ones inherited from kbuild. + # Bypass this override with a custom variable. + export MAKEFLAGS="${KBUILD_MAKEFLAGS}" + cd "${objtree}" + + ${MAKE} KERNELRELEASE="${KERNELRELEASE}" KBUILD_BUILD_VERSION="${pkgrel}" +} + +_package() { + pkgdesc="The ${pkgdesc} kernel and modules" + + export MAKEFLAGS="${KBUILD_MAKEFLAGS}" + cd "${objtree}" + local modulesdir="${pkgdir}/usr/${MODLIB}" + + echo "Installing boot image..." + # systemd expects to find the kernel here to allow hibernation + # https://github.com/systemd/systemd/commit/edda44605f06a41fb86b7ab8128dcf99161d2344 + install -Dm644 "$(${MAKE} -s image_name)" "${modulesdir}/vmlinuz" + + # Used by mkinitcpio to name the kernel + echo "${pkgbase}" > "${modulesdir}/pkgbase" + + echo "Installing modules..." + ${MAKE} INSTALL_MOD_PATH="${pkgdir}/usr" INSTALL_MOD_STRIP=1 \ + DEPMOD=true modules_install + + if [ -d "${srctree}/arch/${SRCARCH}/boot/dts" ]; then + echo "Installing dtbs..." + ${MAKE} INSTALL_DTBS_PATH="${modulesdir}/dtb" dtbs_install + fi + + # remove build link, will be part of -headers package + rm -f "${modulesdir}/build" +} + +_package-headers() { + pkgdesc="Headers and scripts for building modules for the ${pkgdesc} kernel" + + export MAKEFLAGS="${KBUILD_MAKEFLAGS}" + cd "${objtree}" + local builddir="${pkgdir}/usr/${MODLIB}/build" + + echo "Installing build files..." + "${srctree}/scripts/package/install-extmod-build" "${builddir}" + + echo "Installing System.map and config..." + cp System.map "${builddir}/System.map" + cp .config "${builddir}/.config" + + echo "Adding symlink..." + mkdir -p "${pkgdir}/usr/src" + ln -sr "${builddir}" "${pkgdir}/usr/src/${pkgbase}" +} + +_package-api-headers() { + pkgdesc="Kernel headers sanitized for use in userspace" + provides=(linux-api-headers) + conflicts=(linux-api-headers) + + export MAKEFLAGS="${KBUILD_MAKEFLAGS}" + cd "${objtree}" + + ${MAKE} headers_install INSTALL_HDR_PATH="${pkgdir}/usr" +} + +for _p in "${pkgname[@]}"; do + eval "package_$_p() { + $(declare -f "_package${_p#$pkgbase}") + _package${_p#$pkgbase} + }" +done diff --git a/scripts/package/builddeb b/scripts/package/builddeb index e797ad360f7a..c1757db6aa8a 100755 --- a/scripts/package/builddeb +++ b/scripts/package/builddeb @@ -10,7 +10,7 @@ # specified in KDEB_HOOKDIR) that will be called on package install and # removal. -set -e +set -eu is_enabled() { grep -q "^$1=y" include/config/auto.conf diff --git a/scripts/package/buildtar b/scripts/package/buildtar index eb67787f8673..cc87a473c01f 100755 --- a/scripts/package/buildtar +++ b/scripts/package/buildtar @@ -11,7 +11,7 @@ # Wichert Akkerman <wichert@wiggy.net>. # -set -e +set -eu # # Some variables and settings used throughout the script diff --git a/scripts/package/gen-diff-patch b/scripts/package/gen-diff-patch index 8a98b7bb78a0..f272f7770ea3 100755 --- a/scripts/package/gen-diff-patch +++ b/scripts/package/gen-diff-patch @@ -1,6 +1,8 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0-only +set -eu + diff_patch=$1 mkdir -p "$(dirname "${diff_patch}")" diff --git a/scripts/package/install-extmod-build b/scripts/package/install-extmod-build index 76e0765dfcd6..8cc9e13403ae 100755 --- a/scripts/package/install-extmod-build +++ b/scripts/package/install-extmod-build @@ -1,13 +1,10 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0-only -set -e +set -eu destdir=${1} -test -n "${srctree}" -test -n "${SRCARCH}" - is_enabled() { grep -q "^$1=y" include/config/auto.conf } diff --git a/scripts/package/kernel.spec b/scripts/package/kernel.spec index c52d517b9364..74355ff0e106 100644 --- a/scripts/package/kernel.spec +++ b/scripts/package/kernel.spec @@ -27,7 +27,7 @@ The Linux Kernel, the operating system core itself %package headers Summary: Header files for the Linux kernel for use by glibc Group: Development/System -Obsoletes: kernel-headers +Obsoletes: kernel-headers < %{version} Provides: kernel-headers = %{version} %description headers Kernel-headers includes the C header files that specify the interface diff --git a/scripts/package/mkdebian b/scripts/package/mkdebian index 070149c985fe..10637d403777 100755 --- a/scripts/package/mkdebian +++ b/scripts/package/mkdebian @@ -4,7 +4,7 @@ # # Simple script to generate a debian/ directory for a Linux kernel. -set -e +set -eu is_enabled() { grep -q "^$1=y" include/config/auto.conf @@ -19,7 +19,7 @@ if_enabled_echo() { } set_debarch() { - if [ -n "$KBUILD_DEBARCH" ] ; then + if [ "${KBUILD_DEBARCH:+set}" ]; then debarch="$KBUILD_DEBARCH" return fi @@ -125,32 +125,34 @@ gen_source () rm -rf debian mkdir debian -email=${DEBEMAIL-$EMAIL} - -# use email string directly if it contains <email> -if echo "${email}" | grep -q '<.*>'; then - maintainer=${email} +user=${KBUILD_BUILD_USER:-$(id -nu)} +name=${DEBFULLNAME:-${user}} +if [ "${DEBEMAIL:+set}" ]; then + email=${DEBEMAIL} else - # or construct the maintainer string - user=${KBUILD_BUILD_USER-$(id -nu)} - name=${DEBFULLNAME-${user}} - if [ -z "${email}" ]; then - buildhost=${KBUILD_BUILD_HOST-$(hostname -f 2>/dev/null || hostname)} - email="${user}@${buildhost}" - fi - maintainer="${name} <${email}>" + buildhost=${KBUILD_BUILD_HOST:-$(hostname -f 2>/dev/null || hostname)} + email="${user}@${buildhost}" fi +maintainer="${name} <${email}>" -if [ "$1" = --need-source ]; then - gen_source -fi +while [ $# -gt 0 ]; do + case "$1" in + --need-source) + gen_source + shift + ;; + *) + break + ;; + esac +done # Some variables and settings used throughout the script version=$KERNELRELEASE -if [ -n "$KDEB_PKGVERSION" ]; then +if [ "${KDEB_PKGVERSION:+set}" ]; then packageversion=$KDEB_PKGVERSION else - packageversion=$(${srctree}/scripts/setlocalversion --no-local ${srctree})-$($srctree/init/build-version) + packageversion=$(${srctree}/scripts/setlocalversion --no-local ${srctree})-$($srctree/scripts/build-version) fi sourcename=${KDEB_SOURCENAME:-linux-upstream} @@ -164,7 +166,7 @@ debarch= set_debarch # Try to determine distribution -if [ -n "$KDEB_CHANGELOG_DIST" ]; then +if [ "${KDEB_CHANGELOG_DIST:+set}" ]; then distribution=$KDEB_CHANGELOG_DIST # In some cases lsb_release returns the codename as n/a, which breaks dpkg-parsechangelog elif distribution=$(lsb_release -cs 2>/dev/null) && [ -n "$distribution" ] && [ "$distribution" != "n/a" ]; then diff --git a/scripts/package/mkspec b/scripts/package/mkspec index ce201bfa8377..ead54d67a024 100755 --- a/scripts/package/mkspec +++ b/scripts/package/mkspec @@ -9,6 +9,8 @@ # Patched for non-x86 by Opencon (L) 2002 <opencon@rio.skydome.net> # +set -eu + output=$1 mkdir -p "$(dirname "${output}")" @@ -24,7 +26,30 @@ fi cat<<EOF %define ARCH ${ARCH} %define KERNELRELEASE ${KERNELRELEASE} -%define pkg_release $("${srctree}/init/build-version") +%define pkg_release $("${srctree}/scripts/build-version") EOF cat "${srctree}/scripts/package/kernel.spec" + +# collect the user's name and email address for the changelog entry +if [ "$(command -v git)" ]; then + name=$(git config user.name) || true + email=$(git config user.email) || true +fi + +if [ ! "${name:+set}" ]; then + name=${KBUILD_BUILD_USER:-$(id -nu)} +fi + +if [ ! "${email:+set}" ]; then + buildhost=${KBUILD_BUILD_HOST:-$(hostname -f 2>/dev/null || hostname)} + builduser=${KBUILD_BUILD_USER:-$(id -nu)} + email="${builduser}@${buildhost}" +fi + +cat << EOF + +%changelog +* $(LC_ALL=C; date +'%a %b %d %Y') ${name} <${email}> +- Custom built Linux kernel. +EOF diff --git a/scripts/remove-stale-files b/scripts/remove-stale-files index 385610fe3936..f38d26b78c2a 100755 --- a/scripts/remove-stale-files +++ b/scripts/remove-stale-files @@ -20,22 +20,4 @@ set -e # yard. Stale files stay in this file for a while (for some release cycles?), # then will be really dead and removed from the code base entirely. -rm -f arch/powerpc/purgatory/kexec-purgatory.c -rm -f arch/riscv/purgatory/kexec-purgatory.c -rm -f arch/x86/purgatory/kexec-purgatory.c - -rm -f scripts/extract-cert - -rm -f scripts/kconfig/[gmnq]conf-cfg - -rm -f rust/target.json - -rm -f scripts/bin2c - -rm -f .scmversion - -rm -rf include/ksym - -find . -name '*.usyms' | xargs rm -f - rm -f *.spec diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index 1e2ab148d5db..e1900abd44f6 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -149,6 +149,24 @@ FEATURE_DISPLAY ?= \ # FEATURE_GROUP_MEMBERS-libbfd = libbfd-liberty libbfd-liberty-z +# +# Declare list of feature dependency packages that provide pkg-config files. +# +FEATURE_PKG_CONFIG ?= \ + libtraceevent \ + libtracefs + +feature_pkg_config = $(eval $(feature_pkg_config_code)) +define feature_pkg_config_code + FEATURE_CHECK_CFLAGS-$(1) := $(shell $(PKG_CONFIG) --cflags $(1) 2>/dev/null) + FEATURE_CHECK_LDFLAGS-$(1) := $(shell $(PKG_CONFIG) --libs $(1) 2>/dev/null) +endef + +# Set FEATURE_CHECK_(C|LD)FLAGS-$(package) for packages using pkg-config. +ifneq ($(PKG_CONFIG),) + $(foreach package,$(FEATURE_PKG_CONFIG),$(call feature_pkg_config,$(package))) +endif + # Set FEATURE_CHECK_(C|LD)FLAGS-all for all FEATURE_TESTS features. # If in the future we need per-feature checks/flags for features not # mentioned in this list we need to refactor this ;-). diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index a4829b6532d8..c896babf7a74 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -182,20 +182,15 @@ endif FEATURE_CHECK_CFLAGS-libzstd := $(LIBZSTD_CFLAGS) FEATURE_CHECK_LDFLAGS-libzstd := $(LIBZSTD_LDFLAGS) +# for linking with debug library, run like: +# make DEBUG=1 PKG_CONFIG_PATH=/opt/libtraceevent/(lib|lib64)/pkgconfig + ifneq ($(NO_LIBTRACEEVENT),1) ifeq ($(call get-executable,$(PKG_CONFIG)),) $(error Error: $(PKG_CONFIG) needed by libtraceevent is missing on this system, please install it) endif endif -# for linking with debug library, run like: -# make DEBUG=1 PKG_CONFIG_PATH=/opt/libtraceevent/(lib|lib64)/pkgconfig -FEATURE_CHECK_CFLAGS-libtraceevent := $(shell $(PKG_CONFIG) --cflags libtraceevent 2>/dev/null) -FEATURE_CHECK_LDFLAGS-libtraceevent := $(shell $(PKG_CONFIG) --libs libtraceevent 2>/dev/null) - -FEATURE_CHECK_CFLAGS-libtracefs := $(shell $(PKG_CONFIG) --cflags libtracefs 2>/dev/null) -FEATURE_CHECK_LDFLAGS-libtracefs := $(shell $(PKG_CONFIG) --libs libtracefs 2>/dev/null) - FEATURE_CHECK_CFLAGS-bpf = -I. -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(SRCARCH)/include/uapi -I$(srctree)/tools/include/uapi # include ARCH specific config -include $(src-perf)/arch/$(SRCARCH)/Makefile @@ -1206,6 +1201,8 @@ ifneq ($(NO_LIBTRACEEVENT),1) LIBTRACEFS_VERSION_3 := $(word 3, $(subst ., ,$(LIBTRACEFS_VERSION))) LIBTRACEFS_VERSION_CPP := $(shell expr $(LIBTRACEFS_VERSION_1) \* 255 \* 255 + $(LIBTRACEFS_VERSION_2) \* 255 + $(LIBTRACEFS_VERSION_3)) CFLAGS += -DLIBTRACEFS_VERSION=$(LIBTRACEFS_VERSION_CPP) + else + $(warning libtracefs is missing. Please install libtracefs-dev/libtracefs-devel) endif endif diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index e30fd55f8e51..cd3b480d20bd 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c @@ -26,7 +26,6 @@ static bool is_ignored_symbol(const char *name, char type) * when --all-symbols is specified so exclude them to get a * stable symbol list. */ - "kallsyms_addresses", "kallsyms_offsets", "kallsyms_relative_base", "kallsyms_num_syms", diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 2340c4f6d0c2..67414944f245 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -1501,7 +1501,7 @@ void dso__delete(struct dso *dso) auxtrace_cache__free(RC_CHK_ACCESS(dso)->auxtrace_cache); dso_cache__free(dso); dso__free_a2l(dso); - zfree(&RC_CHK_ACCESS(dso)->symsrc_filename); + dso__free_symsrc_filename(dso); nsinfo__zput(RC_CHK_ACCESS(dso)->nsinfo); mutex_destroy(dso__lock(dso)); RC_CHK_FREE(dso); diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 878c1f441868..ed0068251c65 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -602,6 +602,11 @@ static inline void dso__set_symsrc_filename(struct dso *dso, char *val) RC_CHK_ACCESS(dso)->symsrc_filename = val; } +static inline void dso__free_symsrc_filename(struct dso *dso) +{ + zfree(&RC_CHK_ACCESS(dso)->symsrc_filename); +} + static inline enum dso_binary_type dso__symtab_type(const struct dso *dso) { return RC_CHK_ACCESS(dso)->symtab_type; diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c index f6a6f6a91030..16c2b03831f3 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c @@ -413,7 +413,7 @@ static int read_unwind_spec_debug_frame(struct dso *dso, __func__, dso__symsrc_filename(dso), debuglink); - zfree(&dso__symsrc_filename(dso)); + dso__free_symsrc_filename(dso); } dso__set_symsrc_filename(dso, debuglink); } else { diff --git a/tools/testing/selftests/livepatch/test-livepatch.sh b/tools/testing/selftests/livepatch/test-livepatch.sh index e3455a6b1158..65c9c058458d 100755 --- a/tools/testing/selftests/livepatch/test-livepatch.sh +++ b/tools/testing/selftests/livepatch/test-livepatch.sh @@ -4,7 +4,9 @@ . $(dirname $0)/functions.sh -MOD_LIVEPATCH=test_klp_livepatch +MOD_LIVEPATCH1=test_klp_livepatch +MOD_LIVEPATCH2=test_klp_syscall +MOD_LIVEPATCH3=test_klp_callbacks_demo MOD_REPLACE=test_klp_atomic_replace setup_config @@ -16,33 +18,33 @@ setup_config start_test "basic function patching" -load_lp $MOD_LIVEPATCH +load_lp $MOD_LIVEPATCH1 -if [[ "$(cat /proc/cmdline)" != "$MOD_LIVEPATCH: this has been live patched" ]] ; then +if [[ "$(cat /proc/cmdline)" != "$MOD_LIVEPATCH1: this has been live patched" ]] ; then echo -e "FAIL\n\n" die "livepatch kselftest(s) failed" fi -disable_lp $MOD_LIVEPATCH -unload_lp $MOD_LIVEPATCH +disable_lp $MOD_LIVEPATCH1 +unload_lp $MOD_LIVEPATCH1 -if [[ "$(cat /proc/cmdline)" == "$MOD_LIVEPATCH: this has been live patched" ]] ; then +if [[ "$(cat /proc/cmdline)" == "$MOD_LIVEPATCH1: this has been live patched" ]] ; then echo -e "FAIL\n\n" die "livepatch kselftest(s) failed" fi -check_result "% insmod test_modules/$MOD_LIVEPATCH.ko -livepatch: enabling patch '$MOD_LIVEPATCH' -livepatch: '$MOD_LIVEPATCH': initializing patching transition -livepatch: '$MOD_LIVEPATCH': starting patching transition -livepatch: '$MOD_LIVEPATCH': completing patching transition -livepatch: '$MOD_LIVEPATCH': patching complete -% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled -livepatch: '$MOD_LIVEPATCH': initializing unpatching transition -livepatch: '$MOD_LIVEPATCH': starting unpatching transition -livepatch: '$MOD_LIVEPATCH': completing unpatching transition -livepatch: '$MOD_LIVEPATCH': unpatching complete -% rmmod $MOD_LIVEPATCH" +check_result "% insmod test_modules/$MOD_LIVEPATCH1.ko +livepatch: enabling patch '$MOD_LIVEPATCH1' +livepatch: '$MOD_LIVEPATCH1': initializing patching transition +livepatch: '$MOD_LIVEPATCH1': starting patching transition +livepatch: '$MOD_LIVEPATCH1': completing patching transition +livepatch: '$MOD_LIVEPATCH1': patching complete +% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH1/enabled +livepatch: '$MOD_LIVEPATCH1': initializing unpatching transition +livepatch: '$MOD_LIVEPATCH1': starting unpatching transition +livepatch: '$MOD_LIVEPATCH1': completing unpatching transition +livepatch: '$MOD_LIVEPATCH1': unpatching complete +% rmmod $MOD_LIVEPATCH1" # - load a livepatch that modifies the output from /proc/cmdline and @@ -53,7 +55,7 @@ livepatch: '$MOD_LIVEPATCH': unpatching complete start_test "multiple livepatches" -load_lp $MOD_LIVEPATCH +load_lp $MOD_LIVEPATCH1 grep 'live patched' /proc/cmdline > /dev/kmsg grep 'live patched' /proc/meminfo > /dev/kmsg @@ -69,26 +71,26 @@ unload_lp $MOD_REPLACE grep 'live patched' /proc/cmdline > /dev/kmsg grep 'live patched' /proc/meminfo > /dev/kmsg -disable_lp $MOD_LIVEPATCH -unload_lp $MOD_LIVEPATCH +disable_lp $MOD_LIVEPATCH1 +unload_lp $MOD_LIVEPATCH1 grep 'live patched' /proc/cmdline > /dev/kmsg grep 'live patched' /proc/meminfo > /dev/kmsg -check_result "% insmod test_modules/$MOD_LIVEPATCH.ko -livepatch: enabling patch '$MOD_LIVEPATCH' -livepatch: '$MOD_LIVEPATCH': initializing patching transition -livepatch: '$MOD_LIVEPATCH': starting patching transition -livepatch: '$MOD_LIVEPATCH': completing patching transition -livepatch: '$MOD_LIVEPATCH': patching complete -$MOD_LIVEPATCH: this has been live patched +check_result "% insmod test_modules/$MOD_LIVEPATCH1.ko +livepatch: enabling patch '$MOD_LIVEPATCH1' +livepatch: '$MOD_LIVEPATCH1': initializing patching transition +livepatch: '$MOD_LIVEPATCH1': starting patching transition +livepatch: '$MOD_LIVEPATCH1': completing patching transition +livepatch: '$MOD_LIVEPATCH1': patching complete +$MOD_LIVEPATCH1: this has been live patched % insmod test_modules/$MOD_REPLACE.ko replace=0 livepatch: enabling patch '$MOD_REPLACE' livepatch: '$MOD_REPLACE': initializing patching transition livepatch: '$MOD_REPLACE': starting patching transition livepatch: '$MOD_REPLACE': completing patching transition livepatch: '$MOD_REPLACE': patching complete -$MOD_LIVEPATCH: this has been live patched +$MOD_LIVEPATCH1: this has been live patched $MOD_REPLACE: this has been live patched % echo 0 > /sys/kernel/livepatch/$MOD_REPLACE/enabled livepatch: '$MOD_REPLACE': initializing unpatching transition @@ -96,35 +98,57 @@ livepatch: '$MOD_REPLACE': starting unpatching transition livepatch: '$MOD_REPLACE': completing unpatching transition livepatch: '$MOD_REPLACE': unpatching complete % rmmod $MOD_REPLACE -$MOD_LIVEPATCH: this has been live patched -% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled -livepatch: '$MOD_LIVEPATCH': initializing unpatching transition -livepatch: '$MOD_LIVEPATCH': starting unpatching transition -livepatch: '$MOD_LIVEPATCH': completing unpatching transition -livepatch: '$MOD_LIVEPATCH': unpatching complete -% rmmod $MOD_LIVEPATCH" +$MOD_LIVEPATCH1: this has been live patched +% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH1/enabled +livepatch: '$MOD_LIVEPATCH1': initializing unpatching transition +livepatch: '$MOD_LIVEPATCH1': starting unpatching transition +livepatch: '$MOD_LIVEPATCH1': completing unpatching transition +livepatch: '$MOD_LIVEPATCH1': unpatching complete +% rmmod $MOD_LIVEPATCH1" # - load a livepatch that modifies the output from /proc/cmdline and # verify correct behavior -# - load an atomic replace livepatch and verify that only the second is active -# - remove the first livepatch and verify that the atomic replace livepatch -# is still active +# - load two additional livepatches and check the number of livepatch modules +# applied +# - load an atomic replace livepatch and check that the other three modules were +# disabled +# - remove all livepatches besides the atomic replace one and verify that the +# atomic replace livepatch is still active # - remove the atomic replace livepatch and verify that none are active start_test "atomic replace livepatch" -load_lp $MOD_LIVEPATCH +load_lp $MOD_LIVEPATCH1 grep 'live patched' /proc/cmdline > /dev/kmsg grep 'live patched' /proc/meminfo > /dev/kmsg +for mod in $MOD_LIVEPATCH2 $MOD_LIVEPATCH3; do + load_lp "$mod" +done + +mods=(/sys/kernel/livepatch/*) +nmods=${#mods[@]} +if [ "$nmods" -ne 3 ]; then + die "Expecting three modules listed, found $nmods" +fi + load_lp $MOD_REPLACE replace=1 grep 'live patched' /proc/cmdline > /dev/kmsg grep 'live patched' /proc/meminfo > /dev/kmsg -unload_lp $MOD_LIVEPATCH +mods=(/sys/kernel/livepatch/*) +nmods=${#mods[@]} +if [ "$nmods" -ne 1 ]; then + die "Expecting only one moduled listed, found $nmods" +fi + +# These modules were disabled by the atomic replace +for mod in $MOD_LIVEPATCH3 $MOD_LIVEPATCH2 $MOD_LIVEPATCH1; do + unload_lp "$mod" +done grep 'live patched' /proc/cmdline > /dev/kmsg grep 'live patched' /proc/meminfo > /dev/kmsg @@ -135,13 +159,27 @@ unload_lp $MOD_REPLACE grep 'live patched' /proc/cmdline > /dev/kmsg grep 'live patched' /proc/meminfo > /dev/kmsg -check_result "% insmod test_modules/$MOD_LIVEPATCH.ko -livepatch: enabling patch '$MOD_LIVEPATCH' -livepatch: '$MOD_LIVEPATCH': initializing patching transition -livepatch: '$MOD_LIVEPATCH': starting patching transition -livepatch: '$MOD_LIVEPATCH': completing patching transition -livepatch: '$MOD_LIVEPATCH': patching complete -$MOD_LIVEPATCH: this has been live patched +check_result "% insmod test_modules/$MOD_LIVEPATCH1.ko +livepatch: enabling patch '$MOD_LIVEPATCH1' +livepatch: '$MOD_LIVEPATCH1': initializing patching transition +livepatch: '$MOD_LIVEPATCH1': starting patching transition +livepatch: '$MOD_LIVEPATCH1': completing patching transition +livepatch: '$MOD_LIVEPATCH1': patching complete +$MOD_LIVEPATCH1: this has been live patched +% insmod test_modules/$MOD_LIVEPATCH2.ko +livepatch: enabling patch '$MOD_LIVEPATCH2' +livepatch: '$MOD_LIVEPATCH2': initializing patching transition +livepatch: '$MOD_LIVEPATCH2': starting patching transition +livepatch: '$MOD_LIVEPATCH2': completing patching transition +livepatch: '$MOD_LIVEPATCH2': patching complete +% insmod test_modules/$MOD_LIVEPATCH3.ko +livepatch: enabling patch '$MOD_LIVEPATCH3' +livepatch: '$MOD_LIVEPATCH3': initializing patching transition +$MOD_LIVEPATCH3: pre_patch_callback: vmlinux +livepatch: '$MOD_LIVEPATCH3': starting patching transition +livepatch: '$MOD_LIVEPATCH3': completing patching transition +$MOD_LIVEPATCH3: post_patch_callback: vmlinux +livepatch: '$MOD_LIVEPATCH3': patching complete % insmod test_modules/$MOD_REPLACE.ko replace=1 livepatch: enabling patch '$MOD_REPLACE' livepatch: '$MOD_REPLACE': initializing patching transition @@ -149,7 +187,9 @@ livepatch: '$MOD_REPLACE': starting patching transition livepatch: '$MOD_REPLACE': completing patching transition livepatch: '$MOD_REPLACE': patching complete $MOD_REPLACE: this has been live patched -% rmmod $MOD_LIVEPATCH +% rmmod $MOD_LIVEPATCH3 +% rmmod $MOD_LIVEPATCH2 +% rmmod $MOD_LIVEPATCH1 $MOD_REPLACE: this has been live patched % echo 0 > /sys/kernel/livepatch/$MOD_REPLACE/enabled livepatch: '$MOD_REPLACE': initializing unpatching transition diff --git a/tools/testing/selftests/livepatch/test-syscall.sh b/tools/testing/selftests/livepatch/test-syscall.sh index b76a881d4013..289eb7d4c4b3 100755 --- a/tools/testing/selftests/livepatch/test-syscall.sh +++ b/tools/testing/selftests/livepatch/test-syscall.sh @@ -15,7 +15,10 @@ setup_config start_test "patch getpid syscall while being heavily hammered" -for i in $(seq 1 $(getconf _NPROCESSORS_ONLN)); do +NPROC=$(getconf _NPROCESSORS_ONLN) +MAXPROC=128 + +for i in $(seq 1 $(($NPROC < $MAXPROC ? $NPROC : $MAXPROC))); do ./test_klp-call_getpid & pids[$i]="$!" done diff --git a/tools/testing/selftests/livepatch/test-sysfs.sh b/tools/testing/selftests/livepatch/test-sysfs.sh index 6c646afa7395..05a14f5a7bfb 100755 --- a/tools/testing/selftests/livepatch/test-sysfs.sh +++ b/tools/testing/selftests/livepatch/test-sysfs.sh @@ -18,6 +18,7 @@ check_sysfs_rights "$MOD_LIVEPATCH" "" "drwxr-xr-x" check_sysfs_rights "$MOD_LIVEPATCH" "enabled" "-rw-r--r--" check_sysfs_value "$MOD_LIVEPATCH" "enabled" "1" check_sysfs_rights "$MOD_LIVEPATCH" "force" "--w-------" +check_sysfs_rights "$MOD_LIVEPATCH" "replace" "-r--r--r--" check_sysfs_rights "$MOD_LIVEPATCH" "transition" "-r--r--r--" check_sysfs_value "$MOD_LIVEPATCH" "transition" "0" check_sysfs_rights "$MOD_LIVEPATCH" "vmlinux/patched" "-r--r--r--" @@ -83,4 +84,51 @@ test_klp_callbacks_demo: post_unpatch_callback: vmlinux livepatch: 'test_klp_callbacks_demo': unpatching complete % rmmod test_klp_callbacks_demo" +start_test "sysfs test replace enabled" + +MOD_LIVEPATCH=test_klp_atomic_replace +load_lp $MOD_LIVEPATCH replace=1 + +check_sysfs_rights "$MOD_LIVEPATCH" "replace" "-r--r--r--" +check_sysfs_value "$MOD_LIVEPATCH" "replace" "1" + +disable_lp $MOD_LIVEPATCH +unload_lp $MOD_LIVEPATCH + +check_result "% insmod test_modules/$MOD_LIVEPATCH.ko replace=1 +livepatch: enabling patch '$MOD_LIVEPATCH' +livepatch: '$MOD_LIVEPATCH': initializing patching transition +livepatch: '$MOD_LIVEPATCH': starting patching transition +livepatch: '$MOD_LIVEPATCH': completing patching transition +livepatch: '$MOD_LIVEPATCH': patching complete +% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled +livepatch: '$MOD_LIVEPATCH': initializing unpatching transition +livepatch: '$MOD_LIVEPATCH': starting unpatching transition +livepatch: '$MOD_LIVEPATCH': completing unpatching transition +livepatch: '$MOD_LIVEPATCH': unpatching complete +% rmmod $MOD_LIVEPATCH" + +start_test "sysfs test replace disabled" + +load_lp $MOD_LIVEPATCH replace=0 + +check_sysfs_rights "$MOD_LIVEPATCH" "replace" "-r--r--r--" +check_sysfs_value "$MOD_LIVEPATCH" "replace" "0" + +disable_lp $MOD_LIVEPATCH +unload_lp $MOD_LIVEPATCH + +check_result "% insmod test_modules/$MOD_LIVEPATCH.ko replace=0 +livepatch: enabling patch '$MOD_LIVEPATCH' +livepatch: '$MOD_LIVEPATCH': initializing patching transition +livepatch: '$MOD_LIVEPATCH': starting patching transition +livepatch: '$MOD_LIVEPATCH': completing patching transition +livepatch: '$MOD_LIVEPATCH': patching complete +% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled +livepatch: '$MOD_LIVEPATCH': initializing unpatching transition +livepatch: '$MOD_LIVEPATCH': starting unpatching transition +livepatch: '$MOD_LIVEPATCH': completing unpatching transition +livepatch: '$MOD_LIVEPATCH': unpatching complete +% rmmod $MOD_LIVEPATCH" + exit 0 diff --git a/tools/tracing/latency/Makefile.config b/tools/tracing/latency/Makefile.config index b25e531a1f95..0fe6b50f029b 100644 --- a/tools/tracing/latency/Makefile.config +++ b/tools/tracing/latency/Makefile.config @@ -3,8 +3,9 @@ STOP_ERROR := define lib_setup - $(eval EXTLIBS += -l$(1)) $(eval LIB_INCLUDES += $(shell sh -c "$(PKG_CONFIG) --cflags lib$(1)")) + $(eval LDFLAGS += $(shell sh -c "$(PKG_CONFIG) --libs-only-L lib$(1)")) + $(eval EXTLIBS += $(shell sh -c "$(PKG_CONFIG) --libs-only-l lib$(1)")) endef $(call feature_check,libtraceevent) diff --git a/tools/tracing/rtla/Makefile.config b/tools/tracing/rtla/Makefile.config index 0b7ecfb30d19..5f8c286712d4 100644 --- a/tools/tracing/rtla/Makefile.config +++ b/tools/tracing/rtla/Makefile.config @@ -7,7 +7,8 @@ LIBTRACEFS_MIN_VERSION = 1.6 define lib_setup $(eval LIB_INCLUDES += $(shell sh -c "$(PKG_CONFIG) --cflags lib$(1)")) - $(eval EXTLIBS += -l$(1)) + $(eval LDFLAGS += $(shell sh -c "$(PKG_CONFIG) --libs-only-L lib$(1)")) + $(eval EXTLIBS += $(shell sh -c "$(PKG_CONFIG) --libs-only-l lib$(1)")) endef $(call feature_check,libtraceevent) diff --git a/tools/verification/rv/Makefile.config b/tools/verification/rv/Makefile.config index 6d4ba77847b6..066302230eb2 100644 --- a/tools/verification/rv/Makefile.config +++ b/tools/verification/rv/Makefile.config @@ -7,7 +7,8 @@ LIBTRACEFS_MIN_VERSION = 1.3 define lib_setup $(eval LIB_INCLUDES += $(shell sh -c "$(PKG_CONFIG) --cflags lib$(1)")) - $(eval EXTLIBS += -l$(1)) + $(eval LDFLAGS += $(shell sh -c "$(PKG_CONFIG) --libs-only-L lib$(1)")) + $(eval EXTLIBS += $(shell sh -c "$(PKG_CONFIG) --libs-only-l lib$(1)")) endef $(call feature_check,libtraceevent) diff --git a/usr/Makefile b/usr/Makefile index 132ef7e96e6d..f1779496bca7 100644 --- a/usr/Makefile +++ b/usr/Makefile @@ -62,8 +62,8 @@ $(deps_initramfs): ; quiet_cmd_initfs = GEN $@ cmd_initfs = \ $(CONFIG_SHELL) $< -o $@ -l $(obj)/.initramfs_data.cpio.d \ - $(if $(CONFIG_INITRAMFS_ROOT_UID), -u $(CONFIG_INITRAMFS_ROOT_UID)) \ - $(if $(CONFIG_INITRAMFS_ROOT_GID), -g $(CONFIG_INITRAMFS_ROOT_GID)) \ + $(addprefix -u , $(CONFIG_INITRAMFS_ROOT_UID)) \ + $(addprefix -g , $(CONFIG_INITRAMFS_ROOT_GID)) \ $(if $(KBUILD_BUILD_TIMESTAMP), -d "$(KBUILD_BUILD_TIMESTAMP)") \ $(ramfs-input) |